diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000..d1eb0b83b5f74 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +dist/ linguist-generated=true diff --git a/.gitignore b/.gitignore index 7d4f797920d9f..f9b1a4b5901c1 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ benchmark_outputs .checkstyle .mvn/timing.properties .editorconfig +node_modules diff --git a/.travis.yml b/.travis.yml index 9f75965ebe1d2..0f5a36d49fa37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: - TEST_FLAGS="" matrix: - MAVEN_CHECKS=true + - WEBUI_CHECKS=true - TEST_SPECIFIC_MODULES=presto-tests - TEST_SPECIFIC_MODULES=presto-tests TEST_FLAGS="-P ci-only" - TEST_SPECIFIC_MODULES=presto-raptor @@ -17,7 +18,8 @@ env: - TEST_SPECIFIC_MODULES=presto-cassandra - TEST_SPECIFIC_MODULES=presto-hive - TEST_SPECIFIC_MODULES=presto-main - - TEST_OTHER_MODULES=!presto-tests,!presto-raptor,!presto-accumulo,!presto-cassandra,!presto-hive,!presto-kudu,!presto-docs,!presto-server,!presto-server-rpm,!presto-main + - TEST_SPECIFIC_MODULES=presto-mongodb + - TEST_OTHER_MODULES=!presto-tests,!presto-raptor,!presto-accumulo,!presto-cassandra,!presto-hive,!presto-kudu,!presto-docs,!presto-server,!presto-server-rpm,!presto-main,!presto-mongodb - PRODUCT_TESTS_BASIC_ENVIRONMENT=true - PRODUCT_TESTS_SPECIFIC_ENVIRONMENT=true - PRODUCT_TESTS_SPECIFIC_ENVIRONMENT_2=true @@ -28,6 +30,7 @@ sudo: required dist: trusty cache: + yarn: true directories: - $HOME/.m2/repository @@ -78,6 +81,10 @@ script: if [[ -v MAVEN_CHECKS ]]; then ./mvnw install -DskipTests -B -T C1 -P ci fi + - | + if [[ -v WEBUI_CHECKS ]]; then + presto-main/bin/check_webui.sh + fi - | if [[ -v TEST_SPECIFIC_MODULES ]]; then ./mvnw test $MAVEN_SKIP_CHECKS_AND_DOCS -B -pl $TEST_SPECIFIC_MODULES $TEST_FLAGS @@ -89,12 +96,12 @@ script: - | if [[ -v PRODUCT_TESTS_BASIC_ENVIRONMENT ]]; then presto-product-tests/bin/run_on_docker.sh \ - multinode -x quarantine,big_query,storage_formats,profile_specific_tests,tpcds,cassandra,mysql_connector,postgresql_connector,mysql,kafka + multinode -x quarantine,big_query,storage_formats,profile_specific_tests,tpcds,cassandra,mysql_connector,postgresql_connector,mysql,kafka,avro fi - | if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT ]]; then presto-product-tests/bin/run_on_docker.sh \ - singlenode -g hdfs_no_impersonation + singlenode -g hdfs_no_impersonation,avro fi - | if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT ]]; then @@ -111,6 +118,11 @@ script: presto-product-tests/bin/run_on_docker.sh \ singlenode-kerberos-hdfs-impersonation -g storage_formats,cli,hdfs_impersonation,authorization,hive_file_header fi + - | + if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT ]]; then + presto-product-tests/bin/run_on_docker.sh \ + singlenode-kerberos-hdfs-impersonation-cross-realm -g storage_formats,cli,hdfs_impersonation + fi - | if [[ -v PRODUCT_TESTS_SPECIFIC_ENVIRONMENT_2 ]]; then presto-product-tests/bin/run_on_docker.sh \ @@ -174,7 +186,7 @@ script: if [[ -v HIVE_TESTS && -v HIVE_TESTS_AWS_ACCESS_KEY_ID ]]; then env AWS_ACCESS_KEY_ID=$HIVE_TESTS_AWS_ACCESS_KEY_ID \ AWS_SECRET_ACCESS_KEY=$HIVE_TESTS_AWS_SECRET_ACCESS_KEY \ - ./mvnw -pl presto-hive test -B -P test-hive-glue + ./mvnw test $MAVEN_SKIP_CHECKS_AND_DOCS -B -pl presto-hive -P test-hive-glue fi - | if [[ -v KUDU_TESTS ]]; then diff --git a/README.md b/README.md index 800bfddc21c0f..d6567d8821ec8 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ Presto is a distributed SQL query engine for big data. -See the [User Manual](https://prestodb.io/docs/current/) for deployment instructions and end user documentation. +See the [User Manual](https://prestodb.github.io/docs/current/) for deployment instructions and end user documentation. ## Requirements * Mac OS X or Linux -* Java 8 Update 92 or higher (8u92+), 64-bit. Both Oracle JDK and OpenJDK are supported. +* Java 8 Update 151 or higher (8u151+), 64-bit. Both Oracle JDK and OpenJDK are supported. * Maven 3.3.9+ (for building) * Python 2.4+ (for running with the launcher script) @@ -72,7 +72,7 @@ In the sample configuration, the Hive connector is mounted in the `hive` catalog SHOW TABLES FROM hive.default; -## Developers +## Code Style We recommend you use IntelliJ as your IDE. The code style template for the project can be found in the [codestyle](https://github.com/airlift/codestyle) repository along with our general programming and Java guidelines. In addition to those you should also adhere to the following: @@ -84,3 +84,19 @@ We recommend you use IntelliJ as your IDE. The code style template for the proje * Avoid using the ternary operator except for trivial expressions. * Use an assertion from Airlift's `Assertions` class if there is one that covers your case rather than writing the assertion by hand. Over time we may move over to more fluent assertions like AssertJ. * When writing a Git commit message, follow these [guidelines](https://chris.beams.io/posts/git-commit/). + +## Building the Web UI + +The Presto Web UI is composed of several React components and is written in JSX and ES6. This source code is compiled and packaged into browser-compatible Javascript, which is then checked in to the Presto source code (in the `dist` folder). You must have [Node.js](https://nodejs.org/en/download/) and [Yarn](https://yarnpkg.com/en/) installed to execute these commands. To update this folder after making changes, simply run: + + yarn --cwd presto-main/src/main/resources/webapp/src install + +If no Javascript dependencies have changed (i.e., no changes to `package.json`), it is faster to run: + + yarn --cwd presto-main/src/main/resources/webapp/src run package + +To simplify iteration, you can also run in `watch` mode, which automatically re-compiles when changes to source files are detected: + + yarn --cwd presto-main/src/main/resources/webapp/src run watch + +To iterate quickly, simply re-build the project in IntelliJ after packaging is complete. Project resources will be hot-reloaded and changes are reflected on browser refresh. diff --git a/pom.xml b/pom.xml index a8d38b2d3fa10..dab15afb75a07 100644 --- a/pom.xml +++ b/pom.xml @@ -5,12 +5,12 @@ io.airlift airbase - 83 + 88 com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT pom presto-root @@ -30,8 +30,8 @@ scm:git:git://github.com/facebook/presto.git https://github.com/facebook/presto - HEAD - + HEAD + ${project.basedir} @@ -40,23 +40,31 @@ true true - 1.8.0-60 + 1.8.0-151 3.3.9 - 4.6 - 0.172 + 4.7.1 + 0.178 ${dep.airlift.version} 0.36 - 1.11.293 + 1.11.445 3.9.0 - 3.0.0 - 1.12 - 1.49 + 3.4.0 + 1.14 + 2.10 + 1.50 6.10 3.8.0 - - - Asia/Katmandu + 1.2.3 + + + America/Bahia_Banderas methods 2 2g @@ -77,6 +85,7 @@ presto-blackhole presto-memory presto-orc + presto-parquet presto-rcfile presto-hive presto-hive-hadoop2 @@ -112,6 +121,7 @@ presto-plugin-toolkit presto-resource-group-managers presto-password-authenticators + presto-session-property-managers presto-benchto-benchmarks presto-thrift-connector-api presto-thrift-testing-server @@ -156,6 +166,12 @@ ${project.version} + + com.facebook.presto + presto-session-property-managers + ${project.version} + + com.facebook.presto presto-array @@ -180,6 +196,12 @@ ${project.version} + + com.facebook.presto + presto-parquet + ${project.version} + + com.facebook.presto presto-rcfile @@ -230,6 +252,12 @@ ${project.version} + + com.facebook.presto + presto-tpcds + ${project.version} + + com.facebook.presto presto-blackhole @@ -352,6 +380,12 @@ ${project.version} + + com.facebook.presto + presto-benchto-benchmarks + ${project.version} + + com.facebook.presto presto-product-tests @@ -367,19 +401,19 @@ com.facebook.presto.hadoop hadoop-apache2 - 2.7.4-3 + 2.7.4-5 com.facebook.presto.hive hive-apache - 1.2.0-2 + 1.2.2-2 com.facebook.presto.orc orc-protobuf - 6 + 7 @@ -417,7 +451,7 @@ io.airlift aircompressor - 0.11 + 0.13 @@ -549,7 +583,7 @@ io.airlift bytecode - 1.0 + 1.1 @@ -558,6 +592,12 @@ 2.1.5.1 + + io.airlift + joda-to-java-time-bridge + 3 + + io.airlift.drift drift-api @@ -588,12 +628,6 @@ ${dep.drift.version} - - io.airlift.drift - drift-transport-spi - ${dep.drift.version} - - io.airlift.drift drift-transport-netty @@ -621,13 +655,13 @@ org.ow2.asm asm - 6.1.1 + 6.2.1 com.h2database h2 - 1.4.196 + 1.4.197 @@ -681,7 +715,7 @@ org.postgresql postgresql - 42.1.4 + 42.2.5 @@ -699,21 +733,7 @@ com.microsoft.sqlserver mssql-jdbc - 6.1.0.jre8 - - - net.jcip - jcip-annotations - - - commons-logging - commons-logging - - - com.sun.jersey - jersey-core - - + 7.0.0.jre8 @@ -859,7 +879,7 @@ io.airlift testing-mysql-server - 5.7.19-3 + 5.7.22-1 @@ -890,10 +910,16 @@ 1.1.2.6 + + com.github.luben + zstd-jni + 1.3.5-4 + + org.apache.zookeeper zookeeper - 3.4.9 + 3.4.13 jline @@ -990,16 +1016,10 @@ 0.13.1-5 - - dnsjava - dnsjava - 2.1.7 - - com.esri.geometry esri-geometry-api - 2.2.0 + 2.2.2 com.fasterxml.jackson.core @@ -1142,35 +1162,17 @@ - com.ning.maven.plugins - maven-dependency-versions-check-plugin + org.apache.maven.plugins + maven-enforcer-plugin - - - org.javassist - javassist - 3.22.0-CR2 - 3.22.0-GA - - - org.javassist - javassist - 3.22.0-CR2 - 3.22.0-GA - - - com.fasterxml.jackson.core - jackson-core - 2.8.9 - ${dep.jackson.version} - - - com.fasterxml.jackson.core - jackson-databind - 2.8.9 - ${dep.jackson.version} - - + + + + + org.codehaus.plexus:plexus-utils + + + diff --git a/presto-accumulo/pom.xml b/presto-accumulo/pom.xml index 284c8b07b7a63..23d8cfaa8d98a 100644 --- a/presto-accumulo/pom.xml +++ b/presto-accumulo/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-accumulo @@ -245,6 +245,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + org.apache.zookeeper zookeeper diff --git a/presto-accumulo/src/main/java/com/facebook/presto/accumulo/AccumuloConnectorFactory.java b/presto-accumulo/src/main/java/com/facebook/presto/accumulo/AccumuloConnectorFactory.java index 3b8c68f85ad69..84feb97c84576 100644 --- a/presto-accumulo/src/main/java/com/facebook/presto/accumulo/AccumuloConnectorFactory.java +++ b/presto-accumulo/src/main/java/com/facebook/presto/accumulo/AccumuloConnectorFactory.java @@ -38,16 +38,16 @@ public String getName() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { - requireNonNull(connectorId, "connectorId is null"); + requireNonNull(catalogName, "catalogName is null"); requireNonNull(config, "requiredConfig is null"); requireNonNull(context, "context is null"); try { // A plugin is not required to use Guice; it is just very convenient // Unless you don't really know how to Guice, then it is less convenient - Bootstrap app = new Bootstrap(new JsonModule(), new AccumuloModule(connectorId, context.getTypeManager())); + Bootstrap app = new Bootstrap(new JsonModule(), new AccumuloModule(catalogName, context.getTypeManager())); Injector injector = app .strictConfig() .doNotInitializeLogging() diff --git a/presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java b/presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java index de92e7e00c0d5..2a7a6c5371ac7 100644 --- a/presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java +++ b/presto-accumulo/src/main/java/com/facebook/presto/accumulo/index/ColumnCardinalityCache.java @@ -39,7 +39,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.hadoop.io.Text; -import javax.annotation.Nonnull; import javax.annotation.PreDestroy; import javax.inject.Inject; @@ -334,7 +333,7 @@ private class CardinalityCacheLoader * @return The cardinality of the column, which would be zero if the value does not exist */ @Override - public Long load(@Nonnull CacheKey key) + public Long load(CacheKey key) throws Exception { LOG.debug("Loading a non-exact range from Accumulo: %s", key); @@ -360,7 +359,7 @@ public Long load(@Nonnull CacheKey key) } @Override - public Map loadAll(@Nonnull Iterable keys) + public Map loadAll(Iterable keys) throws Exception { int size = Iterables.size(keys); diff --git a/presto-array/pom.xml b/presto-array/pom.xml index d9cbda4ade034..715044b87b45b 100644 --- a/presto-array/pom.xml +++ b/presto-array/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-array diff --git a/presto-atop/pom.xml b/presto-atop/pom.xml index d77528c572d4f..138cb9f446975 100644 --- a/presto-atop/pom.xml +++ b/presto-atop/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-atop diff --git a/presto-atop/src/main/java/com/facebook/presto/atop/AtopConnectorFactory.java b/presto-atop/src/main/java/com/facebook/presto/atop/AtopConnectorFactory.java index e6273f806e391..1fa6323f4fed2 100644 --- a/presto-atop/src/main/java/com/facebook/presto/atop/AtopConnectorFactory.java +++ b/presto-atop/src/main/java/com/facebook/presto/atop/AtopConnectorFactory.java @@ -57,7 +57,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map requiredConfig, ConnectorContext context) + public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) { requireNonNull(requiredConfig, "requiredConfig is null"); @@ -68,7 +68,7 @@ public Connector create(String connectorId, Map requiredConfig, context.getTypeManager(), context.getNodeManager(), context.getNodeManager().getEnvironment(), - connectorId), + catalogName), installModuleIf( AtopConnectorConfig.class, config -> config.getSecurity().equalsIgnoreCase(SECURITY_NONE), diff --git a/presto-base-jdbc/pom.xml b/presto-base-jdbc/pom.xml index eab6027011509..4f3bd39efae92 100644 --- a/presto-base-jdbc/pom.xml +++ b/presto-base-jdbc/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-base-jdbc @@ -68,6 +68,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.inject javax.inject diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/DriverConnectionFactory.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/DriverConnectionFactory.java index 6d6467ffb3129..e55ff263d5c8f 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/DriverConnectionFactory.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/DriverConnectionFactory.java @@ -18,6 +18,7 @@ import java.sql.SQLException; import java.util.Properties; +import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class DriverConnectionFactory @@ -56,6 +57,8 @@ public DriverConnectionFactory(Driver driver, String connectionUrl, Properties c public Connection openConnection() throws SQLException { - return driver.connect(connectionUrl, connectionProperties); + Connection connection = driver.connect(connectionUrl, connectionProperties); + checkState(connection != null, "Driver returned null connection"); + return connection; } } diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnector.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnector.java index 5b079b031c84b..a8efd6b2c49c5 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnector.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnector.java @@ -14,6 +14,7 @@ package com.facebook.presto.plugin.jdbc; import com.facebook.presto.spi.connector.Connector; +import com.facebook.presto.spi.connector.ConnectorAccessControl; import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorPageSinkProvider; import com.facebook.presto.spi.connector.ConnectorRecordSetProvider; @@ -25,6 +26,7 @@ import javax.inject.Inject; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -43,6 +45,7 @@ public class JdbcConnector private final JdbcSplitManager jdbcSplitManager; private final JdbcRecordSetProvider jdbcRecordSetProvider; private final JdbcPageSinkProvider jdbcPageSinkProvider; + private final Optional accessControl; private final ConcurrentMap transactions = new ConcurrentHashMap<>(); @@ -52,13 +55,15 @@ public JdbcConnector( JdbcMetadataFactory jdbcMetadataFactory, JdbcSplitManager jdbcSplitManager, JdbcRecordSetProvider jdbcRecordSetProvider, - JdbcPageSinkProvider jdbcPageSinkProvider) + JdbcPageSinkProvider jdbcPageSinkProvider, + Optional accessControl) { this.lifeCycleManager = requireNonNull(lifeCycleManager, "lifeCycleManager is null"); this.jdbcMetadataFactory = requireNonNull(jdbcMetadataFactory, "jdbcMetadataFactory is null"); this.jdbcSplitManager = requireNonNull(jdbcSplitManager, "jdbcSplitManager is null"); this.jdbcRecordSetProvider = requireNonNull(jdbcRecordSetProvider, "jdbcRecordSetProvider is null"); this.jdbcPageSinkProvider = requireNonNull(jdbcPageSinkProvider, "jdbcPageSinkProvider is null"); + this.accessControl = requireNonNull(accessControl, "accessControl is null"); } @Override @@ -116,6 +121,12 @@ public ConnectorPageSinkProvider getPageSinkProvider() return jdbcPageSinkProvider; } + @Override + public ConnectorAccessControl getAccessControl() + { + return accessControl.orElseThrow(UnsupportedOperationException::new); + } + @Override public final void shutdown() { diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnectorFactory.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnectorFactory.java index 8c5bf872bfa86..59072f7d1189b 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnectorFactory.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcConnectorFactory.java @@ -57,12 +57,12 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map requiredConfig, ConnectorContext context) + public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) { requireNonNull(requiredConfig, "requiredConfig is null"); try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { - Bootstrap app = new Bootstrap(new JdbcModule(connectorId), module); + Bootstrap app = new Bootstrap(new JdbcModule(catalogName), module); Injector injector = app .strictConfig() diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadata.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadata.java index a957043383aef..1641ec459590d 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadata.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcMetadata.java @@ -200,7 +200,9 @@ public void rollback() @Override public ConnectorInsertTableHandle beginInsert(ConnectorSession session, ConnectorTableHandle tableHandle) { - return jdbcClient.beginInsertTable(getTableMetadata(session, tableHandle)); + JdbcOutputTableHandle handle = jdbcClient.beginInsertTable(getTableMetadata(session, tableHandle)); + setRollback(() -> jdbcClient.rollbackCreateTable(handle)); + return handle; } @Override diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcModule.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcModule.java index 55e590933c63e..7071318dac063 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcModule.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/JdbcModule.java @@ -13,10 +13,12 @@ */ package com.facebook.presto.plugin.jdbc; +import com.facebook.presto.spi.connector.ConnectorAccessControl; import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; +import static com.google.inject.multibindings.OptionalBinder.newOptionalBinder; import static io.airlift.configuration.ConfigBinder.configBinder; import static java.util.Objects.requireNonNull; @@ -33,6 +35,7 @@ public JdbcModule(String connectorId) @Override public void configure(Binder binder) { + newOptionalBinder(binder, ConnectorAccessControl.class); binder.bind(JdbcConnectorId.class).toInstance(new JdbcConnectorId(connectorId)); binder.bind(JdbcMetadataFactory.class).in(Scopes.SINGLETON); binder.bind(JdbcSplitManager.class).in(Scopes.SINGLETON); diff --git a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/QueryBuilder.java b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/QueryBuilder.java index 3913d3a8c0f10..b035fd46ab1b2 100644 --- a/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/QueryBuilder.java +++ b/presto-base-jdbc/src/main/java/com/facebook/presto/plugin/jdbc/QueryBuilder.java @@ -19,6 +19,7 @@ import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.BigintType; import com.facebook.presto.spi.type.BooleanType; +import com.facebook.presto.spi.type.CharType; import com.facebook.presto.spi.type.DateType; import com.facebook.presto.spi.type.DoubleType; import com.facebook.presto.spi.type.IntegerType; @@ -169,6 +170,9 @@ else if (typeAndValue.getType().equals(TimestampWithTimeZoneType.TIMESTAMP_WITH_ else if (typeAndValue.getType() instanceof VarcharType) { statement.setString(i + 1, ((Slice) typeAndValue.getValue()).toStringUtf8()); } + else if (typeAndValue.getType() instanceof CharType) { + statement.setString(i + 1, ((Slice) typeAndValue.getValue()).toStringUtf8()); + } else { throw new UnsupportedOperationException("Can't handle type: " + typeAndValue.getType()); } @@ -192,7 +196,8 @@ private static boolean isAcceptedType(Type type) validType.equals(TimeWithTimeZoneType.TIME_WITH_TIME_ZONE) || validType.equals(TimestampType.TIMESTAMP) || validType.equals(TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE) || - validType instanceof VarcharType; + validType instanceof VarcharType || + validType instanceof CharType; } private List toConjuncts(List columns, TupleDomain tupleDomain, List accumulator) diff --git a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcMetadata.java b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcMetadata.java index b95f5466b06ff..4ba63d9589efb 100644 --- a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcMetadata.java +++ b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcMetadata.java @@ -179,9 +179,11 @@ public void getColumnMetadata() @Test(expectedExceptions = PrestoException.class) public void testCreateTable() { - metadata.createTable(SESSION, new ConnectorTableMetadata( - new SchemaTableName("example", "foo"), - ImmutableList.of(new ColumnMetadata("text", VARCHAR))), + metadata.createTable( + SESSION, + new ConnectorTableMetadata( + new SchemaTableName("example", "foo"), + ImmutableList.of(new ColumnMetadata("text", VARCHAR))), false); } diff --git a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcQueryBuilder.java b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcQueryBuilder.java index 4e1139daf8027..c3106e01270d3 100644 --- a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcQueryBuilder.java +++ b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestJdbcQueryBuilder.java @@ -18,6 +18,7 @@ import com.facebook.presto.spi.predicate.Range; import com.facebook.presto.spi.predicate.SortedRangeSet; import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.CharType; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -39,6 +40,7 @@ import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_BIGINT; import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_BOOLEAN; +import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_CHAR; import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_DATE; import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_DOUBLE; import static com.facebook.presto.plugin.jdbc.TestingJdbcTypeHandle.JDBC_INTEGER; @@ -80,6 +82,7 @@ public void setup() { database = new TestingDatabase(); jdbcClient = database.getJdbcClient(); + CharType charType = CharType.createCharType(0); columns = ImmutableList.of( new JdbcColumnHandle("test_id", "col_0", JDBC_BIGINT, BIGINT), @@ -92,7 +95,8 @@ public void setup() new JdbcColumnHandle("test_id", "col_7", JDBC_TINYINT, TINYINT), new JdbcColumnHandle("test_id", "col_8", JDBC_SMALLINT, SMALLINT), new JdbcColumnHandle("test_id", "col_9", JDBC_INTEGER, INTEGER), - new JdbcColumnHandle("test_id", "col_10", JDBC_REAL, REAL)); + new JdbcColumnHandle("test_id", "col_10", JDBC_REAL, REAL), + new JdbcColumnHandle("test_id", "col_11", JDBC_CHAR, charType)); Connection connection = database.getConnection(); try (PreparedStatement preparedStatement = connection.prepareStatement("create table \"test_table\" (" + "" + @@ -106,7 +110,8 @@ public void setup() "\"col_7\" TINYINT, " + "\"col_8\" SMALLINT, " + "\"col_9\" INTEGER, " + - "\"col_10\" REAL " + + "\"col_10\" REAL, " + + "\"col_11\" CHAR(128) " + ")")) { preparedStatement.execute(); StringBuilder stringBuilder = new StringBuilder("insert into \"test_table\" values "); @@ -115,7 +120,7 @@ public void setup() for (int i = 0; i < len; i++) { stringBuilder.append(format( Locale.ENGLISH, - "(%d, %f, %b, 'test_str_%d', '%s', '%s', '%s', %d, %d, %d, %f)", + "(%d, %f, %b, 'test_str_%d', '%s', '%s', '%s', %d, %d, %d, %f, 'test_str_%d')", i, 200000.0 + i / 2.0, i % 2 == 0, @@ -126,7 +131,8 @@ public void setup() i % 128, -i, i - 100, - 100.0f + i)); + 100.0f + i, + i)); dateTime = dateTime.plusHours(26); if (i != len - 1) { stringBuilder.append(","); @@ -227,7 +233,7 @@ public void testBuildSqlWithFloat() } @Test - public void testBuildSqlWithString() + public void testBuildSqlWithVarchar() throws SQLException { TupleDomain tupleDomain = TupleDomain.withColumnDomains(ImmutableMap.of( @@ -253,6 +259,34 @@ public void testBuildSqlWithString() } } + @Test + public void testBuildSqlWithChar() + throws SQLException + { + CharType charType = CharType.createCharType(0); + TupleDomain tupleDomain = TupleDomain.withColumnDomains(ImmutableMap.of( + columns.get(11), Domain.create(SortedRangeSet.copyOf(charType, + ImmutableList.of( + Range.range(charType, utf8Slice("test_str_700"), true, utf8Slice("test_str_702"), false), + Range.equal(charType, utf8Slice("test_str_180")), + Range.equal(charType, utf8Slice("test_str_196")))), + false))); + + Connection connection = database.getConnection(); + try (PreparedStatement preparedStatement = new QueryBuilder("\"").buildSql(jdbcClient, connection, "", "", "test_table", columns, tupleDomain); + ResultSet resultSet = preparedStatement.executeQuery()) { + ImmutableSet.Builder builder = ImmutableSet.builder(); + while (resultSet.next()) { + builder.add((String) resultSet.getObject("col_11")); + } + assertEquals(builder.build(), ImmutableSet.of("test_str_700", "test_str_701", "test_str_180", "test_str_196")); + + assertContains(preparedStatement.toString(), "\"col_11\" >= ?"); + assertContains(preparedStatement.toString(), "\"col_11\" < ?"); + assertContains(preparedStatement.toString(), "\"col_11\" IN (?,?)"); + } + } + @Test public void testBuildSqlWithDateTime() throws SQLException diff --git a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestingJdbcTypeHandle.java b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestingJdbcTypeHandle.java index 4f6cd7cfdc5fe..55b8b377beb62 100644 --- a/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestingJdbcTypeHandle.java +++ b/presto-base-jdbc/src/test/java/com/facebook/presto/plugin/jdbc/TestingJdbcTypeHandle.java @@ -29,6 +29,7 @@ private TestingJdbcTypeHandle() {} public static final JdbcTypeHandle JDBC_REAL = new JdbcTypeHandle(Types.REAL, 8, 0); public static final JdbcTypeHandle JDBC_DOUBLE = new JdbcTypeHandle(Types.DOUBLE, 8, 0); + public static final JdbcTypeHandle JDBC_CHAR = new JdbcTypeHandle(Types.CHAR, 10, 0); public static final JdbcTypeHandle JDBC_VARCHAR = new JdbcTypeHandle(Types.VARCHAR, 10, 0); public static final JdbcTypeHandle JDBC_DATE = new JdbcTypeHandle(Types.DATE, 8, 0); diff --git a/presto-benchmark-driver/pom.xml b/presto-benchmark-driver/pom.xml index 11bc3bd5efdc1..555f0cdbe5739 100644 --- a/presto-benchmark-driver/pom.xml +++ b/presto-benchmark-driver/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-benchmark-driver diff --git a/presto-benchmark-driver/src/main/java/com/facebook/presto/benchmark/driver/BenchmarkQuery.java b/presto-benchmark-driver/src/main/java/com/facebook/presto/benchmark/driver/BenchmarkQuery.java index 07512975dab44..d20077e0c517e 100644 --- a/presto-benchmark-driver/src/main/java/com/facebook/presto/benchmark/driver/BenchmarkQuery.java +++ b/presto-benchmark-driver/src/main/java/com/facebook/presto/benchmark/driver/BenchmarkQuery.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.benchmark.driver; -import com.google.common.base.Function; import com.google.common.base.Splitter; import com.google.common.base.Splitter.MapSplitter; import com.google.common.collect.ImmutableMap; @@ -59,11 +58,6 @@ public BenchmarkQuery(File file) } } - public static Function queryNameGetter() - { - return query -> query.getName(); - } - public String getName() { return name; diff --git a/presto-benchmark/pom.xml b/presto-benchmark/pom.xml index 5d97a57f80aa7..795d9de132477 100644 --- a/presto-benchmark/pom.xml +++ b/presto-benchmark/pom.xml @@ -5,7 +5,7 @@ presto-root com.facebook.presto - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-benchmark diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/AbstractSimpleOperatorBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/AbstractSimpleOperatorBenchmark.java index 22902d4e5aa9f..4daed156ea1e5 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/AbstractSimpleOperatorBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/AbstractSimpleOperatorBenchmark.java @@ -61,7 +61,7 @@ protected DriverFactory createDriverFactory() protected List createDrivers(TaskContext taskContext) { DriverFactory driverFactory = createDriverFactory(); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); Driver driver = driverFactory.createDriver(driverContext); return ImmutableList.of(driver); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/CountAggregationBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/CountAggregationBenchmark.java index 4ffdf88b94f33..6229a2b137edc 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/CountAggregationBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/CountAggregationBenchmark.java @@ -43,7 +43,7 @@ protected List createOperatorFactories() OperatorFactory tableScanOperator = createTableScanOperator(0, new PlanNodeId("test"), "orders", "orderkey"); InternalAggregationFunction countFunction = localQueryRunner.getMetadata().getFunctionRegistry().getAggregateFunctionImplementation( new Signature("count", AGGREGATE, BIGINT.getTypeSignature())); - AggregationOperatorFactory aggregationOperator = new AggregationOperatorFactory(1, new PlanNodeId("test"), Step.SINGLE, ImmutableList.of(countFunction.bind(ImmutableList.of(0), Optional.empty()))); + AggregationOperatorFactory aggregationOperator = new AggregationOperatorFactory(1, new PlanNodeId("test"), Step.SINGLE, ImmutableList.of(countFunction.bind(ImmutableList.of(0), Optional.empty())), false); return ImmutableList.of(tableScanOperator, aggregationOperator); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/DoubleSumAggregationBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/DoubleSumAggregationBenchmark.java index 0819859b671ca..2bc1284c539df 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/DoubleSumAggregationBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/DoubleSumAggregationBenchmark.java @@ -44,7 +44,7 @@ protected List createOperatorFactories() OperatorFactory tableScanOperator = createTableScanOperator(0, new PlanNodeId("test"), "orders", "totalprice"); InternalAggregationFunction doubleSum = MetadataManager.createTestMetadataManager().getFunctionRegistry().getAggregateFunctionImplementation( new Signature("sum", AGGREGATE, DOUBLE.getTypeSignature(), DOUBLE.getTypeSignature())); - AggregationOperatorFactory aggregationOperator = new AggregationOperatorFactory(1, new PlanNodeId("test"), Step.SINGLE, ImmutableList.of(doubleSum.bind(ImmutableList.of(0), Optional.empty()))); + AggregationOperatorFactory aggregationOperator = new AggregationOperatorFactory(1, new PlanNodeId("test"), Step.SINGLE, ImmutableList.of(doubleSum.bind(ImmutableList.of(0), Optional.empty())), false); return ImmutableList.of(tableScanOperator, aggregationOperator); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery1.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery1.java index 2d5dee1e441c2..6b3b0029ca86d 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery1.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery1.java @@ -124,8 +124,9 @@ protected List createOperatorFactories() Optional.empty(), Optional.empty(), 10_000, - new DataSize(16, MEGABYTE), - JOIN_COMPILER); + Optional.of(new DataSize(16, MEGABYTE)), + JOIN_COMPILER, + false); return ImmutableList.of(tableScanOperator, tpchQuery1Operator, aggregationOperator); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery6.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery6.java index b897ea89f03f7..867482b6c0b2c 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery6.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HandTpchQuery6.java @@ -86,7 +86,8 @@ protected List createOperatorFactories() new PlanNodeId("test"), Step.SINGLE, ImmutableList.of( - doubleSum.bind(ImmutableList.of(0), Optional.empty()))); + doubleSum.bind(ImmutableList.of(0), Optional.empty())), + false); return ImmutableList.of(tableScanOperator, tpchQuery6Operator, aggregationOperator); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashAggregationBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashAggregationBenchmark.java index 67eb792fb6047..cd68617c2c1b3 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashAggregationBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashAggregationBenchmark.java @@ -62,8 +62,9 @@ protected List createOperatorFactories() Optional.empty(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), - JOIN_COMPILER); + Optional.of(new DataSize(16, MEGABYTE)), + JOIN_COMPILER, + false); return ImmutableList.of(tableScanOperator, aggregationOperator); } diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildAndJoinBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildAndJoinBenchmark.java index 1578ec342f509..ddb468187e4bd 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildAndJoinBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildAndJoinBenchmark.java @@ -18,9 +18,8 @@ import com.facebook.presto.operator.Driver; import com.facebook.presto.operator.DriverFactory; import com.facebook.presto.operator.HashBuilderOperator.HashBuilderOperatorFactory; -import com.facebook.presto.operator.JoinBridgeDataManager; +import com.facebook.presto.operator.JoinBridgeManager; import com.facebook.presto.operator.LookupJoinOperators; -import com.facebook.presto.operator.LookupSourceFactory; import com.facebook.presto.operator.OperatorFactory; import com.facebook.presto.operator.PagesIndex; import com.facebook.presto.operator.PartitionedLookupSourceFactory; @@ -89,7 +88,7 @@ protected List createDrivers(TaskContext taskContext) } // hash build - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( + JoinBridgeManager lookupSourceFactoryManager = JoinBridgeManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( sourceTypes, ImmutableList.of(0, 1).stream() .map(sourceTypes::get) @@ -116,8 +115,6 @@ protected List createDrivers(TaskContext taskContext) SingleStreamSpillerFactory.unsupportedSingleStreamSpillerFactory()); driversBuilder.add(hashBuilder); DriverFactory hashBuildDriverFactory = new DriverFactory(0, true, false, driversBuilder.build(), OptionalInt.empty(), UNGROUPED_EXECUTION); - Driver hashBuildDriver = hashBuildDriverFactory.createDriver(taskContext.addPipelineContext(0, true, false).addDriverContext()); - hashBuildDriverFactory.noMoreDrivers(); // join ImmutableList.Builder joinDriversBuilder = ImmutableList.builder(); @@ -146,7 +143,10 @@ protected List createDrivers(TaskContext taskContext) joinDriversBuilder.add(joinOperator); joinDriversBuilder.add(new NullOutputOperatorFactory(3, new PlanNodeId("test"))); DriverFactory joinDriverFactory = new DriverFactory(1, true, true, joinDriversBuilder.build(), OptionalInt.empty(), UNGROUPED_EXECUTION); - Driver joinDriver = joinDriverFactory.createDriver(taskContext.addPipelineContext(1, true, true).addDriverContext()); + + Driver hashBuildDriver = hashBuildDriverFactory.createDriver(taskContext.addPipelineContext(0, true, false, false).addDriverContext()); + hashBuildDriverFactory.noMoreDrivers(); + Driver joinDriver = joinDriverFactory.createDriver(taskContext.addPipelineContext(1, true, true, false).addDriverContext()); joinDriverFactory.noMoreDrivers(); return ImmutableList.of(hashBuildDriver, joinDriver); diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildBenchmark.java index 1c2f62306e92b..683add1f02d6e 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashBuildBenchmark.java @@ -16,9 +16,8 @@ import com.facebook.presto.operator.Driver; import com.facebook.presto.operator.DriverFactory; import com.facebook.presto.operator.HashBuilderOperator.HashBuilderOperatorFactory; -import com.facebook.presto.operator.JoinBridgeDataManager; +import com.facebook.presto.operator.JoinBridgeManager; import com.facebook.presto.operator.LookupJoinOperators; -import com.facebook.presto.operator.LookupSourceFactory; import com.facebook.presto.operator.OperatorFactory; import com.facebook.presto.operator.PagesIndex; import com.facebook.presto.operator.PartitionedLookupSourceFactory; @@ -60,7 +59,7 @@ protected List createDrivers(TaskContext taskContext) // hash build List ordersTypes = getColumnTypes("orders", "orderkey", "totalprice"); OperatorFactory ordersTableScan = createTableScanOperator(0, new PlanNodeId("test"), "orders", "orderkey", "totalprice"); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( + JoinBridgeManager lookupSourceFactoryManager = JoinBridgeManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( ordersTypes, ImmutableList.of(0, 1).stream() .map(ordersTypes::get) @@ -86,8 +85,6 @@ protected List createDrivers(TaskContext taskContext) false, SingleStreamSpillerFactory.unsupportedSingleStreamSpillerFactory()); DriverFactory hashBuildDriverFactory = new DriverFactory(0, true, true, ImmutableList.of(ordersTableScan, hashBuilder), OptionalInt.empty(), UNGROUPED_EXECUTION); - Driver hashBuildDriver = hashBuildDriverFactory.createDriver(taskContext.addPipelineContext(0, true, true).addDriverContext()); - hashBuildDriverFactory.noMoreDrivers(); // empty join so build finishes ImmutableList.Builder joinDriversBuilder = ImmutableList.builder(); @@ -105,7 +102,10 @@ protected List createDrivers(TaskContext taskContext) joinDriversBuilder.add(joinOperator); joinDriversBuilder.add(new NullOutputOperatorFactory(3, new PlanNodeId("test"))); DriverFactory joinDriverFactory = new DriverFactory(1, true, true, joinDriversBuilder.build(), OptionalInt.empty(), UNGROUPED_EXECUTION); - Driver joinDriver = joinDriverFactory.createDriver(taskContext.addPipelineContext(1, true, true).addDriverContext()); + + Driver hashBuildDriver = hashBuildDriverFactory.createDriver(taskContext.addPipelineContext(0, true, true, false).addDriverContext()); + hashBuildDriverFactory.noMoreDrivers(); + Driver joinDriver = joinDriverFactory.createDriver(taskContext.addPipelineContext(1, true, true, false).addDriverContext()); joinDriverFactory.noMoreDrivers(); return ImmutableList.of(hashBuildDriver, joinDriver); diff --git a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashJoinBenchmark.java b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashJoinBenchmark.java index b53cee48d156f..638bcd06772a7 100644 --- a/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashJoinBenchmark.java +++ b/presto-benchmark/src/main/java/com/facebook/presto/benchmark/HashJoinBenchmark.java @@ -18,9 +18,8 @@ import com.facebook.presto.operator.DriverContext; import com.facebook.presto.operator.DriverFactory; import com.facebook.presto.operator.HashBuilderOperator.HashBuilderOperatorFactory; -import com.facebook.presto.operator.JoinBridgeDataManager; +import com.facebook.presto.operator.JoinBridgeManager; import com.facebook.presto.operator.LookupJoinOperators; -import com.facebook.presto.operator.LookupSourceFactory; import com.facebook.presto.operator.LookupSourceProvider; import com.facebook.presto.operator.OperatorFactory; import com.facebook.presto.operator.PagesIndex; @@ -51,7 +50,7 @@ public class HashJoinBenchmark extends AbstractOperatorBenchmark { private static final LookupJoinOperators LOOKUP_JOIN_OPERATORS = new LookupJoinOperators(); - private JoinBridgeDataManager lookupSourceFactoryManager; + private DriverFactory probeDriverFactory; public HashJoinBenchmark(LocalQueryRunner localQueryRunner) { @@ -66,10 +65,10 @@ from lineitem join orders using (orderkey) @Override protected List createDrivers(TaskContext taskContext) { - if (lookupSourceFactoryManager == null) { + if (probeDriverFactory == null) { List ordersTypes = getColumnTypes("orders", "orderkey", "totalprice"); OperatorFactory ordersTableScan = createTableScanOperator(0, new PlanNodeId("test"), "orders", "orderkey", "totalprice"); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( + JoinBridgeManager lookupSourceFactoryManager = JoinBridgeManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( ordersTypes, ImmutableList.of(0, 1).stream() .map(ordersTypes::get) @@ -95,27 +94,25 @@ protected List createDrivers(TaskContext taskContext) false, SingleStreamSpillerFactory.unsupportedSingleStreamSpillerFactory()); - DriverContext driverContext = taskContext.addPipelineContext(0, false, false).addDriverContext(); - Driver driver = new DriverFactory(0, false, false, ImmutableList.of(ordersTableScan, hashBuilder), OptionalInt.empty(), UNGROUPED_EXECUTION) - .createDriver(driverContext); - Future lookupSourceProvider = lookupSourceFactoryManager.forLifespan(Lifespan.taskWide()).createLookupSourceProvider(); + DriverContext driverContext = taskContext.addPipelineContext(0, false, false, false).addDriverContext(); + DriverFactory buildDriverFactory = new DriverFactory(0, false, false, ImmutableList.of(ordersTableScan, hashBuilder), OptionalInt.empty(), UNGROUPED_EXECUTION); + + List lineItemTypes = getColumnTypes("lineitem", "orderkey", "quantity"); + OperatorFactory lineItemTableScan = createTableScanOperator(0, new PlanNodeId("test"), "lineitem", "orderkey", "quantity"); + OperatorFactory joinOperator = LOOKUP_JOIN_OPERATORS.innerJoin(1, new PlanNodeId("test"), lookupSourceFactoryManager, lineItemTypes, Ints.asList(0), OptionalInt.empty(), Optional.empty(), OptionalInt.empty(), unsupportedPartitioningSpillerFactory()); + NullOutputOperatorFactory output = new NullOutputOperatorFactory(2, new PlanNodeId("test")); + this.probeDriverFactory = new DriverFactory(1, true, true, ImmutableList.of(lineItemTableScan, joinOperator, output), OptionalInt.empty(), UNGROUPED_EXECUTION); + + Driver driver = buildDriverFactory.createDriver(driverContext); + Future lookupSourceProvider = lookupSourceFactoryManager.getJoinBridge(Lifespan.taskWide()).createLookupSourceProvider(); while (!lookupSourceProvider.isDone()) { driver.process(); } getFutureValue(lookupSourceProvider).close(); - this.lookupSourceFactoryManager = lookupSourceFactoryManager; } - List lineItemTypes = getColumnTypes("lineitem", "orderkey", "quantity"); - OperatorFactory lineItemTableScan = createTableScanOperator(0, new PlanNodeId("test"), "lineitem", "orderkey", "quantity"); - - OperatorFactory joinOperator = LOOKUP_JOIN_OPERATORS.innerJoin(1, new PlanNodeId("test"), lookupSourceFactoryManager, lineItemTypes, Ints.asList(0), OptionalInt.empty(), Optional.empty(), OptionalInt.empty(), unsupportedPartitioningSpillerFactory()); - - NullOutputOperatorFactory output = new NullOutputOperatorFactory(2, new PlanNodeId("test")); - - DriverFactory driverFactory = new DriverFactory(1, true, true, ImmutableList.of(lineItemTableScan, joinOperator, output), OptionalInt.empty(), UNGROUPED_EXECUTION); - DriverContext driverContext = taskContext.addPipelineContext(1, true, true).addDriverContext(); - Driver driver = driverFactory.createDriver(driverContext); + DriverContext driverContext = taskContext.addPipelineContext(1, true, true, false).addDriverContext(); + Driver driver = probeDriverFactory.createDriver(driverContext); return ImmutableList.of(driver); } diff --git a/presto-benchmark/src/test/java/com/facebook/presto/benchmark/BenchmarkCPUCounters.java b/presto-benchmark/src/test/java/com/facebook/presto/benchmark/BenchmarkCPUCounters.java new file mode 100644 index 0000000000000..8261f58bde416 --- /dev/null +++ b/presto-benchmark/src/test/java/com/facebook/presto/benchmark/BenchmarkCPUCounters.java @@ -0,0 +1,99 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.benchmark; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.TimeUnit; + +import static java.lang.Integer.getInteger; + +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(2) +@Warmup(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS) +@BenchmarkMode(Mode.AverageTime) +public class BenchmarkCPUCounters +{ + private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); + + private static final int ITERATIONS = 1000; + + @Benchmark + @OperationsPerInvocation(ITERATIONS) + public void nanoTime(Blackhole blackhole) + { + for (int i = 0; i < ITERATIONS; i++) { + blackhole.consume(System.nanoTime()); + } + } + + @Benchmark + @OperationsPerInvocation(ITERATIONS) + public void cpuTime(Blackhole blackhole) + { + for (int i = 0; i < ITERATIONS; i++) { + blackhole.consume(currentThreadCpuTime()); + } + } + + @Benchmark + @OperationsPerInvocation(ITERATIONS) + public void userTime(Blackhole blackhole) + { + for (int i = 0; i < ITERATIONS; i++) { + blackhole.consume(currentThreadUserTime()); + } + } + + private static long currentThreadCpuTime() + { + return THREAD_MX_BEAN.getCurrentThreadCpuTime(); + } + + private static long currentThreadUserTime() + { + return THREAD_MX_BEAN.getCurrentThreadUserTime(); + } + + public static void main(String[] args) + throws RunnerException + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .threads(getInteger("threads", 1)) + .include(".*" + BenchmarkCPUCounters.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-benchto-benchmarks/pom.xml b/presto-benchto-benchmarks/pom.xml index 2306e3c90cb16..03921b5274d55 100644 --- a/presto-benchto-benchmarks/pom.xml +++ b/presto-benchto-benchmarks/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-benchto-benchmarks @@ -12,7 +12,6 @@ ${project.parent.basedir} - 1.1.3 false false @@ -28,6 +27,48 @@ benchto-driver 0.4 + + + com.facebook.presto + presto-main + test-jar + test + + + + com.facebook.presto + presto-main + test + + + + com.facebook.presto + presto-tpch + test + + + + com.facebook.presto + presto-tpcds + test + + + + io.airlift + testing + test + + + + org.testng + testng + test + + + + com.google.guava + guava + diff --git a/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/AbstractCostBasedPlanTest.java b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/AbstractCostBasedPlanTest.java new file mode 100644 index 0000000000000..bb746df9a3de3 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/AbstractCostBasedPlanTest.java @@ -0,0 +1,244 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.sql.planner; + +import com.facebook.presto.spi.ConnectorTableHandle; +import com.facebook.presto.sql.planner.assertions.BasePlanTest; +import com.facebook.presto.sql.planner.plan.AggregationNode; +import com.facebook.presto.sql.planner.plan.ExchangeNode; +import com.facebook.presto.sql.planner.plan.JoinNode; +import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.TableScanNode; +import com.facebook.presto.sql.planner.plan.ValuesNode; +import com.facebook.presto.tpcds.TpcdsTableHandle; +import com.facebook.presto.tpch.TpchTableHandle; +import com.google.common.base.Strings; +import com.google.common.base.VerifyException; +import com.google.common.io.Resources; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; + +import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED; +import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; +import static com.facebook.presto.testing.TestngUtils.toDataProvider; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static com.google.common.io.Files.createParentDirs; +import static com.google.common.io.Files.write; +import static com.google.common.io.Resources.getResource; +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.Files.isDirectory; +import static java.util.Locale.ENGLISH; +import static java.util.stream.Collectors.joining; +import static org.testng.Assert.assertEquals; + +public abstract class AbstractCostBasedPlanTest + extends BasePlanTest +{ + public AbstractCostBasedPlanTest(LocalQueryRunnerSupplier supplier) + { + super(supplier); + } + + protected abstract Stream getQueryResourcePaths(); + + @DataProvider + public Object[][] getQueriesDataProvider() + { + return getQueryResourcePaths() + .collect(toDataProvider()); + } + + @Test(dataProvider = "getQueriesDataProvider") + public void test(String queryResourcePath) + { + assertEquals(generateQueryPlan(read(queryResourcePath)), read(getQueryPlanResourcePath(queryResourcePath))); + } + + private String getQueryPlanResourcePath(String queryResourcePath) + { + return queryResourcePath.replaceAll("\\.sql$", ".plan.txt"); + } + + public void generate() + throws Exception + { + initPlanTest(); + try { + getQueryResourcePaths() + .parallel() + .forEach(queryResourcePath -> { + try { + Path queryPlanWritePath = Paths.get( + getSourcePath().toString(), + "src/test/resources", + getQueryPlanResourcePath(queryResourcePath)); + createParentDirs(queryPlanWritePath.toFile()); + write(generateQueryPlan(read(queryResourcePath)).getBytes(UTF_8), queryPlanWritePath.toFile()); + System.out.println("Generated expected plan for query: " + queryResourcePath); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + finally { + destroyPlanTest(); + } + } + + private static String read(String resource) + { + try { + return Resources.toString(getResource(AbstractCostBasedPlanTest.class, resource), UTF_8); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private String generateQueryPlan(String query) + { + String sql = query.replaceAll("\\s+;\\s+$", "") + .replace("${database}.${schema}.", "") + .replace("\"${database}\".\"${schema}\".\"${prefix}", "\""); + Plan plan = plan(sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, false); + + JoinOrderPrinter joinOrderPrinter = new JoinOrderPrinter(); + plan.getRoot().accept(joinOrderPrinter, 0); + return joinOrderPrinter.result(); + } + + private static Path getSourcePath() + { + Path workingDir = Paths.get(System.getProperty("user.dir")); + verify(isDirectory(workingDir), "Working directory is not a directory"); + String topDirectoryName = workingDir.getFileName().toString(); + switch (topDirectoryName) { + case "presto-benchto-benchmarks": + return workingDir; + case "presto": + return workingDir.resolve("presto-benchto-benchmarks"); + default: + throw new IllegalStateException("This class must be executed from presto-benchto-benchmarks or presto source directory"); + } + } + + private static class JoinOrderPrinter + extends SimplePlanVisitor + { + private final StringBuilder result = new StringBuilder(); + + public String result() + { + return result.toString(); + } + + @Override + public Void visitJoin(JoinNode node, Integer indent) + { + JoinNode.DistributionType distributionType = node.getDistributionType() + .orElseThrow(() -> new VerifyException("Expected distribution type to be set")); + if (node.isCrossJoin()) { + checkState(node.getType() == INNER && distributionType == REPLICATED, "Expected CROSS JOIN to be INNER REPLICATED"); + output(indent, "cross join:"); + } + else { + output(indent, "join (%s, %s):", node.getType(), distributionType); + } + + return visitPlan(node, indent + 1); + } + + @Override + public Void visitExchange(ExchangeNode node, Integer indent) + { + Partitioning partitioning = node.getPartitioningScheme().getPartitioning(); + output( + indent, + "%s exchange (%s, %s, %s)", + node.getScope().name().toLowerCase(ENGLISH), + node.getType(), + partitioning.getHandle(), + partitioning.getArguments().stream() + .map(Object::toString) + .sorted() // Currently, order of hash columns is not deterministic + .collect(joining(", ", "[", "]"))); + + return visitPlan(node, indent + 1); + } + + @Override + public Void visitAggregation(AggregationNode node, Integer indent) + { + output( + indent, + "%s aggregation over (%s)", + node.getStep().name().toLowerCase(ENGLISH), + node.getGroupingKeys().stream() + .map(Object::toString) + .sorted() + .collect(joining(", "))); + + return visitPlan(node, indent + 1); + } + + @Override + public Void visitTableScan(TableScanNode node, Integer indent) + { + ConnectorTableHandle connectorTableHandle = node.getTable().getConnectorHandle(); + if (connectorTableHandle instanceof TpcdsTableHandle) { + output(indent, "scan %s", ((TpcdsTableHandle) connectorTableHandle).getTableName()); + } + else if (connectorTableHandle instanceof TpchTableHandle) { + output(indent, "scan %s", ((TpchTableHandle) connectorTableHandle).getTableName()); + } + else { + throw new IllegalStateException(format("Unexpected ConnectorTableHandle: %s", connectorTableHandle.getClass())); + } + + return null; + } + + @Override + public Void visitSemiJoin(final SemiJoinNode node, Integer indent) + { + output(indent, "semijoin (%s):", node.getDistributionType().get()); + + return visitPlan(node, indent + 1); + } + + @Override + public Void visitValues(ValuesNode node, Integer indent) + { + output(indent, "values (%s rows)", node.getRows().size()); + + return null; + } + + private void output(int indent, String message, Object... args) + { + String formattedMessage = format(message, args); + result.append(format("%s%s\n", Strings.repeat(" ", indent), formattedMessage)); + } + } +} diff --git a/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpcdsCostBasedPlan.java b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpcdsCostBasedPlan.java new file mode 100644 index 0000000000000..c582ebc9dfd69 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpcdsCostBasedPlan.java @@ -0,0 +1,94 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.sql.planner; + +import com.facebook.presto.Session; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinReorderingStrategy; +import com.facebook.presto.testing.LocalQueryRunner; +import com.facebook.presto.tpcds.TpcdsConnectorFactory; +import com.google.common.collect.ImmutableMap; + +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; +import static com.facebook.presto.SystemSessionProperties.JOIN_REORDERING_STRATEGY; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static java.lang.String.format; + +/** + * This class tests cost-based optimization rules related to joins. It contains unmodified TPCDS queries. + * This class is using TPCDS connector configured in way to mock Hive connector with unpartitioned TPCDS tables. + */ +public class TestTpcdsCostBasedPlan + extends AbstractCostBasedPlanTest +{ + /* + * CAUTION: The expected plans here are not necessarily optimal yet. Their role is to prevent + * inadvertent regressions. A conscious improvement to the planner may require changing some + * of the expected plans, but any such change should be verified on an actual cluster with + * large amount of data. + */ + + public TestTpcdsCostBasedPlan() + { + super(() -> { + String catalog = "local"; + Session.SessionBuilder sessionBuilder = testSessionBuilder() + .setCatalog(catalog) + .setSchema("sf3000.0") + .setSystemProperty("task_concurrency", "1") // these tests don't handle exchanges from local parallel + .setSystemProperty(JOIN_REORDERING_STRATEGY, JoinReorderingStrategy.AUTOMATIC.name()) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()); + + LocalQueryRunner queryRunner = LocalQueryRunner.queryRunnerWithFakeNodeCountForStats(sessionBuilder.build(), 8); + queryRunner.createCatalog( + catalog, + new TpcdsConnectorFactory(1), + ImmutableMap.of()); + return queryRunner; + }); + } + + @Override + protected Stream getQueryResourcePaths() + { + return IntStream.range(1, 100) + .boxed() + .flatMap(i -> { + String queryId = format("q%02d", i); + if (i == 14 || i == 23 || i == 24 || i == 39) { + return Stream.of(queryId + "_1", queryId + "_2"); + } + return Stream.of(queryId); + }) + .map(queryId -> format("/sql/presto/tpcds/%s.sql", queryId)); + } + + @SuppressWarnings("unused") + public static final class UpdateTestFiles + { + // Intellij doesn't handle well situation when test class has main(), hence inner class. + + private UpdateTestFiles() {} + + public static void main(String[] args) + throws Exception + { + new TestTpcdsCostBasedPlan().generate(); + } + } +} diff --git a/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpchCostBasedPlan.java b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpchCostBasedPlan.java new file mode 100644 index 0000000000000..463f514468351 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/java/com/facebook/presto/sql/planner/TestTpchCostBasedPlan.java @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.sql.planner; + +import com.facebook.presto.Session.SessionBuilder; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinReorderingStrategy; +import com.facebook.presto.testing.LocalQueryRunner; +import com.facebook.presto.tpch.ColumnNaming; +import com.facebook.presto.tpch.TpchConnectorFactory; +import com.google.common.collect.ImmutableMap; + +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; +import static com.facebook.presto.SystemSessionProperties.JOIN_REORDERING_STRATEGY; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.facebook.presto.tpch.TpchConnectorFactory.TPCH_COLUMN_NAMING_PROPERTY; +import static java.lang.String.format; + +/** + * This class tests cost-based optimization rules related to joins. It contains unmodified TPCH queries. + * This class is using TPCH connector configured in way to mock Hive connector with unpartitioned TPCH tables. + */ +public class TestTpchCostBasedPlan + extends AbstractCostBasedPlanTest +{ + /* + * CAUTION: The expected plans here are not necessarily optimal yet. Their role is to prevent + * inadvertent regressions. A conscious improvement to the planner may require changing some + * of the expected plans, but any such change should be verified on an actual cluster with + * large amount of data. + */ + + public TestTpchCostBasedPlan() + { + super(() -> { + String catalog = "local"; + SessionBuilder sessionBuilder = testSessionBuilder() + .setCatalog(catalog) + .setSchema("sf3000.0") + .setSystemProperty("task_concurrency", "1") // these tests don't handle exchanges from local parallel + .setSystemProperty(JOIN_REORDERING_STRATEGY, JoinReorderingStrategy.AUTOMATIC.name()) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()); + + LocalQueryRunner queryRunner = LocalQueryRunner.queryRunnerWithFakeNodeCountForStats(sessionBuilder.build(), 8); + queryRunner.createCatalog( + catalog, + new TpchConnectorFactory(1, false, false), + ImmutableMap.of(TPCH_COLUMN_NAMING_PROPERTY, ColumnNaming.SIMPLIFIED.name())); + return queryRunner; + }); + } + + @Override + protected Stream getQueryResourcePaths() + { + return IntStream.rangeClosed(1, 22) + .mapToObj(i -> format("q%02d", i)) + .map(queryId -> format("/sql/presto/tpch/%s.sql", queryId)); + } + + @SuppressWarnings("unused") + public static final class UpdateTestFiles + { + // Intellij doesn't handle well situation when test class has main(), hence inner class. + + private UpdateTestFiles() {} + + public static void main(String[] args) + throws Exception + { + new TestTpchCostBasedPlan().generate(); + } + } +} diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q01.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q01.plan.txt new file mode 100644 index 0000000000000..89d5d56a431df --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q01.plan.txt @@ -0,0 +1,41 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + cross join: + join (LEFT, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk"]) + join (INNER, REPLICATED): + final aggregation over (sr_customer_sk, sr_store_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk", "sr_store_sk"]) + partial aggregation over (sr_customer_sk, sr_store_sk) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (sr_store_sk_24) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_store_sk_24"]) + partial aggregation over (sr_store_sk_24) + final aggregation over (sr_customer_sk_20, sr_store_sk_24) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk_20", "sr_store_sk_24"]) + partial aggregation over (sr_customer_sk_20, sr_store_sk_24) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q02.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q02.plan.txt new file mode 100644 index 0000000000000..0d0a5fabd8a8d --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q02.plan.txt @@ -0,0 +1,36 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + final aggregation over (d_week_seq) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq"]) + partial aggregation over (d_week_seq) + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan web_sales + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_83"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_395"]) + join (INNER, PARTITIONED): + final aggregation over (d_week_seq_232) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_232"]) + partial aggregation over (d_week_seq_232) + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan web_sales + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_316"]) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q03.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q03.plan.txt new file mode 100644 index 0000000000000..70aae3fa1bdde --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q03.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_year, i_brand, i_brand_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year", "i_brand", "i_brand_id"]) + partial aggregation over (d_year, i_brand, i_brand_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q04.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q04.plan.txt new file mode 100644 index 0000000000000..82dc10df6a25a --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q04.plan.txt @@ -0,0 +1,183 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_id_206"]) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_id_206"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_birth_country, c_customer_id, c_email_address, c_first_name, c_last_name, c_login, c_preferred_cust_flag, d_year) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country", "c_customer_id", "c_email_address", "c_first_name", "c_last_name", "c_login", "c_preferred_cust_flag", "d_year"]) + partial aggregation over (c_birth_country, c_customer_id, c_email_address, c_first_name, c_last_name, c_login, c_preferred_cust_flag, d_year) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_birth_country_42, c_customer_id_29, c_email_address_44, c_first_name_36, c_last_name_37, c_login_43, c_preferred_cust_flag_38, d_year_52) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + single aggregation over (c_birth_country_136, c_customer_id_123, c_email_address_138, c_first_name_130, c_last_name_131, c_login_137, c_preferred_cust_flag_132, d_year_146) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_599"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_birth_country_260, c_customer_id_247, c_email_address_262, c_first_name_254, c_last_name_255, c_login_261, c_preferred_cust_flag_256, d_year_293) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_260", "c_customer_id_247", "c_email_address_262", "c_first_name_254", "c_last_name_255", "c_login_261", "c_preferred_cust_flag_256", "d_year_293"]) + partial aggregation over (c_birth_country_260, c_customer_id_247, c_email_address_262, c_first_name_254, c_last_name_255, c_login_261, c_preferred_cust_flag_256, d_year_293) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_birth_country_367, c_customer_id_354, c_email_address_369, c_first_name_361, c_last_name_362, c_login_368, c_preferred_cust_flag_363, d_year_411) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + single aggregation over (c_birth_country_495, c_customer_id_482, c_email_address_497, c_first_name_489, c_last_name_490, c_login_496, c_preferred_cust_flag_491, d_year_539) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_640"]) + single aggregation over (c_birth_country_653, c_customer_id_640, c_email_address_655, c_first_name_647, c_last_name_648, c_login_654, c_preferred_cust_flag_649, d_year_686) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_747"]) + final aggregation over (c_birth_country_760, c_customer_id_747, c_email_address_762, c_first_name_754, c_last_name_755, c_login_761, c_preferred_cust_flag_756, d_year_804) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_760", "c_customer_id_747", "c_email_address_762", "c_first_name_754", "c_last_name_755", "c_login_761", "c_preferred_cust_flag_756", "d_year_804"]) + partial aggregation over (c_birth_country_760, c_customer_id_747, c_email_address_762, c_first_name_754, c_last_name_755, c_login_761, c_preferred_cust_flag_756, d_year_804) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + remote exchange (REPARTITION, HASH, ["c_customer_id_875"]) + single aggregation over (c_birth_country_888, c_customer_id_875, c_email_address_890, c_first_name_882, c_last_name_883, c_login_889, c_preferred_cust_flag_884, d_year_932) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_1033"]) + single aggregation over (c_birth_country_1046, c_customer_id_1033, c_email_address_1048, c_first_name_1040, c_last_name_1041, c_login_1047, c_preferred_cust_flag_1042, d_year_1079) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_1140"]) + final aggregation over (c_birth_country_1153, c_customer_id_1140, c_email_address_1155, c_first_name_1147, c_last_name_1148, c_login_1154, c_preferred_cust_flag_1149, d_year_1197) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_1153", "c_customer_id_1140", "c_email_address_1155", "c_first_name_1147", "c_last_name_1148", "c_login_1154", "c_preferred_cust_flag_1149", "d_year_1197"]) + partial aggregation over (c_birth_country_1153, c_customer_id_1140, c_email_address_1155, c_first_name_1147, c_last_name_1148, c_login_1154, c_preferred_cust_flag_1149, d_year_1197) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + remote exchange (REPARTITION, HASH, ["c_customer_id_1268"]) + single aggregation over (c_birth_country_1281, c_customer_id_1268, c_email_address_1283, c_first_name_1275, c_last_name_1276, c_login_1282, c_preferred_cust_flag_1277, d_year_1325) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_1778"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + single aggregation over (c_birth_country_1439, c_customer_id_1426, c_email_address_1441, c_first_name_1433, c_last_name_1434, c_login_1440, c_preferred_cust_flag_1435, d_year_1472) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + single aggregation over (c_birth_country_1546, c_customer_id_1533, c_email_address_1548, c_first_name_1540, c_last_name_1541, c_login_1547, c_preferred_cust_flag_1542, d_year_1590) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_birth_country_1674, c_customer_id_1661, c_email_address_1676, c_first_name_1668, c_last_name_1669, c_login_1675, c_preferred_cust_flag_1670, d_year_1718) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_1674", "c_customer_id_1661", "c_email_address_1676", "c_first_name_1668", "c_last_name_1669", "c_login_1675", "c_preferred_cust_flag_1670", "d_year_1718"]) + partial aggregation over (c_birth_country_1674, c_customer_id_1661, c_email_address_1676, c_first_name_1668, c_last_name_1669, c_login_1675, c_preferred_cust_flag_1670, d_year_1718) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_1682"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_1660"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_1819"]) + single aggregation over (c_birth_country_1832, c_customer_id_1819, c_email_address_1834, c_first_name_1826, c_last_name_1827, c_login_1833, c_preferred_cust_flag_1828, d_year_1865) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_1926"]) + single aggregation over (c_birth_country_1939, c_customer_id_1926, c_email_address_1941, c_first_name_1933, c_last_name_1934, c_login_1940, c_preferred_cust_flag_1935, d_year_1983) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_2054"]) + final aggregation over (c_birth_country_2067, c_customer_id_2054, c_email_address_2069, c_first_name_2061, c_last_name_2062, c_login_2068, c_preferred_cust_flag_2063, d_year_2111) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_2067", "c_customer_id_2054", "c_email_address_2069", "c_first_name_2061", "c_last_name_2062", "c_login_2068", "c_preferred_cust_flag_2063", "d_year_2111"]) + partial aggregation over (c_birth_country_2067, c_customer_id_2054, c_email_address_2069, c_first_name_2061, c_last_name_2062, c_login_2068, c_preferred_cust_flag_2063, d_year_2111) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_2075"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_2053"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q05.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q05.plan.txt new file mode 100644 index 0000000000000..9577f71abc72a --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q05.plan.txt @@ -0,0 +1,57 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + remote exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + partial aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (s_store_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_id"]) + partial aggregation over (s_store_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan store_sales + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + final aggregation over (cp_catalog_page_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cp_catalog_page_id"]) + partial aggregation over (cp_catalog_page_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan catalog_sales + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_page + final aggregation over (web_site_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["web_site_id"]) + partial aggregation over (web_site_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan web_sales + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk_216", "ws_order_number_230"]) + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wr_item_sk", "wr_order_number"]) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q06.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q06.plan.txt new file mode 100644 index 0000000000000..1fc16834e6e44 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q06.plan.txt @@ -0,0 +1,48 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ca_state) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state"]) + partial aggregation over (ca_state) + cross join: + join (LEFT, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_month_seq_3) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_month_seq_3"]) + partial aggregation over (d_month_seq_3) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (i_category_43) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_category_43"]) + partial aggregation over (i_category_43) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q07.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q07.plan.txt new file mode 100644 index 0000000000000..32f4763b443da --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q07.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id"]) + partial aggregation over (i_item_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q08.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q08.plan.txt new file mode 100644 index 0000000000000..059fe336d437f --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q08.plan.txt @@ -0,0 +1,36 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_name"]) + partial aggregation over (s_store_name) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["substr_55"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["substr_56"]) + final aggregation over (expr_29) + local exchange (REPARTITION, HASH, ["expr_29"]) + remote exchange (REPARTITION, HASH, ["expr_47"]) + partial aggregation over (expr_47) + scan customer_address + remote exchange (REPARTITION, HASH, ["expr_50"]) + partial aggregation over (expr_50) + final aggregation over (ca_zip_11) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_zip_11"]) + partial aggregation over (ca_zip_11) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ca_address_sk_2"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q09.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q09.plan.txt new file mode 100644 index 0000000000000..6a40a2a3ea3a4 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q09.plan.txt @@ -0,0 +1,122 @@ +remote exchange (GATHER, SINGLE, []) + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + scan reason + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan store_sales diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q10.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q10.plan.txt new file mode 100644 index 0000000000000..50e17a7b593a1 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q10.plan.txt @@ -0,0 +1,50 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (cd_credit_rating, cd_dep_college_count, cd_dep_count, cd_dep_employed_count, cd_education_status, cd_gender, cd_marital_status, cd_purchase_estimate) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_credit_rating", "cd_dep_college_count", "cd_dep_count", "cd_dep_employed_count", "cd_education_status", "cd_gender", "cd_marital_status", "cd_purchase_estimate"]) + partial aggregation over (cd_credit_rating, cd_dep_college_count, cd_dep_count, cd_dep_employed_count, cd_education_status, cd_gender, cd_marital_status, cd_purchase_estimate) + join (RIGHT, PARTITIONED): + final aggregation over (cs_ship_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_ship_customer_sk"]) + partial aggregation over (cs_ship_customer_sk) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + join (RIGHT, PARTITIONED): + final aggregation over (ws_bill_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + partial aggregation over (ws_bill_customer_sk) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, REPLICATED): + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + final aggregation over (ss_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + partial aggregation over (ss_customer_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q11.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q11.plan.txt new file mode 100644 index 0000000000000..d49d4dafb75d8 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q11.plan.txt @@ -0,0 +1,96 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_id_112"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_birth_country, c_customer_id, c_email_address, c_first_name, c_last_name, c_login, c_preferred_cust_flag, d_year) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country", "c_customer_id", "c_email_address", "c_first_name", "c_last_name", "c_login", "c_preferred_cust_flag", "d_year"]) + partial aggregation over (c_birth_country, c_customer_id, c_email_address, c_first_name, c_last_name, c_login, c_preferred_cust_flag, d_year) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_birth_country_42, c_customer_id_29, c_email_address_44, c_first_name_36, c_last_name_37, c_login_43, c_preferred_cust_flag_38, d_year_52) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_377"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_birth_country_166, c_customer_id_153, c_email_address_168, c_first_name_160, c_last_name_161, c_login_167, c_preferred_cust_flag_162, d_year_199) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_166", "c_customer_id_153", "c_email_address_168", "c_first_name_160", "c_last_name_161", "c_login_167", "c_preferred_cust_flag_162", "d_year_199"]) + partial aggregation over (c_birth_country_166, c_customer_id_153, c_email_address_168, c_first_name_160, c_last_name_161, c_login_167, c_preferred_cust_flag_162, d_year_199) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_birth_country_273, c_customer_id_260, c_email_address_275, c_first_name_267, c_last_name_268, c_login_274, c_preferred_cust_flag_269, d_year_317) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_418"]) + single aggregation over (c_birth_country_431, c_customer_id_418, c_email_address_433, c_first_name_425, c_last_name_426, c_login_432, c_preferred_cust_flag_427, d_year_464) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_525"]) + final aggregation over (c_birth_country_538, c_customer_id_525, c_email_address_540, c_first_name_532, c_last_name_533, c_login_539, c_preferred_cust_flag_534, d_year_582) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_538", "c_customer_id_525", "c_email_address_540", "c_first_name_532", "c_last_name_533", "c_login_539", "c_preferred_cust_flag_534", "d_year_582"]) + partial aggregation over (c_birth_country_538, c_customer_id_525, c_email_address_540, c_first_name_532, c_last_name_533, c_login_539, c_preferred_cust_flag_534, d_year_582) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_546"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_524"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_683"]) + single aggregation over (c_birth_country_696, c_customer_id_683, c_email_address_698, c_first_name_690, c_last_name_691, c_login_697, c_preferred_cust_flag_692, d_year_729) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_790"]) + final aggregation over (c_birth_country_803, c_customer_id_790, c_email_address_805, c_first_name_797, c_last_name_798, c_login_804, c_preferred_cust_flag_799, d_year_847) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_birth_country_803", "c_customer_id_790", "c_email_address_805", "c_first_name_797", "c_last_name_798", "c_login_804", "c_preferred_cust_flag_799", "d_year_847"]) + partial aggregation over (c_birth_country_803, c_customer_id_790, c_email_address_805, c_first_name_797, c_last_name_798, c_login_804, c_preferred_cust_flag_799, d_year_847) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_811"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_789"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q12.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q12.plan.txt new file mode 100644 index 0000000000000..6f94c98c52243 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q12.plan.txt @@ -0,0 +1,17 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_class"]) + final aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_category", "i_class", "i_current_price", "i_item_desc", "i_item_id"]) + partial aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q13.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q13.plan.txt new file mode 100644 index 0000000000000..011d5b20fbbab --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q13.plan.txt @@ -0,0 +1,25 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_1.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_1.plan.txt new file mode 100644 index 0000000000000..ec4c1ac8a9016 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_1.plan.txt @@ -0,0 +1,244 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (channel$gid, groupid, i_brand_id$gid_1742, i_category_id$gid_1744, i_class_id$gid_1743) + local exchange (REPARTITION, HASH, ["channel$gid", "groupid", "i_brand_id$gid_1742", "i_category_id$gid_1744", "i_class_id$gid_1743"]) + remote exchange (REPARTITION, HASH, ["channel$gid", "groupid", "i_brand_id$gid_1742", "i_category_id$gid_1744", "i_class_id$gid_1743"]) + partial aggregation over (channel$gid, groupid, i_brand_id$gid_1742, i_category_id$gid_1744, i_class_id$gid_1743) + local exchange (REPARTITION, ROUND_ROBIN, []) + cross join: + final aggregation over (i_brand_id, i_category_id, i_class_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id", "i_category_id", "i_class_id"]) + partial aggregation over (i_brand_id, i_category_id, i_class_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_1"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_brand_id_8", "i_category_id_12", "i_class_id_10"]) + scan item + final aggregation over (expr_216, expr_217, expr_218) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_53", "i_category_id_57", "i_class_id_55"]) + partial aggregation over (i_brand_id_53, i_category_id_57, i_class_id_55) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_108", "i_category_id_112", "i_class_id_110"]) + partial aggregation over (i_brand_id_108, i_category_id_112, i_class_id_110) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_167", "i_category_id_171", "i_class_id_169"]) + partial aggregation over (i_brand_id_167, i_category_id_171, i_class_id_169) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + cross join: + final aggregation over (i_brand_id_508, i_category_id_512, i_class_id_510) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_508", "i_category_id_512", "i_class_id_510"]) + partial aggregation over (i_brand_id_508, i_category_id_512, i_class_id_510) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk_482"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_552"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_brand_id_559", "i_category_id_563", "i_class_id_561"]) + scan item + final aggregation over (expr_836, expr_837, expr_838) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_604", "i_category_id_608", "i_class_id_606"]) + partial aggregation over (i_brand_id_604, i_category_id_608, i_class_id_606) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_694", "i_category_id_698", "i_class_id_696"]) + partial aggregation over (i_brand_id_694, i_category_id_698, i_class_id_696) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_787", "i_category_id_791", "i_class_id_789"]) + partial aggregation over (i_brand_id_787, i_category_id_791, i_class_id_789) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + cross join: + final aggregation over (i_brand_id_1135, i_category_id_1139, i_class_id_1137) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_1135", "i_category_id_1139", "i_class_id_1137"]) + partial aggregation over (i_brand_id_1135, i_category_id_1139, i_class_id_1137) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk_1097"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_1179"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_brand_id_1186", "i_category_id_1190", "i_class_id_1188"]) + scan item + final aggregation over (expr_1463, expr_1464, expr_1465) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_1231", "i_category_id_1235", "i_class_id_1233"]) + partial aggregation over (i_brand_id_1231, i_category_id_1235, i_class_id_1233) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_1321", "i_category_id_1325", "i_class_id_1323"]) + partial aggregation over (i_brand_id_1321, i_category_id_1325, i_class_id_1323) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_1414", "i_category_id_1418", "i_class_id_1416"]) + partial aggregation over (i_brand_id_1414, i_category_id_1418, i_class_id_1416) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_2.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_2.plan.txt new file mode 100644 index 0000000000000..6b3da8b0b0a43 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q14_2.plan.txt @@ -0,0 +1,173 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + cross join: + final aggregation over (i_brand_id, i_category_id, i_class_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id", "i_category_id", "i_class_id"]) + partial aggregation over (i_brand_id, i_category_id, i_class_id) + join (INNER, REPLICATED): + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_1"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_brand_id_8", "i_category_id_12", "i_class_id_10"]) + scan item + final aggregation over (expr_216, expr_217, expr_218) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_53", "i_category_id_57", "i_class_id_55"]) + partial aggregation over (i_brand_id_53, i_category_id_57, i_class_id_55) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_108", "i_category_id_112", "i_class_id_110"]) + partial aggregation over (i_brand_id_108, i_category_id_112, i_class_id_110) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_167", "i_category_id_171", "i_class_id_169"]) + partial aggregation over (i_brand_id_167, i_category_id_171, i_class_id_169) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + cross join: + final aggregation over (i_brand_id_534, i_category_id_538, i_class_id_536) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_534", "i_category_id_538", "i_class_id_536"]) + partial aggregation over (i_brand_id_534, i_category_id_538, i_class_id_536) + join (INNER, REPLICATED): + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk_506"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_578"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_brand_id_585", "i_category_id_589", "i_class_id_587"]) + scan item + final aggregation over (expr_862, expr_863, expr_864) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_630", "i_category_id_634", "i_class_id_632"]) + partial aggregation over (i_brand_id_630, i_category_id_634, i_class_id_632) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_720", "i_category_id_724", "i_class_id_722"]) + partial aggregation over (i_brand_id_720, i_category_id_724, i_class_id_722) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + remote exchange (REPARTITION, HASH, ["i_brand_id_813", "i_category_id_817", "i_class_id_815"]) + partial aggregation over (i_brand_id_813, i_category_id_817, i_class_id_815) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over () + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q15.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q15.plan.txt new file mode 100644 index 0000000000000..f359fbefb40f2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q15.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ca_zip) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_zip"]) + partial aggregation over (ca_zip) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q16.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q16.plan.txt new file mode 100644 index 0000000000000..8fc57e09f6a8f --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q16.plan.txt @@ -0,0 +1,31 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (LEFT, PARTITIONED): + final aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_state, cc_county, cs_call_center_sk, cs_ext_ship_cost, cs_net_profit, cs_order_number, cs_ship_addr_sk, cs_ship_date_sk, cs_warehouse_sk, d_date, unique) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_order_number_17"]) + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_order_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center + final aggregation over (cr_order_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cr_order_number"]) + partial aggregation over (cr_order_number) + scan catalog_returns diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q17.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q17.plan.txt new file mode 100644 index 0000000000000..2736834ce145a --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q17.plan.txt @@ -0,0 +1,37 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_desc, i_item_id, s_state) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_desc", "i_item_id", "s_state"]) + partial aggregation over (i_item_desc, i_item_id, s_state) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_sk", "sr_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_item_sk", "ss_ticket_number"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk", "sr_item_sk", "sr_ticket_number"]) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk", "cs_item_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q18.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q18.plan.txt new file mode 100644 index 0000000000000..70547d9eecf53 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q18.plan.txt @@ -0,0 +1,33 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ca_country$gid, ca_county$gid, ca_state$gid, groupid, i_item_id$gid) + local exchange (REPARTITION, HASH, ["ca_country$gid", "ca_county$gid", "ca_state$gid", "groupid", "i_item_id$gid"]) + remote exchange (REPARTITION, HASH, ["ca_country$gid", "ca_county$gid", "ca_state$gid", "groupid", "i_item_id$gid"]) + partial aggregation over (ca_country$gid, ca_county$gid, ca_state$gid, groupid, i_item_id$gid) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_cdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk_0"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q19.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q19.plan.txt new file mode 100644 index 0000000000000..c500acf6fca3d --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q19.plan.txt @@ -0,0 +1,29 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_brand, i_brand_id, i_manufact, i_manufact_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand", "i_brand_id", "i_manufact", "i_manufact_id"]) + partial aggregation over (i_brand, i_brand_id, i_manufact, i_manufact_id) + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q20.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q20.plan.txt new file mode 100644 index 0000000000000..852a9d2a3e5c1 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q20.plan.txt @@ -0,0 +1,17 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_class"]) + final aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_category", "i_class", "i_current_price", "i_item_desc", "i_item_id"]) + partial aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q21.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q21.plan.txt new file mode 100644 index 0000000000000..50e40917c430b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q21.plan.txt @@ -0,0 +1,19 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_id, w_warehouse_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id", "w_warehouse_name"]) + partial aggregation over (i_item_id, w_warehouse_name) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q22.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q22.plan.txt new file mode 100644 index 0000000000000..d323affadfc06 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q22.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (groupid, i_brand$gid, i_category$gid, i_class$gid, i_product_name$gid) + local exchange (REPARTITION, HASH, ["groupid", "i_brand$gid", "i_category$gid", "i_class$gid", "i_product_name$gid"]) + remote exchange (REPARTITION, HASH, ["groupid", "i_brand$gid", "i_category$gid", "i_class$gid", "i_product_name$gid"]) + partial aggregation over (groupid, i_brand$gid, i_category$gid, i_class$gid, i_product_name$gid) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_1.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_1.plan.txt new file mode 100644 index 0000000000000..bb4fd6610c702 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_1.plan.txt @@ -0,0 +1,115 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk"]) + final aggregation over (d_date_3, i_item_sk, substr) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_3", "i_item_sk", "substr"]) + partial aggregation over (d_date_3, i_item_sk, substr) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + cross join: + final aggregation over (c_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + partial aggregation over (c_customer_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_customer_sk_110) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_110"]) + partial aggregation over (c_customer_sk_110) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + partial aggregation over () + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_275"]) + final aggregation over (d_date_249, i_item_sk_275, substr_297) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_249", "i_item_sk_275", "substr_297"]) + partial aggregation over (d_date_249, i_item_sk_275, substr_297) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_355"]) + cross join: + final aggregation over (c_customer_sk_355) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_355"]) + partial aggregation over (c_customer_sk_355) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_customer_sk_405) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_405"]) + partial aggregation over (c_customer_sk_405) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_2.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_2.plan.txt new file mode 100644 index 0000000000000..a4ede20f017fa --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q23_2.plan.txt @@ -0,0 +1,130 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_first_name, c_last_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name"]) + partial aggregation over (c_first_name, c_last_name) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk"]) + final aggregation over (d_date_3, i_item_sk, substr) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_3", "i_item_sk", "substr"]) + partial aggregation over (d_date_3, i_item_sk, substr) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_80"]) + cross join: + final aggregation over (c_customer_sk_80) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_80"]) + partial aggregation over (c_customer_sk_80) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_customer_sk_128) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_128"]) + partial aggregation over (c_customer_sk_128) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + final aggregation over (c_first_name_229, c_last_name_230) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_229", "c_last_name_230"]) + partial aggregation over (c_first_name_229, c_last_name_230) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_221"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_319"]) + final aggregation over (d_date_293, i_item_sk_319, substr_341) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_293", "i_item_sk_319", "substr_341"]) + partial aggregation over (d_date_293, i_item_sk_319, substr_341) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_399"]) + cross join: + final aggregation over (c_customer_sk_399) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_399"]) + partial aggregation over (c_customer_sk_399) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_customer_sk_449) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_449"]) + partial aggregation over (c_customer_sk_449) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_1.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_1.plan.txt new file mode 100644 index 0000000000000..cfd0fbc60c469 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_1.plan.txt @@ -0,0 +1,65 @@ +remote exchange (GATHER, SINGLE, []) + cross join: + final aggregation over (c_first_name, c_last_name, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "s_store_name"]) + partial aggregation over (c_first_name, c_last_name, s_store_name) + final aggregation over (c_first_name, c_last_name, ca_state, i_color, i_current_price, i_manager_id, i_size, i_units, s_state, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "ca_state", "i_color", "i_current_price", "i_manager_id", "i_size", "i_units", "s_state", "s_store_name"]) + partial aggregation over (c_first_name, c_last_name, ca_state, i_color, i_current_price, i_manager_id, i_size, i_units, s_state, s_store_name) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_birth_country", "s_zip"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_zip", "upper"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_first_name_181, c_last_name_182, ca_state_199, i_color_168, i_current_price_156, i_manager_id_171, i_size_166, i_units_169, s_state_146, s_store_name_127) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_181", "c_last_name_182", "ca_state_199", "i_color_168", "i_current_price_156", "i_manager_id_171", "i_size_166", "i_units_169", "s_state_146", "s_store_name_127"]) + partial aggregation over (c_first_name_181, c_last_name_182, ca_state_199, i_color_168, i_current_price_156, i_manager_id_171, i_size_166, i_units_169, s_state_146, s_store_name_127) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk_81", "ss_ticket_number_88"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_item_sk_104", "sr_ticket_number_111"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_2.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_2.plan.txt new file mode 100644 index 0000000000000..cfd0fbc60c469 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q24_2.plan.txt @@ -0,0 +1,65 @@ +remote exchange (GATHER, SINGLE, []) + cross join: + final aggregation over (c_first_name, c_last_name, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "s_store_name"]) + partial aggregation over (c_first_name, c_last_name, s_store_name) + final aggregation over (c_first_name, c_last_name, ca_state, i_color, i_current_price, i_manager_id, i_size, i_units, s_state, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "ca_state", "i_color", "i_current_price", "i_manager_id", "i_size", "i_units", "s_state", "s_store_name"]) + partial aggregation over (c_first_name, c_last_name, ca_state, i_color, i_current_price, i_manager_id, i_size, i_units, s_state, s_store_name) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_birth_country", "s_zip"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_zip", "upper"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_first_name_181, c_last_name_182, ca_state_199, i_color_168, i_current_price_156, i_manager_id_171, i_size_166, i_units_169, s_state_146, s_store_name_127) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_181", "c_last_name_182", "ca_state_199", "i_color_168", "i_current_price_156", "i_manager_id_171", "i_size_166", "i_units_169", "s_state_146", "s_store_name_127"]) + partial aggregation over (c_first_name_181, c_last_name_182, ca_state_199, i_color_168, i_current_price_156, i_manager_id_171, i_size_166, i_units_169, s_state_146, s_store_name_127) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk_81", "ss_ticket_number_88"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_item_sk_104", "sr_ticket_number_111"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q25.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q25.plan.txt new file mode 100644 index 0000000000000..f7d2512d33bd4 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q25.plan.txt @@ -0,0 +1,37 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_desc, i_item_id, s_store_id, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_desc", "i_item_id", "s_store_id", "s_store_name"]) + partial aggregation over (i_item_desc, i_item_id, s_store_id, s_store_name) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_sk", "sr_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_item_sk", "ss_ticket_number"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk", "sr_item_sk", "sr_ticket_number"]) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk", "cs_item_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q26.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q26.plan.txt new file mode 100644 index 0000000000000..555bb67d3baa4 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q26.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id"]) + partial aggregation over (i_item_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q27.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q27.plan.txt new file mode 100644 index 0000000000000..ed0e2e73e9f3c --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q27.plan.txt @@ -0,0 +1,24 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (groupid, i_item_id$gid, s_state$gid) + local exchange (REPARTITION, HASH, ["groupid", "i_item_id$gid", "s_state$gid"]) + remote exchange (REPARTITION, HASH, ["groupid", "i_item_id$gid", "s_state$gid"]) + partial aggregation over (groupid, i_item_id$gid, s_state$gid) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q28.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q28.plan.txt new file mode 100644 index 0000000000000..f8b139b7041c9 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q28.plan.txt @@ -0,0 +1,47 @@ +cross join: + cross join: + cross join: + cross join: + cross join: + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price"]) + scan store_sales + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price_27"]) + scan store_sales + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price_68"]) + scan store_sales + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price_109"]) + scan store_sales + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price_150"]) + scan store_sales + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_list_price_191"]) + scan store_sales diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q29.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q29.plan.txt new file mode 100644 index 0000000000000..f81f367cc0b29 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q29.plan.txt @@ -0,0 +1,37 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_desc, i_item_id, s_store_id, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_desc", "i_item_id", "s_store_id", "s_store_name"]) + partial aggregation over (i_item_desc, i_item_id, s_store_id, s_store_name) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk", "cs_item_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk", "sr_customer_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_item_sk", "ss_ticket_number"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_customer_sk", "sr_item_sk", "sr_ticket_number"]) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q30.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q30.plan.txt new file mode 100644 index 0000000000000..8953e9982f55b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q30.plan.txt @@ -0,0 +1,50 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + cross join: + join (LEFT, REPLICATED): + join (INNER, REPLICATED): + final aggregation over (ca_state, wr_returning_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "wr_returning_customer_sk"]) + partial aggregation over (ca_state, wr_returning_customer_sk) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_returning_addr_sk"]) + join (INNER, REPLICATED): + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (ca_state_90) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state_90"]) + partial aggregation over (ca_state_90) + final aggregation over (ca_state_90, wr_returning_customer_sk_37) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state_90", "wr_returning_customer_sk_37"]) + partial aggregation over (ca_state_90, wr_returning_customer_sk_37) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_returning_addr_sk_40"]) + join (INNER, REPLICATED): + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_82"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q31.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q31.plan.txt new file mode 100644 index 0000000000000..e616ab836864f --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q31.plan.txt @@ -0,0 +1,90 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + final aggregation over (ca_county_81, d_qoy_56, d_year_52) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_81", "d_qoy_56", "d_year_52"]) + partial aggregation over (ca_county_81, d_qoy_56, d_year_52) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + final aggregation over (ca_county_345, d_qoy_320, d_year_316) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_345", "d_qoy_320", "d_year_316"]) + partial aggregation over (ca_county_345, d_qoy_320, d_year_316) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_173", NullableValue{type=integer, value=2000}, NullableValue{type=integer, value=2}]) + join (INNER, PARTITIONED): + final aggregation over (ca_county_173, d_qoy_148, d_year_144) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_173", "d_qoy_148", "d_year_144"]) + partial aggregation over (ca_county_173, d_qoy_148, d_year_144) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + final aggregation over (ca_county_448, d_qoy_423, d_year_419) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_448", "d_qoy_423", "d_year_419"]) + partial aggregation over (ca_county_448, d_qoy_423, d_year_419) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county", NullableValue{type=integer, value=2000}, NullableValue{type=integer, value=2}]) + join (INNER, PARTITIONED): + final aggregation over (ca_county, d_qoy, d_year) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county", "d_qoy", "d_year"]) + partial aggregation over (ca_county, d_qoy, d_year) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + final aggregation over (ca_county_242, d_qoy_217, d_year_213) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_county_242", "d_qoy_217", "d_year_213"]) + partial aggregation over (ca_county_242, d_qoy_217, d_year_213) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q32.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q32.plan.txt new file mode 100644 index 0000000000000..bcf3e2fd621a0 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q32.plan.txt @@ -0,0 +1,30 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + cross join: + join (RIGHT, PARTITIONED): + final aggregation over (cs_item_sk_15) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk_15"]) + partial aggregation over (cs_item_sk_15) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q33.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q33.plan.txt new file mode 100644 index 0000000000000..df84a9229a5c9 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q33.plan.txt @@ -0,0 +1,70 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_266) + local exchange (REPARTITION, HASH, ["expr_266"]) + partial aggregation over (i_manufact_id) + final aggregation over (i_manufact_id) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_manufact_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_manufact_id"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manufact_id_14"]) + scan item + partial aggregation over (i_manufact_id_98) + final aggregation over (i_manufact_id_98) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_manufact_id_98) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_manufact_id_98"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manufact_id_121"]) + scan item + partial aggregation over (i_manufact_id_210) + final aggregation over (i_manufact_id_210) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_manufact_id_210) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_manufact_id_210"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manufact_id_233"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q34.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q34.plan.txt new file mode 100644 index 0000000000000..580389d9f5469 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q34.plan.txt @@ -0,0 +1,25 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + final aggregation over (ss_customer_sk, ss_ticket_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_ticket_number"]) + partial aggregation over (ss_customer_sk, ss_ticket_number) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q35.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q35.plan.txt new file mode 100644 index 0000000000000..2bfeeeb541e2d --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q35.plan.txt @@ -0,0 +1,49 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ca_state, cd_dep_college_count, cd_dep_count, cd_dep_employed_count, cd_gender, cd_marital_status) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "cd_dep_college_count", "cd_dep_count", "cd_dep_employed_count", "cd_gender", "cd_marital_status"]) + partial aggregation over (ca_state, cd_dep_college_count, cd_dep_count, cd_dep_employed_count, cd_gender, cd_marital_status) + join (LEFT, PARTITIONED): + join (LEFT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_cdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + final aggregation over (ss_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + partial aggregation over (ss_customer_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk"]) + scan customer_demographics + final aggregation over (ws_bill_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + partial aggregation over (ws_bill_customer_sk) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + final aggregation over (cs_ship_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_ship_customer_sk"]) + partial aggregation over (cs_ship_customer_sk) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q36.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q36.plan.txt new file mode 100644 index 0000000000000..5fab99375b4ae --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q36.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_13", "expr_14"]) + final aggregation over (groupid, i_category$gid, i_class$gid) + local exchange (REPARTITION, HASH, ["groupid", "i_category$gid", "i_class$gid"]) + remote exchange (REPARTITION, HASH, ["groupid", "i_category$gid", "i_class$gid"]) + partial aggregation over (groupid, i_category$gid, i_class$gid) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q37.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q37.plan.txt new file mode 100644 index 0000000000000..e1157d3a5d0f9 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q37.plan.txt @@ -0,0 +1,19 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_current_price, i_item_desc, i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_current_price", "i_item_desc", "i_item_id"]) + partial aggregation over (i_current_price, i_item_desc, i_item_id) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q38.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q38.plan.txt new file mode 100644 index 0000000000000..ae3a9a0e34d0b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q38.plan.txt @@ -0,0 +1,50 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (c_first_name_114, c_last_name_113, d_date_115) + local exchange (REPARTITION, HASH, ["c_first_name_114", "c_last_name_113", "d_date_115"]) + partial aggregation over (c_first_name, c_last_name, d_date) + final aggregation over (c_first_name, c_last_name, d_date) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "d_date"]) + partial aggregation over (c_first_name, c_last_name, d_date) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + partial aggregation over (c_first_name_42, c_last_name_43, d_date_8) + final aggregation over (c_first_name_42, c_last_name_43, d_date_8) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_42", "c_last_name_43", "d_date_8"]) + partial aggregation over (c_first_name_42, c_last_name_43, d_date_8) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_34"]) + scan customer + partial aggregation over (c_first_name_97, c_last_name_98, d_date_63) + final aggregation over (c_first_name_97, c_last_name_98, d_date_63) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_97", "c_last_name_98", "d_date_63"]) + partial aggregation over (c_first_name_97, c_last_name_98, d_date_63) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_89"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_1.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_1.plan.txt new file mode 100644 index 0000000000000..3626d2d2403be --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_1.plan.txt @@ -0,0 +1,41 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_sk", "w_warehouse_sk"]) + final aggregation over (d_moy, i_item_sk, w_warehouse_name, w_warehouse_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy", "i_item_sk", "w_warehouse_name", "w_warehouse_sk"]) + partial aggregation over (d_moy, i_item_sk, w_warehouse_name, w_warehouse_sk) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_66", "w_warehouse_sk_88"]) + final aggregation over (d_moy_110, i_item_sk_66, w_warehouse_name_90, w_warehouse_sk_88) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy_110", "i_item_sk_66", "w_warehouse_name_90", "w_warehouse_sk_88"]) + partial aggregation over (d_moy_110, i_item_sk_66, w_warehouse_name_90, w_warehouse_sk_88) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_2.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_2.plan.txt new file mode 100644 index 0000000000000..3626d2d2403be --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q39_2.plan.txt @@ -0,0 +1,41 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_sk", "w_warehouse_sk"]) + final aggregation over (d_moy, i_item_sk, w_warehouse_name, w_warehouse_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy", "i_item_sk", "w_warehouse_name", "w_warehouse_sk"]) + partial aggregation over (d_moy, i_item_sk, w_warehouse_name, w_warehouse_sk) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_66", "w_warehouse_sk_88"]) + final aggregation over (d_moy_110, i_item_sk_66, w_warehouse_name_90, w_warehouse_sk_88) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy_110", "i_item_sk_66", "w_warehouse_name_90", "w_warehouse_sk_88"]) + partial aggregation over (d_moy_110, i_item_sk_66, w_warehouse_name_90, w_warehouse_sk_88) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q40.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q40.plan.txt new file mode 100644 index 0000000000000..da765df658906 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q40.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_item_id, w_state) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id", "w_state"]) + partial aggregation over (i_item_id, w_state) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q41.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q41.plan.txt new file mode 100644 index 0000000000000..6dcbeb51be654 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q41.plan.txt @@ -0,0 +1,20 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_product_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_product_name"]) + partial aggregation over (i_product_name) + cross join: + join (LEFT, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (i_manufact_14) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manufact_14"]) + partial aggregation over (i_manufact_14) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q42.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q42.plan.txt new file mode 100644 index 0000000000000..87792b40cd988 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q42.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_year, i_category, i_category_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year", "i_category", "i_category_id"]) + partial aggregation over (d_year, i_category, i_category_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q43.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q43.plan.txt new file mode 100644 index 0000000000000..001dbca108273 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q43.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (s_store_id, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_id", "s_store_name"]) + partial aggregation over (s_store_id, s_store_name) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q44.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q44.plan.txt new file mode 100644 index 0000000000000..d7f796253b756 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q44.plan.txt @@ -0,0 +1,50 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk_61"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["rank"]) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + cross join: + final aggregation over (ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + partial aggregation over (ss_item_sk) + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ss_store_sk_13) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_store_sk_13"]) + partial aggregation over (ss_store_sk_13) + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["rank_131"]) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + cross join: + final aggregation over (ss_item_sk_61) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk_61"]) + partial aggregation over (ss_item_sk_61) + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ss_store_sk_98) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_store_sk_98"]) + partial aggregation over (ss_store_sk_98) + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk"]) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_148"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q45.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q45.plan.txt new file mode 100644 index 0000000000000..64156b350f750 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q45.plan.txt @@ -0,0 +1,30 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ca_city, ca_zip) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_city", "ca_zip"]) + partial aggregation over (ca_city, ca_zip) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id"]) + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_2"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q46.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q46.plan.txt new file mode 100644 index 0000000000000..cfaebe1d13199 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q46.plan.txt @@ -0,0 +1,33 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + final aggregation over (ca_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_addr_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_26"]) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q47.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q47.plan.txt new file mode 100644 index 0000000000000..52a0683174a91 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q47.plan.txt @@ -0,0 +1,61 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand", "i_category", "s_company_name", "s_store_name"]) + final aggregation over (d_moy, d_year, i_brand, i_category, s_company_name, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy", "d_year", "i_brand", "i_category", "s_company_name", "s_store_name"]) + partial aggregation over (d_moy, d_year, i_brand, i_category, s_company_name, s_store_name) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_73", "i_category_77", "s_company_name_155", "s_store_name_143"]) + final aggregation over (d_moy_118, d_year_116, i_brand_73, i_category_77, s_company_name_155, s_store_name_143) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy_118", "d_year_116", "i_brand_73", "i_category_77", "s_company_name_155", "s_store_name_143"]) + partial aggregation over (d_moy_118, d_year_116, i_brand_73, i_category_77, s_company_name_155, s_store_name_143) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_250", "i_category_254", "s_company_name_332", "s_store_name_320"]) + final aggregation over (d_moy_295, d_year_293, i_brand_250, i_category_254, s_company_name_332, s_store_name_320) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy_295", "d_year_293", "i_brand_250", "i_category_254", "s_company_name_332", "s_store_name_320"]) + partial aggregation over (d_moy_295, d_year_293, i_brand_250, i_category_254, s_company_name_332, s_store_name_320) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q48.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q48.plan.txt new file mode 100644 index 0000000000000..a9e080182b8bf --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q48.plan.txt @@ -0,0 +1,22 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_addr_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q49.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q49.plan.txt new file mode 100644 index 0000000000000..3d53750691346 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q49.plan.txt @@ -0,0 +1,54 @@ +local exchange (GATHER, SINGLE, []) + final aggregation over (expr_244, expr_245, expr_246, expr_247, expr_248) + local exchange (REPARTITION, HASH, ["expr_244", "expr_245", "expr_246", "expr_247", "expr_248"]) + partial aggregation over (expr_12, expr_53, rank, rank_23, ws_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ws_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + partial aggregation over (ws_item_sk) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_item_sk", "wr_order_number"]) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk", "ws_order_number"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over (cs_item_sk, expr_101, expr_136, rank_113, rank_115) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (cs_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + partial aggregation over (cs_item_sk) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cr_item_sk", "cr_order_number"]) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk", "cs_order_number"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + partial aggregation over (expr_194, expr_239, rank_206, rank_208, ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + partial aggregation over (ss_item_sk) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["sr_item_sk", "sr_ticket_number"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk", "ss_ticket_number"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q50.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q50.plan.txt new file mode 100644 index 0000000000000..9f5732a36de79 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q50.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (s_city, s_company_id, s_county, s_state, s_store_name, s_street_name, s_street_number, s_street_type, s_suite_number, s_zip) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_city", "s_company_id", "s_county", "s_state", "s_store_name", "s_street_name", "s_street_number", "s_street_type", "s_suite_number", "s_zip"]) + partial aggregation over (s_city, s_company_id, s_county, s_state, s_store_name, s_street_name, s_street_number, s_street_type, s_suite_number, s_zip) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q51.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q51.plan.txt new file mode 100644 index 0000000000000..cd54861265c93 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q51.plan.txt @@ -0,0 +1,27 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_77"]) + join (FULL, PARTITIONED): + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + final aggregation over (d_date, ws_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date", "ws_item_sk"]) + partial aggregation over (d_date, ws_item_sk) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk"]) + final aggregation over (d_date_23, ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_23", "ss_item_sk"]) + partial aggregation over (d_date_23, ss_item_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q52.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q52.plan.txt new file mode 100644 index 0000000000000..33752e693e6ce --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q52.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_year, i_brand, i_brand_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year", "i_brand", "i_brand_id"]) + partial aggregation over (d_year, i_brand, i_brand_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q53.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q53.plan.txt new file mode 100644 index 0000000000000..cd10ab3698e65 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q53.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manufact_id"]) + final aggregation over (d_qoy, i_manufact_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_qoy", "i_manufact_id"]) + partial aggregation over (d_qoy, i_manufact_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q54.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q54.plan.txt new file mode 100644 index 0000000000000..20e57837636eb --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q54.plan.txt @@ -0,0 +1,65 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_134) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_134"]) + partial aggregation over (expr_134) + final aggregation over (c_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + partial aggregation over (c_customer_sk) + cross join: + cross join: + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (c_current_addr_sk, c_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_current_addr_sk", "c_customer_sk"]) + partial aggregation over (c_current_addr_sk, c_customer_sk) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + scan catalog_sales + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_86) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_86"]) + partial aggregation over (expr_86) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_118) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_118"]) + partial aggregation over (expr_118) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q55.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q55.plan.txt new file mode 100644 index 0000000000000..0322599f01e50 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q55.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_brand, i_brand_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand", "i_brand_id"]) + partial aggregation over (i_brand, i_brand_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q56.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q56.plan.txt new file mode 100644 index 0000000000000..05da2df474848 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q56.plan.txt @@ -0,0 +1,70 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_266) + local exchange (REPARTITION, HASH, ["expr_266"]) + partial aggregation over (i_item_id) + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_2"]) + scan item + partial aggregation over (i_item_id_86) + final aggregation over (i_item_id_86) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id_86) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id_86"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_109"]) + scan item + partial aggregation over (i_item_id_198) + final aggregation over (i_item_id_198) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id_198) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id_198"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_221"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q57.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q57.plan.txt new file mode 100644 index 0000000000000..22d2cb70c38d2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q57.plan.txt @@ -0,0 +1,61 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name", "i_brand", "i_category"]) + final aggregation over (cc_name, d_moy, d_year, i_brand, i_category) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name", "d_moy", "d_year", "i_brand", "i_category"]) + partial aggregation over (cc_name, d_moy, d_year, i_brand, i_category) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name_147", "i_brand_65", "i_category_69"]) + final aggregation over (cc_name_147, d_moy_121, d_year_119, i_brand_65, i_category_69) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name_147", "d_moy_121", "d_year_119", "i_brand_65", "i_category_69"]) + partial aggregation over (cc_name_147, d_moy_121, d_year_119, i_brand_65, i_category_69) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name_328", "i_brand_246", "i_category_250"]) + final aggregation over (cc_name_328, d_moy_302, d_year_300, i_brand_246, i_category_250) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name_328", "d_moy_302", "d_year_300", "i_brand_246", "i_category_250"]) + partial aggregation over (cc_name_328, d_moy_302, d_year_300, i_brand_246, i_category_250) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q58.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q58.plan.txt new file mode 100644 index 0000000000000..17a858e63eafd --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q58.plan.txt @@ -0,0 +1,76 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id"]) + partial aggregation over (i_item_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_3"]) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + scan date_dim + final aggregation over (i_item_id_79) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_79"]) + partial aggregation over (i_item_id_79) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date_102"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_131"]) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + scan date_dim + final aggregation over (i_item_id_210) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_210"]) + partial aggregation over (i_item_id_210) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date_233"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_262"]) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q59.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q59.plan.txt new file mode 100644 index 0000000000000..37c1bbaf2d1b8 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q59.plan.txt @@ -0,0 +1,40 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_week_seq", "s_store_id"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + final aggregation over (d_week_seq, ss_store_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq", "ss_store_sk"]) + partial aggregation over (d_week_seq, ss_store_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_361", "s_store_id_235"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + final aggregation over (d_week_seq_147, ss_store_sk_127) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_147", "ss_store_sk_127"]) + partial aggregation over (d_week_seq_147, ss_store_sk_127) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q60.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q60.plan.txt new file mode 100644 index 0000000000000..05da2df474848 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q60.plan.txt @@ -0,0 +1,70 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (expr_266) + local exchange (REPARTITION, HASH, ["expr_266"]) + partial aggregation over (i_item_id) + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_2"]) + scan item + partial aggregation over (i_item_id_86) + final aggregation over (i_item_id_86) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id_86) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id_86"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_109"]) + scan item + partial aggregation over (i_item_id_198) + final aggregation over (i_item_id_198) + local exchange (GATHER, SINGLE, []) + partial aggregation over (i_item_id_198) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["i_item_id_198"]) + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_221"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q61.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q61.plan.txt new file mode 100644 index 0000000000000..f9f0b76869567 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q61.plan.txt @@ -0,0 +1,57 @@ +cross join: + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk_7"]) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_84"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q62.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q62.plan.txt new file mode 100644 index 0000000000000..c91381b40a0a2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q62.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (sm_type, substr, web_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sm_type", "substr", "web_name"]) + partial aggregation over (sm_type, substr, web_name) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan ship_mode + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q63.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q63.plan.txt new file mode 100644 index 0000000000000..23e28eddc4b7d --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q63.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_manager_id"]) + final aggregation over (d_moy, i_manager_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy", "i_manager_id"]) + partial aggregation over (d_moy, i_manager_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q64.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q64.plan.txt new file mode 100644 index 0000000000000..b974738680e61 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q64.plan.txt @@ -0,0 +1,194 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + final aggregation over (ca_city, ca_city_98, ca_street_name, ca_street_name_95, ca_street_number, ca_street_number_94, ca_zip, ca_zip_101, d_year, d_year_28, d_year_56, i_item_sk, i_product_name, s_store_name, s_zip) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_city, ca_city_98, ca_street_name, ca_street_name_95, ca_street_number, ca_street_number_94, ca_zip, ca_zip_101, d_year, d_year_28, d_year_56, i_item_sk, i_product_name, s_store_name, s_zip) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["hd_income_band_sk_88"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["hd_income_band_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_addr_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_hdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_hdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_promo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_cdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_cdemo_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_first_shipto_date_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_first_sales_date_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_store_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_sold_date_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["sr_item_sk"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store_returns + final aggregation over (cs_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk"]) + partial aggregation over (cs_item_sk) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk", "cs_order_number"]) + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cr_item_sk", "cr_order_number"]) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_sk"]) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_22"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_50"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk_78"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["p_promo_sk"]) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["hd_demo_sk"]) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["hd_demo_sk_87"]) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_92"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ib_income_band_sk"]) + scan income_band + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ib_income_band_sk_105"]) + scan income_band + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk"]) + scan item + final aggregation over (ca_city_547, ca_city_560, ca_street_name_544, ca_street_name_557, ca_street_number_543, ca_street_number_556, ca_zip_550, ca_zip_563, d_year_369, d_year_397, d_year_425, i_item_sk_573, i_product_name_594, s_store_name_452, s_zip_472) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_city_547, ca_city_560, ca_street_name_544, ca_street_name_557, ca_street_number_543, ca_street_number_556, ca_zip_550, ca_zip_563, d_year_369, d_year_397, d_year_425, i_item_sk_573, i_product_name_594, s_store_name_452, s_zip_472) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk_292"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["hd_income_band_sk_537"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["hd_income_band_sk_532"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk_480"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_addr_sk_240"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_hdemo_sk_479"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_hdemo_sk_239"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_promo_sk_242"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_cdemo_sk_478"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_cdemo_sk_238"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_first_shipto_date_sk_481"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_first_sales_date_sk_482"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk_237"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_store_sk_241"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_sold_date_sk_234"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["sr_item_sk_259"]) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store_returns + final aggregation over (cs_item_sk_292) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk_292"]) + partial aggregation over (cs_item_sk_292) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_item_sk_292", "cs_order_number_294"]) + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cr_item_sk_313", "cr_order_number_327"]) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_363"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_sk_447"]) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_476"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_391"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_419"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk_494"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_demo_sk_503"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["p_promo_sk_512"]) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["hd_demo_sk_531"]) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["hd_demo_sk_536"]) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_541"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_554"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ib_income_band_sk_567"]) + scan income_band + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ib_income_band_sk_570"]) + scan income_band + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_573"]) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q65.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q65.plan.txt new file mode 100644 index 0000000000000..f09082ea78cc4 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q65.plan.txt @@ -0,0 +1,34 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + final aggregation over (ss_item_sk_26, ss_store_sk_31) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk_26", "ss_store_sk_31"]) + partial aggregation over (ss_item_sk_26, ss_store_sk_31) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["s_store_sk"]) + scan store + final aggregation over (ss_store_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_store_sk"]) + partial aggregation over (ss_store_sk) + final aggregation over (ss_item_sk, ss_store_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk", "ss_store_sk"]) + partial aggregation over (ss_item_sk, ss_store_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q66.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q66.plan.txt new file mode 100644 index 0000000000000..a9b44132630ab --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q66.plan.txt @@ -0,0 +1,48 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (concat_306, d_year_307, w_city_302, w_country_305, w_county_303, w_state_304, w_warehouse_name_300, w_warehouse_sq_ft_301) + local exchange (REPARTITION, HASH, ["concat_306", "d_year_307", "w_city_302", "w_country_305", "w_county_303", "w_state_304", "w_warehouse_name_300", "w_warehouse_sq_ft_301"]) + partial aggregation over (concat, d_year, w_city, w_country, w_county, w_state, w_warehouse_name, w_warehouse_sq_ft) + final aggregation over (d_year, w_city, w_country, w_county, w_state, w_warehouse_name, w_warehouse_sq_ft) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year", "w_city", "w_country", "w_county", "w_state", "w_warehouse_name", "w_warehouse_sq_ft"]) + partial aggregation over (d_year, w_city, w_country, w_county, w_state, w_warehouse_name, w_warehouse_sq_ft) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan ship_mode + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + partial aggregation over (concat_242, d_year_136, w_city_124, w_country_128, w_county_125, w_state_126, w_warehouse_name_118, w_warehouse_sq_ft_119) + final aggregation over (d_year_136, w_city_124, w_country_128, w_county_125, w_state_126, w_warehouse_name_118, w_warehouse_sq_ft_119) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year_136", "w_city_124", "w_country_128", "w_county_125", "w_state_126", "w_warehouse_name_118", "w_warehouse_sq_ft_119"]) + partial aggregation over (d_year_136, w_city_124, w_country_128, w_county_125, w_state_126, w_warehouse_name_118, w_warehouse_sq_ft_119) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan ship_mode + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q67.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q67.plan.txt new file mode 100644 index 0000000000000..6b438220e7039 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q67.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_category$gid"]) + final aggregation over (d_moy$gid, d_qoy$gid, d_year$gid, groupid, i_brand$gid, i_category$gid, i_class$gid, i_product_name$gid, s_store_id$gid) + local exchange (REPARTITION, HASH, ["d_moy$gid", "d_qoy$gid", "d_year$gid", "groupid", "i_brand$gid", "i_category$gid", "i_class$gid", "i_product_name$gid", "s_store_id$gid"]) + remote exchange (REPARTITION, HASH, ["d_moy$gid", "d_qoy$gid", "d_year$gid", "groupid", "i_brand$gid", "i_category$gid", "i_class$gid", "i_product_name$gid", "s_store_id$gid"]) + partial aggregation over (d_moy$gid, d_qoy$gid, d_year$gid, groupid, i_brand$gid, i_category$gid, i_class$gid, i_product_name$gid, s_store_id$gid) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q68.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q68.plan.txt new file mode 100644 index 0000000000000..ac2fd19b2cb05 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q68.plan.txt @@ -0,0 +1,33 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_current_addr_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + final aggregation over (ca_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ca_address_sk"]) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_addr_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_address_sk_32"]) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q69.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q69.plan.txt new file mode 100644 index 0000000000000..b4e12b0f4a331 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q69.plan.txt @@ -0,0 +1,50 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (cd_credit_rating, cd_education_status, cd_gender, cd_marital_status, cd_purchase_estimate) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_credit_rating", "cd_education_status", "cd_gender", "cd_marital_status", "cd_purchase_estimate"]) + partial aggregation over (cd_credit_rating, cd_education_status, cd_gender, cd_marital_status, cd_purchase_estimate) + join (LEFT, PARTITIONED): + join (RIGHT, PARTITIONED): + final aggregation over (ws_bill_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + partial aggregation over (ws_bill_customer_sk) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cd_demo_sk"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_current_cdemo_sk"]) + join (INNER, PARTITIONED): + final aggregation over (ss_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + partial aggregation over (ss_customer_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + final aggregation over (cs_ship_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_ship_customer_sk"]) + partial aggregation over (cs_ship_customer_sk) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q70.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q70.plan.txt new file mode 100644 index 0000000000000..20609c3c276ed --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q70.plan.txt @@ -0,0 +1,34 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_110", "expr_111"]) + final aggregation over (groupid, s_county$gid, s_state$gid_102) + local exchange (REPARTITION, HASH, ["groupid", "s_county$gid", "s_state$gid_102"]) + remote exchange (REPARTITION, HASH, ["groupid", "s_county$gid", "s_state$gid_102"]) + partial aggregation over (groupid, s_county$gid, s_state$gid_102) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["s_state"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_state_48"]) + final aggregation over (s_state_48) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_state_48"]) + partial aggregation over (s_state_48) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q71.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q71.plan.txt new file mode 100644 index 0000000000000..599dd0ab9f9c0 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q71.plan.txt @@ -0,0 +1,31 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (i_brand, i_brand_id, t_hour, t_minute) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand", "i_brand_id", "t_hour", "t_minute"]) + partial aggregation over (i_brand, i_brand_id, t_hour, t_minute) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q72.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q72.plan.txt new file mode 100644 index 0000000000000..bcda1a8ede5a2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q72.plan.txt @@ -0,0 +1,48 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_week_seq, i_item_desc, w_warehouse_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq", "i_item_desc", "w_warehouse_name"]) + partial aggregation over (d_week_seq, i_item_desc, w_warehouse_name) + join (LEFT, REPLICATED): + join (LEFT, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_week_seq_15", "inv_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk", "d_week_seq"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_returns diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q73.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q73.plan.txt new file mode 100644 index 0000000000000..9fa2321b10235 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q73.plan.txt @@ -0,0 +1,25 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + final aggregation over (ss_customer_sk, ss_ticket_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_ticket_number"]) + partial aggregation over (ss_customer_sk, ss_ticket_number) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q74.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q74.plan.txt new file mode 100644 index 0000000000000..c9ef35f5720ab --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q74.plan.txt @@ -0,0 +1,96 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["c_customer_id_84"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_customer_id, c_first_name, c_last_name, d_year) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id", "c_first_name", "c_last_name", "d_year"]) + partial aggregation over (c_customer_id, c_first_name, c_last_name, d_year) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_customer_id_17, c_first_name_24, c_last_name_25, d_year_40) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_301"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (c_customer_id_109, c_first_name_116, c_last_name_117, d_year_155) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_109", "c_first_name_116", "c_last_name_117", "d_year_155"]) + partial aggregation over (c_customer_id_109, c_first_name_116, c_last_name_117, d_year_155) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + single aggregation over (c_customer_id_200, c_first_name_207, c_last_name_208, d_year_257) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_326"]) + single aggregation over (c_customer_id_326, c_first_name_333, c_last_name_334, d_year_372) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_417"]) + final aggregation over (c_customer_id_417, c_first_name_424, c_last_name_425, d_year_474) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_417", "c_first_name_424", "c_last_name_425", "d_year_474"]) + partial aggregation over (c_customer_id_417, c_first_name_424, c_last_name_425, d_year_474) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_438"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_416"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_543"]) + single aggregation over (c_customer_id_543, c_first_name_550, c_last_name_551, d_year_589) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + values (0 rows) + values (0 rows) + values (0 rows) + remote exchange (REPARTITION, HASH, ["c_customer_id_634"]) + final aggregation over (c_customer_id_634, c_first_name_641, c_last_name_642, d_year_691) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_id_634", "c_first_name_641", "c_last_name_642", "d_year_691"]) + partial aggregation over (c_customer_id_634, c_first_name_641, c_last_name_642, d_year_691) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk_655"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_633"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q75.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q75.plan.txt new file mode 100644 index 0000000000000..b036635321401 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q75.plan.txt @@ -0,0 +1,105 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + single aggregation over (d_year_172, i_brand_id_173, i_category_id_175, i_class_id_174, i_manufact_id_176) + final aggregation over (d_year_172, expr_177, expr_178, i_brand_id_173, i_category_id_175, i_class_id_174, i_manufact_id_176) + local exchange (REPARTITION, HASH, ["d_year_172", "i_brand_id_173", "i_category_id_175", "i_class_id_174", "i_manufact_id_176"]) + remote exchange (REPARTITION, HASH, ["i_brand_id", "i_category_id", "i_class_id", "i_manufact_id"]) + partial aggregation over (d_year, expr, expr_13, i_brand_id, i_category_id, i_class_id, i_manufact_id) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cr_item_sk", "cr_order_number"]) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk", "cs_order_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + remote exchange (REPARTITION, HASH, ["i_brand_id_28", "i_category_id_32", "i_class_id_30", "i_manufact_id_34"]) + partial aggregation over (d_year_51, expr_84, expr_85, i_brand_id_28, i_category_id_32, i_class_id_30, i_manufact_id_34) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["sr_item_sk", "sr_ticket_number"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk", "ss_ticket_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + remote exchange (REPARTITION, HASH, ["i_brand_id_107", "i_category_id_111", "i_class_id_109", "i_manufact_id_113"]) + partial aggregation over (d_year_130, expr_163, expr_164, i_brand_id_107, i_category_id_111, i_class_id_109, i_manufact_id_113) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_item_sk", "wr_order_number"]) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk", "ws_order_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + single aggregation over (d_year_619, i_brand_id_620, i_category_id_622, i_class_id_621, i_manufact_id_623) + final aggregation over (d_year_619, expr_624, expr_625, i_brand_id_620, i_category_id_622, i_class_id_621, i_manufact_id_623) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand_id_275", "i_category_id_279", "i_class_id_277", "i_manufact_id_281"]) + partial aggregation over (d_year_298, expr_358, expr_359, i_brand_id_275, i_category_id_279, i_class_id_277, i_manufact_id_281) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cr_item_sk_324", "cr_order_number_338"]) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_item_sk_249", "cs_order_number_251"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + remote exchange (REPARTITION, HASH, ["i_brand_id_397", "i_category_id_401", "i_class_id_399", "i_manufact_id_403"]) + partial aggregation over (d_year_420, expr_473, expr_474, i_brand_id_397, i_category_id_401, i_class_id_399, i_manufact_id_403) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["sr_item_sk_446", "sr_ticket_number_453"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_item_sk_369", "ss_ticket_number_376"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + remote exchange (REPARTITION, HASH, ["i_brand_id_530", "i_category_id_534", "i_class_id_532", "i_manufact_id_536"]) + partial aggregation over (d_year_553, expr_610, expr_611, i_brand_id_530, i_category_id_534, i_class_id_532, i_manufact_id_536) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_item_sk_579", "wr_order_number_590"]) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk_492", "ws_order_number_506"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q76.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q76.plan.txt new file mode 100644 index 0000000000000..8e6f704d4410b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q76.plan.txt @@ -0,0 +1,40 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (d_qoy_168, d_year_167, expr_165, expr_166, i_category_169) + local exchange (REPARTITION, HASH, ["d_qoy_168", "d_year_167", "expr_165", "expr_166", "i_category_169"]) + local exchange (REPARTITION, ROUND_ROBIN, []) + remote exchange (REPARTITION, HASH, ["d_qoy", "d_year", "expr_12", "expr_217", "i_category"]) + partial aggregation over (d_qoy, d_year, expr_12, expr_217, i_category) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + remote exchange (REPARTITION, HASH, ["d_qoy_49", "d_year_45", "expr_223", "expr_68", "i_category_29"]) + partial aggregation over (d_qoy_49, d_year_45, expr_223, expr_68, i_category_29) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_sold_date_sk"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_sk_17"]) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_sk_39"]) + scan date_dim + remote exchange (REPARTITION, HASH, ["d_qoy_129", "d_year_125", "expr_147", "expr_160", "i_category_109"]) + partial aggregation over (d_qoy_129, d_year_125, expr_147, expr_160, i_category_109) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q77.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q77.plan.txt new file mode 100644 index 0000000000000..32474cf29af00 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q77.plan.txt @@ -0,0 +1,82 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + remote exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + partial aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, ROUND_ROBIN, []) + join (LEFT, PARTITIONED): + final aggregation over (s_store_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_sk"]) + partial aggregation over (s_store_sk) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + final aggregation over (s_store_sk_46) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_sk_46"]) + partial aggregation over (s_store_sk_46) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + cross join: + final aggregation over (cs_call_center_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_call_center_sk"]) + partial aggregation over (cs_call_center_sk) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (cr_call_center_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cr_call_center_sk"]) + partial aggregation over (cr_call_center_sk) + join (INNER, REPLICATED): + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + join (LEFT, PARTITIONED): + final aggregation over (wp_web_page_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wp_web_page_sk"]) + partial aggregation over (wp_web_page_sk) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_page + final aggregation over (wp_web_page_sk_298) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wp_web_page_sk_298"]) + partial aggregation over (wp_web_page_sk_298) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_page diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q78.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q78.plan.txt new file mode 100644 index 0000000000000..cba938a811083 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q78.plan.txt @@ -0,0 +1,46 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + join (INNER, PARTITIONED): + final aggregation over (d_year, ss_customer_sk, ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year", "ss_customer_sk", "ss_item_sk"]) + partial aggregation over (d_year, ss_customer_sk, ss_item_sk) + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + final aggregation over (d_year_53, ws_bill_customer_sk, ws_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_year_53", "ws_bill_customer_sk", "ws_item_sk"]) + partial aggregation over (d_year_53, ws_bill_customer_sk, ws_item_sk) + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + final aggregation over (cs_bill_customer_sk, cs_item_sk, d_year_130) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk", "cs_item_sk", "d_year_130"]) + partial aggregation over (cs_bill_customer_sk, cs_item_sk, d_year_130) + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q79.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q79.plan.txt new file mode 100644 index 0000000000000..1541432df999d --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q79.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, REPLICATED): + final aggregation over (s_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_city", "ss_addr_sk", "ss_customer_sk", "ss_ticket_number"]) + partial aggregation over (s_city, ss_addr_sk, ss_customer_sk, ss_ticket_number) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q80.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q80.plan.txt new file mode 100644 index 0000000000000..9fbd54c9adae9 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q80.plan.txt @@ -0,0 +1,82 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + remote exchange (REPARTITION, HASH, ["channel$gid", "groupid", "id$gid"]) + partial aggregation over (channel$gid, groupid, id$gid) + local exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (s_store_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["s_store_id"]) + partial aggregation over (s_store_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + final aggregation over (cp_catalog_page_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cp_catalog_page_id"]) + partial aggregation over (cp_catalog_page_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan catalog_page + final aggregation over (web_site_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["web_site_id"]) + partial aggregation over (web_site_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (LEFT, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan promotion + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q81.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q81.plan.txt new file mode 100644 index 0000000000000..c32e34721947a --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q81.plan.txt @@ -0,0 +1,48 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + cross join: + join (LEFT, REPLICATED): + join (INNER, REPLICATED): + final aggregation over (ca_state, cr_returning_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state", "cr_returning_customer_sk"]) + partial aggregation over (ca_state, cr_returning_customer_sk) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over (ca_state_93) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state_93"]) + partial aggregation over (ca_state_93) + final aggregation over (ca_state_93, cr_returning_customer_sk_37) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ca_state_93", "cr_returning_customer_sk_37"]) + partial aggregation over (ca_state_93, cr_returning_customer_sk_37) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q82.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q82.plan.txt new file mode 100644 index 0000000000000..0c8eb3bf442d7 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q82.plan.txt @@ -0,0 +1,19 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (i_current_price, i_item_desc, i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_current_price", "i_item_desc", "i_item_id"]) + partial aggregation over (i_current_price, i_item_desc, i_item_id) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan inventory + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q83.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q83.plan.txt new file mode 100644 index 0000000000000..bcc5cc802a003 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q83.plan.txt @@ -0,0 +1,73 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, PARTITIONED): + final aggregation over (i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id"]) + partial aggregation over (i_item_id) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_3"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_week_seq_5"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_34"]) + scan date_dim + join (INNER, PARTITIONED): + final aggregation over (i_item_id_82) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_82"]) + partial aggregation over (i_item_id_82) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date_105"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_134"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_week_seq_136"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_165"]) + scan date_dim + final aggregation over (i_item_id_216) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_item_id_216"]) + partial aggregation over (i_item_id_216) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_date_239"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_date_268"]) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["d_week_seq_270"]) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_week_seq_299"]) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q84.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q84.plan.txt new file mode 100644 index 0000000000000..ec22af2c6a55b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q84.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + join (INNER, REPLICATED): + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan income_band diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q85.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q85.plan.txt new file mode 100644 index 0000000000000..77a2fc4bdef06 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q85.plan.txt @@ -0,0 +1,37 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (r_reason_desc) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["r_reason_desc"]) + partial aggregation over (r_reason_desc) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cd_demo_sk_0", "cd_education_status_3", "cd_marital_status_2"]) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cd_education_status", "cd_marital_status", "wr_returning_cdemo_sk"]) + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_item_sk", "ws_order_number"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wr_item_sk", "wr_order_number"]) + join (INNER, REPLICATED): + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_page + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan reason diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q86.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q86.plan.txt new file mode 100644 index 0000000000000..5f7832a4e2f6c --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q86.plan.txt @@ -0,0 +1,17 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_10", "expr_9"]) + final aggregation over (groupid, i_category$gid, i_class$gid) + local exchange (REPARTITION, HASH, ["groupid", "i_category$gid", "i_class$gid"]) + remote exchange (REPARTITION, HASH, ["groupid", "i_category$gid", "i_class$gid"]) + partial aggregation over (groupid, i_category$gid, i_class$gid) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q87.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q87.plan.txt new file mode 100644 index 0000000000000..8f1baf197b0ad --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q87.plan.txt @@ -0,0 +1,50 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (expr_130, expr_131, expr_132) + local exchange (REPARTITION, HASH, ["expr_130", "expr_131", "expr_132"]) + partial aggregation over (c_first_name, c_last_name, d_date) + final aggregation over (c_first_name, c_last_name, d_date) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name", "c_last_name", "d_date"]) + partial aggregation over (c_first_name, c_last_name, d_date) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer + partial aggregation over (c_first_name_47, c_last_name_48, d_date_13) + final aggregation over (c_first_name_47, c_last_name_48, d_date_13) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_47", "c_last_name_48", "d_date_13"]) + partial aggregation over (c_first_name_47, c_last_name_48, d_date_13) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk"]) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_39"]) + scan customer + partial aggregation over (c_first_name_108, c_last_name_109, d_date_74) + final aggregation over (c_first_name_108, c_last_name_109, d_date_74) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_first_name_108", "c_last_name_109", "d_date_74"]) + partial aggregation over (c_first_name_108, c_last_name_109, d_date_74) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_bill_customer_sk"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["c_customer_sk_100"]) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q88.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q88.plan.txt new file mode 100644 index 0000000000000..6e297c60e2012 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q88.plan.txt @@ -0,0 +1,143 @@ +cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + cross join: + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q89.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q89.plan.txt new file mode 100644 index 0000000000000..396d705bbc386 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q89.plan.txt @@ -0,0 +1,21 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_brand", "i_category", "s_company_name", "s_store_name"]) + final aggregation over (d_moy, i_brand, i_category, i_class, s_company_name, s_store_name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["d_moy", "i_brand", "i_category", "i_class", "s_company_name", "s_store_name"]) + partial aggregation over (d_moy, i_brand, i_category, i_class, s_company_name, s_store_name) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q90.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q90.plan.txt new file mode 100644 index 0000000000000..0ddf21aa574df --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q90.plan.txt @@ -0,0 +1,35 @@ +cross join: + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_page + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_page + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q91.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q91.plan.txt new file mode 100644 index 0000000000000..a8367e55cdb02 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q91.plan.txt @@ -0,0 +1,32 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (cc_call_center_id, cc_manager, cc_name, cd_education_status, cd_marital_status) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_call_center_id", "cc_manager", "cc_name", "cd_education_status", "cd_marital_status"]) + partial aggregation over (cc_call_center_id, cc_manager, cc_name, cd_education_status, cd_marital_status) + join (INNER, REPLICATED): + scan call_center + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan catalog_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q92.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q92.plan.txt new file mode 100644 index 0000000000000..fe73a232a885b --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q92.plan.txt @@ -0,0 +1,30 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + cross join: + join (RIGHT, PARTITIONED): + final aggregation over (ws_item_sk_3) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk_3"]) + partial aggregation over (ws_item_sk_3) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_item_sk"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q93.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q93.plan.txt new file mode 100644 index 0000000000000..01324ef4f25f5 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q93.plan.txt @@ -0,0 +1,16 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (ss_customer_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk"]) + partial aggregation over (ss_customer_sk) + join (INNER, REPLICATED): + join (LEFT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ss_item_sk", "ss_ticket_number"]) + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["sr_item_sk", "sr_ticket_number"]) + scan store_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan reason diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q94.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q94.plan.txt new file mode 100644 index 0000000000000..c134240654765 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q94.plan.txt @@ -0,0 +1,31 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (LEFT, PARTITIONED): + final aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + local exchange (GATHER, SINGLE, []) + partial aggregation over (ca_state, d_date, unique, web_company_name, ws_ext_ship_cost, ws_net_profit, ws_order_number, ws_ship_addr_sk, ws_ship_date_sk, ws_warehouse_sk, ws_web_site_sk) + join (RIGHT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_order_number_17"]) + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_order_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site + final aggregation over (wr_order_number) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wr_order_number"]) + partial aggregation over (wr_order_number) + scan web_returns diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q95.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q95.plan.txt new file mode 100644 index 0000000000000..1f190927bfd80 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q95.plan.txt @@ -0,0 +1,41 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + local exchange (GATHER, SINGLE, []) + semijoin (PARTITIONED): + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_order_number"]) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer_address + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_site + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_order_number_17"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["ws_order_number_17"]) + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_order_number_51"]) + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["wr_order_number"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["wr_order_number"]) + join (INNER, REPLICATED): + scan web_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan web_returns + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ws_order_number_141"]) + scan web_sales diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q96.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q96.plan.txt new file mode 100644 index 0000000000000..6ac294895946f --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q96.plan.txt @@ -0,0 +1,17 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan time_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan household_demographics + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan store diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q97.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q97.plan.txt new file mode 100644 index 0000000000000..28b301c990947 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q97.plan.txt @@ -0,0 +1,23 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (FULL, PARTITIONED): + final aggregation over (ss_customer_sk, ss_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["ss_customer_sk", "ss_item_sk"]) + partial aggregation over (ss_customer_sk, ss_item_sk) + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + final aggregation over (cs_bill_customer_sk, cs_item_sk) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cs_bill_customer_sk", "cs_item_sk"]) + partial aggregation over (cs_bill_customer_sk, cs_item_sk) + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q98.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q98.plan.txt new file mode 100644 index 0000000000000..9775b6c511f56 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q98.plan.txt @@ -0,0 +1,18 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_class"]) + final aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["i_category", "i_class", "i_current_price", "i_item_desc", "i_item_id"]) + partial aggregation over (i_category, i_class, i_current_price, i_item_desc, i_item_id) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan store_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan item diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q99.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q99.plan.txt new file mode 100644 index 0000000000000..e077b55eaab26 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpcds/q99.plan.txt @@ -0,0 +1,23 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (cc_name, sm_type, substr) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["cc_name", "sm_type", "substr"]) + partial aggregation over (cc_name, sm_type, substr) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan catalog_sales + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan date_dim + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan ship_mode + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan warehouse + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan call_center diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q01.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q01.plan.txt new file mode 100644 index 0000000000000..d632b41b395aa --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q01.plan.txt @@ -0,0 +1,8 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (linestatus, returnflag) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["linestatus", "returnflag"]) + partial aggregation over (linestatus, returnflag) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q02.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q02.plan.txt new file mode 100644 index 0000000000000..98d7dfd9ce458 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q02.plan.txt @@ -0,0 +1,46 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + cross join: + join (RIGHT, PARTITIONED): + final aggregation over (partkey_15) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_15"]) + partial aggregation over (partkey_15) + join (INNER, REPLICATED): + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan region + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey_4"]) + join (INNER, REPLICATED): + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey"]) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan region + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q03.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q03.plan.txt new file mode 100644 index 0000000000000..054e207ee6b27 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q03.plan.txt @@ -0,0 +1,15 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (orderdate, orderkey_3, shippriority) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderdate", "orderkey_3", "shippriority"]) + partial aggregation over (orderdate, orderkey_3, shippriority) + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan customer diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q04.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q04.plan.txt new file mode 100644 index 0000000000000..f1a033c8f1dea --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q04.plan.txt @@ -0,0 +1,15 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (orderpriority) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderpriority"]) + partial aggregation over (orderpriority) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + scan orders + final aggregation over (orderkey_0) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_0"]) + partial aggregation over (orderkey_0) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q05.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q05.plan.txt new file mode 100644 index 0000000000000..7583600ca6080 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q05.plan.txt @@ -0,0 +1,29 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (name_15) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["name_15"]) + partial aggregation over (name_15) + join (INNER, REPLICATED): + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["custkey_0"]) + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["custkey"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan region + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan supplier diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q06.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q06.plan.txt new file mode 100644 index 0000000000000..e11a487cf0302 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q06.plan.txt @@ -0,0 +1,5 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q07.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q07.plan.txt new file mode 100644 index 0000000000000..375d69c002424 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q07.plan.txt @@ -0,0 +1,29 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (expr_24, name_15, name_19) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_24", "name_15", "name_19"]) + partial aggregation over (expr_24, name_15, name_19) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_3"]) + join (INNER, REPLICATED): + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q08.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q08.plan.txt new file mode 100644 index 0000000000000..0f2d764aa8708 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q08.plan.txt @@ -0,0 +1,38 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (expr) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr"]) + partial aggregation over (expr) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey_4"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey_7"]) + join (INNER, REPLICATED): + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan region + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey"]) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q09.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q09.plan.txt new file mode 100644 index 0000000000000..1dc7e904716e7 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q09.plan.txt @@ -0,0 +1,32 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (expr_18, name_15) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["expr_18", "name_15"]) + partial aggregation over (expr_18, name_15) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["nationkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey_4"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["partkey"]) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_3"]) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey"]) + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_8"]) + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_11"]) + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["nationkey_14"]) + scan nation diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q10.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q10.plan.txt new file mode 100644 index 0000000000000..45310667f8724 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q10.plan.txt @@ -0,0 +1,19 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (acctbal, address, comment_4, custkey_3, name, name_7, phone) + local exchange (GATHER, SINGLE, []) + partial aggregation over (acctbal, address, comment_4, custkey_3, name, name_7, phone) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["custkey_3"]) + join (INNER, REPLICATED): + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["custkey"]) + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan orders diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q11.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q11.plan.txt new file mode 100644 index 0000000000000..bfc5af4b5bb23 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q11.plan.txt @@ -0,0 +1,32 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + cross join: + final aggregation over (partkey) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey"]) + partial aggregation over (partkey) + join (INNER, REPLICATED): + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, REPLICATED): + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q12.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q12.plan.txt new file mode 100644 index 0000000000000..7fb5b836dfa90 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q12.plan.txt @@ -0,0 +1,13 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (shipmode) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["shipmode"]) + partial aggregation over (shipmode) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_0"]) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q13.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q13.plan.txt new file mode 100644 index 0000000000000..96af9081b5217 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q13.plan.txt @@ -0,0 +1,16 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (count) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["count"]) + partial aggregation over (count) + final aggregation over (custkey) + local exchange (GATHER, SINGLE, []) + partial aggregation over (custkey) + join (LEFT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["custkey"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["custkey_0"]) + scan orders diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q14.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q14.plan.txt new file mode 100644 index 0000000000000..e46c0418a7596 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q14.plan.txt @@ -0,0 +1,10 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["partkey_0"]) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey"]) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q15.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q15.plan.txt new file mode 100644 index 0000000000000..66ec494a2bf4c --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q15.plan.txt @@ -0,0 +1,23 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + join (INNER, REPLICATED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey"]) + scan supplier + final aggregation over (suppkey_0) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_0"]) + partial aggregation over (suppkey_0) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + final aggregation over (suppkey_16) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_16"]) + partial aggregation over (suppkey_16) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q16.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q16.plan.txt new file mode 100644 index 0000000000000..ef581b90df64a --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q16.plan.txt @@ -0,0 +1,21 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (brand, size, type) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["brand", "size", "type"]) + partial aggregation over (brand, size, type) + final aggregation over (brand, size, suppkey, type) + local exchange (GATHER, SINGLE, []) + partial aggregation over (brand, size, suppkey, type) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["partkey"]) + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_0"]) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_3"]) + scan supplier diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q17.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q17.plan.txt new file mode 100644 index 0000000000000..bbcdb95d961dd --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q17.plan.txt @@ -0,0 +1,22 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + cross join: + join (RIGHT, PARTITIONED): + final aggregation over (partkey_4) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_4"]) + partial aggregation over (partkey_4) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey"]) + join (INNER, REPLICATED): + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q18.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q18.plan.txt new file mode 100644 index 0000000000000..a10b26c531c08 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q18.plan.txt @@ -0,0 +1,24 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (custkey, name, orderdate, orderkey, totalprice) + local exchange (GATHER, SINGLE, []) + partial aggregation over (custkey, name, orderdate, orderkey, totalprice) + semijoin (PARTITIONED): + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey_3"]) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["custkey_0"]) + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["custkey"]) + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_6"]) + final aggregation over (orderkey_6) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_6"]) + partial aggregation over (orderkey_6) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q19.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q19.plan.txt new file mode 100644 index 0000000000000..fa00639798fe2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q19.plan.txt @@ -0,0 +1,10 @@ +final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["partkey"]) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_0"]) + scan part diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q20.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q20.plan.txt new file mode 100644 index 0000000000000..70c9c82a66708 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q20.plan.txt @@ -0,0 +1,31 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey"]) + join (INNER, REPLICATED): + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_4"]) + cross join: + join (LEFT, PARTITIONED): + semijoin (PARTITIONED): + remote exchange (REPARTITION, HASH, ["partkey"]) + scan partsupp + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_8"]) + scan part + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_18"]) + final aggregation over (partkey_18, suppkey_19) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["partkey_18", "suppkey_19"]) + partial aggregation over (partkey_18, suppkey_19) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + single aggregation over () + values (1 rows) diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q21.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q21.plan.txt new file mode 100644 index 0000000000000..26c25bcf6b2e2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q21.plan.txt @@ -0,0 +1,35 @@ +local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + final aggregation over (name) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["name"]) + partial aggregation over (name) + single aggregation over (commitdate, exists, name, name_7, nationkey, orderkey, orderstatus, receiptdate, suppkey, unique) + join (LEFT, PARTITIONED): + final aggregation over (commitdate, name, name_7, nationkey, orderkey, orderstatus, receiptdate, suppkey, unique_189) + local exchange (GATHER, SINGLE, []) + partial aggregation over (commitdate, name, name_7, nationkey, orderkey, orderstatus, receiptdate, suppkey, unique_189) + join (LEFT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["nationkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["orderkey"]) + join (INNER, PARTITIONED): + remote exchange (REPARTITION, HASH, ["suppkey"]) + scan supplier + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["suppkey_0"]) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_3"]) + scan orders + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["nationkey_6"]) + scan nation + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_10"]) + scan lineitem + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["orderkey_92"]) + scan lineitem diff --git a/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q22.plan.txt b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q22.plan.txt new file mode 100644 index 0000000000000..da536bc1f9df2 --- /dev/null +++ b/presto-benchto-benchmarks/src/test/resources/sql/presto/tpch/q22.plan.txt @@ -0,0 +1,23 @@ +remote exchange (GATHER, SINGLE, []) + local exchange (GATHER, UNKNOWN, []) + remote exchange (REPARTITION, ROUND_ROBIN, []) + final aggregation over (substr) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["substr"]) + partial aggregation over (substr) + join (LEFT, PARTITIONED): + remote exchange (REPARTITION, HASH, ["custkey"]) + cross join: + scan customer + local exchange (GATHER, SINGLE, []) + remote exchange (REPLICATE, BROADCAST, []) + final aggregation over () + local exchange (GATHER, SINGLE, []) + remote exchange (GATHER, SINGLE, []) + partial aggregation over () + scan customer + final aggregation over (custkey_13) + local exchange (GATHER, SINGLE, []) + remote exchange (REPARTITION, HASH, ["custkey_13"]) + partial aggregation over (custkey_13) + scan orders diff --git a/presto-blackhole/pom.xml b/presto-blackhole/pom.xml index d91e811d2e9a3..50fda69aa2973 100644 --- a/presto-blackhole/pom.xml +++ b/presto-blackhole/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-blackhole diff --git a/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnectorFactory.java b/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnectorFactory.java index de8d70e457d22..cd90c24895213 100644 --- a/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnectorFactory.java +++ b/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleConnectorFactory.java @@ -41,7 +41,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map requiredConfig, ConnectorContext context) + public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) { ListeningScheduledExecutorService executorService = listeningDecorator(newSingleThreadScheduledExecutor(daemonThreadsNamed("blackhole"))); return new BlackHoleConnector( diff --git a/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleNodePartitioningProvider.java b/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleNodePartitioningProvider.java index 3135cfe2d46fb..6fd0bd5a2a12b 100644 --- a/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleNodePartitioningProvider.java +++ b/presto-blackhole/src/main/java/com/facebook/presto/plugin/blackhole/BlackHoleNodePartitioningProvider.java @@ -16,21 +16,19 @@ import com.facebook.presto.spi.BucketFunction; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorSplit; -import com.facebook.presto.spi.Node; import com.facebook.presto.spi.NodeManager; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; -import com.google.common.collect.ImmutableMap; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.function.ToIntFunction; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static com.facebook.presto.spi.connector.ConnectorBucketNodeMap.createBucketNodeMap; import static java.util.Objects.requireNonNull; public class BlackHoleNodePartitioningProvider @@ -44,18 +42,10 @@ public BlackHoleNodePartitioningProvider(NodeManager nodeManager) } @Override - public Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { - Set nodes = nodeManager.getRequiredWorkerNodes(); - - // create on part per node - ImmutableMap.Builder distribution = ImmutableMap.builder(); - int partNumber = 0; - for (Node node : nodes) { - distribution.put(partNumber, node); - partNumber++; - } - return distribution.build(); + // create one bucket per node + return createBucketNodeMap(nodeManager.getRequiredWorkerNodes().size()); } @Override diff --git a/presto-blackhole/src/test/java/com/facebook/presto/plugin/blackhole/TestBlackHoleSmoke.java b/presto-blackhole/src/test/java/com/facebook/presto/plugin/blackhole/TestBlackHoleSmoke.java index ab6e719a43817..ab4e119b1b8b0 100644 --- a/presto-blackhole/src/test/java/com/facebook/presto/plugin/blackhole/TestBlackHoleSmoke.java +++ b/presto-blackhole/src/test/java/com/facebook/presto/plugin/blackhole/TestBlackHoleSmoke.java @@ -258,7 +258,7 @@ public void testSelectAllTypes() assertEquals(row.getField(6), 0.0); assertEquals(row.getField(7), false); assertEquals(row.getField(8), LocalDate.ofEpochDay(0)); - assertEquals(row.getField(9), LocalDateTime.of(1970, 1, 1, 0, 0, 0)); + assertEquals(row.getField(9), LocalDateTime.of(1969, 12, 31, 13, 0, 0)); // TODO #7122 should be 1970-01-01 00:00:00 assertEquals(row.getField(10), "****************".getBytes()); assertEquals(row.getField(11), new BigDecimal("0.00")); assertEquals(row.getField(12), new BigDecimal("00000000000000000000.0000000000")); diff --git a/presto-cassandra/pom.xml b/presto-cassandra/pom.xml index cad48c47680ad..1baa35de2d36d 100644 --- a/presto-cassandra/pom.xml +++ b/presto-cassandra/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-cassandra @@ -77,6 +77,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.inject javax.inject @@ -189,6 +195,20 @@ + + + org.apache.maven.plugins + maven-enforcer-plugin + + + + + org.yaml:snakeyaml + + + + + diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientConfig.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientConfig.java index f88bacdeb56f9..5293bd709d5ad 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientConfig.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientConfig.java @@ -14,6 +14,7 @@ package com.facebook.presto.cassandra; import com.datastax.driver.core.ConsistencyLevel; +import com.datastax.driver.core.ProtocolVersion; import com.datastax.driver.core.SocketOptions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; @@ -31,6 +32,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; @@ -48,6 +50,7 @@ public class CassandraClientConfig private int nativeProtocolPort = 9042; private int partitionSizeForBatchSelect = 100; private int splitSize = 1_024; + private Long splitsPerNode; private boolean allowDropTable; private String username; private String password; @@ -66,6 +69,7 @@ public class CassandraClientConfig private Duration noHostAvailableRetryTimeout = new Duration(1, MINUTES); private int speculativeExecutionLimit = 1; private Duration speculativeExecutionDelay = new Duration(500, MILLISECONDS); + private ProtocolVersion protocolVersion = ProtocolVersion.V3; @NotNull @Size(min = 1) @@ -152,6 +156,18 @@ public CassandraClientConfig setSplitSize(int splitSize) return this; } + public Optional getSplitsPerNode() + { + return Optional.ofNullable(splitsPerNode); + } + + @Config("cassandra.splits-per-node") + public CassandraClientConfig setSplitsPerNode(Long splitsPerNode) + { + this.splitsPerNode = splitsPerNode; + return this; + } + public boolean getAllowDropTable() { return this.allowDropTable; @@ -379,4 +395,16 @@ public CassandraClientConfig setSpeculativeExecutionDelay(Duration speculativeEx this.speculativeExecutionDelay = speculativeExecutionDelay; return this; } + + public ProtocolVersion getProtocolVersion() + { + return protocolVersion; + } + + @Config("cassandra.protocol-version") + public CassandraClientConfig setProtocolVersion(ProtocolVersion version) + { + this.protocolVersion = version; + return this; + } } diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientModule.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientModule.java index 21a386902d830..159999719ac52 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientModule.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraClientModule.java @@ -14,7 +14,6 @@ package com.facebook.presto.cassandra; import com.datastax.driver.core.Cluster; -import com.datastax.driver.core.ProtocolVersion; import com.datastax.driver.core.QueryOptions; import com.datastax.driver.core.SocketOptions; import com.datastax.driver.core.policies.ConstantSpeculativeExecutionPolicy; @@ -63,6 +62,7 @@ public void configure(Binder binder) binder.bind(CassandraRecordSetProvider.class).in(Scopes.SINGLETON); binder.bind(CassandraPageSinkProvider.class).in(Scopes.SINGLETON); binder.bind(CassandraPartitionManager.class).in(Scopes.SINGLETON); + binder.bind(CassandraSessionProperties.class).in(Scopes.SINGLETON); configBinder(binder).bindConfig(CassandraClientConfig.class); @@ -80,7 +80,7 @@ public static CassandraSession createCassandraSession( requireNonNull(extraColumnMetadataCodec, "extraColumnMetadataCodec is null"); Cluster.Builder clusterBuilder = Cluster.builder() - .withProtocolVersion(ProtocolVersion.V3); + .withProtocolVersion(config.getProtocolVersion()); List contactPoints = requireNonNull(config.getContactPoints(), "contactPoints is null"); checkArgument(!contactPoints.isEmpty(), "empty contactPoints"); diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnector.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnector.java index 4722c3054d028..5d8ebfa496090 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnector.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnector.java @@ -19,12 +19,15 @@ import com.facebook.presto.spi.connector.ConnectorRecordSetProvider; import com.facebook.presto.spi.connector.ConnectorSplitManager; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.session.PropertyMetadata; import com.facebook.presto.spi.transaction.IsolationLevel; import io.airlift.bootstrap.LifeCycleManager; import io.airlift.log.Logger; import javax.inject.Inject; +import java.util.List; + import static com.facebook.presto.spi.transaction.IsolationLevel.READ_UNCOMMITTED; import static com.facebook.presto.spi.transaction.IsolationLevel.checkConnectorSupports; import static java.util.Objects.requireNonNull; @@ -39,6 +42,7 @@ public class CassandraConnector private final CassandraSplitManager splitManager; private final ConnectorRecordSetProvider recordSetProvider; private final ConnectorPageSinkProvider pageSinkProvider; + private final List> sessionProperties; @Inject public CassandraConnector( @@ -46,13 +50,15 @@ public CassandraConnector( CassandraMetadata metadata, CassandraSplitManager splitManager, CassandraRecordSetProvider recordSetProvider, - CassandraPageSinkProvider pageSinkProvider) + CassandraPageSinkProvider pageSinkProvider, + CassandraSessionProperties sessionProperties) { this.lifeCycleManager = requireNonNull(lifeCycleManager, "lifeCycleManager is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.splitManager = requireNonNull(splitManager, "splitManager is null"); this.recordSetProvider = requireNonNull(recordSetProvider, "recordSetProvider is null"); this.pageSinkProvider = requireNonNull(pageSinkProvider, "pageSinkProvider is null"); + this.sessionProperties = requireNonNull(sessionProperties.getSessionProperties(), "sessionProperties is null"); } @Override @@ -92,6 +98,12 @@ public ConnectorPageSinkProvider getPageSinkProvider() return pageSinkProvider; } + @Override + public List> getSessionProperties() + { + return sessionProperties; + } + @Override public final void shutdown() { diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnectorFactory.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnectorFactory.java index 597cc19821f96..428f335d7c6cf 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnectorFactory.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraConnectorFactory.java @@ -58,7 +58,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { requireNonNull(config, "config is null"); @@ -66,7 +66,7 @@ public Connector create(String connectorId, Map config, Connecto Bootstrap app = new Bootstrap( new MBeanModule(), new JsonModule(), - new CassandraClientModule(connectorId), + new CassandraClientModule(catalogName), new Module() { @Override diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSession.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSession.java index 2e515e45b541f..214a0d0787b2d 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSession.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSession.java @@ -54,9 +54,10 @@ CassandraTable getTable(SchemaTableName schemaTableName) /** * Get the list of partitions matching the given filters on partition keys. + * * @param table the table to get partitions from * @param filterPrefix the list of possible values for each partition key. - * Order of values should match {@link CassandraTable#getPartitionKeyColumns()} + * Order of values should match {@link CassandraTable#getPartitionKeyColumns()} * @return list of {@link CassandraPartition} */ List getPartitions(CassandraTable table, List> filterPrefix); diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSessionProperties.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSessionProperties.java new file mode 100644 index 0000000000000..24e9e123e4b4e --- /dev/null +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSessionProperties.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cassandra; + +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.session.PropertyMetadata; +import com.google.common.collect.ImmutableList; + +import javax.inject.Inject; + +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.spi.session.PropertyMetadata.longProperty; + +public final class CassandraSessionProperties +{ + private static final String SPLITS_PER_NODE = "splits_per_node"; + + private final List> sessionProperties; + + @Inject + public CassandraSessionProperties(CassandraClientConfig cassandraClientConfig) + { + sessionProperties = ImmutableList.of( + longProperty( + SPLITS_PER_NODE, + "Number of splits per node. By default, the values from the system.size_estimates table are used. Only override when connecting to Cassandra versions < 2.1.5.", + cassandraClientConfig.getSplitsPerNode().orElse(null), + false)); + } + + public List> getSessionProperties() + { + return sessionProperties; + } + + public static Optional getSplitsPerNode(ConnectorSession session) + { + return Optional.ofNullable(session.getProperty(SPLITS_PER_NODE, Long.class)); + } +} diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSplitManager.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSplitManager.java index 8070c652a9f21..4e06044058495 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSplitManager.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraSplitManager.java @@ -32,8 +32,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import static com.facebook.presto.cassandra.CassandraSessionProperties.getSplitsPerNode; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -74,7 +76,7 @@ public ConnectorSplitSource getSplits(ConnectorTransactionHandle transaction, Co CassandraPartition cassandraPartition = partitions.get(0); if (cassandraPartition.isUnpartitioned() || cassandraPartition.isIndexedColumnPredicatePushdown()) { CassandraTable table = cassandraSession.getTable(cassandraTableHandle.getSchemaTableName()); - List splits = getSplitsByTokenRange(table, cassandraPartition.getPartitionId()); + List splits = getSplitsByTokenRange(table, cassandraPartition.getPartitionId(), getSplitsPerNode(session)); return new FixedSplitSource(splits); } } @@ -82,14 +84,14 @@ public ConnectorSplitSource getSplits(ConnectorTransactionHandle transaction, Co return new FixedSplitSource(getSplitsForPartitions(cassandraTableHandle, partitions, layoutHandle.getClusteringPredicates())); } - private List getSplitsByTokenRange(CassandraTable table, String partitionId) + private List getSplitsByTokenRange(CassandraTable table, String partitionId, Optional sessionSplitsPerNode) { String schema = table.getTableHandle().getSchemaName(); String tableName = table.getTableHandle().getTableName(); String tokenExpression = table.getTokenExpression(); ImmutableList.Builder builder = ImmutableList.builder(); - List tokenSplits = tokenSplitMgr.getSplits(schema, tableName); + List tokenSplits = tokenSplitMgr.getSplits(schema, tableName, sessionSplitsPerNode); for (CassandraTokenSplitManager.TokenSplit tokenSplit : tokenSplits) { String condition = buildTokenCondition(tokenExpression, tokenSplit.getStartToken(), tokenSplit.getEndToken()); List addresses = new HostAddressFactory().AddressNamesToHostAddressList(tokenSplit.getHosts()); diff --git a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraTokenSplitManager.java b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraTokenSplitManager.java index cc9fe2e400fb9..16cbba7071678 100644 --- a/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraTokenSplitManager.java +++ b/presto-cassandra/src/main/java/com/facebook/presto/cassandra/CassandraTokenSplitManager.java @@ -43,20 +43,22 @@ public class CassandraTokenSplitManager { private final CassandraSession session; private final int splitSize; + private final Optional configSplitsPerNode; @Inject public CassandraTokenSplitManager(CassandraSession session, CassandraClientConfig config) { - this(session, config.getSplitSize()); + this(session, config.getSplitSize(), config.getSplitsPerNode()); } - public CassandraTokenSplitManager(CassandraSession session, int splitSize) + public CassandraTokenSplitManager(CassandraSession session, int splitSize, Optional configSplitsPerNode) { this.session = requireNonNull(session, "session is null"); this.splitSize = splitSize; + this.configSplitsPerNode = configSplitsPerNode; } - public List getSplits(String keyspace, String table) + public List getSplits(String keyspace, String table, Optional sessionSplitsPerNode) { Set tokenRanges = session.getTokenRanges(); @@ -71,7 +73,7 @@ public List getSplits(String keyspace, String table) } Optional tokenRing = createForPartitioner(session.getPartitioner()); - long totalPartitionsCount = getTotalPartitionsCount(keyspace, table); + long totalPartitionsCount = getTotalPartitionsCount(keyspace, table, sessionSplitsPerNode); List splits = new ArrayList<>(); for (TokenRange tokenRange : tokenRanges) { @@ -116,8 +118,14 @@ private Set unwrap(Set tokenRanges) return result.build(); } - private long getTotalPartitionsCount(String keyspace, String table) + public long getTotalPartitionsCount(String keyspace, String table, Optional sessionSplitsPerNode) { + if (sessionSplitsPerNode.isPresent()) { + return sessionSplitsPerNode.get(); + } + else if (configSplitsPerNode.isPresent()) { + return configSplitsPerNode.get(); + } List estimates = session.getSizeEstimates(keyspace, table); return estimates.stream() .mapToLong(SizeEstimate::getPartitionsCount) diff --git a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraClientConfig.java b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraClientConfig.java index 7d9503f355b10..d2b3c09842c3f 100644 --- a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraClientConfig.java +++ b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraClientConfig.java @@ -22,6 +22,8 @@ import java.util.Map; +import static com.datastax.driver.core.ProtocolVersion.V2; +import static com.datastax.driver.core.ProtocolVersion.V3; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -38,6 +40,7 @@ public void testDefaults() .setNativeProtocolPort(9042) .setPartitionSizeForBatchSelect(100) .setSplitSize(1_024) + .setSplitsPerNode(null) .setAllowDropTable(false) .setUsername(null) .setPassword(null) @@ -55,7 +58,8 @@ public void testDefaults() .setWhiteListAddresses("") .setNoHostAvailableRetryTimeout(new Duration(1, MINUTES)) .setSpeculativeExecutionLimit(1) - .setSpeculativeExecutionDelay(new Duration(500, MILLISECONDS))); + .setSpeculativeExecutionDelay(new Duration(500, MILLISECONDS)) + .setProtocolVersion(V3)); } @Test @@ -68,6 +72,7 @@ public void testExplicitPropertyMappings() .put("cassandra.consistency-level", "TWO") .put("cassandra.partition-size-for-batch-select", "77") .put("cassandra.split-size", "1025") + .put("cassandra.splits-per-node", "10000") .put("cassandra.allow-drop-table", "true") .put("cassandra.username", "my_username") .put("cassandra.password", "my_password") @@ -86,6 +91,7 @@ public void testExplicitPropertyMappings() .put("cassandra.no-host-available-retry-timeout", "3m") .put("cassandra.speculative-execution.limit", "10") .put("cassandra.speculative-execution.delay", "101s") + .put("cassandra.protocol-version", "V2") .build(); CassandraClientConfig expected = new CassandraClientConfig() @@ -95,6 +101,7 @@ public void testExplicitPropertyMappings() .setConsistencyLevel(ConsistencyLevel.TWO) .setPartitionSizeForBatchSelect(77) .setSplitSize(1_025) + .setSplitsPerNode(10_000L) .setAllowDropTable(true) .setUsername("my_username") .setPassword("my_password") @@ -112,7 +119,8 @@ public void testExplicitPropertyMappings() .setWhiteListAddresses("host1") .setNoHostAvailableRetryTimeout(new Duration(3, MINUTES)) .setSpeculativeExecutionLimit(10) - .setSpeculativeExecutionDelay(new Duration(101, SECONDS)); + .setSpeculativeExecutionDelay(new Duration(101, SECONDS)) + .setProtocolVersion(V2); ConfigAssertions.assertFullMapping(properties, expected); } diff --git a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraConnector.java b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraConnector.java index 483e996ce8261..3239739bcaacb 100644 --- a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraConnector.java +++ b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraConnector.java @@ -16,6 +16,7 @@ import com.datastax.driver.core.utils.Bytes; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ColumnMetadata; +import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorSplit; import com.facebook.presto.spi.ConnectorSplitSource; import com.facebook.presto.spi.ConnectorTableHandle; @@ -34,6 +35,7 @@ import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; import com.facebook.presto.testing.TestingConnectorContext; +import com.facebook.presto.testing.TestingConnectorSession; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.testng.annotations.AfterMethod; @@ -54,10 +56,10 @@ import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.facebook.presto.spi.type.Varchars.isVarcharType; -import static com.facebook.presto.testing.TestingConnectorSession.SESSION; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.concurrent.MoreFutures.getFutureValue; @@ -73,6 +75,16 @@ public class TestCassandraConnector { protected static final String INVALID_DATABASE = "totally_invalid_database"; private static final Date DATE = new Date(); + private static final ConnectorSession SESSION = new TestingConnectorSession( + "user", + Optional.of("test"), + Optional.empty(), + UTC_KEY, + ENGLISH, + System.currentTimeMillis(), + new CassandraSessionProperties(new CassandraClientConfig()).getSessionProperties(), + ImmutableMap.of(), + true); protected String database; protected SchemaTableName table; protected SchemaTableName tableUnpartitioned; diff --git a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraIntegrationSmokeTest.java b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraIntegrationSmokeTest.java index b55cfc2a86793..33415534de142 100644 --- a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraIntegrationSmokeTest.java +++ b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraIntegrationSmokeTest.java @@ -20,14 +20,13 @@ import com.facebook.presto.tests.AbstractTestIntegrationSmokeTest; import com.google.common.collect.ImmutableList; import io.airlift.units.Duration; -import org.joda.time.DateTime; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.sql.Timestamp; import java.time.LocalDateTime; -import java.util.Date; import java.util.List; import static com.datastax.driver.core.utils.Bytes.toRawHexString; @@ -56,7 +55,6 @@ import static com.google.common.primitives.Ints.toByteArray; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.stream.Collectors.toList; -import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; @Test(singleThreaded = true) @@ -66,9 +64,8 @@ public class TestCassandraIntegrationSmokeTest private static final String KEYSPACE = "smoke_test"; private static final Session SESSION = createCassandraSession(KEYSPACE); - private static final DateTime DATE_TIME_UTC = new DateTime(1970, 1, 1, 3, 4, 5, UTC); - private static final Date DATE_LOCAL = new Date(DATE_TIME_UTC.getMillis()); - private static final LocalDateTime TIMESTAMP_LOCAL = LocalDateTime.of(1970, 1, 1, 3, 4, 5); + private static final Timestamp DATE_TIME_LOCAL = Timestamp.valueOf(LocalDateTime.of(1970, 1, 1, 3, 4, 5, 0)); + private static final LocalDateTime TIMESTAMP_LOCAL = LocalDateTime.of(1969, 12, 31, 23, 4, 5); // TODO #7122 should match DATE_TIME_LOCAL private CassandraSession session; @@ -81,7 +78,7 @@ public TestCassandraIntegrationSmokeTest() public void setUp() { session = EmbeddedCassandra.getSession(); - createTestTables(session, KEYSPACE, DATE_LOCAL); + createTestTables(session, KEYSPACE, DATE_TIME_LOCAL); } @Override @@ -106,7 +103,7 @@ public void testPartitionKeyPredicate() " AND typeinteger = 7" + " AND typelong = 1007" + " AND typebytes = from_hex('" + toRawHexString(ByteBuffer.wrap(toByteArray(7))) + "')" + - " AND typetimestamp = TIMESTAMP '1970-01-01 03:04:05'" + + " AND typetimestamp = TIMESTAMP '1969-12-31 23:04:05'" + " AND typeansi = 'ansi 7'" + " AND typeboolean = false" + " AND typedecimal = 128.0" + @@ -237,17 +234,17 @@ public void testClusteringKeyPushdownInequality() assertEquals(execute(sql).getRowCount(), 4); sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2"; assertEquals(execute(sql).getRowCount(), 1); - sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three = timestamp '1970-01-01 03:04:05.020'"; + sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three = timestamp '1969-12-31 23:04:05.020'"; assertEquals(execute(sql).getRowCount(), 1); - sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three = timestamp '1970-01-01 03:04:05.010'"; + sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three = timestamp '1969-12-31 23:04:05.010'"; assertEquals(execute(sql).getRowCount(), 0); sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two IN (1,2)"; assertEquals(execute(sql).getRowCount(), 2); sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two > 1 AND clust_two < 3"; assertEquals(execute(sql).getRowCount(), 1); - sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three >= timestamp '1970-01-01 03:04:05.010' AND clust_three <= timestamp '1970-01-01 03:04:05.020'"; + sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two=2 AND clust_three >= timestamp '1969-12-31 23:04:05.010' AND clust_three <= timestamp '1969-12-31 23:04:05.020'"; assertEquals(execute(sql).getRowCount(), 1); - sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two IN (1,2) AND clust_three >= timestamp '1970-01-01 03:04:05.010' AND clust_three <= timestamp '1970-01-01 03:04:05.020'"; + sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two IN (1,2) AND clust_three >= timestamp '1969-12-31 23:04:05.010' AND clust_three <= timestamp '1969-12-31 23:04:05.020'"; assertEquals(execute(sql).getRowCount(), 2); sql = "SELECT * FROM " + TABLE_CLUSTERING_KEYS_INEQUALITY + " WHERE key='key_1' AND clust_one='clust_one' AND clust_two IN (1,2,3) AND clust_two < 2"; assertEquals(execute(sql).getRowCount(), 1); diff --git a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraTokenSplitManager.java b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraTokenSplitManager.java index 367ede2cf3900..74a41c032001a 100644 --- a/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraTokenSplitManager.java +++ b/presto-cassandra/src/test/java/com/facebook/presto/cassandra/TestCassandraTokenSplitManager.java @@ -18,6 +18,7 @@ import org.testng.annotations.Test; import java.util.List; +import java.util.Optional; import static com.facebook.presto.cassandra.CassandraTestingUtils.createKeyspace; import static java.lang.String.format; @@ -39,7 +40,28 @@ public void setUp() EmbeddedCassandra.start(); session = EmbeddedCassandra.getSession(); createKeyspace(session, KEYSPACE); - splitManager = new CassandraTokenSplitManager(session, SPLIT_SIZE); + splitManager = new CassandraTokenSplitManager(session, SPLIT_SIZE, Optional.empty()); + } + + @Test + public void testPartitionCountOverride() + throws Exception + { + String tableName = "partition_count_override_table"; + session.execute(format("CREATE TABLE %s.%s (key text PRIMARY KEY)", KEYSPACE, tableName)); + EmbeddedCassandra.refreshSizeEstimates(KEYSPACE, tableName); + + CassandraTokenSplitManager onlyConfigSplitsPerNode = new CassandraTokenSplitManager(session, SPLIT_SIZE, Optional.of(12_345L)); + assertEquals(12_345L, onlyConfigSplitsPerNode.getTotalPartitionsCount(KEYSPACE, tableName, Optional.empty())); + + CassandraTokenSplitManager onlySessionSplitsPerNode = new CassandraTokenSplitManager(session, SPLIT_SIZE, Optional.empty()); + assertEquals(67_890L, onlySessionSplitsPerNode.getTotalPartitionsCount(KEYSPACE, tableName, Optional.of(67_890L))); + + CassandraTokenSplitManager sessionOverrideConfig = new CassandraTokenSplitManager(session, SPLIT_SIZE, Optional.of(12_345L)); + assertEquals(67_890L, sessionOverrideConfig.getTotalPartitionsCount(KEYSPACE, tableName, Optional.of(67_890L))); + + CassandraTokenSplitManager defaultSplitManager = new CassandraTokenSplitManager(session, SPLIT_SIZE, Optional.empty()); + assertEquals(0, defaultSplitManager.getTotalPartitionsCount(KEYSPACE, tableName, Optional.empty())); } @Test @@ -49,7 +71,7 @@ public void testEmptyTable() String tableName = "empty_table"; session.execute(format("CREATE TABLE %s.%s (key text PRIMARY KEY)", KEYSPACE, tableName)); EmbeddedCassandra.refreshSizeEstimates(KEYSPACE, tableName); - List splits = splitManager.getSplits(KEYSPACE, tableName); + List splits = splitManager.getSplits(KEYSPACE, tableName, Optional.empty()); // even for the empty table at least one split must be produced, in case the statistics are inaccurate assertEquals(splits.size(), 1); session.execute(format("DROP TABLE %s.%s", KEYSPACE, tableName)); @@ -65,7 +87,7 @@ public void testNonEmptyTable() session.execute(format("INSERT INTO %s.%s (key) VALUES ('%s')", KEYSPACE, tableName, "value" + i)); } EmbeddedCassandra.refreshSizeEstimates(KEYSPACE, tableName); - List splits = splitManager.getSplits(KEYSPACE, tableName); + List splits = splitManager.getSplits(KEYSPACE, tableName, Optional.empty()); assertEquals(splits.size(), PARTITION_COUNT / SPLIT_SIZE); session.execute(format("DROP TABLE %s.%s", KEYSPACE, tableName)); } diff --git a/presto-cli/pom.xml b/presto-cli/pom.xml index 35727787265d7..3788986c9b705 100644 --- a/presto-cli/pom.xml +++ b/presto-cli/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-cli diff --git a/presto-cli/src/main/java/com/facebook/presto/cli/AbstractWarningsPrinter.java b/presto-cli/src/main/java/com/facebook/presto/cli/AbstractWarningsPrinter.java new file mode 100644 index 0000000000000..fa207a06e2018 --- /dev/null +++ b/presto-cli/src/main/java/com/facebook/presto/cli/AbstractWarningsPrinter.java @@ -0,0 +1,116 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cli; + +import com.facebook.presto.spi.PrestoWarning; + +import java.util.List; +import java.util.OptionalInt; + +import static com.facebook.presto.cli.ConsolePrinter.REAL_TERMINAL; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +abstract class AbstractWarningsPrinter + implements WarningsPrinter +{ + private static final String WARNING_BEGIN = ((char) 27) + "[33m"; + private static final String WARNING_END = ((char) 27) + "[39m"; + + private final OptionalInt maxWarnings; + private boolean hasProcessedWarnings; + private int processedWarnings; + + AbstractWarningsPrinter(OptionalInt maxWarnings) + { + this.maxWarnings = requireNonNull(maxWarnings, "maxWarnings is null"); + } + + private String getWarningMessage(PrestoWarning warning) + { + // If this is a real terminal color the warnings yellow + if (REAL_TERMINAL) { + return format("%sWARNING: %s%s", WARNING_BEGIN, warning.getMessage(), WARNING_END); + } + return format("WARNING: %s", warning.getMessage()); + } + + private List getNewWarnings(List warnings) + { + int end = warnings.size(); + if (maxWarnings.isPresent()) { + end = Math.min(processedWarnings + maxWarnings.getAsInt(), end); + } + List subList = warnings.subList(processedWarnings, end).stream() + .map(this::getWarningMessage) + .collect(toImmutableList()); + processedWarnings = end; + return subList; + } + + protected abstract void print(List warnings); + + protected abstract void printSeparator(); + + private void printWithSeparators(List warnings) + { + // Print warnings separated from previous and subsequent output + if (!warnings.isEmpty()) { + printSeparator(); + print(warnings); + printSeparator(); + } + } + + private void printWithInitialSeparator(List warnings) + { + // Separate first warnings from previous output + if (!hasProcessedWarnings && !warnings.isEmpty()) { + printSeparator(); + hasProcessedWarnings = true; + print(warnings); + } + } + + private void printWithTrailingSeparator(List warnings) + { + // Print warnings and separate from subsequent output + if (!warnings.isEmpty()) { + print(warnings); + printSeparator(); + } + } + + @Override + public void print(List warnings, boolean withInitialSeparator, boolean withTrailingSeparator) + { + requireNonNull(warnings, "warnings is null"); + List newWarnings = getNewWarnings(warnings); + if (withInitialSeparator) { + if (withTrailingSeparator) { + printWithSeparators(newWarnings); + } + else { + printWithInitialSeparator(newWarnings); + } + } + else if (withTrailingSeparator) { + printWithTrailingSeparator(newWarnings); + } + else { + print(newWarnings); + } + } +} diff --git a/presto-cli/src/main/java/com/facebook/presto/cli/Query.java b/presto-cli/src/main/java/com/facebook/presto/cli/Query.java index 6c3ba10e8ebb3..6768cb704f86d 100644 --- a/presto-cli/src/main/java/com/facebook/presto/cli/Query.java +++ b/presto-cli/src/main/java/com/facebook/presto/cli/Query.java @@ -19,6 +19,7 @@ import com.facebook.presto.client.QueryError; import com.facebook.presto.client.QueryStatusInfo; import com.facebook.presto.client.StatementClient; +import com.facebook.presto.spi.PrestoWarning; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -37,6 +38,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -131,13 +133,14 @@ private boolean renderQueryOutput(PrintStream out, OutputFormat outputFormat, bo StatusPrinter statusPrinter = null; @SuppressWarnings("resource") PrintStream errorChannel = interactive ? out : System.err; + WarningsPrinter warningsPrinter = new PrintStreamWarningsPrinter(System.err); if (interactive) { statusPrinter = new StatusPrinter(client, out, debug); statusPrinter.printInitialStatusUpdates(); } else { - waitForData(); + processInitialStatusUpdates(warningsPrinter); } // if running or finished @@ -158,8 +161,14 @@ else if (results.getColumns() == null) { checkState(!client.isRunning()); if (statusPrinter != null) { + // Print all warnings at the end of the query + new PrintStreamWarningsPrinter(System.err).print(client.finalStatusInfo().getWarnings(), true, true); statusPrinter.printFinalInfo(); } + else { + // Print remaining warnings separated + warningsPrinter.print(client.finalStatusInfo().getWarnings(), true, true); + } if (client.isClientAborted()) { errorChannel.println("Query aborted by user"); @@ -179,11 +188,20 @@ else if (results.getColumns() == null) { return true; } - private void waitForData() + private void processInitialStatusUpdates(WarningsPrinter warningsPrinter) { while (client.isRunning() && (client.currentData().getData() == null)) { + warningsPrinter.print(client.currentStatusInfo().getWarnings(), true, false); client.advance(); } + List warnings; + if (client.isRunning()) { + warnings = client.currentStatusInfo().getWarnings(); + } + else { + warnings = client.finalStatusInfo().getWarnings(); + } + warningsPrinter.print(warnings, false, true); } private void renderUpdate(PrintStream out, QueryStatusInfo results) @@ -357,4 +375,29 @@ private static void renderErrorLocation(String query, ErrorLocation location, Pr out.println(padding + "^"); } } + + private static class PrintStreamWarningsPrinter + extends AbstractWarningsPrinter + { + private final PrintStream printStream; + + PrintStreamWarningsPrinter(PrintStream printStream) + { + super(OptionalInt.empty()); + this.printStream = requireNonNull(printStream, "printStream is null"); + } + + @Override + protected void print(List warnings) + { + warnings.stream() + .forEach(printStream::println); + } + + @Override + protected void printSeparator() + { + printStream.println(); + } + } } diff --git a/presto-cli/src/main/java/com/facebook/presto/cli/QueryRunner.java b/presto-cli/src/main/java/com/facebook/presto/cli/QueryRunner.java index 71ac5bbe483d1..1d0989e93fda1 100644 --- a/presto-cli/src/main/java/com/facebook/presto/cli/QueryRunner.java +++ b/presto-cli/src/main/java/com/facebook/presto/cli/QueryRunner.java @@ -14,6 +14,7 @@ package com.facebook.presto.cli; import com.facebook.presto.client.ClientSession; +import com.facebook.presto.client.SocketChannelSocketFactory; import com.facebook.presto.client.StatementClient; import com.google.common.net.HostAndPort; import okhttp3.OkHttpClient; @@ -72,6 +73,8 @@ public QueryRunner( OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.socketFactory(new SocketChannelSocketFactory()); + setupTimeouts(builder, 30, SECONDS); setupCookieJar(builder); setupSocksProxy(builder, socksProxy); diff --git a/presto-cli/src/main/java/com/facebook/presto/cli/StatusPrinter.java b/presto-cli/src/main/java/com/facebook/presto/cli/StatusPrinter.java index d3e42fc088d3a..8d0629d8f60c0 100644 --- a/presto-cli/src/main/java/com/facebook/presto/cli/StatusPrinter.java +++ b/presto-cli/src/main/java/com/facebook/presto/cli/StatusPrinter.java @@ -24,6 +24,8 @@ import io.airlift.units.Duration; import java.io.PrintStream; +import java.util.List; +import java.util.OptionalInt; import java.util.concurrent.atomic.AtomicInteger; import static com.facebook.presto.cli.FormatUtils.formatCount; @@ -40,8 +42,11 @@ import static java.lang.Character.toUpperCase; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.stream.IntStream.range; public class StatusPrinter { @@ -87,6 +92,7 @@ public void printInitialStatusUpdates() { long lastPrint = System.nanoTime(); try { + WarningsPrinter warningsPrinter = new ConsoleWarningsPrinter(console); while (client.isRunning()) { try { // exit status loop if there is pending output @@ -103,7 +109,7 @@ public void printInitialStatusUpdates() client.cancelLeafStage(); } else if (key == CTRL_C) { - updateScreen(); + updateScreen(warningsPrinter); update = false; client.close(); } @@ -115,7 +121,7 @@ else if (toUpperCase(key) == 'D') { // update screen if (update) { - updateScreen(); + updateScreen(warningsPrinter); lastPrint = System.nanoTime(); } @@ -135,10 +141,10 @@ else if (toUpperCase(key) == 'D') { } } - private void updateScreen() + private void updateScreen(WarningsPrinter warningsPrinter) { console.repositionCursor(); - printQueryInfo(client.currentStatusInfo()); + printQueryInfo(client.currentStatusInfo(), warningsPrinter); } public void printFinalInfo() @@ -157,7 +163,7 @@ public void printFinalInfo() out.println(); // Query 12, FINISHED, 1 node - String querySummary = String.format("Query %s, %s, %,d %s", + String querySummary = format("Query %s, %s, %,d %s", results.getId(), stats.getState(), nodes, @@ -169,7 +175,7 @@ public void printFinalInfo() } // Splits: 1000 total, 842 done (84.20%) - String splitsSummary = String.format("Splits: %,d total, %,d done (%.2f%%)", + String splitsSummary = format("Splits: %,d total, %,d done (%.2f%%)", stats.getTotalSplits(), stats.getCompletedSplits(), stats.getProgressPercentage().orElse(0.0)); @@ -178,7 +184,7 @@ public void printFinalInfo() if (debug) { // CPU Time: 565.2s total, 26K rows/s, 3.85MB/s Duration cpuTime = millis(stats.getCpuTimeMillis()); - String cpuTimeSummary = String.format("CPU Time: %.1fs total, %5s rows/s, %8s, %d%% active", + String cpuTimeSummary = format("CPU Time: %.1fs total, %5s rows/s, %8s, %d%% active", cpuTime.getValue(SECONDS), formatCountRate(stats.getProcessedRows(), cpuTime, false), formatDataRate(bytes(stats.getProcessedBytes()), cpuTime, true), @@ -188,21 +194,21 @@ public void printFinalInfo() double parallelism = cpuTime.getValue(MILLISECONDS) / wallTime.getValue(MILLISECONDS); // Per Node: 3.5 parallelism, 83.3K rows/s, 0.7 MB/s - String perNodeSummary = String.format("Per Node: %.1f parallelism, %5s rows/s, %8s", + String perNodeSummary = format("Per Node: %.1f parallelism, %5s rows/s, %8s", parallelism / nodes, formatCountRate((double) stats.getProcessedRows() / nodes, wallTime, false), formatDataRate(bytes(stats.getProcessedBytes() / nodes), wallTime, true)); reprintLine(perNodeSummary); // Parallelism: 5.3 - out.println(String.format("Parallelism: %.1f", parallelism)); + out.println(format("Parallelism: %.1f", parallelism)); //Peak Memory: 1.97GB reprintLine("Peak Memory: " + formatDataSize(bytes(stats.getPeakMemoryBytes()), true)); } // 0:32 [2.12GB, 15M rows] [67MB/s, 463K rows/s] - String statsLine = String.format("%s [%s rows, %s] [%s rows/s, %s]", + String statsLine = format("%s [%s rows, %s] [%s rows/s, %s]", formatTime(wallTime), formatCount(stats.getProcessedRows()), formatDataSize(bytes(stats.getProcessedBytes()), true), @@ -215,7 +221,7 @@ public void printFinalInfo() out.println(); } - private void printQueryInfo(QueryStatusInfo results) + private void printQueryInfo(QueryStatusInfo results, WarningsPrinter warningsPrinter) { StatementStats stats = results.getStats(); Duration wallTime = nanosSince(start); @@ -235,14 +241,14 @@ private void printQueryInfo(QueryStatusInfo results) reprintLine("80 characters wide"); reprintLine(""); reprintLine(stats.getState()); - reprintLine(String.format("%s %d%%", formatTime(wallTime), progressPercentage)); + reprintLine(format("%s %d%%", formatTime(wallTime), progressPercentage)); return; } int nodes = stats.getNodes(); // Query 10, RUNNING, 1 node, 778 splits - String querySummary = String.format("Query %s, %s, %,d %s, %,d splits", + String querySummary = format("Query %s, %s, %,d %s, %,d splits", results.getId(), stats.getState(), nodes, @@ -261,7 +267,7 @@ private void printQueryInfo(QueryStatusInfo results) if (debug) { // Splits: 620 queued, 34 running, 124 done - String splitsSummary = String.format("Splits: %,d queued, %,d running, %,d done", + String splitsSummary = format("Splits: %,d queued, %,d running, %,d done", stats.getQueuedSplits(), stats.getRunningSplits(), stats.getCompletedSplits()); @@ -269,7 +275,7 @@ private void printQueryInfo(QueryStatusInfo results) // CPU Time: 56.5s total, 36.4K rows/s, 4.44MB/s, 60% active Duration cpuTime = millis(stats.getCpuTimeMillis()); - String cpuTimeSummary = String.format("CPU Time: %.1fs total, %5s rows/s, %8s, %d%% active", + String cpuTimeSummary = format("CPU Time: %.1fs total, %5s rows/s, %8s, %d%% active", cpuTime.getValue(SECONDS), formatCountRate(stats.getProcessedRows(), cpuTime, false), formatDataRate(bytes(stats.getProcessedBytes()), cpuTime, true), @@ -279,14 +285,14 @@ private void printQueryInfo(QueryStatusInfo results) double parallelism = cpuTime.getValue(MILLISECONDS) / wallTime.getValue(MILLISECONDS); // Per Node: 3.5 parallelism, 83.3K rows/s, 0.7 MB/s - String perNodeSummary = String.format("Per Node: %.1f parallelism, %5s rows/s, %8s", + String perNodeSummary = format("Per Node: %.1f parallelism, %5s rows/s, %8s", parallelism / nodes, formatCountRate((double) stats.getProcessedRows() / nodes, wallTime, false), formatDataRate(bytes(stats.getProcessedBytes() / nodes), wallTime, true)); reprintLine(perNodeSummary); // Parallelism: 5.3 - reprintLine(String.format("Parallelism: %.1f", parallelism)); + reprintLine(format("Parallelism: %.1f", parallelism)); //Peak Memory: 1.97GB reprintLine("Peak Memory: " + formatDataSize(bytes(stats.getPeakMemoryBytes()), true)); @@ -302,7 +308,7 @@ private void printQueryInfo(QueryStatusInfo results) stats.getTotalSplits()); // 0:17 [ 103MB, 802K rows] [5.74MB/s, 44.9K rows/s] [=====>> ] 10% - String progressLine = String.format("%s [%5s rows, %6s] [%5s rows/s, %8s] [%s] %d%%", + String progressLine = format("%s [%5s rows, %6s] [%5s rows/s, %8s] [%s] %d%%", formatTime(wallTime), formatCount(stats.getProcessedRows()), formatDataSize(bytes(stats.getProcessedBytes()), true), @@ -317,7 +323,7 @@ private void printQueryInfo(QueryStatusInfo results) String progressBar = formatProgressBar(progressWidth, Ints.saturatedCast(nanosSince(start).roundTo(SECONDS))); // 0:17 [ 103MB, 802K rows] [5.74MB/s, 44.9K rows/s] [ <=> ] - String progressLine = String.format("%s [%5s rows, %6s] [%5s rows/s, %8s] [%s]", + String progressLine = format("%s [%5s rows, %6s] [%5s rows/s, %8s] [%s]", formatTime(wallTime), formatCount(stats.getProcessedRows()), formatDataSize(bytes(stats.getProcessedBytes()), true), @@ -332,7 +338,7 @@ private void printQueryInfo(QueryStatusInfo results) reprintLine(""); // STAGE S ROWS RPS BYTES BPS QUEUED RUN DONE - String stagesHeader = String.format("%10s%1s %5s %6s %5s %7s %6s %5s %5s", + String stagesHeader = format("%10s%1s %5s %6s %5s %7s %6s %5s %5s", "STAGE", "S", "ROWS", @@ -348,7 +354,7 @@ private void printQueryInfo(QueryStatusInfo results) } else { // Query 31 [S] i[2.7M 67.3MB 62.7MBps] o[35 6.1KB 1KBps] splits[252/16/380] - String querySummary = String.format("Query %s [%s] i[%s %s %s] o[%s %s %s] splits[%,d/%,d/%,d]", + String querySummary = format("Query %s [%s] i[%s %s %s] o[%s %s %s] splits[%,d/%,d/%,d]", results.getId(), stats.getState(), @@ -365,6 +371,7 @@ private void printQueryInfo(QueryStatusInfo results) stats.getCompletedSplits()); reprintLine(querySummary); } + warningsPrinter.print(results.getWarnings(), false, false); } private void printStageTree(StageStats stage, String indent, AtomicInteger stageNumberCounter) @@ -393,7 +400,7 @@ private void printStageTree(StageStats stage, String indent, AtomicInteger stage rowsPerSecond = formatCountRate(stage.getProcessedRows(), elapsedTime, false); } - String stageSummary = String.format("%10s%1s %5s %6s %5s %7s %6s %5s %5s", + String stageSummary = format("%10s%1s %5s %6s %5s %7s %6s %5s %5s", name, stageStateCharacter(stage.getState()), @@ -440,4 +447,35 @@ private static double percentage(double count, double total) } return min(100, (count * 100.0) / total); } + + private static class ConsoleWarningsPrinter + extends AbstractWarningsPrinter + { + private static final int DISPLAYED_WARNINGS = 5; + private final ConsolePrinter console; + + ConsoleWarningsPrinter(ConsolePrinter console) + { + super(OptionalInt.of(DISPLAYED_WARNINGS)); + this.console = requireNonNull(console, "console is null"); + } + + @Override + protected void print(List warnings) + { + console.reprintLine(""); + warnings.stream() + .forEach(console::reprintLine); + + // Remove warnings from previous screen + range(0, DISPLAYED_WARNINGS - warnings.size()) + .forEach(line -> console.reprintLine("")); + } + + @Override + protected void printSeparator() + { + console.reprintLine(""); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesBridge.java b/presto-cli/src/main/java/com/facebook/presto/cli/WarningsPrinter.java similarity index 65% rename from presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesBridge.java rename to presto-cli/src/main/java/com/facebook/presto/cli/WarningsPrinter.java index 112dd1527d441..d98791ab56feb 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesBridge.java +++ b/presto-cli/src/main/java/com/facebook/presto/cli/WarningsPrinter.java @@ -11,15 +11,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.operator; +package com.facebook.presto.cli; -import com.google.common.util.concurrent.ListenableFuture; +import com.facebook.presto.spi.PrestoWarning; -public interface NestedLoopJoinPagesBridge -{ - ListenableFuture getPagesFuture(); - - ListenableFuture setPages(NestedLoopJoinPages nestedLoopJoinPages); +import java.util.List; - void destroy(); +public interface WarningsPrinter +{ + void print(List warnings, boolean withInitialSeparator, boolean withTrailingSeparator); } diff --git a/presto-cli/src/test/java/com/facebook/presto/cli/TestConsoleHistory.java b/presto-cli/src/test/java/com/facebook/presto/cli/TestConsoleHistory.java index 3df31594852e8..2a9f3e3eed9ab 100644 --- a/presto-cli/src/test/java/com/facebook/presto/cli/TestConsoleHistory.java +++ b/presto-cli/src/test/java/com/facebook/presto/cli/TestConsoleHistory.java @@ -25,7 +25,8 @@ public class TestConsoleHistory { @Test - public void testNonExistingHomeFolder() throws Exception + public void testNonExistingHomeFolder() + throws Exception { File historyFile = new File("/?", ".history"); assertFalse(historyFile.canRead(), "historyFile is readable"); diff --git a/presto-cli/src/test/java/com/facebook/presto/cli/TestQueryRunner.java b/presto-cli/src/test/java/com/facebook/presto/cli/TestQueryRunner.java index 5432f7a4fe04e..52b1bea996fa4 100644 --- a/presto-cli/src/test/java/com/facebook/presto/cli/TestQueryRunner.java +++ b/presto-cli/src/test/java/com/facebook/presto/cli/TestQueryRunner.java @@ -122,6 +122,7 @@ private String createResults() StatementStats.builder().setState("FINISHED").build(), //new StatementStats("FINISHED", false, true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null), null, + ImmutableList.of(), null, null); return QUERY_RESULTS_CODEC.toJson(queryResults); diff --git a/presto-client/pom.xml b/presto-client/pom.xml index ae0452c5c42e7..5c5b61e015649 100644 --- a/presto-client/pom.xml +++ b/presto-client/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-client diff --git a/presto-client/src/main/java/com/facebook/presto/client/FailureInfo.java b/presto-client/src/main/java/com/facebook/presto/client/FailureInfo.java index 675d9890b003b..3db250458bd38 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/FailureInfo.java +++ b/presto-client/src/main/java/com/facebook/presto/client/FailureInfo.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -60,7 +59,6 @@ public FailureInfo( this.errorLocation = errorLocation; } - @Nonnull @JsonProperty public String getType() { @@ -81,14 +79,12 @@ public FailureInfo getCause() return cause; } - @Nonnull @JsonProperty public List getSuppressed() { return suppressed; } - @Nonnull @JsonProperty public List getStack() { @@ -152,7 +148,7 @@ private static class FailureException FailureException(String type, String message, FailureException cause) { - super(message, cause, true, true); + super(message, cause); this.type = requireNonNull(type, "type is null"); } diff --git a/presto-client/src/main/java/com/facebook/presto/client/JsonResponse.java b/presto-client/src/main/java/com/facebook/presto/client/JsonResponse.java index 12609e66296a3..f86d7d2096b29 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/JsonResponse.java +++ b/presto-client/src/main/java/com/facebook/presto/client/JsonResponse.java @@ -24,6 +24,7 @@ import javax.annotation.Nullable; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.UncheckedIOException; import static com.google.common.base.MoreObjects.toStringHelper; @@ -145,6 +146,11 @@ public static JsonResponse execute(JsonCodec codec, OkHttpClient clien return new JsonResponse<>(response.code(), response.message(), response.headers(), body); } catch (IOException e) { + // OkHttp throws this after clearing the interrupt status + // TODO: remove after updating to Okio 1.15.0+ + if ((e instanceof InterruptedIOException) && "thread interrupted".equals(e.getMessage())) { + Thread.currentThread().interrupt(); + } throw new UncheckedIOException(e); } } diff --git a/presto-client/src/main/java/com/facebook/presto/client/QueryError.java b/presto-client/src/main/java/com/facebook/presto/client/QueryError.java index 61d574ef2fec1..f55e50ea31a44 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/QueryError.java +++ b/presto-client/src/main/java/com/facebook/presto/client/QueryError.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -52,7 +51,6 @@ public QueryError( this.failureInfo = failureInfo; } - @Nonnull @JsonProperty public String getMessage() { @@ -72,14 +70,12 @@ public int getErrorCode() return errorCode; } - @Nonnull @JsonProperty public String getErrorName() { return errorName; } - @Nonnull @JsonProperty public String getErrorType() { diff --git a/presto-client/src/main/java/com/facebook/presto/client/QueryResults.java b/presto-client/src/main/java/com/facebook/presto/client/QueryResults.java index 86593d3753ff8..684929459b815 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/QueryResults.java +++ b/presto-client/src/main/java/com/facebook/presto/client/QueryResults.java @@ -13,11 +13,11 @@ */ package com.facebook.presto.client; +import com.facebook.presto.spi.PrestoWarning; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -25,6 +25,7 @@ import java.util.List; import static com.facebook.presto.client.FixJsonDataUtils.fixData; +import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.unmodifiableIterable; @@ -42,6 +43,7 @@ public class QueryResults private final Iterable> data; private final StatementStats stats; private final QueryError error; + private final List warnings; private final String updateType; private final Long updateCount; @@ -55,10 +57,22 @@ public QueryResults( @JsonProperty("data") List> data, @JsonProperty("stats") StatementStats stats, @JsonProperty("error") QueryError error, + @JsonProperty("warnings") List warnings, @JsonProperty("updateType") String updateType, @JsonProperty("updateCount") Long updateCount) { - this(id, infoUri, partialCancelUri, nextUri, columns, fixData(columns, data), stats, error, updateType, updateCount); + this( + id, + infoUri, + partialCancelUri, + nextUri, + columns, + fixData(columns, data), + stats, + error, + firstNonNull(warnings, ImmutableList.of()), + updateType, + updateCount); } public QueryResults( @@ -70,6 +84,7 @@ public QueryResults( Iterable> data, StatementStats stats, QueryError error, + List warnings, String updateType, Long updateCount) { @@ -82,11 +97,11 @@ public QueryResults( checkArgument(data == null || columns != null, "data present without columns"); this.stats = requireNonNull(stats, "stats is null"); this.error = error; + this.warnings = ImmutableList.copyOf(requireNonNull(warnings, "warnings is null")); this.updateType = updateType; this.updateCount = updateCount; } - @Nonnull @JsonProperty @Override public String getId() @@ -94,7 +109,6 @@ public String getId() return id; } - @Nonnull @JsonProperty @Override public URI getInfoUri() @@ -134,7 +148,6 @@ public Iterable> getData() return data; } - @Nonnull @JsonProperty @Override public StatementStats getStats() @@ -150,6 +163,13 @@ public QueryError getError() return error; } + @JsonProperty + @Override + public List getWarnings() + { + return warnings; + } + @Nullable @JsonProperty @Override diff --git a/presto-client/src/main/java/com/facebook/presto/client/QueryStatusInfo.java b/presto-client/src/main/java/com/facebook/presto/client/QueryStatusInfo.java index e7832d5e7f5b8..8f573abce2100 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/QueryStatusInfo.java +++ b/presto-client/src/main/java/com/facebook/presto/client/QueryStatusInfo.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.client; +import com.facebook.presto.spi.PrestoWarning; + import java.net.URI; import java.util.List; @@ -32,6 +34,8 @@ public interface QueryStatusInfo QueryError getError(); + List getWarnings(); + String getUpdateType(); Long getUpdateCount(); diff --git a/presto-client/src/main/java/com/facebook/presto/client/SocketChannelSocketFactory.java b/presto-client/src/main/java/com/facebook/presto/client/SocketChannelSocketFactory.java new file mode 100644 index 0000000000000..90f199255a307 --- /dev/null +++ b/presto-client/src/main/java/com/facebook/presto/client/SocketChannelSocketFactory.java @@ -0,0 +1,67 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.client; + +import javax.net.SocketFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketException; +import java.nio.channels.SocketChannel; + +/** + * Workaround for JDK IPv6 bug on Mac. Sockets created with the basic socket + * API often cannot connect to IPv6 destinations due to JDK-8131133. However, + * NIO sockets do not have this problem, even if used in blocking mode. + */ +public class SocketChannelSocketFactory + extends SocketFactory +{ + @Override + public Socket createSocket() + throws IOException + { + return SocketChannel.open().socket(); + } + + @Override + public Socket createSocket(String host, int port) + throws IOException + { + return SocketChannel.open(new InetSocketAddress(host, port)).socket(); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) + throws IOException + { + throw new SocketException("not supported"); + } + + @Override + public Socket createSocket(InetAddress address, int port) + throws IOException + { + return SocketChannel.open(new InetSocketAddress(address, port)).socket(); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) + throws IOException + { + throw new SocketException("not supported"); + } +} diff --git a/presto-client/src/main/java/com/facebook/presto/client/StageStats.java b/presto-client/src/main/java/com/facebook/presto/client/StageStats.java index 4b6064aec92d3..7c8334047efae 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/StageStats.java +++ b/presto-client/src/main/java/com/facebook/presto/client/StageStats.java @@ -17,7 +17,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import java.util.List; @@ -36,7 +35,6 @@ public class StageStats private final int queuedSplits; private final int runningSplits; private final int completedSplits; - private final long userTimeMillis; private final long cpuTimeMillis; private final long wallTimeMillis; private final long processedRows; @@ -53,7 +51,6 @@ public StageStats( @JsonProperty("queuedSplits") int queuedSplits, @JsonProperty("runningSplits") int runningSplits, @JsonProperty("completedSplits") int completedSplits, - @JsonProperty("userTimeMillis") long userTimeMillis, @JsonProperty("cpuTimeMillis") long cpuTimeMillis, @JsonProperty("wallTimeMillis") long wallTimeMillis, @JsonProperty("processedRows") long processedRows, @@ -68,7 +65,6 @@ public StageStats( this.queuedSplits = queuedSplits; this.runningSplits = runningSplits; this.completedSplits = completedSplits; - this.userTimeMillis = userTimeMillis; this.cpuTimeMillis = cpuTimeMillis; this.wallTimeMillis = wallTimeMillis; this.processedRows = processedRows; @@ -82,7 +78,6 @@ public String getStageId() return stageId; } - @Nonnull @JsonProperty public String getState() { @@ -125,12 +120,6 @@ public int getCompletedSplits() return completedSplits; } - @JsonProperty - public long getUserTimeMillis() - { - return userTimeMillis; - } - @JsonProperty public long getCpuTimeMillis() { @@ -155,7 +144,6 @@ public long getProcessedBytes() return processedBytes; } - @Nonnull @JsonProperty public List getSubStages() { @@ -173,7 +161,6 @@ public String toString() .add("queuedSplits", queuedSplits) .add("runningSplits", runningSplits) .add("completedSplits", completedSplits) - .add("userTimeMillis", userTimeMillis) .add("cpuTimeMillis", cpuTimeMillis) .add("wallTimeMillis", wallTimeMillis) .add("processedRows", processedRows) @@ -197,7 +184,6 @@ public static class Builder private int queuedSplits; private int runningSplits; private int completedSplits; - private long userTimeMillis; private long cpuTimeMillis; private long wallTimeMillis; private long processedRows; @@ -254,12 +240,6 @@ public Builder setCompletedSplits(int completedSplits) return this; } - public Builder setUserTimeMillis(long userTimeMillis) - { - this.userTimeMillis = userTimeMillis; - return this; - } - public Builder setCpuTimeMillis(long cpuTimeMillis) { this.cpuTimeMillis = cpuTimeMillis; @@ -301,7 +281,6 @@ public StageStats build() queuedSplits, runningSplits, completedSplits, - userTimeMillis, cpuTimeMillis, wallTimeMillis, processedRows, diff --git a/presto-client/src/main/java/com/facebook/presto/client/StatementStats.java b/presto-client/src/main/java/com/facebook/presto/client/StatementStats.java index 31654c0b8b336..26078eea64d9b 100644 --- a/presto-client/src/main/java/com/facebook/presto/client/StatementStats.java +++ b/presto-client/src/main/java/com/facebook/presto/client/StatementStats.java @@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -37,7 +36,6 @@ public class StatementStats private final int queuedSplits; private final int runningSplits; private final int completedSplits; - private final long userTimeMillis; private final long cpuTimeMillis; private final long wallTimeMillis; private final long queuedTimeMillis; @@ -57,7 +55,6 @@ public StatementStats( @JsonProperty("queuedSplits") int queuedSplits, @JsonProperty("runningSplits") int runningSplits, @JsonProperty("completedSplits") int completedSplits, - @JsonProperty("userTimeMillis") long userTimeMillis, @JsonProperty("cpuTimeMillis") long cpuTimeMillis, @JsonProperty("wallTimeMillis") long wallTimeMillis, @JsonProperty("queuedTimeMillis") long queuedTimeMillis, @@ -75,7 +72,6 @@ public StatementStats( this.queuedSplits = queuedSplits; this.runningSplits = runningSplits; this.completedSplits = completedSplits; - this.userTimeMillis = userTimeMillis; this.cpuTimeMillis = cpuTimeMillis; this.wallTimeMillis = wallTimeMillis; this.queuedTimeMillis = queuedTimeMillis; @@ -86,7 +82,6 @@ public StatementStats( this.rootStage = rootStage; } - @Nonnull @JsonProperty public String getState() { @@ -135,12 +130,6 @@ public int getCompletedSplits() return completedSplits; } - @JsonProperty - public long getUserTimeMillis() - { - return userTimeMillis; - } - @JsonProperty public long getCpuTimeMillis() { @@ -211,7 +200,6 @@ public String toString() .add("queuedSplits", queuedSplits) .add("runningSplits", runningSplits) .add("completedSplits", completedSplits) - .add("userTimeMillis", userTimeMillis) .add("cpuTimeMillis", cpuTimeMillis) .add("wallTimeMillis", wallTimeMillis) .add("queuedTimeMillis", queuedTimeMillis) @@ -238,7 +226,6 @@ public static class Builder private int queuedSplits; private int runningSplits; private int completedSplits; - private long userTimeMillis; private long cpuTimeMillis; private long wallTimeMillis; private long queuedTimeMillis; @@ -298,12 +285,6 @@ public Builder setCompletedSplits(int completedSplits) return this; } - public Builder setUserTimeMillis(long userTimeMillis) - { - this.userTimeMillis = userTimeMillis; - return this; - } - public Builder setCpuTimeMillis(long cpuTimeMillis) { this.cpuTimeMillis = cpuTimeMillis; @@ -363,7 +344,6 @@ public StatementStats build() queuedSplits, runningSplits, completedSplits, - userTimeMillis, cpuTimeMillis, wallTimeMillis, queuedTimeMillis, diff --git a/presto-client/src/test/java/com/facebook/presto/client/TestQueryResults.java b/presto-client/src/test/java/com/facebook/presto/client/TestQueryResults.java new file mode 100644 index 0000000000000..39a7a8e17aa88 --- /dev/null +++ b/presto-client/src/test/java/com/facebook/presto/client/TestQueryResults.java @@ -0,0 +1,65 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.client; + +import io.airlift.json.JsonCodec; +import org.testng.annotations.Test; + +import static io.airlift.json.JsonCodec.jsonCodec; +import static org.testng.Assert.assertEquals; + +public class TestQueryResults +{ + private static final JsonCodec QUERY_RESULTS_CODEC = jsonCodec(QueryResults.class); + + @Test + public void testCompatibility() + { + String goldenValue = "{\n" + + " \"id\" : \"20160128_214710_00012_rk68b\",\n" + + " \"infoUri\" : \"http://localhost:54855/query.html?20160128_214710_00012_rk68b\",\n" + + " \"columns\" : [ {\n" + + " \"name\" : \"_col0\",\n" + + " \"type\" : \"bigint\",\n" + + " \"typeSignature\" : {\n" + + " \"rawType\" : \"bigint\",\n" + + " \"typeArguments\" : [ ],\n" + + " \"literalArguments\" : [ ],\n" + + " \"arguments\" : [ ]\n" + + " }\n" + + " } ],\n" + + " \"data\" : [ [ 123 ] ],\n" + + " \"stats\" : {\n" + + " \"state\" : \"FINISHED\",\n" + + " \"queued\" : false,\n" + + " \"scheduled\" : false,\n" + + " \"nodes\" : 0,\n" + + " \"totalSplits\" : 0,\n" + + " \"queuedSplits\" : 0,\n" + + " \"runningSplits\" : 0,\n" + + " \"completedSplits\" : 0,\n" + + " \"cpuTimeMillis\" : 0,\n" + + " \"wallTimeMillis\" : 0,\n" + + " \"queuedTimeMillis\" : 0,\n" + + " \"elapsedTimeMillis\" : 0,\n" + + " \"processedRows\" : 0,\n" + + " \"processedBytes\" : 0,\n" + + " \"peakMemoryBytes\" : 0\n" + + " }\n" + + "}"; + + QueryResults results = QUERY_RESULTS_CODEC.fromJson(goldenValue); + assertEquals(results.getId(), "20160128_214710_00012_rk68b"); + } +} diff --git a/presto-docs/Makefile b/presto-docs/Makefile index cf89add56332c..86bd058f4720a 100644 --- a/presto-docs/Makefile +++ b/presto-docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -j auto SPHINXBUILD = sphinx-build PAPER = BUILDDIR = target diff --git a/presto-docs/pom.xml b/presto-docs/pom.xml index 870929bec0a70..4916abd4ef78f 100644 --- a/presto-docs/pom.xml +++ b/presto-docs/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-docs diff --git a/presto-docs/src/main/sphinx/admin.rst b/presto-docs/src/main/sphinx/admin.rst index 52f69b6472c2e..8413652242e1c 100644 --- a/presto-docs/src/main/sphinx/admin.rst +++ b/presto-docs/src/main/sphinx/admin.rst @@ -10,4 +10,5 @@ Administration admin/properties admin/spill admin/resource-groups + admin/session-property-managers admin/dist-sort diff --git a/presto-docs/src/main/sphinx/admin/properties.rst b/presto-docs/src/main/sphinx/admin/properties.rst index 230efde474d0b..eeea1323ec63f 100644 --- a/presto-docs/src/main/sphinx/admin/properties.rst +++ b/presto-docs/src/main/sphinx/admin/properties.rst @@ -48,6 +48,11 @@ General Properties redistributing all the data across the network. This can also be specified on a per-query basis using the ``redistribute_writes`` session property. +.. _tuning-memory: + +Memory Management Properties +---------------------------- + ``query.max-memory-per-node`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,7 +63,8 @@ General Properties User memory is allocated during execution for things that are directly attributable to or controllable by a user query. For example, memory used by the hash tables built during execution, memory used during sorting, etc. - When a query hits this limit it will be killed by Presto. + When the user memory allocation of a query on any worker hits this limit + it will be killed. ``query.max-total-memory-per-node`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -69,10 +75,38 @@ General Properties This is the max amount of user and system memory a query can use on a worker. System memory is allocated during execution for things that are not directly attributable to or controllable by a user query. For example, memory allocated - by the readers, writers, and network buffers, etc. The value of - ``query.max-total-memory-per-node`` must be greater than + by the readers, writers, network buffers, etc. When the sum of the user and + system memory allocated by a query on any worker hits this limit it will be killed. + The value of ``query.max-total-memory-per-node`` must be greater than ``query.max-memory-per-node``. +``query.max-memory`` +^^^^^^^^^^^^^^^^^^^^ + + * **Type:** ``data size`` + * **Default value:** ``20GB`` + + This is the max amount of user memory a query can use across the entire cluster. + User memory is allocated during execution for things that are directly + attributable to or controllable by a user query. For example, memory used + by the hash tables built during execution, memory used during sorting, etc. + When the user memory allocation of a query across all workers hits this limit + it will be killed. + +``query.max-total-memory`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + + * **Type:** ``data size`` + * **Default value:** ``query.max-total-memory * 2`` + + This is the max amount of user and system memory a query can use across the entire cluster. + System memory is allocated during execution for things that are not directly + attributable to or controllable by a user query. For example, memory allocated + by the readers, writers, network buffers, etc. When the sum of the user and + system memory allocated by a query across all workers hits this limit it will be + killed. The value of ``query.max-total-memory`` must be greater than + ``query.max-memory``. + ``memory.heap-headroom-per-node`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -425,6 +459,12 @@ Node Scheduler Properties * **Allowed values:** ``legacy``, ``flat`` * **Default value:** ``legacy`` + Sets the network topology to use when scheduling splits. ``legacy`` will ignore + the topology when scheduling splits. ``flat`` will try to schedule splits on the host + where the data is located by reserving 50% of the work queue for local splits. + It is recommended to use ``flat`` for clusters where distributed storage runs on + the same nodes as Presto workers. + Optimizer Properties -------------------- diff --git a/presto-docs/src/main/sphinx/admin/resource-groups.rst b/presto-docs/src/main/sphinx/admin/resource-groups.rst index 4c302d7cc8d6c..49d22985a08a2 100644 --- a/presto-docs/src/main/sphinx/admin/resource-groups.rst +++ b/presto-docs/src/main/sphinx/admin/resource-groups.rst @@ -3,11 +3,11 @@ Resource Groups =============== Resource groups place limits on resource usage, and can enforce queueing policies on -queries that run within them or divide their resources among sub groups. A query +queries that run within them or divide their resources among sub-groups. A query belongs to a single resource group, and consumes resources from that group (and its ancestors). Except for the limit on queued queries, when a resource group runs out of a resource it does not cause running queries to fail; instead new queries become queued. -A resource group may have sub groups or may accept queries, but may not do both. +A resource group may have sub-groups or may accept queries, but may not do both. The resource groups and associated selection rules are configured by a manager which is pluggable. Add an ``etc/resource-groups.properties`` file with the following contents to enable @@ -43,48 +43,50 @@ Resource Group Properties group may use in a period. * ``schedulingPolicy`` (optional): specifies how queued queries are selected to run, - and how sub groups become eligible to start their queries. May be one of three values: + and how sub-groups become eligible to start their queries. May be one of three values: - * ``fair`` (default): queued queries are processed first-in-first-out, and sub groups + * ``fair`` (default): queued queries are processed first-in-first-out, and sub-groups must take turns starting new queries (if they have any queued). + * ``weighted_fair``: sub-groups are selected based on their ``schedulingWeight`` and the number of + queries they are already running concurrently. The expected share of running queries for a + sub-group is computed based on the weights for all currently eligible sub-groups. The sub-group + with the least concurrency relative to its share is selected to start the next query. + * ``weighted``: queued queries are selected stochastically in proportion to their priority (specified via the ``query_priority`` :doc:`session property `). Sub groups are selected to start new queries in proportion to their ``schedulingWeight``. - * ``query_priority``: all sub groups must also be configured with ``query_priority``. + * ``query_priority``: all sub-groups must also be configured with ``query_priority``. Queued queries will be selected strictly according to their priority. -* ``schedulingWeight`` (optional): weight of this sub group. See above. +* ``schedulingWeight`` (optional): weight of this sub-group. See above. Defaults to ``1``. * ``jmxExport`` (optional): If true, group statistics are exported to JMX for monitoring. Defaults to ``false``. -* ``queuedTimeLimit`` (optional): maximum amount of time a query may be in the queue for this group - before it is marked as failed. - -* ``runningTimeLimit`` (optional): maximum amount of time a query in this group can execute - (not including queued time) before it is marked as failed. +* ``subGroups`` (optional): list of sub-groups. -* ``subGroups`` (optional): list of sub groups. +Selector Rules +-------------- -Selector Properties -------------------- +* ``user`` (optional): regex to match against user name. -* ``user`` (optional): regex to match against user name. Defaults to ``.*`` +* ``source`` (optional): regex to match against source string. -* ``source`` (optional): regex to match against source string. Defaults to ``.*`` - -* ``queryType`` (optional): string to match against the type of the query submitted. The query type can be: +* ``queryType`` (optional): string to match against the type of the query submitted: * ``DATA_DEFINITION``: Queries that alter/create/drop the metadata of schemas/tables/views, and that manage prepared statements, privileges, sessions, and transactions. * ``DELETE``: ``DELETE`` queries. * ``DESCRIBE``: ``DESCRIBE``, ``DESCRIBE INPUT``, ``DESCRIBE OUTPUT``, and ``SHOW`` queries. * ``EXPLAIN``: ``EXPLAIN`` queries. - * ``INSERT``: ``INSERT`` and ``CREATE TABLE AS SELECT`` queries. + * ``INSERT``: ``INSERT`` and ``CREATE TABLE AS`` queries. * ``SELECT``: ``SELECT`` queries. +* ``clientTags`` (optional): list of tags. To match, every tag in this list must be in the list of + client-provided tags associated with the query. + * ``group`` (required): the group these queries will run in. Global Properties @@ -93,37 +95,73 @@ Global Properties * ``cpuQuotaPeriod`` (optional): the period in which cpu quotas are enforced. Selectors are processed sequentially and the first one that matches will be used. -In the example configuration below, there are five resource group templates. -In the ``adhoc_${USER}`` group, ``${USER}`` will be expanded to the name of the -user that submitted the query. ``${SOURCE}`` is also supported, which expands -to the source submitting the query. The source name can be set as follows: + +Providing Selector Properties +----------------------------- + +The source name can be set as follows: * CLI: use the ``--source`` option. * JDBC: set the ``ApplicationName`` client info property on the ``Connection`` instance. -There are three selectors that define which queries run in which resource group: +Client tags can be set as follows: + + * CLI: use the ``--client-tags`` option. + + * JDBC: set the ``ClientTags`` client info property on the ``Connection`` instance. + +Example +------- + +In the example configuration below, there are several resource groups, some of which are templates. +Templates allow administrators to construct resource group trees dynamically. For example, in +the ``pipeline_${USER}`` group, ``${USER}`` will be expanded to the name of the user that submitted +the query. ``${SOURCE}`` is also supported, which will be expanded to the source that submitted the +query. You may also use custom named variables in the ``source`` and ``user`` regular expressions. + +There are four selectors that define which queries run in which resource group: + + * The first selector matches queries from ``bob`` and places them in the admin group. + + * The second selector matches all data definition (DDL) queries from a source name that includes "pipeline" + and places them in the ``global.data_definition`` group. This could help reduce queue times for this + class of queries, since they are expected to be fast. + + * The third selector matches queries from a source name that includes "pipeline", and places them in a + dynamically-created per-user pipeline group under the ``global.pipeline`` group. + + * The fourth selector matches queries that come from BI tools (which have a source matching the regular + expression ``"jdbc#(?.*)"``), and have client provided tags that are a superset of "hi-pri". + These are placed in a dynamically-created sub-group under the ``global.pipeline.tools`` group. The dynamic + sub-group will be created based on the named variable ``tool_name``, which is extracted from the in the + regular expression for source. Consider a query with a source "jdbc#powerfulbi", user "kayla", and + client tags "hipri" and "fast". This query would be routed to the ``global.pipeline.bi-powerfulbi.kayla`` + resource group. + + * The last selector is a catch-all, which places all queries that have not yet been matched into a per-user + adhoc group. - * The first selector places queries from ``bob`` into the admin group. +Together, these selectors implement the following policy: - * The second selector states that all data definition queries that come from a source that includes "pipeline" - should run in the user's personal data definition group, which belongs to the - ``globa.data_definition`` parent group. +* The user "bob" is an admin and can run up to 50 concurrent queries. Queries will be run based on user-provided + priority. - * The third selector states that all queries that come from a source that includes "pipeline" - should run in the user's personal pipeline group, which belongs to the ``global.pipeline`` - parent group. +For the remaining users: - * The last selector is a catch all, which puts all queries into the user's adhoc group. +* No more than 100 total queries may run concurrently. -All together these selectors implement the policy that ``bob`` is an admin and -all other users are subject to the following limits: +* Up to 5 concurrent DDL queries with a source "pipeline" can run. Queries are run in FIFO order. - * Users are allowed to have up to 2 adhoc queries running. Additionally, they may run one pipeline. +* Non-DDL queries will run under the ``global.pipeline`` group, with a total concurrency of 45, and a per-user + concurrency of 5. Queries are run in FIFO order. - * No more than 5 "pipeline" queries may run at once. +* For BI tools, each tool can run up to 10 concurrent queries, and each user can run up to 3. If the total demand + exceeds the limit of 10, the user with the fewest running queries will get the next concurrency slot. This policy + results in fairness when under contention. + +* All remaining queries are placed into a per-user group under ``global.adhoc.other`` that behaves similarly. - * No more than 100 total queries may run at once, unless they're from the admin. .. code-block:: json @@ -138,34 +176,66 @@ all other users are subject to the following limits: "jmxExport": true, "subGroups": [ { - "name": "data_definition_${USER}", + "name": "data_definition", "softMemoryLimit": "10%", - "hardConcurrencyLimit": 3, - "maxQueued": 10, + "hardConcurrencyLimit": 5, + "maxQueued": 100, "schedulingWeight": 1 }, { - "name": "adhoc_${USER}", + "name": "adhoc", "softMemoryLimit": "10%", - "hardConcurrencyLimit": 2, + "hardConcurrencyLimit": 50, "maxQueued": 1, - "schedulingWeight": 9, - "schedulingPolicy": "query_priority" + "schedulingWeight": 10, + "subGroups": [ + { + "name": "other", + "softMemoryLimit": "10%", + "hardConcurrencyLimit": 2, + "maxQueued": 1, + "schedulingWeight": 10, + "schedulingPolicy": "weighted_fair", + "subGroups": [ + { + "name": "${USER}", + "softMemoryLimit": "10%", + "hardConcurrencyLimit": 1, + "maxQueued": 100 + } + ] + }, + { + "name": "bi-${tool_name}", + "softMemoryLimit": "10%", + "hardConcurrencyLimit": 10, + "maxQueued": 100, + "schedulingWeight": 10, + "schedulingPolicy": "weighted_fair" + "subGroups": [ + { + "name": "${USER}", + "softMemoryLimit": "10%", + "hardConcurrencyLimit": 3, + "maxQueued": 10 + } + ] + } + ] }, { "name": "pipeline", - "softMemoryLimit": "20%", - "hardConcurrencyLimit": 5, + "softMemoryLimit": "80%", + "hardConcurrencyLimit": 45, "maxQueued": 100, "schedulingWeight": 1, "jmxExport": true, "subGroups": [ { "name": "pipeline_${USER}", - "softMemoryLimit": "10%", - "hardConcurrencyLimit": 1, + "softMemoryLimit": "50%", + "hardConcurrencyLimit": 5, "maxQueued": 100, - "schedulingPolicy": "query_priority" } ] } @@ -174,7 +244,7 @@ all other users are subject to the following limits: { "name": "admin", "softMemoryLimit": "100%", - "hardConcurrencyLimit": 200, + "hardConcurrencyLimit": 50, "maxQueued": 100, "schedulingPolicy": "query_priority", "jmxExport": true @@ -188,14 +258,19 @@ all other users are subject to the following limits: { "source": ".*pipeline.*", "queryType": "DATA_DEFINITION", - "group": "global.data_definition_${USER}" + "group": "global.data_definition" }, { "source": ".*pipeline.*", "group": "global.pipeline.pipeline_${USER}" }, { - "group": "global.adhoc_${USER}" + "source": "jdbc#(?.*)", + "clientTags": ["hipri"], + "group": "global.adhoc.bi-${tool_name}.${USER}" + }, + { + "group": "global.adhoc.other.${USER}" } ], "cpuQuotaPeriod": "1h" diff --git a/presto-docs/src/main/sphinx/admin/session-property-managers.rst b/presto-docs/src/main/sphinx/admin/session-property-managers.rst new file mode 100644 index 0000000000000..8070824ba7194 --- /dev/null +++ b/presto-docs/src/main/sphinx/admin/session-property-managers.rst @@ -0,0 +1,90 @@ +========================= +Session Property Managers +========================= + +Administrators can add session properties to control the behavior for subsets of their workload. +These properties are defaults and can be overridden by users (if authorized to do so). Session +properties can be used to control resource usage, enable or disable features, and change query +characteristics. Session property managers are pluggable. + +Add an ``etc/session-property-config.properties`` file with the following contents to enable +the built-in manager that reads a JSON config file: + +.. code-block:: none + + session-property-config.configuration-manager=file + session-property-manager.config-file=etc/session-property-config.json + +Change the value of ``session-property-manager.config-file`` to point to a JSON config file, +which can be an absolute path, or a path relative to the Presto data directory. + +This configuration file consists of a list of match rules, each of which specify a list of +conditions that the query must meet, and a list of session properties that should be applied +by default. All matching rules contribute to constructing a list of session properties. Rules +are applied in the order they are specified. Rules specified later in the file override values +for properties that have been previously encountered. + +Match Rules +----------- + +* ``user`` (optional): regex to match against user name. + +* ``source`` (optional): regex to match against source string. + +* ``queryType`` (optional): string to match against the type of the query submitted: + * ``DATA_DEFINITION``: Queries that alter/create/drop the metadata of schemas/tables/views, and that manage + prepared statements, privileges, sessions, and transactions. + * ``DELETE``: ``DELETE`` queries. + * ``DESCRIBE``: ``DESCRIBE``, ``DESCRIBE INPUT``, ``DESCRIBE OUTPUT``, and ``SHOW`` queries. + * ``EXPLAIN``: ``EXPLAIN`` queries. + * ``INSERT``: ``INSERT`` and ``CREATE TABLE AS`` queries. + * ``SELECT``: ``SELECT`` queries. + +* ``clientTags`` (optional): list of tags. To match, every tag in this list must be in the list of + client-provided tags associated with the query. + +* ``group`` (optional): regex to match against the fully qualified name of the resource group the query is + routed to. + +* ``sessionProperties``: map with string keys and values. Each entry is a system or catalog property name and + corresponding value. Values must be specified as strings, no matter the actual data type. + +Example +------- + +Consider the following set of requirements: + +* All queries running under the ``global`` resource group must have an execution time limit of 8 hours. + +* All interactive queries are routed to subgroups under the ``global.interactive`` group, and have an execution time + limit of 1 hour (tighter than the constraint on ``global``). + +* All ETL queries (tagged with 'etl') are routed to subgroups under the ``global.pipeline`` group, and must be + configured with certain properties to control writer behavior. + +These requirements can be expressed with the following rules: + +.. code-block:: json + + [ + { + "group": "global.*", + "sessionProperties": { + "query_max_execution_time": "8h", + } + }, + { + "group": "global.interactive.*", + "sessionProperties": { + "query_max_execution_time": "1h" + } + }, + { + "group": "global.pipeline.*", + "clientTags": ["etl"], + "sessionProperties": { + "scale_writers": "true", + "writer_min_size": "1GB" + } + } + ] diff --git a/presto-docs/src/main/sphinx/connector.rst b/presto-docs/src/main/sphinx/connector.rst index 11c77f6d3bd2d..786f757cf9ce9 100644 --- a/presto-docs/src/main/sphinx/connector.rst +++ b/presto-docs/src/main/sphinx/connector.rst @@ -17,7 +17,6 @@ from different data sources. connector/kafka connector/kafka-tutorial connector/kudu - connector/avro-schema-evolution connector/localfile connector/memory connector/mongodb diff --git a/presto-docs/src/main/sphinx/connector/avro-schema-evolution.rst b/presto-docs/src/main/sphinx/connector/avro-schema-evolution.rst deleted file mode 100644 index 3e9d0e05ca941..0000000000000 --- a/presto-docs/src/main/sphinx/connector/avro-schema-evolution.rst +++ /dev/null @@ -1,13 +0,0 @@ -=========================== -Avro Schema Evolution Rules -=========================== - -Presto's Hive and Kafka connectors support Avro's schema evolution feature with backward compatibility. With backward compatibility, -a newer schema can be used to read Avro data created with an older schema. Newly added/renamed fields *must* have a default value in the Avro schema file. - -The schema evolution behavior is as follows: - -* Column added in new schema - Data created with an older schema will produce a “default” value when table is using the new schema. -* Column removed in new schema - Data created with an older schema will no longer output the data from the column that was removed. -* Column is renamed in the new schema - This is equivalent to removing the column and adding a new one, and data created with an older schema will produce a “default” value when table is using the new schema. -* Changing type of column in the new schema - If the type coercion is supported by Avro or at the connector level (eg: Hive), then the conversion happens. An error is thrown for incompatible types. diff --git a/presto-docs/src/main/sphinx/connector/cassandra.rst b/presto-docs/src/main/sphinx/connector/cassandra.rst index 9c93f5bbb4328..1700be1f1a761 100644 --- a/presto-docs/src/main/sphinx/connector/cassandra.rst +++ b/presto-docs/src/main/sphinx/connector/cassandra.rst @@ -67,6 +67,9 @@ Property Name Description ``cassandra.password`` Password used for authentication to the Cassandra cluster. This is a global setting used for all connections, regardless of the user who is connected to Presto. + +``cassandra.protocol-version`` It is possible to override the protocol version for older Cassandra clusters. + This property defaults to ``V3``. Possible values include ``V2``, ``V3`` and ``V4``. ================================================== ====================================================================== .. note:: @@ -88,6 +91,11 @@ Property Name Description ``cassandra.split-size`` Number of keys per split when querying Cassandra. +``cassandra.splits-per-node`` Number of splits per node. By default, the values from the + ``system.size_estimates`` table are used. Only override when + connecting to Cassandra versions < 2.1.5, which lacks + the ``system.size_estimates`` table. + ``cassandra.client.read-timeout`` Maximum time the Cassandra driver will wait for an answer to a query from one Cassandra node. Note that the underlying Cassandra driver may retry a query against more than one node in diff --git a/presto-docs/src/main/sphinx/connector/hive.rst b/presto-docs/src/main/sphinx/connector/hive.rst index fc8ab027ce6ca..3cf76ff7e3b15 100644 --- a/presto-docs/src/main/sphinx/connector/hive.rst +++ b/presto-docs/src/main/sphinx/connector/hive.rst @@ -129,7 +129,7 @@ Property Name Description absolutely necessary to access HDFS. Example: ``/etc/hdfs-site.xml`` -``hive.storage-format`` The default file format used when creating new tables. ``RCBINARY`` +``hive.storage-format`` The default file format used when creating new tables. ``ORC`` ``hive.compression-codec`` The compression codec to use when writing files. ``GZIP`` @@ -179,8 +179,15 @@ Property Name Description ``hive.collect-column-statistics-on-write`` Enables automatic column level statistics collection ``false`` on write. See `Table Statistics <#table-statistics>`__ for details. + +``hive.s3select-pushdown.enabled`` Enable query pushdown to AWS S3 Select service. ``false`` + +``hive.s3select-pushdown.max-connections`` Maximum number of simultaneously open connections to S3 for 500 + S3SelectPushdown. ================================================== ============================================================ ============ +.. _s3selectpushdown: + Amazon S3 Configuration ----------------------- @@ -247,6 +254,9 @@ Property Name Description class also implements ``Configurable`` from the Hadoop API, the Hadoop configuration will be passed in after the object has been created. + +``hive.s3.upload-acl-type`` Canned ACL to use while uploading files to S3 (defaults + to ``Private``). ============================================ ================================================================= S3 Credentials @@ -338,6 +348,78 @@ the ``org.apache.hadoop.conf.Configurable`` interface from the Hadoop Java API, will be passed in after the object instance is created and before it is asked to provision or retrieve any encryption keys. +S3SelectPushdown +^^^^^^^^^^^^^^^^ + +S3SelectPushdown enables pushing down projection (SELECT) and predicate (WHERE) +processing to `S3 Select `_. +With S3SelectPushdown Presto only retrieves the required data from S3 instead of +entire S3 objects reducing both latency and network usage. + +Is S3 Select a good fit for my workload? +######################################## + +Performance of S3SelectPushdown depends on the amount of data filtered by the +query. Filtering a large number of rows should result in better performance. If +the query doesn't filter any data then pushdown may not add any additional value +and user will be charged for S3 Select requests. Thus, we recommend that you +benchmark your workloads with and without S3 Select to see if using it may be +suitable for your workload. By default, S3SelectPushdown is disabled and you +should enable it in production after proper benchmarking and cost analysis. For +more information on S3 Select request cost, please see +`Amazon S3 Cloud Storage Pricing `_. + +Use the following guidelines to determine if S3 Select is a good fit for your +workload: + +* Your query filters out more than half of the original data set. +* Your query filter predicates use columns that have a data type supported by + Presto and S3 Select. + The ``TIMESTAMP``, ``REAL``, and ``DOUBLE`` data types are not supported by S3 + Select Pushdown. We recommend using the decimal data type for numerical data. + For more information about supported data types for S3 Select, see the + `Data Types documentation `_. +* Your network connection between Amazon S3 and the Amazon EMR cluster has good + transfer speed and available bandwidth. Amazon S3 Select does not compress + HTTP responses, so the response size may increase for compressed input files. + +Considerations and Limitations +############################## + +* Only objects stored in CSV format are supported. Objects can be uncompressed + or optionally compressed with gzip or bzip2. +* The "AllowQuotedRecordDelimiters" property is not supported. If this property + is specified, the query fails. +* Amazon S3 server-side encryption with customer-provided encryption keys + (SSE-C) and client-side encryption are not supported. +* S3 Select Pushdown is not a substitute for using columnar or compressed file + formats such as ORC and Parquet. + +Enabling S3 Select Pushdown +########################### + +You can enable S3 Select Pushdown using the ``s3_select_pushdown_enabled`` +Hive session property or using the ``hive.s3select-pushdown.enabled`` +configuration property. The session property will override the config +property, allowing you enable or disable on a per-query basis. + +Understanding and Tuning the Maximum Connections +################################################ + +Presto can use its native S3 file system or EMRFS. When using the native FS, the +maximum connections is configured via the ``hive.s3.max-connections`` +configuration property. When using EMRFS, the maximum connections is configured +via the ``fs.s3.maxConnections`` Hadoop configuration property. + +S3 Select Pushdown bypasses the file systems when accessing Amazon S3 for +predicate operations. In this case, the value of +``hive.s3select-pushdown.max-connections`` determines the maximum number of +client connections allowed for those operations from worker nodes. + +If your workload experiences the error *Timeout waiting for connection from +pool*, increase the value of both ``hive.s3select-pushdown.max-connections`` and +the maximum connections configuration for the file system you are using. + Table Statistics ---------------- @@ -385,6 +467,72 @@ as Hive. For example, converting the string ``'foo'`` to a number, or converting the string ``'1234'`` to a ``tinyint`` (which has a maximum value of ``127``). +Avro Schema Evolution +--------------------- + +Presto supports querying and manipulating Hive tables with Avro storage format which has the schema set +based on an Avro schema file/literal. It is also possible to create tables in Presto which infers the schema +from a valid Avro schema file located locally or remotely in HDFS/Web server. + +To specify that Avro schema should be used for interpreting table's data one must use ``avro_schema_url`` table property. +The schema can be placed remotely in +HDFS (e.g. ``avro_schema_url = 'hdfs://user/avro/schema/avro_data.avsc'``), +S3 (e.g. ``avro_schema_url = 's3n:///schema_bucket/schema/avro_data.avsc'``), +a web server (e.g. ``avro_schema_url = 'http://example.org/schema/avro_data.avsc'``) +as well as local file system. This url where the schema is located, must be accessible from the +Hive metastore and Presto coordinator/worker nodes. + +The table created in Presto using ``avro_schema_url`` behaves the same way as a Hive table with ``avro.schema.url`` or ``avro.schema.literal`` set. + +Example:: + + CREATE TABLE hive.avro.avro_data ( + id bigint + ) + WITH ( + format = 'AVRO', + avro_schema_url = '/usr/local/avro_data.avsc' + ) + +The columns listed in the DDL (``id`` in the above example) will be ignored if ``avro_schema_url`` is specified. +The table schema will match the schema in the Avro schema file. Before any read operation, the Avro schema is +accessed so query result reflects any changes in schema. Thus Presto takes advantage of Avro's backward compatibility abilities. + +If the schema of the table changes in the Avro schema file, the new schema can still be used to read old data. +Newly added/renamed fields *must* have a default value in the Avro schema file. + +The schema evolution behavior is as follows: + +* Column added in new schema: + Data created with an older schema will produce a *default* value when table is using the new schema. + +* Column removed in new schema: + Data created with an older schema will no longer output the data from the column that was removed. + +* Column is renamed in the new schema: + This is equivalent to removing the column and adding a new one, and data created with an older schema + will produce a *default* value when table is using the new schema. + +* Changing type of column in the new schema: + If the type coercion is supported by Avro or the Hive connector, then the conversion happens. + An error is thrown for incompatible types. + +Limitations +^^^^^^^^^^^ + +The following operations are not supported when ``avro_schema_url`` is set: + +* ``CREATE TABLE AS`` is not supported. +* Using partitioning(``partitioned_by``) or bucketing(``bucketed_by``) columns are not supported in ``CREATE TABLE``. +* ``ALTER TABLE`` commands modifying columns are not supported. + +Procedures +---------- + +* ``system.create_empty_partition(schema_name, table_name, partition_columns, partition_values)`` + + Create an empty partition in the specified table. + Examples -------- @@ -423,6 +571,14 @@ Drop a partition from the ``page_views`` table:: WHERE ds = DATE '2016-08-09' AND country = 'US' +Add an empty partition to the ``page_views`` table:: + + CALL system.create_empty_partition( + schema_name => 'web', + table_name => 'page_views', + partition_columns => ARRAY['ds', 'country'], + partition_values => ARRAY['2016-08-09', 'US']); + Query the ``page_views`` table:: SELECT * FROM hive.web.page_views diff --git a/presto-docs/src/main/sphinx/connector/kafka.rst b/presto-docs/src/main/sphinx/connector/kafka.rst index fbac5717baf35..09fa28d7d5c60 100644 --- a/presto-docs/src/main/sphinx/connector/kafka.rst +++ b/presto-docs/src/main/sphinx/connector/kafka.rst @@ -240,7 +240,7 @@ Field Required Type Description ``name`` required string Name of the column in the Presto table. ``type`` required string Presto type of the column. ``dataFormat`` optional string Selects the column decoder for this field. Defaults to the default decoder for this row data format and column type. -``dataSchema`` optional string The path or URL where the AVRO schema resides. Used only for AVRO decoder. +``dataSchema`` optional string The path or URL where the Avro schema resides. Used only for Avro decoder. ``mapping`` optional string Mapping information for the column. This is decoder specific, see below. ``formatHint`` optional string Sets a column specific format hint to the column decoder. ``hidden`` optional boolean Hides the column from ``DESCRIBE `` and ``SELECT *``. Defaults to ``false``. @@ -259,7 +259,7 @@ The Kafka connector contains the following decoders: * ``raw`` - Kafka message is not interpreted, ranges of raw message bytes are mapped to table columns * ``csv`` - Kafka message is interpreted as comma separated message, and fields are mapped to table columns * ``json`` - Kafka message is parsed as JSON and JSON fields are mapped to table columns -* ``avro`` - Kafka message is parsed based on an AVRO schema and AVRO fields are mapped to table columns +* ``avro`` - Kafka message is parsed based on an Avro schema and Avro fields are mapped to table columns .. note:: @@ -338,8 +338,12 @@ The CSV decoder converts the bytes representing a message or key into a string using UTF-8 encoding and then interprets the result as a CSV (comma-separated value) line. -For fields, the ``type`` and ``mapping`` attributes must be defined. -``dataFormat and ``formatHint`` are not supported and must be omitted. +For fields, the ``type`` and ``mapping`` attributes must be defined: + +* ``type`` - Presto data type (see table below for list of supported data types) +* ``mapping`` - the index of the field in the CSV record + +``dataFormat`` and ``formatHint`` are not supported and must be omitted. Table below lists supported Presto types which can be used in ``type`` and decoding scheme: @@ -446,8 +450,6 @@ For fields, the following attributes are supported: * ``type`` - Presto type of column. * ``mapping`` - slash-separated list of field names to select a field from the Avro schema. If field specified in ``mapping`` does not exist in the original Avro schema then a read operation will return NULL. - - Table below lists supported Presto types which can be used in ``type`` for the equivalent Avro field type/s. ===================================== ======================================= @@ -467,4 +469,19 @@ Avro schema evolution The Avro decoder supports schema evolution feature with backward compatibility. With backward compatibility, a newer schema can be used to read Avro data created with an older schema. Any change in the Avro schema must also be -reflected in Presto's topic definition file. For the schema evolution rules see, :doc:`/connector/avro-schema-evolution`. +reflected in Presto's topic definition file. Newly added/renamed fields *must* have a default value in the Avro schema file. + +The schema evolution behavior is as follows: + +* Column added in new schema: + Data created with an older schema will produce a *default* value when table is using the new schema. + +* Column removed in new schema: + Data created with an older schema will no longer output the data from the column that was removed. + +* Column is renamed in the new schema: + This is equivalent to removing the column and adding a new one, and data created with an older schema + will produce a *default* value when table is using the new schema. + +* Changing type of column in the new schema: + If the type coercion is supported by Avro, then the conversion happens. An error is thrown for incompatible types. diff --git a/presto-docs/src/main/sphinx/connector/kudu.rst b/presto-docs/src/main/sphinx/connector/kudu.rst index 478187b81738c..442343631779a 100644 --- a/presto-docs/src/main/sphinx/connector/kudu.rst +++ b/presto-docs/src/main/sphinx/connector/kudu.rst @@ -19,7 +19,7 @@ Compatibility Connector is compatible with all Apache Kudu versions starting from 1.0. If the connector uses features that are not available on the target server, an error will be returned. -Apache Kudu 1.7.0 is currently used for testing. +Apache Kudu 1.8.0 is currently used for testing. Configuration @@ -53,16 +53,16 @@ replacing the properties as appropriate: ####################### ## Default timeout used for administrative operations (e.g. createTable, deleteTable, etc.) - #kudu.client.defaultAdminOperationTimeout = 30s + #kudu.client.default-admin-operation-timeout = 30s ## Default timeout used for user operations - #kudu.client.defaultOperationTimeout = 30s + #kudu.client.default-operation-timeout = 30s ## Default timeout to use when waiting on data from a socket - #kudu.client.defaultSocketReadTimeout = 10s + #kudu.client.default-socket-read-timeout = 10s ## Disable Kudu client's collection of statistics. - #kudu.client.disableStatistics = false + #kudu.client.disable-statistics = false Querying Data @@ -103,7 +103,7 @@ Example On creating a Kudu table you must/can specify addition information about the primary key, encoding, and compression of columns and hash or range -partitioning, and the number of replicas. Details see in section +partitioning. Details see in section `Create Table`_. - The table can be described using @@ -303,7 +303,7 @@ Supported Presto SQL statements | | range partitions`_ | +------------------------------------------+-------------------------------+ -Not supported are ``SHOW PARTITIONS FROM ...``, ``ALTER SCHEMA ... RENAME`` +``ALTER SCHEMA ... RENAME TO ...`` is not supported. Create Table @@ -324,13 +324,21 @@ Simple Example: details varchar WITH (nullable = true, encoding = 'plain') ) WITH ( partition_by_hash_columns = ARRAY['user_id'], - partition_by_hash_buckets = 5 + partition_by_hash_buckets = 5, + number_of_replicas = 3 ); -Here the table is partitioned into five partitions by hash values of the column ``user_id``. -Note that the primary key consists of ``user_id`` and ``event_name``. +The primary key consists of ``user_id`` and ``event_name``, the table is partitioned into +five partitions by hash values of the column ``user_id``, and the ``number_of_replicas`` is +explicitly set to 3. + The primary key columns must always be the first columns of the column list. All columns used in partitions must be part of the primary key. + +The table property ``number_of_replicas`` is optional. It defines the +number of tablet replicas and must be an odd number. If it is not specified, +the default replication factor from the Kudu master configuration is used. + Kudu supports two different kinds of partitioning: hash and range partitioning. Hash partitioning distributes rows by hash value into one of many buckets. Range partitions distributes rows using a totally-ordered range partition key. diff --git a/presto-docs/src/main/sphinx/connector/mongodb.rst b/presto-docs/src/main/sphinx/connector/mongodb.rst index 6c890ab0bffe6..fc91c25262d81 100644 --- a/presto-docs/src/main/sphinx/connector/mongodb.rst +++ b/presto-docs/src/main/sphinx/connector/mongodb.rst @@ -187,7 +187,7 @@ A schema collection consists of a MongoDB document for a table. "table": ..., "fields": [ { "name" : ..., - "type" : "varchar|bigint|boolean|double|date|array|...", + "type" : "varchar|bigint|boolean|double|date|array(bigint)|...", "hidden" : false }, ... ] diff --git a/presto-docs/src/main/sphinx/connector/system.rst b/presto-docs/src/main/sphinx/connector/system.rst index 0afb5bf2e2ae0..5819988c4e5d3 100644 --- a/presto-docs/src/main/sphinx/connector/system.rst +++ b/presto-docs/src/main/sphinx/connector/system.rst @@ -82,6 +82,7 @@ idle time, initialization parameters, and accessed catalogs. System Connector Procedures --------------------------- -.. function:: runtime.kill_query(id) +.. function:: runtime.kill_query(query_id, message) - Kill the query with the specified ``id``. + Kill the query identified by ``query_id``. The query failure message + will include the specified ``message``. diff --git a/presto-docs/src/main/sphinx/connector/thrift.rst b/presto-docs/src/main/sphinx/connector/thrift.rst index a5f59b03664cb..9bd1abcfb3bf9 100644 --- a/presto-docs/src/main/sphinx/connector/thrift.rst +++ b/presto-docs/src/main/sphinx/connector/thrift.rst @@ -9,8 +9,9 @@ In order to use the Thrift connector with an external system, you need to implem the ``PrestoThriftService`` interface, found below. Next, you configure the Thrift connector to point to a set of machines, called Thrift servers, that implement the interface. As part of the interface implementation, the Thrift servers will provide metadata, -splits and data. The Thrift server instances are assumed to be stateless and independent -from each other. +splits and data. The connector will randomly choose a server to talk to from the available +instances for metadata calls, or for data calls unless the splits include a list of addresses. +All requests are assumed to be idempotent and can be retried freely among any server. Configuration ------------- diff --git a/presto-docs/src/main/sphinx/develop/example-http.rst b/presto-docs/src/main/sphinx/develop/example-http.rst index 5beddde15ad54..71db9fa700cee 100644 --- a/presto-docs/src/main/sphinx/develop/example-http.rst +++ b/presto-docs/src/main/sphinx/develop/example-http.rst @@ -65,7 +65,7 @@ and exception handling: // A plugin is not required to use Guice; it is just very convenient Bootstrap app = new Bootstrap( new JsonModule(), - new ExampleModule(connectorId)); + new ExampleModule(catalogName)); Injector injector = app .strictConfig() diff --git a/presto-docs/src/main/sphinx/ext/download.py b/presto-docs/src/main/sphinx/ext/download.py index ed5531ce4588f..91b11d58fe930 100644 --- a/presto-docs/src/main/sphinx/ext/download.py +++ b/presto-docs/src/main/sphinx/ext/download.py @@ -57,3 +57,7 @@ def download_link_role(role, rawtext, text, lineno, inliner, options={}, content return [node], [] app.add_role('maven_download', download_link_role) + + return { + 'parallel_read_safe': True, + } diff --git a/presto-docs/src/main/sphinx/ext/issue.py b/presto-docs/src/main/sphinx/ext/issue.py index de5bb92c4e0b2..0e6fd32df6742 100644 --- a/presto-docs/src/main/sphinx/ext/issue.py +++ b/presto-docs/src/main/sphinx/ext/issue.py @@ -26,3 +26,7 @@ def issue_role(role, rawtext, text, lineno, inliner, options={}, content=[]): def setup(app): app.add_role('issue', issue_role) + + return { + 'parallel_read_safe': True, + } diff --git a/presto-docs/src/main/sphinx/functions.rst b/presto-docs/src/main/sphinx/functions.rst index 36c7111339cd6..d2f7a524822b0 100644 --- a/presto-docs/src/main/sphinx/functions.rst +++ b/presto-docs/src/main/sphinx/functions.rst @@ -24,6 +24,8 @@ Functions and Operators functions/map functions/url functions/geospatial + functions/hyperloglog + functions/qdigest functions/color functions/session functions/teradata diff --git a/presto-docs/src/main/sphinx/functions/aggregate.rst b/presto-docs/src/main/sphinx/functions/aggregate.rst index b750a147f14f0..b92827c542a5f 100644 --- a/presto-docs/src/main/sphinx/functions/aggregate.rst +++ b/presto-docs/src/main/sphinx/functions/aggregate.rst @@ -105,6 +105,44 @@ General Aggregate Functions Returns ``n`` smallest values of all input values of ``x``. +.. function:: reduce_agg(inputValue T, initialState S, inputFunction(S, T, S), combineFunction(S, S, S)) -> S + + Reduces all input values into a single value. ```inputFunction`` will be invoked + for each input value. In addition to taking the input value, ``inputFunction`` + takes the current state, initially ``initialState``, and returns the new state. + ``combineFunction`` will be invoked to combine two states into a new state. + The final state is returned:: + + SELECT id, reduce_agg(value, 0, (a, b) -> a + b, (a, b) -> a + b) + FROM ( + VALUES + (1, 2) + (1, 3), + (1, 4), + (2, 20), + (2, 30), + (2, 40) + ) AS t(id, value) + GROUP BY id; + -- (1, 9) + -- (2, 90) + + SELECT id, reduce_agg(value, 1, (a, b) -> a * b, (a, b) -> a * b) + FROM ( + VALUES + (1, 2), + (1, 3), + (1, 4), + (2, 20), + (2, 30), + (2, 40) + ) AS t(id, value) + GROUP BY id; + -- (1, 24) + -- (2, 24000) + + The state type must be a boolean, integer, floating-point, or date/time/interval. + .. function:: sum(x) -> [same as input] Returns the sum of all input values. @@ -123,20 +161,20 @@ Bitwise Aggregate Functions Map Aggregate Functions ----------------------- -.. function:: histogram(x) -> map +.. function:: histogram(x) -> map(K,bigint) Returns a map containing the count of the number of times each input value occurs. -.. function:: map_agg(key, value) -> map +.. function:: map_agg(key, value) -> map(K,V) Returns a map created from the input ``key`` / ``value`` pairs. -.. function:: map_union(x) -> map +.. function:: map_union(x(K,V)) -> map(K,V) Returns the union of all the input maps. If a key is found in multiple input maps, that key's value in the resulting map comes from an arbitrary input map. -.. function:: multimap_agg(key, value) -> map> +.. function:: multimap_agg(key, value) -> map(K,array(V)) Returns a multimap created from the input ``key`` / ``value`` pairs. Each key can be associated with multiple values. @@ -206,6 +244,36 @@ Approximate Aggregate Functions Each element of the array must be between zero and one, and the array must be constant for all input rows. +.. function:: approx_set(x) -> HyperLogLog + :noindex: + + See :doc:`hyperloglog`. + +.. function:: merge(x) -> HyperLogLog + :noindex: + + See :doc:`hyperloglog`. + +.. function:: merge(qdigest(T)) -> qdigest(T) + :noindex: + + See :doc:`qdigest`. + +.. function:: qdigest_agg(x) -> qdigest<[same as x]> + :noindex: + + See :doc:`qdigest`. + +.. function:: qdigest_agg(x, w) -> qdigest<[same as x]> + :noindex: + + See :doc:`qdigest`. + +.. function:: qdigest_agg(x, w, accuracy) -> qdigest<[same as x]> + :noindex: + + See :doc:`qdigest`. + .. function:: numeric_histogram(buckets, value, weight) -> map Computes an approximate histogram with up to ``buckets`` number of buckets diff --git a/presto-docs/src/main/sphinx/functions/array.rst b/presto-docs/src/main/sphinx/functions/array.rst index bca3420ecae60..37a1ed8f1809e 100644 --- a/presto-docs/src/main/sphinx/functions/array.rst +++ b/presto-docs/src/main/sphinx/functions/array.rst @@ -62,7 +62,7 @@ Array Functions Sorts and returns the array ``x``. The elements of ``x`` must be orderable. Null elements will be placed at the end of the returned array. -.. function:: array_sort(array, function) -> array +.. function:: array_sort(array(T), function(T,T,int)) -> array(T) Sorts and returns the ``array`` based on the given comparator ``function``. The comparator will take two nullable arguments representing two nullable elements of the ``array``. It returns -1, 0, or 1 @@ -111,13 +111,13 @@ Array Functions Returns true if the array ``x`` contains the ``element``. -.. function:: element_at(array, index) -> E +.. function:: element_at(array(E), index) -> E Returns element of ``array`` at given ``index``. If ``index`` > 0, this function provides the same functionality as the SQL-standard subscript operator (``[]``). If ``index`` < 0, ``element_at`` accesses elements from the last to the first. -.. function:: filter(array, function) -> array +.. function:: filter(array(T), function(T,boolean)) -> array(T) Constructs an array from those elements of ``array`` for which ``function`` returns true:: @@ -129,7 +129,17 @@ Array Functions Flattens an ``array(array(T))`` to an ``array(T)`` by concatenating the contained arrays. -.. function:: reduce(array, initialState S, inputFunction, outputFunction) -> R +.. function:: ngrams(array(T), n) -> array(array(T)) + + Returns ``n``-grams for the ``array``:: + + SELECT ngrams(ARRAY['foo', 'bar', 'baz', 'foo'], 2); -- [['foo', 'bar'], ['bar', 'baz'], ['baz', 'foo']] + SELECT ngrams(ARRAY['foo', 'bar', 'baz', 'foo'], 3); -- [['foo', 'bar', 'baz'], ['bar', 'baz', 'foo']] + SELECT ngrams(ARRAY['foo', 'bar', 'baz', 'foo'], 4); -- [['foo', 'bar', 'baz', 'foo']] + SELECT ngrams(ARRAY['foo', 'bar', 'baz', 'foo'], 5); -- [['foo', 'bar', 'baz', 'foo']] + SELECT ngrams(ARRAY[1, 2, 3, 4], 2); -- [[1, 2], [2, 3], [3, 4]] + +.. function:: reduce(array(T), initialState S, inputFunction(S,T,S), outputFunction(S,R)) -> R Returns a single value reduced from ``array``. ``inputFunction`` will be invoked for each element in ``array`` in order. In addition to taking @@ -158,26 +168,26 @@ Array Functions Returns an array which has the reversed order of array ``x``. -.. function:: sequence(start, stop) -> array +.. function:: sequence(start, stop) -> array(bigint) Generate a sequence of integers from ``start`` to ``stop``, incrementing by ``1`` if ``start`` is less than or equal to ``stop``, otherwise ``-1``. -.. function:: sequence(start, stop, step) -> array +.. function:: sequence(start, stop, step) -> array(bigint) Generate a sequence of integers from ``start`` to ``stop``, incrementing by ``step``. -.. function:: sequence(start, stop) -> array +.. function:: sequence(start, stop) -> array(date) Generate a sequence of dates from ``start`` date to ``stop`` date, incrementing by ``1`` day if ``start`` date is less than or equal to ``stop`` date, otherwise ``-1`` day. -.. function:: sequence(start, stop, step) -> array +.. function:: sequence(start, stop, step) -> array(date) Generate a sequence of dates from ``start`` to ``stop``, incrementing by ``step``. The type of ``step`` can be either ``INTERVAL DAY TO SECOND`` or ``INTERVAL YEAR TO MONTH``. -.. function:: sequence(start, stop, step) -> array +.. function:: sequence(start, stop, step) -> array(timestamp) Generate a sequence of timestamps from ``start`` to ``stop``, incrementing by ``step``. The type of ``step`` can be either ``INTERVAL DAY TO SECOND`` or ``INTERVAL YEAR TO MONTH``. @@ -191,7 +201,7 @@ Array Functions Subsets array ``x`` starting from index ``start`` (or starting from the end if ``start`` is negative) with a length of ``length``. -.. function:: transform(array, function) -> array +.. function:: transform(array(T), function(T,U)) -> array(U) Returns an array that is the result of applying ``function`` to each element of ``array``:: @@ -201,7 +211,7 @@ Array Functions SELECT transform(ARRAY ['x', 'abc', 'z'], x -> x || '0'); -- ['x0', 'abc0', 'z0'] SELECT transform(ARRAY [ARRAY [1, NULL, 2], ARRAY[3, NULL]], a -> filter(a, x -> x IS NOT NULL)); -- [[1, 2], [3]] -.. function:: zip(array1, array2[, ...]) -> array +.. function:: zip(array1, array2[, ...]) -> array(row) Merges the given arrays, element-wise, into a single array of rows. The M-th element of the N-th argument will be the N-th field of the M-th output element. @@ -209,7 +219,7 @@ Array Functions SELECT zip(ARRAY[1, 2], ARRAY['1b', null, '3b']); -- [ROW(1, '1b'), ROW(2, null), ROW(null, '3b')] -.. function:: zip_with(array, array, function) -> array +.. function:: zip_with(array(T), array(U), function(T,U,R)) -> array(R) Merges the two given arrays, element-wise, into a single array using ``function``. If one array is shorter, nulls are appended at the end to match the length of the longer array, before applying ``function``:: diff --git a/presto-docs/src/main/sphinx/functions/binary.rst b/presto-docs/src/main/sphinx/functions/binary.rst index 60cb8d11b1b27..5df00216b6a44 100644 --- a/presto-docs/src/main/sphinx/functions/binary.rst +++ b/presto-docs/src/main/sphinx/functions/binary.rst @@ -22,6 +22,21 @@ Binary Functions This function provides the same functionality as the SQL-standard concatenation operator (``||``). +.. function:: substr(binary, start) -> varbinary + :noindex: + + Returns the rest of ``binary`` from the starting position ``start``, + measured in bytes. Positions start with ``1``. A negative starting position + is interpreted as being relative to the end of the string. + +.. function:: substr(binary, start, length) -> varbinary + :noindex: + + Returns a substring from ``binary`` of length ``length`` from the starting + position ``start``, measured in bytes. Positions start with ``1``. A + negative starting position is interpreted as being relative to the end of + the string. + .. function:: to_base64(binary) -> varchar Encodes ``binary`` into a base64 string representation. diff --git a/presto-docs/src/main/sphinx/functions/conversion.rst b/presto-docs/src/main/sphinx/functions/conversion.rst index bde4ed19b36d4..3391f68fd774f 100644 --- a/presto-docs/src/main/sphinx/functions/conversion.rst +++ b/presto-docs/src/main/sphinx/functions/conversion.rst @@ -22,6 +22,35 @@ Conversion Functions Like :func:`cast`, but returns null if the cast fails. +Data Size +--------- + +The ``parse_presto_data_size`` function supports the following units: + +======= ============= ============== +Unit Description Value +======= ============= ============== +``B`` Bytes 1 +``kB`` Kilobytes 1024 +``MB`` Megabytes 1024\ :sup:`2` +``GB`` Gigabytes 1024\ :sup:`3` +``TB`` Terabytes 1024\ :sup:`4` +``PB`` Petabytes 1024\ :sup:`5` +``EB`` Exabytes 1024\ :sup:`6` +``ZB`` Zettabytes 1024\ :sup:`7` +``YB`` Yottabytes 1024\ :sup:`8` +======= ============= ============== + +.. function:: parse_presto_data_size(string) -> decimal(38) + + Parses ``string`` of format ``value unit`` into a number, where + ``value`` is the fractional number of ``unit`` values:: + + SELECT parse_presto_data_size('1B'); -- 1 + SELECT parse_presto_data_size('1kB'); -- 1024 + SELECT parse_presto_data_size('1MB'); -- 1048576 + SELECT parse_presto_data_size('2.3MB'); -- 2411724 + Miscellaneous ------------- diff --git a/presto-docs/src/main/sphinx/functions/datetime.rst b/presto-docs/src/main/sphinx/functions/datetime.rst index 90c7d615f37ca..d948d2b063e22 100644 --- a/presto-docs/src/main/sphinx/functions/datetime.rst +++ b/presto-docs/src/main/sphinx/functions/datetime.rst @@ -336,6 +336,10 @@ Convenience Extraction Functions Returns the hour of the day from ``x``. The value ranges from ``0`` to ``23``. +.. function:: millisecond(x) -> bigint + + Returns the millisecond of the second from ``x``. + .. function:: minute(x) -> bigint Returns the minute of the hour from ``x``. diff --git a/presto-docs/src/main/sphinx/functions/geospatial.rst b/presto-docs/src/main/sphinx/functions/geospatial.rst index 2c46928eced14..9acf10dfc324d 100644 --- a/presto-docs/src/main/sphinx/functions/geospatial.rst +++ b/presto-docs/src/main/sphinx/functions/geospatial.rst @@ -2,8 +2,8 @@ Geospatial Functions ==================== -Presto Geospatial functions support the SQL/MM specification. -They are compliant with the Open Geospatial Consortium’s (OGC) OpenGIS Specifications. +Presto Geospatial functions that begin with the ``ST_`` prefix support the SQL/MM specification +and are compliant with the Open Geospatial Consortium’s (OGC) OpenGIS Specifications. As such, many Presto Geospatial functions require, or more accurately, assume that geometries that are operated on are both simple and valid. For example, it does not make sense to calculate the area of a polygon that has a hole defined outside of the @@ -22,27 +22,49 @@ Presto Geospatial functions support the Well-Known Text (WKT) form of spatial ob Constructors ------------ -.. function:: ST_Point(double, double) -> Point +.. function:: ST_AsBinary(Geometry) -> varbinary - Returns a geometry type point object with the given coordinate values. + Returns the WKB representation of the geometry. + +.. function:: ST_AsText(Geometry) -> varchar + + Returns the WKT representation of the geometry. For empty geometries, + ``ST_AsText(ST_LineFromText('LINESTRING EMPTY'))`` will produce ``'MULTILINESTRING EMPTY'`` + and ``ST_AsText(ST_Polygon('POLYGON EMPTY'))`` will produce ``'MULTIPOLYGON EMPTY'``. + +.. function:: ST_GeometryFromText(varchar) -> Geometry + + Returns a geometry type object from WKT representation. + +.. function:: ST_GeomFromBinary(varbinary) -> Geometry + + Returns a geometry type object from WKB representation. .. function:: ST_LineFromText(varchar) -> LineString Returns a geometry type linestring object from WKT representation. -.. function:: ST_Polygon(varchar) -> Polygon +.. function:: ST_LineString(array(Point)) -> LineString - Returns a geometry type polygon object from WKT representation. + Returns a LineString formed from an array of points. If there are fewer than + two non-empty points in the input array, an empty LineString will be returned. + Throws an exception if any element in the array is `null` or empty or same as the previous one. + The returned geometry may not be simple, e.g. may self-intersect or may contain + duplicate vertexes depending on the input. -.. function:: ST_GeometryFromText(varchar) -> Geometry +.. function:: ST_MultiPoint(array(Point)) -> MultiPoint - Returns a geometry type object from WKT representation. + Returns a MultiPoint geometry object formed from the specified points. Return `null` if input array is empty. + Throws an exception if any element in the array is `null` or empty. + The returned geometry may not be simple and may contain duplicate points if input array has duplicates. -.. function:: ST_AsText(Geometry) -> varchar +.. function:: ST_Point(double, double) -> Point - Returns the WKT representation of the geometry. For empty geometries, - ``ST_AsText(ST_LineFromText('LINESTRING EMPTY'))`` will produce ``'MULTILINESTRING EMPTY'`` - and ``ST_AsText(ST_Polygon('POLYGON EMPTY'))`` will produce ``'MULTIPOLYGON EMPTY'``. + Returns a geometry type point object with the given coordinate values. + +.. function:: ST_Polygon(varchar) -> Polygon + + Returns a geometry type polygon object from WKT representation. Relationship Tests ------------------ @@ -69,7 +91,7 @@ Relationship Tests .. function:: ST_Intersects(Geometry, Geometry) -> boolean Returns ``true`` if the given geometries spatially intersect in two dimensions - (share any portion of space) and ``false`` if they don not (they are disjoint). + (share any portion of space) and ``false`` if they do not (they are disjoint). .. function:: ST_Overlaps(Geometry, Geometry) -> boolean @@ -92,6 +114,12 @@ Relationship Tests Operations ---------- +.. function:: geometry_union(array(Geometry)) -> Geometry + + Returns a geometry that represents the point set union of the input geometries. Performance + of this function, in conjunction with :func:`array_agg` to first aggregate the input geometries, + may be better than :func:`geometry_union_agg`, at the expense of higher memory utilization. + .. function:: ST_Boundary(Geometry) -> Geometry Returns the closure of the combinatorial boundary of this geometry. @@ -109,7 +137,7 @@ Operations Returns the bounding rectangular polygon of a geometry. -.. function:: ST_EnvelopeAsPts(Geometry) -> Geometry +.. function:: ST_EnvelopeAsPts(Geometry) -> array(Geometry) Returns an array of two points: the lower left and upper right corners of the bounding rectangular polygon of a geometry. Returns null if input geometry is empty. @@ -130,7 +158,7 @@ Operations Returns a geometry that represents the point set union of the input geometries. - This function doesn't support geometry collections. + See also: :func:`geometry_union`, :func:`geometry_union_agg` Accessors @@ -151,7 +179,6 @@ Accessors .. function:: ST_ConvexHull(Geometry) -> Geometry Returns the minimum convex geometry that encloses all input geometries. - This function doesn't support geometry collections. .. function:: ST_CoordDim(Geometry) -> bigint @@ -311,7 +338,6 @@ Aggregations .. function:: convex_hull_agg(Geometry) -> Geometry Returns the minimum convex geometry that encloses all input geometries. - This function doesn't support geometry collections. .. function:: geometry_union_agg(Geometry) -> Geometry @@ -338,12 +364,12 @@ These functions convert between geometries and and longitude. Latitude must be within ``[-85.05112878, 85.05112878]`` range. Longitude must be within ``[-180, 180]`` range. Zoom levels from 1 to 23 are supported. -.. function:: bing_tiles_around(latitude, longitude, zoom_level) -> array +.. function:: bing_tiles_around(latitude, longitude, zoom_level) -> array(BingTile) Returns a collection of Bing tiles that surround the point specified by the latitude and longitude arguments at a given zoom level. -.. function:: bing_tiles_around(latitude, longitude, zoom_level, radius_in_km) -> array +.. function:: bing_tiles_around(latitude, longitude, zoom_level, radius_in_km) -> array(BingTile) Returns a minimum set of Bing tiles at specified zoom level that cover a circle of specified radius in km around a specified (latitude, longitude) point. @@ -364,7 +390,7 @@ These functions convert between geometries and Returns the zoom level of a given Bing tile. -.. function:: geometry_to_bing_tiles(geometry, zoom_level) -> array +.. function:: geometry_to_bing_tiles(geometry, zoom_level) -> array(BingTile) Returns the minimum set of Bing tiles that fully covers a given geometry at a given zoom level. Zoom levels from 1 to 23 are supported. diff --git a/presto-docs/src/main/sphinx/functions/hyperloglog.rst b/presto-docs/src/main/sphinx/functions/hyperloglog.rst new file mode 100644 index 0000000000000..1410a25c5f57d --- /dev/null +++ b/presto-docs/src/main/sphinx/functions/hyperloglog.rst @@ -0,0 +1,74 @@ +===================== +HyperLogLog Functions +===================== + +Presto implements the :func:`approx_distinct` function using the +`HyperLogLog `_ data structure. + +Data Structures +--------------- + +Presto implements HyperLogLog data sketches as a set of 32-bit buckets which +store a *maximum hash*. They can be stored sparsely (as a map from bucket ID +to bucket), or densely (as a contiguous memory block). The HyperLogLog data +structure starts as the sparse representation, switching to dense when it is +more efficient. The P4HyperLogLog structure is initialized densely and +remains dense for its lifetime. + +:ref:`hyperloglog_type` implicitly casts to :ref:`p4hyperloglog_type`, +while one can explicitly cast ``HyperLogLog`` to ``P4HyperLogLog``:: + + cast(hll AS P4HyperLogLog) + +Serialization +------------- + +Data sketches can be serialized to and deserialized from ``varbinary``. This +allows them to be stored for later use. Combined with the ability to merge +multiple sketches, this allows one to calculate :func:`approx_distinct` of the +elements of a partition of a query, then for the entirety of a query with very +little cost. + +For example, calculating the ``HyperLogLog`` for daily unique users will allow +weekly or monthly unique users to be calculated incrementally by combining the +dailies. This is similar to computing weekly revenue by summing daily revenue. +Uses of :func:`approx_distinct` with ``GROUPING SETS`` can be converted to use +``HyperLogLog``. Examples:: + + CREATE TABLE visit_summaries ( + visit_date date, + hll varbinary + ); + + INSERT INTO visit_summaries + SELECT visit_date, cast(approx_set(user_id) AS varbinary) + FROM user_visits + GROUP BY visit_date; + + SELECT cardinality(merge(cast(hll AS HyperLogLog))) AS weekly_unique_users + FROM visit_summaries + WHERE visit_date >= current_date - interval '7' day; + +Functions +--------- + +.. function:: approx_set(x) -> HyperLogLog + + Returns the ``HyperLogLog`` sketch of the input data set of ``x``. This + data sketch underlies :func:`approx_distinct` and can be stored and + used later by calling ``cardinality()``. + +.. function:: cardinality(hll) -> bigint + :noindex: + + This will perform :func:`approx_distinct` on the data summarized by the + ``hll`` HyperLogLog data sketch. + +.. function:: empty_approx_set() -> HyperLogLog + + Returns an empty ``HyperLogLog``. + +.. function:: merge(HyperLogLog) -> HyperLogLog + + Returns the ``HyperLogLog`` of the aggregate union of the individual ``hll`` + HyperLogLog structures. diff --git a/presto-docs/src/main/sphinx/functions/json.rst b/presto-docs/src/main/sphinx/functions/json.rst index 1d0382269c671..d3403a18a95d3 100644 --- a/presto-docs/src/main/sphinx/functions/json.rst +++ b/presto-docs/src/main/sphinx/functions/json.rst @@ -22,8 +22,7 @@ Cast to JSON SELECT CAST(ARRAY[1, 23, 456] AS JSON); -- JSON '[1,23,456]' SELECT CAST(ARRAY[1, NULL, 456] AS JSON); -- JSON '[1,null,456]' SELECT CAST(ARRAY[ARRAY[1, 23], ARRAY[456]] AS JSON); -- JSON '[[1,23],[456]]' - SELECT CAST(MAP(ARRAY['k1', 'k2', 'k3'], ARRAY[1, 23, 456]) AS JSON); -- JSON '{"k1":1,"k2":23,"k3":456}' - SELECT CAST(MAP(ARRAY['k1', 'k2', 'k3'], ARRAY[1, 23, 456]) AS JSON); -- JSON '{"k1":1,"k2":23,"k3":456}' + SELECT CAST(MAP_FROM_ENTRIES(ARRAY[('k1', 1), ('k2', 23), ('k3', 456)]) AS JSON); -- JSON '{"k1":1,"k2":23,"k3":456}' SELECT CAST(CAST(ROW(123, 'abc', true) AS ROW(v1 BIGINT, v2 VARCHAR, v3 BOOLEAN)) AS JSON); -- JSON '[123,"abc",true]' .. note:: diff --git a/presto-docs/src/main/sphinx/functions/map.rst b/presto-docs/src/main/sphinx/functions/map.rst index cc5a7dfb67003..71d1eb118e8ec 100644 --- a/presto-docs/src/main/sphinx/functions/map.rst +++ b/presto-docs/src/main/sphinx/functions/map.rst @@ -17,7 +17,7 @@ Map Functions Returns the cardinality (size) of the map ``x``. -.. function:: element_at(map, key) -> V +.. function:: element_at(map(K,V), key) -> V :noindex: Returns value for given ``key``, or ``NULL`` if the key is not contained in the map. @@ -28,7 +28,7 @@ Map Functions SELECT map(); -- {} -.. function:: map(array, array) -> map +.. function:: map(array(K), array(V)) -> map(K,V) Returns a map created using the given key/value arrays. :: @@ -36,30 +36,30 @@ Map Functions See also :func:`map_agg` and :func:`multimap_agg` for creating a map as an aggregation. -.. function:: map_from_entries(array>) -> map +.. function:: map_from_entries(array(row(K,V))) -> map(K,V) Returns a map created from the given array of entries. :: SELECT map_from_entries(ARRAY[(1, 'x'), (2, 'y')]); -- {1 -> 'x', 2 -> 'y'} -.. function:: multimap_from_entries(array>) -> map> +.. function:: multimap_from_entries(array(row(K,V))) -> map(K,array(V)) Returns a multimap created from the given array of entries. Each key can be associated with multiple values. :: SELECT multimap_from_entries(ARRAY[(1, 'x'), (2, 'y'), (1, 'z')]); -- {1 -> ['x', 'z'], 2 -> ['y']} -.. function:: map_entries(map) -> array> +.. function:: map_entries(map(K,V)) -> array(row(K,V)) Returns an array of all entries in the given map. :: SELECT map_entries(MAP(ARRAY[1, 2], ARRAY['x', 'y'])); -- [ROW(1, 'x'), ROW(2, 'y')] -.. function:: map_concat(map1, map2, ..., mapN) -> map +.. function:: map_concat(map1(K,V), map2(K,V), ..., mapN(K,V)) -> map(K,V) Returns the union of all the given maps. If a key is found in multiple given maps, that key's value in the resulting map comes from the last one of those maps. -.. function:: map_filter(map, function) -> MAP +.. function:: map_filter(map(K,V), function(K,V,boolean)) -> map(K,V) Constructs a map from those entries of ``map`` for which ``function`` returns true:: @@ -67,15 +67,15 @@ Map Functions SELECT map_filter(MAP(ARRAY[10, 20, 30], ARRAY['a', NULL, 'c']), (k, v) -> v IS NOT NULL); -- {10 -> a, 30 -> c} SELECT map_filter(MAP(ARRAY['k1', 'k2', 'k3'], ARRAY[20, 3, 15]), (k, v) -> v > 10); -- {k1 -> 20, k3 -> 15} -.. function:: map_keys(x) -> array +.. function:: map_keys(x(K,V)) -> array(K) Returns all the keys in the map ``x``. -.. function:: map_values(x) -> array +.. function:: map_values(x(K,V)) -> array(V) Returns all the values in the map ``x``. -.. function:: map_zip_with(map, map, function) -> map +.. function:: map_zip_with(map(K,V1), map(K,V2), function(K,V1,V2,V3)) -> map(K,V3) Merges the two given maps into a single map by applying ``function`` to the pair of values with the same key. For keys only presented in one map, NULL will be passed as the value for the missing key. :: @@ -90,7 +90,7 @@ Map Functions MAP(ARRAY['a', 'b', 'c'], ARRAY[1, 2, 3]), (k, v1, v2) -> k || CAST(v1/v2 AS VARCHAR)); -.. function:: transform_keys(map, function) -> map +.. function:: transform_keys(map(K1,V), function(K1,V,K2)) -> map(K2,V) Returns a map that applies ``function`` to each entry of ``map`` and transforms the keys:: @@ -101,7 +101,7 @@ Map Functions SELECT transform_keys(MAP(ARRAY [1, 2], ARRAY [1.0, 1.4]), -- {one -> 1.0, two -> 1.4} (k, v) -> MAP(ARRAY[1, 2], ARRAY['one', 'two'])[k]); -.. function:: transform_values(map, function) -> map +.. function:: transform_values(map(K,V1), function(K,V1,V2)) -> map(K,V2) Returns a map that applies ``function`` to each entry of ``map`` and transforms the values:: diff --git a/presto-docs/src/main/sphinx/functions/math.rst b/presto-docs/src/main/sphinx/functions/math.rst index f5f595d52d62d..20b712d47fc3a 100644 --- a/presto-docs/src/main/sphinx/functions/math.rst +++ b/presto-docs/src/main/sphinx/functions/math.rst @@ -73,6 +73,18 @@ Mathematical Functions The mean and value v must be real values and the standard deviation must be a real and positive value. +.. function:: inverse_beta_cdf(a, b, p) -> double + + Compute the inverse of the Beta cdf with given a, b parameters for the cumulative + probability (p): P(N < n). The a, b parameters must be positive real values. + The probability p must lie on the interval [0, 1]. + +.. function:: beta_cdf(a, b, v) -> double + + Compute the Beta cdf with given a, b parameters: P(N < v; a, b). + The a, b parameters must be positive real numbers and value v must be a real value. + The value v must lie on the interval [0, 1]. + .. function:: ln(x) -> double Returns the natural logarithm of ``x``. diff --git a/presto-docs/src/main/sphinx/functions/qdigest.rst b/presto-docs/src/main/sphinx/functions/qdigest.rst new file mode 100644 index 0000000000000..ec7152af8b0b1 --- /dev/null +++ b/presto-docs/src/main/sphinx/functions/qdigest.rst @@ -0,0 +1,53 @@ +========================= +Quantile Digest Functions +========================= + +Presto implements the ``approx_percentile`` function with the quantile digest +data structure. The underlying data structure, :ref:`qdigest `, +is exposed as a data type in Presto, and can be created, queried and stored +separately from ``approx_percentile``. + +Data Structures +--------------- + +A quantile digest is a data sketch which stores approximate percentile +information. The presto type for this data structure is called ``qdigest``, +and it takes a parameter which must be one of ``bigint``, ``double`` or +``real`` which represent the set of numbers that may be ingested by the +``qdigest``. They may be merged without losing precision, and for storage +and retrieval they may be cast to/from ``VARBINARY``. + +Functions +--------- + +.. function:: merge(qdigest) -> qdigest + :noindex: + + Merges all input ``qdigest``\ s into a single ``qdigest``. + +.. function:: value_at_quantile(qdigest(T), quantile) -> T + + Returns the approximate percentile values from the quantile digest given + the number ``quantile`` between 0 and 1. + +.. function:: values_at_quantiles(qdigest(T), quantiles) -> T + + Returns the approximate percentile values as an array given the input + quantile digest and array of values between 0 and 1 which + represent the quantiles to return. + +.. function:: qdigest_agg(x) -> qdigest<[same as x]> + + Returns the ``qdigest`` which is composed of all input values of ``x``. + +.. function:: qdigest_agg(x, w) -> qdigest<[same as x]> + + Returns the ``qdigest`` which is composed of all input values of ``x`` using + the per-item weight ``w``. + +.. function:: qdigest_agg(x, w, accuracy) -> qdigest<[same as x]> + + Returns the ``qdigest`` which is composed of all input values of ``x`` using + the per-item weight ``w`` and maximum error of ``accuracy``. ``accuracy`` + must be a value greater than zero and less than one, and it must be constant + for all input rows. diff --git a/presto-docs/src/main/sphinx/functions/regexp.rst b/presto-docs/src/main/sphinx/functions/regexp.rst index dd4b88899da07..ff64abf28a8ac 100644 --- a/presto-docs/src/main/sphinx/functions/regexp.rst +++ b/presto-docs/src/main/sphinx/functions/regexp.rst @@ -41,14 +41,14 @@ with a few notable exceptions: .. _Capturing groups: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#cg -.. function:: regexp_extract_all(string, pattern) -> array +.. function:: regexp_extract_all(string, pattern) -> array(varchar) Returns the substring(s) matched by the regular expression ``pattern`` in ``string``:: SELECT regexp_extract_all('1a 2b 14m', '\d+'); -- [1, 2, 14] -.. function:: regexp_extract_all(string, pattern, group) -> array +.. function:: regexp_extract_all(string, pattern, group) -> array(varchar) Finds all occurrences of the regular expression ``pattern`` in ``string`` and returns the `capturing group number`_ ``group``:: @@ -74,7 +74,7 @@ with a few notable exceptions: Evaluates the regular expression ``pattern`` and determines if it is contained within ``string``. - This function is similar to the ``LIKE`` operator, expect that the + This function is similar to the ``LIKE`` operator, except that the pattern only needs to be contained within ``string``, rather than needing to match all of ``string``. In other words, this performs a *contains* operation rather than a *match* operation. You can match @@ -109,7 +109,7 @@ with a few notable exceptions: SELECT regexp_replace('new york', '(\w)(\w*)', x -> upper(x[1]) || lower(x[2])); --'New York' -.. function:: regexp_split(string, pattern) -> array +.. function:: regexp_split(string, pattern) -> array(varchar) Splits ``string`` using the regular expression ``pattern`` and returns an array. Trailing empty strings are preserved:: diff --git a/presto-docs/src/main/sphinx/functions/string.rst b/presto-docs/src/main/sphinx/functions/string.rst index fc0f91eff51a5..328ddf8c7d990 100644 --- a/presto-docs/src/main/sphinx/functions/string.rst +++ b/presto-docs/src/main/sphinx/functions/string.rst @@ -96,11 +96,11 @@ String Functions Removes trailing whitespace from ``string``. -.. function:: split(string, delimiter) -> array +.. function:: split(string, delimiter) -> array(varchar) Splits ``string`` on ``delimiter`` and returns an array. -.. function:: split(string, delimiter, limit) -> array +.. function:: split(string, delimiter, limit) -> array(varchar) Splits ``string`` on ``delimiter`` and returns an array of size at most ``limit``. The last element in the array always contain everything @@ -118,7 +118,7 @@ String Functions ``entryDelimiter`` splits ``string`` into key-value pairs. ``keyValueDelimiter`` splits each pair into key and value. -.. function:: split_to_multimap(string, entryDelimiter, keyValueDelimiter) -> map> +.. function:: split_to_multimap(string, entryDelimiter, keyValueDelimiter) -> map(varchar, array(varchar)) Splits ``string`` by ``entryDelimiter`` and ``keyValueDelimiter`` and returns a map containing an array of values for each unique key. ``entryDelimiter`` splits ``string`` diff --git a/presto-docs/src/main/sphinx/include/PrestoThriftService.thrift b/presto-docs/src/main/sphinx/include/PrestoThriftService.thrift index 58e9c01fe64de..977910bf99592 100644 --- a/presto-docs/src/main/sphinx/include/PrestoThriftService.thrift +++ b/presto-docs/src/main/sphinx/include/PrestoThriftService.thrift @@ -200,7 +200,18 @@ struct PrestoThriftSplitBatch { } struct PrestoThriftSplit { + /** + * Encodes all the information needed to identify a batch of rows to return to Presto. + * For a basic scan, includes schema name, table name, and output constraint. + * For an index scan, includes schema name, table name, set of keys to lookup and output constraint. + */ 1: PrestoThriftId splitId; + + /** + * Identifies the set of hosts on which the rows are available. If empty, then the rows + * are expected to be available on any host. The hosts in this list may be independent + * from the hosts used to serve metadata requests. + */ 2: list hosts; } @@ -323,7 +334,7 @@ service PrestoThriftService { * @param schemaTableName schema and table name * @param indexColumnNames specifies columns and their order for keys * @param outputColumnNames a list of column names to return - * @param keys keys for which records need to be returned + * @param keys keys for which records need to be returned; includes only unique and non-null values * @param outputConstraint constraint on the returned data * @param maxSplitCount maximum number of splits to return * @param nextToken token from a previous split batch or {@literal null} if it is the first call diff --git a/presto-docs/src/main/sphinx/installation/jdbc.rst b/presto-docs/src/main/sphinx/installation/jdbc.rst index c9ed1cc9794a0..52d24bf1487f9 100644 --- a/presto-docs/src/main/sphinx/installation/jdbc.rst +++ b/presto-docs/src/main/sphinx/installation/jdbc.rst @@ -89,7 +89,7 @@ Name Description to validate HTTPS server certificates. ``SSLTrustStorePassword`` The password for the TrustStore. ``KerberosRemoteServiceName`` Presto coordinator Kerberos service name. This parameter is - required for Kerberos authentiation. + required for Kerberos authentication. ``KerberosPrincipal`` The principal to use when authenticating to the Presto coordinator. ``KerberosUseCanonicalHostname`` Use the canonical hostname of the Presto coordinator for the Kerberos service principal by first resolving the hostname to an IP address diff --git a/presto-docs/src/main/sphinx/installation/verifier.rst b/presto-docs/src/main/sphinx/installation/verifier.rst index 1d4f3a9d3a47a..16ec75235522f 100644 --- a/presto-docs/src/main/sphinx/installation/verifier.rst +++ b/presto-docs/src/main/sphinx/installation/verifier.rst @@ -28,6 +28,7 @@ Create a MySQL database with the following table and load it with the queries yo control_postqueries TEXT, control_username VARCHAR(256) NOT NULL default 'verifier-test', control_password VARCHAR(256), + session_properties_json VARCHAR(2048), PRIMARY KEY (id) ); diff --git a/presto-docs/src/main/sphinx/language/types.rst b/presto-docs/src/main/sphinx/language/types.rst index 4b4b6e1054fac..039f60dcec024 100644 --- a/presto-docs/src/main/sphinx/language/types.rst +++ b/presto-docs/src/main/sphinx/language/types.rst @@ -234,3 +234,48 @@ Network Address using the canonical format defined in :rfc:`5952`. Examples: ``IPADDRESS '10.0.0.1'``, ``IPADDRESS '2001:db8::1'`` + +HyperLogLog +----------- + +Calculating the approximate distinct count can be done much more cheaply than an exact count using the +`HyperLogLog `_ data sketch. See :doc:`/functions/hyperloglog`. + +.. _hyperloglog_type: + +``HyperLogLog`` +^^^^^^^^^^^^^^^ + + A HyperLogLog sketch allows efficient computation of :func:`approx_distinct`. It starts as a + sparse representation, switching to a dense representation when it becomes more efficient. + +.. _p4hyperloglog_type: + +``P4HyperLogLog`` +^^^^^^^^^^^^^^^^^ + + A P4HyperLogLog sketch is similar to :ref:`hyperloglog_type`, but it starts (and remains) + in the dense representation. + +Quantile Digest +--------------- + +.. _qdigest_type: + +``QDigest`` +^^^^^^^^^^^ + + A quantile digest (qdigest) is a summary structure which captures the approximate + distribution of data for a given input set, and can be queried to retrieve approximate + quantile values from the distribution. The level of accuracy for a qdigest + is tunable, allowing for more precise results at the expense of space. + + A qdigest can be used to give approximate answer to queries asking for what value + belongs at a certain quantile. A useful property of qdigests is that they are + additive, meaning they can be merged together without losing precision. + + A qdigest may be helpful whenever the partial results of ``approx_percentile`` + can be reused. For example, one may be interested in a daily reading of the 99th + percentile values that are read over the course of a week. Instead of calculating + the past week of data with ``approx_percentile``, ``qdigest``\ s could be stored + daily, and quickly merged to retrieve the 99th percentile value. diff --git a/presto-docs/src/main/sphinx/release.rst b/presto-docs/src/main/sphinx/release.rst index 14400d4eae5a2..23a56a7526432 100644 --- a/presto-docs/src/main/sphinx/release.rst +++ b/presto-docs/src/main/sphinx/release.rst @@ -5,6 +5,14 @@ Release Notes .. toctree:: :maxdepth: 1 + release/release-0.216 + release/release-0.215 + release/release-0.214 + release/release-0.213 + release/release-0.212 + release/release-0.211 + release/release-0.210 + release/release-0.209 release/release-0.208 release/release-0.207 release/release-0.206 diff --git a/presto-docs/src/main/sphinx/release/release-0.102.rst b/presto-docs/src/main/sphinx/release/release-0.102.rst index 2c8d634744375..228fadc07e97c 100644 --- a/presto-docs/src/main/sphinx/release/release-0.102.rst +++ b/presto-docs/src/main/sphinx/release/release-0.102.rst @@ -37,7 +37,7 @@ General Changes * Optimize map subscript operator. * Add :func:`from_utf8` and :func:`to_utf8` functions. * Add ``task_writer_count`` session property to set ``task.writer-count``. -* Add cast from ``ARRAY`` to ``ARRAY``. +* Add cast from ``ARRAY(F)`` to ``ARRAY(T)``. * Extend implicit coercions to ``ARRAY`` element types. * Implement implicit coercions in ``VALUES`` expressions. * Fix potential deadlock in scheduler. diff --git a/presto-docs/src/main/sphinx/release/release-0.111.rst b/presto-docs/src/main/sphinx/release/release-0.111.rst index 06822ac45cb6a..5488b2386d100 100644 --- a/presto-docs/src/main/sphinx/release/release-0.111.rst +++ b/presto-docs/src/main/sphinx/release/release-0.111.rst @@ -9,6 +9,6 @@ General Changes * Optimize ``CASE`` expressions on a constant. * Add basic support for ``IF NOT EXISTS`` for ``CREATE TABLE``. * Semi-joins are hash-partitioned if ``distributed_join`` is turned on. -* Add support for partial cast from JSON. For example, ``json`` can be cast to ``array``, ``map``, etc. +* Add support for partial cast from JSON. For example, ``json`` can be cast to ``array(json)``, ``map(varchar, json)``, etc. * Add implicit coercions for ``UNION``. * Expose query stats in the JDBC driver ``ResultSet``. diff --git a/presto-docs/src/main/sphinx/release/release-0.121.rst b/presto-docs/src/main/sphinx/release/release-0.121.rst index 74a4549183d4a..989260393aac6 100644 --- a/presto-docs/src/main/sphinx/release/release-0.121.rst +++ b/presto-docs/src/main/sphinx/release/release-0.121.rst @@ -8,4 +8,4 @@ General Changes * Fix regression that causes task scheduler to not retry requests in some cases. * Throttle task info refresher on errors. * Fix planning failure that prevented the use of large ``IN`` lists. -* Fix comparison of ``array`` where ``x`` is a comparable, non-orderable type. +* Fix comparison of ``array(T)`` where ``T`` is a comparable, non-orderable type. diff --git a/presto-docs/src/main/sphinx/release/release-0.140.rst b/presto-docs/src/main/sphinx/release/release-0.140.rst index 3244cd6be1fe3..9b84d4fb904da 100644 --- a/presto-docs/src/main/sphinx/release/release-0.140.rst +++ b/presto-docs/src/main/sphinx/release/release-0.140.rst @@ -12,7 +12,7 @@ General Changes * Fix logging of ``failure_host`` and ``failure_task`` fields in ``QueryCompletionEvent``. * Fix race which can cause queries to fail with a ``REMOTE_TASK_ERROR``. -* Optimize :func:`array_distinct` for ``array``. +* Optimize :func:`array_distinct` for ``array(bigint)``. * Optimize ``>`` operator for :ref:`array_type`. * Fix an optimization issue that could result in non-deterministic functions being evaluated more than once producing unexpected results. diff --git a/presto-docs/src/main/sphinx/release/release-0.208.rst b/presto-docs/src/main/sphinx/release/release-0.208.rst index e4f1db45f7694..59e3564ef2755 100644 --- a/presto-docs/src/main/sphinx/release/release-0.208.rst +++ b/presto-docs/src/main/sphinx/release/release-0.208.rst @@ -2,6 +2,11 @@ Release 0.208 ============= +.. warning:: + + This release has the potential for data loss in the Hive connector + when writing bucketed sorted tables. + General Changes --------------- diff --git a/presto-docs/src/main/sphinx/release/release-0.209.rst b/presto-docs/src/main/sphinx/release/release-0.209.rst new file mode 100644 index 0000000000000..d419c19dd6994 --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.209.rst @@ -0,0 +1,77 @@ +============= +Release 0.209 +============= + +General Changes +--------------- + +* Fix incorrect predicate pushdown when grouping sets contain the empty grouping set (:issue:`11296`). +* Fix ``X-Forwarded-Proto`` header handling for requests to the ``/`` path (:issue:`11168`). +* Fix a regression that results in execution failure when at least one + of the arguments to :func:`min_by` or :func:`max_by` is a constant ``NULL``. +* Fix failure when some buckets are completely filtered out during bucket-by-bucket execution. +* Fix execution failure of queries due to a planning deficiency involving + complex nested joins where a join that is not eligible for bucket-by-bucket + execution feeds into the build side of a join that is eligible. +* Improve numerical stability for :func:`corr`, :func:`covar_samp`, + :func:`regr_intercept`, and :func:`regr_slope`. +* Do not include column aliases when checking column access permissions. +* Eliminate unnecessary data redistribution for scalar correlated subqueries. +* Remove table scan original constraint information from ``EXPLAIN`` output. +* Introduce distinct error codes for global and per-node memory limit errors. +* Include statistics and cost estimates for ``EXPLAIN (TYPE DISTRIBUTED)`` and ``EXPLAIN ANALYZE``. +* Support equality checks for ``ARRAY``, ``MAP``, and ``ROW`` values containing nulls. +* Improve statistics estimation and fix potential negative nulls fraction + estimates for expressions that include ``NOT`` or ``OR``. +* Completely remove the ``SHOW PARTITIONS`` statement. +* Add :func:`bing_tiles_around` variant that takes a radius. +* Add the :func:`convex_hull_agg` and :func:`geometry_union_agg` geospatial aggregation functions. +* Add ``(TYPE IO, FORMAT JSON)`` option for :doc:`/sql/explain` that shows + input tables with constraints and the output table in JSON format. +* Add :doc:`/connector/kudu`. +* Raise required Java version to 8u151. This avoids correctness issues for + map to map cast when running under some earlier JVM versions, including 8u92. + +Web UI Changes +-------------- + +* Fix the kill query button on the live plan and stage performance pages. + +CLI Changes +----------- + +* Prevent spurious *"No route to host"* errors on macOS when using IPv6. + +JDBC Driver Changes +------------------- + +* Prevent spurious *"No route to host"* errors on macOS when using IPv6. + +Hive Connector Changes +---------------------- + +* Fix data loss when writing bucketed sorted tables. Partitions would + be missing arbitrary rows if any of the temporary files for a bucket + had the same size. The ``numRows`` partition property contained the + correct number of rows and can be used to detect if this occurred. +* Fix cleanup of temporary files when writing bucketed sorted tables. +* Allow creating schemas when using ``file`` based security. +* Reduce the number of cases where tiny ORC stripes will be written when + some columns are highly dictionary compressed. +* Improve memory accounting when reading ORC files. Previously, buffer + memory and object overhead was not tracked for stream readers. +* ORC struct columns are now mapped by name rather than ordinal. + This correctly handles missing or extra struct fields in the ORC file. +* Add procedure ``system.create_empty_partition()`` for creating empty partitions. + +Kafka Connector Changes +----------------------- + +* Support Avro formatted Kafka messages. +* Support backward compatible Avro schema evolution. + +SPI Changes +----------- + +* Allow using ``Object`` as a parameter type or return type for SQL + functions when the correponding SQL type is an unbounded generic. diff --git a/presto-docs/src/main/sphinx/release/release-0.210.rst b/presto-docs/src/main/sphinx/release/release-0.210.rst new file mode 100644 index 0000000000000..863d29dc22a5f --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.210.rst @@ -0,0 +1,49 @@ +============= +Release 0.210 +============= + +General Changes +--------------- + +* Fix planning failure when aliasing columns of tables containing hidden + columns (:issue:`11385`). +* Fix correctness issue when ``GROUP BY DISTINCT`` terms contain references to + the same column using different syntactic forms (:issue:`11120`). +* Fix failures when querying ``information_schema`` tables using capitalized names. +* Improve performance when converting between ``ROW`` types. +* Remove user CPU time tracking as introduces non-trivial overhead. +* Select join distribution type automatically for queries involving outer joins. + +Hive Connector Changes +---------------------- + +* Fix a security bug introduced in 0.209 when using ``hive.security=file``, + which would allow any user to create, drop, or rename schemas. +* Prevent ORC writer from writing stripes larger than the max configured size + when converting a highly dictionary compressed column to direct encoding. +* Support creating Avro tables with a custom schema using the ``avro_schema_url`` + table property. +* Support backward compatible Avro schema evolution. +* Support cross-realm Kerberos authentication for HDFS and Hive Metastore. + +JDBC Driver Changes +------------------- + +* Deallocate prepared statement when ``PreparedStatement`` is closed. Previously, + ``Connection`` became unusable after many prepared statements were created. +* Remove ``getUserTimeMillis()`` from ``QueryStats`` and ``StageStats``. + +SPI Changes +----------- + +* ``SystemAccessControl.checkCanSetUser()`` now takes an ``Optional`` + rather than a nullable ``Principal``. +* Rename ``connectorId`` to ``catalogName`` in ``ConnectorFactory``, + ``QueryInputMetadata``, and ``QueryOutputMetadata``. +* Pass ``ConnectorTransactionHandle`` to ``ConnectorAccessControl.checkCanSetCatalogSessionProperty()``. +* Remove ``getUserTime()`` from ``SplitStatistics`` (referenced in ``SplitCompletedEvent``). + +.. note:: + These are backwards incompatible changes with the previous SPI. + If you have written a plugin, you will need to update your code + before deploying this release. diff --git a/presto-docs/src/main/sphinx/release/release-0.211.rst b/presto-docs/src/main/sphinx/release/release-0.211.rst new file mode 100644 index 0000000000000..bcd4e7b4f9773 --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.211.rst @@ -0,0 +1,57 @@ +============= +Release 0.211 +============= + +General Changes +--------------- + +* Fix missing final query plan in ``QueryCompletedEvent``. Statistics and cost estimates + are removed from the plan text because they may not be available during event generation. +* Update the default value of the ``http-server.https.excluded-cipher`` config + property to exclude cipher suites with a weak hash algorithm or without forward secrecy. + Specifically, this means all ciphers that use the RSA key exchange are excluded by default. + Consequently, TLS 1.0 or TLS 1.1 are no longer supported with the default configuration. + The ``http-server.https.excluded-cipher`` config property can be set to empty string + to restore the old behavior. +* Add :func:`ST_GeomFromBinary` and :func:`ST_AsBinary` functions that convert + geometries to and from Well-Known Binary format. +* Remove the ``verbose_stats`` session property, and rename the ``task.verbose-stats`` + configuration property to ``task.per-operator-cpu-timer-enabled``. +* Improve query planning performance for queries containing multiple joins + and a large number of columns (:issue:`11196`). +* Add built-in :doc:`file based property manager ` + to automate the setting of session properties based on query characteristics. +* Allow running on a JVM from any vendor that meets the functional requirements. + +Hive Connector Changes +---------------------- + +* Fix regression in 0.210 that causes query failure when writing ORC or DWRF files + that occurs for specific patterns of input data. When the writer attempts to give up + using dictionary encoding for a column that is highly compressed, the process of + transitioning to use direct encoding instead can fail. +* Fix coordinator OOM when a query scans many partitions of a Hive table (:issue:`11322`). +* Improve readability of columns, partitioning, and transactions in explain plains. + +Thrift Connector Changes +------------------------ + +* Fix lack of retry for network errors while sending requests. + +Resource Group Changes +---------------------- + +* Add documentation for new resource group scheduling policies. +* Remove running and queue time limits from resource group configuration. + Legacy behavior can be replicated by using the + :doc:`file based property manager ` + to set session properties. + +SPI Changes +----------- + +* Clarify semantics of ``predicate`` in ``ConnectorTableLayout``. +* Reduce flexibility of ``unenforcedConstraint`` that a connector can return in ``getTableLayouts``. + For each column in the predicate, the connector must enforce the entire domain or none. +* Make the null vector in ``ArrayBlock``, ``MapBlock``, and ``RowBlock`` optional. + When it is not present, all entries in the ``Block`` are non-null. diff --git a/presto-docs/src/main/sphinx/release/release-0.212.rst b/presto-docs/src/main/sphinx/release/release-0.212.rst new file mode 100644 index 0000000000000..6112a0f2fdd8d --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.212.rst @@ -0,0 +1,37 @@ +============= +Release 0.212 +============= + +General Changes +--------------- + +* Fix query failures when the :func:`ST_GeomFromBinary` function is run on multiple rows. +* Fix memory accounting for the build side of broadcast joins. +* Fix occasional query failures when running ``EXPLAIN ANALYZE``. +* Enhance :func:`ST_ConvexHull` and :func:`convex_hull_agg` functions to support geometry collections. +* Improve performance for some queries using ``DISTINCT``. +* Improve performance for some queries that perform filtered global aggregations. +* Remove ``round(x, d)`` and ``truncate(x, d)`` functions where ``d`` is a ``BIGINT`` (:issue:`11462`). +* Add :func:`ST_LineString` function to form a ``LineString`` from an array of points. + +Hive Connector Changes +---------------------- + +* Prevent ORC writer from writing stripes larger than the max configured size for some rare data + patterns (:issue:`11526`). +* Restrict the maximum line length for text files. The default limit of 100MB can be changed + using the ``hive.text.max-line-length`` configuration property. +* Add sanity checks that fail queries if statistics read from the metastore are corrupt. Corrupt + statistics can be ignored by setting the ``hive.ignore-corrupted-statistics`` + configuration property or the ``ignore_corrupted_statistics`` session property. + +Thrift Connector Changes +------------------------ + +* Fix retry for network errors that occur while sending a Thrift request. +* Remove failed connections from connection pool. + +Verifier Changes +---------------- + +* Record the query ID of the test query regardless of query outcome. diff --git a/presto-docs/src/main/sphinx/release/release-0.213.rst b/presto-docs/src/main/sphinx/release/release-0.213.rst new file mode 100644 index 0000000000000..e1d10f86f196d --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.213.rst @@ -0,0 +1,105 @@ +============= +Release 0.213 +============= + +General Changes +--------------- + +* Fix split scheduling backpressure when plan contains colocated join. Previously, splits + for the second and subsequent scan nodes (in scheduling order) were scheduled continuously + until completion, rather than pausing due to sufficient pending splits. +* Fix query execution failure or indefinite hang during grouped execution when all splits + for any lifespan are completely filtered out. +* Fix grouped execution to respect the configured concurrent lifespans per task. + Previously, it always used a single lifespan per task. +* Fix execution failure when using grouped execution with right or full outer joins + where the right side is not partitioned on the join key. +* Fix a scenario where too many rows are returned to clients in a single response. +* Do not allow setting invalid property values with :doc:`/sql/set-session`. +* Disable stats calculator by default as it can cause a planning failure for + certain complex queries. It can be enabled with the ``experimental.enable-stats-calculator`` + configuration property or the ``enable_stats_calculator`` session property. +* Avoid making guesses when estimating filters for joins. Previously, if nothing + was known about the filter, a ``0.9`` coefficient was applied as a filter factor. + Now, if nothing is known about a filter, the estimate will be unknown. A ``0.9`` + coefficient will be applied for all additional conjuncts if at least a single + conjunct can be reasonably estimated. +* Improve inference of predicates for inner joins. +* Improve ``EXPLAIN ANALYZE`` output by adding CPU time and enhancing accuracy of CPU fraction. +* Include stats and cost estimates in textual plans created on query completion. +* Enhance ``SHOW STATS`` to support ``IN`` and ``BETWEEN`` predicates in the + ``WHERE`` condition of the ``SELECT`` clause. +* Remove transaction from explain plan for indexes joins. +* Add ``max_drivers_per_task`` session property, allowing users to limit concurrency by + specifying a number lower than the system configured maximum. This can cause the + query to run slower and consume less resources. +* Add ``join-max-broadcast-table-size`` configuration property and + ``join_max_broadcast_table_size`` session property to control the maximum estimated size + of a table that can be broadcast when using ``AUTOMATIC`` join distribution type (:issue:`11667`). +* Add experimental config option ``experimental.reserved-pool-enabled`` to disable the reserved memory pool. +* Add ``targetResultSize`` query parameter to ``/v1/statement`` endpoint to control response data size. + +Geospatial Changes +------------------ + +* Fix :func:`ST_Distance` function to return ``NULL`` if any of the inputs is an + empty geometry as required by the SQL/MM specification. +* Add :func:`ST_MultiPoint` function to construct multi-point geometry from an array of points. +* Add :func:`geometry_union` function to efficiently union arrays of geometries. +* Add support for distributed spatial joins (:issue:`11072`). + +Server RPM Changes +------------------ + +* Allow running on a JVM from any vendor. + +Web UI Changes +-------------- + +* Remove legacy plan UI. +* Add support for filtering queries by all error categories. +* Add dialog to show errors refreshing data from coordinator. +* Change worker thread list to not show thread stacks by default to improve page peformance. + +Hive Connector Changes +---------------------- + +* Fix LZO and LZOP decompression to work with certain data compressed by Hadoop. +* Fix ORC writer validation percentage so that zero does not result in 100% validation. +* Fix potential out-of-bounds read for ZSTD on corrupted input. +* Stop assuming no distinct values when column null fraction statistic is less than ``1.0``. +* Treat ``-1`` as an absent null count for compatibility with statistics written by + `Impala `_. +* Preserve original exception for metastore network errors. +* Preserve exceptions from Avro deserializer +* Categorize text line length exceeded error. +* Remove the old Parquet reader. The ``hive.parquet-optimized-reader.enabled`` + configuration property and ``parquet_optimized_reader_enabled`` session property + no longer exist. +* Remove the ``hive.parquet-predicate-pushdown.enabled`` configuration property + and ``parquet_predicate_pushdown_enabled`` session property. + Pushdown is always enabled now in the Parquet reader. +* Enable optimized ORC writer by default. It can be disabled using the + ``hive.orc.optimized-writer.enabled`` configuration property or the + ``orc_optimized_writer_enabled`` session property. +* Use ORC file format as the default for new tables or partitions. +* Add support for Avro tables where the Avro schema URL is an HDFS location. +* Add ``hive.parquet.writer.block-size`` and ``hive.parquet.writer.page-size`` + configuration properties and ``parquet_writer_block_size`` and + ``parquet_writer_page_size`` session properties for tuning Parquet writer options. + +Memory Connector Changes +------------------------ + +* Improve table data size accounting. + +Thrift Connector Changes +------------------------ + +* Include constraint in explain plan for index joins. +* Improve readability of columns, tables, layouts, and indexes in explain plans. + +Verifier Changes +---------------- + +* Rewrite queries in parallel when shadowing writes. diff --git a/presto-docs/src/main/sphinx/release/release-0.214.rst b/presto-docs/src/main/sphinx/release/release-0.214.rst new file mode 100644 index 0000000000000..fc1c07239fa38 --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.214.rst @@ -0,0 +1,64 @@ +============= +Release 0.214 +============= + +General Changes +--------------- + +* Fix history leak in coordinator for failed or canceled queries. +* Fix memory leak related to query tracking in coordinator that was introduced + in :doc:`/release/release-0.213`. +* Fix planning failures when lambdas are used in join filter expression. +* Fix responses to client for certain types of errors that are encountered + during query creation. +* Improve error message when an invalid comparator is provided to the + :func:`array_sort` function. +* Improve performance of lookup operations on map data types. +* Improve planning and query performance for queries with ``TINYINT``, + ``SMALLINT`` and ``VARBINARY`` literals. +* Fix issue where queries containing distributed ``ORDER BY`` and aggregation + could sometimes fail to make progress when data was spilled. +* Make top N row number optimization work in some cases when columns are pruned. +* Add session property ``optimize-top-n-row-number`` and configuration property + ``optimizer.optimize-top-n-row-number`` to toggle the top N row number + optimization. +* Add :func:`ngrams` function to generate N-grams from an array. +* Add :ref:`qdigest ` type and associated :doc:`/functions/qdigest`. +* Add functionality to delay query execution until a minimum number of workers + nodes are available. The minimum number of workers can be set with the + ``query-manager.required-workers`` configuration property, and the max wait + time with the ``query-manager.required-workers-max-wait`` configuration property. +* Remove experimental pre-allocated memory system, and the related configuration + property ``experimental.preallocate-memory-threshold``. + +Security Changes +---------------- + +* Add functionality to refresh the configuration of file-based access controllers. + The refresh interval can be set using the ``security.refresh-period`` + configuration property. + +JDBC Driver Changes +------------------- + +* Clear update count after calling ``Statement.getMoreResults()``. + +Web UI Changes +-------------- + +* Show query warnings on the query detail page. +* Allow selecting non-default sort orders in query list view. + +Hive Connector Changes +---------------------- + +* Prevent ORC writer from writing stripes larger than the maximum configured size. +* Add ``hive.s3.upload-acl-type`` configuration property to specify the type of + ACL to use while uploading files to S3. +* Add Hive metastore API recording tool for remote debugging purposes. +* Add support for retrying on metastore connection errors. + +Verifier Changes +---------------- + +* Handle SQL execution timeouts while rewriting queries. diff --git a/presto-docs/src/main/sphinx/release/release-0.215.rst b/presto-docs/src/main/sphinx/release/release-0.215.rst new file mode 100644 index 0000000000000..be380951bd622 --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.215.rst @@ -0,0 +1,59 @@ +============= +Release 0.215 +============= + +General Changes +--------------- + +* Fix regression in 0.214 that could cause queries to produce incorrect results for queries + using map types. +* Fix reporting of the processed input data for source stages in ``EXPLAIN ANALYZE``. +* Fail queries that use non-leaf resource groups. Previously, they would remain queued forever. +* Improve CPU usage for specific queries (:issue:`11757`). +* Extend stats and cost model to support :func:`row_number` window function estimates. +* Improve the join type selection and the reordering of join sides for cases where + the join output size cannot be estimated. +* Add dynamic scheduling support to grouped execution. When a stage is executed + with grouped execution and the stage has no remote sources, table partitions can be + scheduled to tasks in a dynamic way, which can help mitigating skew for queries using + grouped execution. This feature can be enabled with the + ``dynamic_schedule_for_grouped_execution`` session property or the + ``dynamic-schedule-for-grouped-execution`` config property. +* Add :func:`beta_cdf` and :func:`inverse_beta_cdf` functions. +* Split the reporting of raw input data and processed input data for source operators. +* Remove collection and reporting of raw input data statistics for the ``Values``, + ``Local Exchange``, and ``Local Merge Sort`` operators. +* Simplify ``EXPLAIN (TYPE IO)`` output when there are too many discrete components. + This avoids large output at the cost of reduced granularity. +* Add :func:`parse_presto_data_size` function. +* Add support for ``UNION ALL`` to optimizer's cost model. +* Add support for estimating the cost of filters by using a default filter factor. + The default value for the filter factor can be configured with the ``default_filter_factor_enabled`` + session property or the ``optimizer.default-filter-factor-enabled``. + +Geospatial Changes +------------------ + +* Add input validation checks to :func:`ST_LineString` to conform with the specification. +* Improve spatial join performance. +* Enable spatial joins for join conditions expressed with the :func:`ST_Within` function. + +Web UI Changes +-------------- + +* Fix *Capture Snapshot* button for showing current thread stacks. +* Fix dropdown for expanding stage skew component on the query details page. +* Improve the performance of the thread snapshot component on the worker status page. +* Make the reporting of *Cumulative Memory* usage consistent on the query list and query details pages. +* Remove legacy thread UI. + +Hive Changes +------------ + +* Add predicate pushdown support for the ``DATE`` type to the Parquet reader. This change also fixes + a bug that may cause queries with predicates on ``DATE`` columns to fail with type mismatch errors. + +Redis Changes +------------- + +* Prevent printing the value of the ``redis.password`` configuration property to log files. diff --git a/presto-docs/src/main/sphinx/release/release-0.216.rst b/presto-docs/src/main/sphinx/release/release-0.216.rst new file mode 100644 index 0000000000000..9c4b32fc29bc5 --- /dev/null +++ b/presto-docs/src/main/sphinx/release/release-0.216.rst @@ -0,0 +1,71 @@ +============= +Release 0.216 +============= + +General Changes +--------------- + +* Fix correctness issue for :func:`array_intersect` and :func:`array_distinct` when input contains + both zeros and nulls. +* Fix ``count(*)`` aggregation on empty relation when ``optimize_mixed_distinct_aggregation`` is enabled. +* Improve table scan performance for structural types. +* Improve performance for :func:`array_intersect`. +* Add :func:`reduce_agg` aggregate function. +* Add :func:`millisecond` function. +* Add an optimizer rule to filter the window partitions before executing the window operators. +* Remove ``ON`` keyword for :doc:`/sql/show-stats`. +* Restrict ``WHERE`` clause in :doc:`/sql/show-stats` to filters that can be pushed down to the connectors. +* Remove ``node_id`` column from ``system.runtime.queries`` table. +* Return final results to clients immediately for failed queries. + +Web UI +------ + +* Fix rendering of live plan view for queries involving index joins. + +Hive Connector Changes +---------------------- + +* Fix accounting of time spent reading Parquet data. +* Fix a corner case where the ORC writer fails with integer overflow when writing + highly compressible data using dictionary encoding (:issue:`11930`). +* Fail queries reading Parquet files if statistics in those Parquet files are + corrupt (e.g., min > max). To disable this behavior, set the configuration + property ``hive.parquet.fail-on-corrupted-statistics`` + or session property ``parquet_fail_with_corrupted_statistics`` to false. +* Add support for :ref:`S3 select pushdown `, which enables pushing down + projections and predicates into S3 for text files. + +Kudu Connector Changes +---------------------- + +* Add ``number_of_replicas`` table property to ``SHOW CREATE TABLE`` output. + +Cassandra Connector Changes +--------------------------- + +* Add ``cassandra.splits-per-node`` and ``cassandra.protocol-version`` configuration properties + to allow connecting to Cassandra servers older than 2.1.5. + +MySQL, PostgreSQL, Redshift, and SQL Server Changes +--------------------------------------------------- + +* Add support for predicate pushdown for columns of ``char(x)`` type. + +Verifier Changes +---------------- + +* Add ``run-teardown-on-result-mismatch`` configuration property to facilitate debugging. + When set to false, temporary tables will not be dropped after checksum failures. + +SPI Changes +----------- + +* Make ``ConnectorBucketNodeMap`` a top level class. +* Use list instead of map for bucket-to-node mapping. + +.. note:: + + This is a backwards incompatible change with the previous connector SPI. + If you have written a connector that uses bucketing, you will need to + update your code before deploying this release. diff --git a/presto-docs/src/main/sphinx/rest/query.rst b/presto-docs/src/main/sphinx/rest/query.rst index 5b39862a61596..79cbb758217f2 100644 --- a/presto-docs/src/main/sphinx/rest/query.rst +++ b/presto-docs/src/main/sphinx/rest/query.rst @@ -61,7 +61,6 @@ on a Presto installation. "totalMemoryReservation" : "0B", "totalScheduledTime" : "5.84ms", "totalCpuTime" : "710.49us", - "totalUserTime" : "0.00ns", "totalBlockedTime" : "27.38ms", "rawInputDataSize" : "27B", "rawInputPositions" : 1, diff --git a/presto-docs/src/main/sphinx/rest/statement.rst b/presto-docs/src/main/sphinx/rest/statement.rst index e7412f3c93908..540523e00a811 100644 --- a/presto-docs/src/main/sphinx/rest/statement.rst +++ b/presto-docs/src/main/sphinx/rest/statement.rst @@ -77,7 +77,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":0, - "userTimeMillis":0, "cpuTimeMillis":0, "wallTimeMillis":0, "processedRows":0, @@ -92,7 +91,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":0, - "userTimeMillis":0, "cpuTimeMillis":0, "wallTimeMillis":0, "processedRows":0, @@ -108,7 +106,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":0, - "userTimeMillis":0, "cpuTimeMillis":0, "wallTimeMillis":0, "processedRows":0, @@ -174,7 +171,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":2, - "userTimeMillis":0, "cpuTimeMillis":1, "wallTimeMillis":4, "processedRows":1, @@ -189,7 +185,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":1, - "userTimeMillis":0, "cpuTimeMillis":0, "wallTimeMillis":0, "processedRows":1, @@ -205,7 +200,6 @@ Statement Resource "queuedSplits":0, "runningSplits":0, "completedSplits":1, - "userTimeMillis":0, "cpuTimeMillis":0, "wallTimeMillis":4, "processedRows":1, diff --git a/presto-docs/src/main/sphinx/rest/task.rst b/presto-docs/src/main/sphinx/rest/task.rst index 801e778c4f090..75b61516ab2c7 100644 --- a/presto-docs/src/main/sphinx/rest/task.rst +++ b/presto-docs/src/main/sphinx/rest/task.rst @@ -50,7 +50,6 @@ execution of queries on a Presto installation. "memoryReservation" : "0B", "totalScheduledTime" : "0.00ns", "totalCpuTime" : "0.00ns", - "totalUserTime" : "0.00ns", "totalBlockedTime" : "0.00ns", "rawInputDataSize" : "0B", "rawInputPositions" : 0, @@ -123,7 +122,6 @@ execution of queries on a Presto installation. "memoryReservation" : "174.76kB", "totalScheduledTime" : "4.19ms", "totalCpuTime" : "4.09ms", - "totalUserTime" : "0.00ns", "totalBlockedTime" : "29.50ms", "rawInputDataSize" : "10.90kB", "rawInputPositions" : 154, @@ -173,7 +171,6 @@ execution of queries on a Presto installation. }, "totalScheduledTime" : "4.19ms", "totalCpuTime" : "4.09ms", - "totalUserTime" : "0.00ns", "totalBlockedTime" : "29.50ms", "rawInputDataSize" : "10.90kB", "rawInputPositions" : 154, diff --git a/presto-docs/src/main/sphinx/security/built-in-system-access-control.rst b/presto-docs/src/main/sphinx/security/built-in-system-access-control.rst index 338935bbdfdb5..a85e91f48551b 100644 --- a/presto-docs/src/main/sphinx/security/built-in-system-access-control.rst +++ b/presto-docs/src/main/sphinx/security/built-in-system-access-control.rst @@ -71,6 +71,18 @@ rules. If you want to limit access on a system level in any other way, you must implement a custom SystemAccessControl plugin (see :doc:`/develop/system-access-control`). +Refresh +-------- + +By default, when a change is made to the ``security.config-file``, Presto must be restarted +to load the changes. There is an optional property to refresh the properties without requiring a +Presto restart. The refresh period is specified in the ``etc/access-control.properties``: + +.. code-block:: none + + security.refresh-period=1s + + Catalog Rules ------------- diff --git a/presto-docs/src/main/sphinx/security/server.rst b/presto-docs/src/main/sphinx/security/server.rst index 91020e7b04774..fb50d6248b8f5 100644 --- a/presto-docs/src/main/sphinx/security/server.rst +++ b/presto-docs/src/main/sphinx/security/server.rst @@ -118,15 +118,20 @@ Property Description .. note:: - Monitor CPU usage on the Presto coordinator after enabling HTTPS. Java will - choose CPU-intensive cipher suites by default. If the CPU usage is - unacceptably high after enabling HTTPS, you can configure Java to use - specific cipher suites by setting the ``http-server.https.included-cipher`` - property: + Monitor CPU usage on the Presto coordinator after enabling HTTPS. Java + prefers the more CPU-intensive cipher suites if you allow it to choose from + a big list. If the CPU usage is unacceptably high after enabling HTTPS, + you can configure Java to use specific cipher suites by setting + the ``http-server.https.included-cipher`` property to only allow + cheap ciphers. Non forward secrecy (FS) ciphers are disabled by default. + As a result, if you want to choose non FS ciphers, you need to set the + ``http-server.https.excluded-cipher`` property to an empty list in order to + override the default exclusions. .. code-block:: none http-server.https.included-cipher=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA256 + http-server.https.excluded-cipher= The Java documentation lists the `supported cipher suites `_. diff --git a/presto-docs/src/main/sphinx/sql.rst b/presto-docs/src/main/sphinx/sql.rst index 929bb26a32f7b..1b98c56317df9 100644 --- a/presto-docs/src/main/sphinx/sql.rst +++ b/presto-docs/src/main/sphinx/sql.rst @@ -42,6 +42,7 @@ This chapter describes the SQL syntax used in Presto. sql/show-grants sql/show-schemas sql/show-session + sql/show-stats sql/show-tables sql/start-transaction sql/use diff --git a/presto-docs/src/main/sphinx/sql/explain.rst b/presto-docs/src/main/sphinx/sql/explain.rst index d618b81b1b939..737adb369c318 100644 --- a/presto-docs/src/main/sphinx/sql/explain.rst +++ b/presto-docs/src/main/sphinx/sql/explain.rst @@ -11,8 +11,8 @@ Synopsis where option can be one of: - FORMAT { TEXT | GRAPHVIZ | IO } - TYPE { LOGICAL | DISTRIBUTED | VALIDATE | JSON } + FORMAT { TEXT | GRAPHVIZ | JSON } + TYPE { LOGICAL | DISTRIBUTED | VALIDATE | IO } Description ----------- diff --git a/presto-docs/src/main/sphinx/sql/show-grants.rst b/presto-docs/src/main/sphinx/sql/show-grants.rst index 16058fffcba07..b1656a8ca8b4c 100644 --- a/presto-docs/src/main/sphinx/sql/show-grants.rst +++ b/presto-docs/src/main/sphinx/sql/show-grants.rst @@ -27,7 +27,7 @@ Examples List the grants for the current user on table ``orders``:: - SHOW GRANTS ON TABLE ``orders``; + SHOW GRANTS ON TABLE orders; List the grants for the current user on all the tables in all schemas of the current catalog:: diff --git a/presto-docs/src/main/sphinx/sql/show-stats.rst b/presto-docs/src/main/sphinx/sql/show-stats.rst new file mode 100644 index 0000000000000..4234a306f6320 --- /dev/null +++ b/presto-docs/src/main/sphinx/sql/show-stats.rst @@ -0,0 +1,31 @@ +========== +SHOW STATS +========== + +Synopsis +-------- + +.. code-block:: none + + SHOW STATS FOR table + SHOW STATS FOR ( SELECT * FROM table [ WHERE condition ] ) + +Description +----------- + +Returns approximated statistics for the named table or for the results of a (limited) query. + +Statistics are returned for each column, along with a summary row. +(column_name will be ``NULL`` for the summary row). + +===================== ============ +Column Description +===================== ============ +column_name The name of the column +data_size The total size in bytes of all of the values in the column +distinct_values_count The number of distinct values in the column +nulls_fractions The portion of the values in the column that are ``NULL`` +row_count The number of rows (only returned for the summary row) +low_value The lowest value found in this column (only for some types) +high_value The highest value found in this column (only for some types) +===================== ============ diff --git a/presto-example-http/pom.xml b/presto-example-http/pom.xml index debccb3cda1cf..2ec01bff9f97f 100644 --- a/presto-example-http/pom.xml +++ b/presto-example-http/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-example-http diff --git a/presto-example-http/src/main/java/com/facebook/presto/example/ExampleConnectorFactory.java b/presto-example-http/src/main/java/com/facebook/presto/example/ExampleConnectorFactory.java index 354433bd01468..4b185ee70dbef 100644 --- a/presto-example-http/src/main/java/com/facebook/presto/example/ExampleConnectorFactory.java +++ b/presto-example-http/src/main/java/com/facebook/presto/example/ExampleConnectorFactory.java @@ -42,14 +42,14 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(final String connectorId, Map requiredConfig, ConnectorContext context) + public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) { requireNonNull(requiredConfig, "requiredConfig is null"); try { // A plugin is not required to use Guice; it is just very convenient Bootstrap app = new Bootstrap( new JsonModule(), - new ExampleModule(connectorId, context.getTypeManager())); + new ExampleModule(catalogName, context.getTypeManager())); Injector injector = app .strictConfig() diff --git a/presto-example-http/src/test/java/com/facebook/presto/example/TestExampleMetadata.java b/presto-example-http/src/test/java/com/facebook/presto/example/TestExampleMetadata.java index 09bb1ffeeaf80..4ca7e73431162 100644 --- a/presto-example-http/src/test/java/com/facebook/presto/example/TestExampleMetadata.java +++ b/presto-example-http/src/test/java/com/facebook/presto/example/TestExampleMetadata.java @@ -144,9 +144,11 @@ public void getColumnMetadata() @Test(expectedExceptions = PrestoException.class) public void testCreateTable() { - metadata.createTable(SESSION, new ConnectorTableMetadata( - new SchemaTableName("example", "foo"), - ImmutableList.of(new ColumnMetadata("text", createUnboundedVarcharType()))), + metadata.createTable( + SESSION, + new ConnectorTableMetadata( + new SchemaTableName("example", "foo"), + ImmutableList.of(new ColumnMetadata("text", createUnboundedVarcharType()))), false); } diff --git a/presto-geospatial-toolkit/pom.xml b/presto-geospatial-toolkit/pom.xml index 62cd09e613239..8345fc1a5c7a9 100644 --- a/presto-geospatial-toolkit/pom.xml +++ b/presto-geospatial-toolkit/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-geospatial-toolkit @@ -15,6 +15,11 @@ + + org.openjdk.jol + jol-core + + com.esri.geometry esri-geometry-api @@ -25,6 +30,11 @@ jts-core + + com.fasterxml.jackson.core + jackson-annotations + + com.google.guava guava @@ -35,6 +45,11 @@ slice + + io.airlift + json + + com.google.code.findbugs jsr305 @@ -58,5 +73,6 @@ testng test + diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/GeometryUtils.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/GeometryUtils.java index a48f06c0ce7ea..066e1dbbcef7b 100644 --- a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/GeometryUtils.java +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/GeometryUtils.java @@ -33,7 +33,7 @@ private GeometryUtils() {} /** * Copy of com.esri.core.geometry.Interop.translateFromAVNaN - * + *

* deserializeEnvelope needs to recognize custom NAN values generated by * ESRI's serialization of empty geometries. */ @@ -44,7 +44,7 @@ private static double translateFromAVNaN(double n) /** * Copy of com.esri.core.geometry.Interop.translateToAVNaN - * + *

* JtsGeometrySerde#serialize must serialize NaN's the same way ESRI library does to achieve binary compatibility */ public static double translateToAVNaN(double n) diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java new file mode 100644 index 0000000000000..6b15cae5196f9 --- /dev/null +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTree.java @@ -0,0 +1,363 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.geospatial; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Predicate; +import com.google.common.collect.ComparisonChain; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; + +import static com.facebook.presto.geospatial.KdbTree.Node.newInternal; +import static com.facebook.presto.geospatial.KdbTree.Node.newLeaf; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +/** + * 2-dimensional K-D-B Tree + * see https://en.wikipedia.org/wiki/K-D-B-tree + */ +public class KdbTree +{ + private static final int MAX_LEVELS = 10_000; + + private final Node root; + + public static final class Node + { + private final Rectangle extent; + private final OptionalInt leafId; + private final Optional left; + private final Optional right; + + public static Node newLeaf(Rectangle extent, int leafId) + { + return new Node(extent, OptionalInt.of(leafId), Optional.empty(), Optional.empty()); + } + + public static Node newInternal(Rectangle extent, Node left, Node right) + { + return new Node(extent, OptionalInt.empty(), Optional.of(left), Optional.of(right)); + } + + @JsonCreator + public Node( + @JsonProperty("extent") Rectangle extent, + @JsonProperty("leafId") OptionalInt leafId, + @JsonProperty("left") Optional left, + @JsonProperty("right") Optional right) + { + this.extent = requireNonNull(extent, "extent is null"); + this.leafId = requireNonNull(leafId, "leafId is null"); + this.left = requireNonNull(left, "left is null"); + this.right = requireNonNull(right, "right is null"); + if (leafId.isPresent()) { + checkArgument(leafId.getAsInt() >= 0, "leafId must be >= 0"); + checkArgument(!left.isPresent(), "Leaf node cannot have left child"); + checkArgument(!right.isPresent(), "Leaf node cannot have right child"); + } + else { + checkArgument(left.isPresent(), "Intermediate node must have left child"); + checkArgument(right.isPresent(), "Intermediate node must have right child"); + } + } + + @JsonProperty + public Rectangle getExtent() + { + return extent; + } + + @JsonProperty + public OptionalInt getLeafId() + { + return leafId; + } + + @JsonProperty + public Optional getLeft() + { + return left; + } + + @JsonProperty + public Optional getRight() + { + return right; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) { + return false; + } + + if (!(obj instanceof Node)) { + return false; + } + + Node other = (Node) obj; + return this.extent.equals(other.extent) + && Objects.equals(this.leafId, other.leafId) + && Objects.equals(this.left, other.left) + && Objects.equals(this.right, other.right); + } + + @Override + public int hashCode() + { + return Objects.hash(extent, leafId, left, right); + } + } + + @JsonCreator + public KdbTree(@JsonProperty("root") Node root) + { + this.root = requireNonNull(root, "root is null"); + } + + @JsonProperty + public Node getRoot() + { + return root; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) { + return false; + } + + if (!(obj instanceof KdbTree)) { + return false; + } + + KdbTree other = (KdbTree) obj; + return this.root.equals(other.root); + } + + @Override + public int hashCode() + { + return Objects.hash(root); + } + + public Map getLeaves() + { + ImmutableMap.Builder leaves = ImmutableMap.builder(); + addLeaves(root, leaves, node -> true); + return leaves.build(); + } + + public Map findIntersectingLeaves(Rectangle envelope) + { + ImmutableMap.Builder leaves = ImmutableMap.builder(); + addLeaves(root, leaves, node -> node.extent.intersects(envelope)); + return leaves.build(); + } + + private static void addLeaves(Node node, ImmutableMap.Builder leaves, Predicate predicate) + { + if (!predicate.apply(node)) { + return; + } + + if (node.leafId.isPresent()) { + leaves.put(node.leafId.getAsInt(), node.extent); + } + else { + addLeaves(node.left.get(), leaves, predicate); + addLeaves(node.right.get(), leaves, predicate); + } + } + + private interface SplitDimension + { + Comparator getComparator(); + + double getValue(Rectangle rectangle); + + SplitResult split(Rectangle rectangle, double value); + } + + private static final SplitDimension BY_X = new SplitDimension() { + private final Comparator comparator = (first, second) -> ComparisonChain.start() + .compare(first.getXMin(), second.getXMin()) + .compare(first.getYMin(), second.getYMin()) + .result(); + + @Override + public Comparator getComparator() + { + return comparator; + } + + @Override + public double getValue(Rectangle rectangle) + { + return rectangle.getXMin(); + } + + @Override + public SplitResult split(Rectangle rectangle, double x) + { + checkArgument(rectangle.getXMin() < x && x < rectangle.getXMax()); + return new SplitResult<>( + new Rectangle(rectangle.getXMin(), rectangle.getYMin(), x, rectangle.getYMax()), + new Rectangle(x, rectangle.getYMin(), rectangle.getXMax(), rectangle.getYMax())); + } + }; + + private static final SplitDimension BY_Y = new SplitDimension() { + private final Comparator comparator = (first, second) -> ComparisonChain.start() + .compare(first.getYMin(), second.getYMin()) + .compare(first.getXMin(), second.getXMin()) + .result(); + + @Override + public Comparator getComparator() + { + return comparator; + } + + @Override + public double getValue(Rectangle rectangle) + { + return rectangle.getYMin(); + } + + @Override + public SplitResult split(Rectangle rectangle, double y) + { + checkArgument(rectangle.getYMin() < y && y < rectangle.getYMax()); + return new SplitResult<>( + new Rectangle(rectangle.getXMin(), rectangle.getYMin(), rectangle.getXMax(), y), + new Rectangle(rectangle.getXMin(), y, rectangle.getXMax(), rectangle.getYMax())); + } + }; + + private static final class LeafIdAllocator + { + private int nextId; + + public int next() + { + return nextId++; + } + } + + public static KdbTree buildKdbTree(int maxItemsPerNode, Rectangle extent, List items) + { + checkArgument(maxItemsPerNode > 0, "maxItemsPerNode must be > 0"); + requireNonNull(extent, "extent is null"); + requireNonNull(items, "items is null"); + return new KdbTree(buildKdbTreeNode(maxItemsPerNode, 0, extent, items, new LeafIdAllocator())); + } + + private static Node buildKdbTreeNode(int maxItemsPerNode, int level, Rectangle extent, List items, LeafIdAllocator leafIdAllocator) + { + checkArgument(maxItemsPerNode > 0, "maxItemsPerNode must be > 0"); + checkArgument(level >= 0, "level must be >= 0"); + checkArgument(level <= MAX_LEVELS, "level must be <= 10,000"); + requireNonNull(extent, "extent is null"); + requireNonNull(items, "items is null"); + + if (items.size() <= maxItemsPerNode || level == MAX_LEVELS) { + return newLeaf(extent, leafIdAllocator.next()); + } + + // Split over longer side + boolean splitVertically = extent.getWidth() >= extent.getHeight(); + Optional> splitResult = trySplit(splitVertically ? BY_X : BY_Y, maxItemsPerNode, level, extent, items, leafIdAllocator); + if (!splitResult.isPresent()) { + // Try spitting by the other side + splitResult = trySplit(splitVertically ? BY_Y : BY_X, maxItemsPerNode, level, extent, items, leafIdAllocator); + } + + if (!splitResult.isPresent()) { + return newLeaf(extent, leafIdAllocator.next()); + } + + return newInternal(extent, splitResult.get().getLeft(), splitResult.get().getRight()); + } + + private static final class SplitResult + { + private final T left; + private final T right; + + private SplitResult(T left, T right) + { + this.left = requireNonNull(left, "left is null"); + this.right = requireNonNull(right, "right is null"); + } + + public T getLeft() + { + return left; + } + + public T getRight() + { + return right; + } + } + + private static Optional> trySplit(SplitDimension splitDimension, int maxItemsPerNode, int level, Rectangle extent, List items, LeafIdAllocator leafIdAllocator) + { + checkArgument(items.size() > 1, "Number of items to split must be > 1"); + + // Sort envelopes by xMin or yMin + List sortedItems = ImmutableList.sortedCopyOf(splitDimension.getComparator(), items); + + // Find a mid-point + int middleIndex = (sortedItems.size() - 1) / 2; + Rectangle middleEnvelope = sortedItems.get(middleIndex); + double splitValue = splitDimension.getValue(middleEnvelope); + int splitIndex = middleIndex; + + // skip over duplicate values + while (splitIndex < sortedItems.size() && splitDimension.getValue(sortedItems.get(splitIndex)) == splitValue) { + splitIndex++; + } + + // all values between left-of-middle and the end are the same, so can't split + if (splitIndex == sortedItems.size()) { + return Optional.empty(); + } + + // about half of the objects are <= splitValue, the rest are >= next value + // assuming the input set of objects is a sample from a much larger set, + // let's split in the middle; this way objects from the larger set with values + // between splitValue and next value will get split somewhat evenly into left + // and right partitions + splitValue = (splitValue + splitDimension.getValue(sortedItems.get(splitIndex))) / 2; + + SplitResult childExtents = splitDimension.split(extent, splitValue); + + return Optional.of(new SplitResult( + buildKdbTreeNode(maxItemsPerNode, level + 1, childExtents.getLeft(), sortedItems.subList(0, splitIndex), leafIdAllocator), + buildKdbTreeNode(maxItemsPerNode, level + 1, childExtents.getRight(), sortedItems.subList(splitIndex, sortedItems.size()), leafIdAllocator))); + } +} diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTreeUtils.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTreeUtils.java new file mode 100644 index 0000000000000..a2899bf4571eb --- /dev/null +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/KdbTreeUtils.java @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.geospatial; + +import io.airlift.json.JsonCodec; +import io.airlift.json.JsonCodecFactory; + +import static java.util.Objects.requireNonNull; + +public class KdbTreeUtils +{ + private static final JsonCodec KDB_TREE_CODEC = new JsonCodecFactory().jsonCodec(KdbTree.class); + + private KdbTreeUtils() {} + + public static KdbTree fromJson(String json) + { + requireNonNull(json, "json is null"); + return KDB_TREE_CODEC.fromJson(json); + } + + public static String toJson(KdbTree kdbTree) + { + requireNonNull(kdbTree, "kdbTree is null"); + return KDB_TREE_CODEC.toJson(kdbTree); + } +} diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java new file mode 100644 index 0000000000000..8f7794f2c53cd --- /dev/null +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/Rectangle.java @@ -0,0 +1,142 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.geospatial; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.openjdk.jol.info.ClassLayout; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.util.Objects.requireNonNull; + +public final class Rectangle +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(Rectangle.class).instanceSize(); + + private final double xMin; + private final double yMin; + private final double xMax; + private final double yMax; + + @JsonCreator + public Rectangle( + @JsonProperty("xmin") double xMin, + @JsonProperty("ymin") double yMin, + @JsonProperty("xmax") double xMax, + @JsonProperty("ymax") double yMax) + { + checkArgument(xMin <= xMax, "xMin is greater than xMax"); + checkArgument(yMin <= yMax, "yMin is greater than yMax"); + this.xMin = xMin; + this.yMin = yMin; + this.xMax = xMax; + this.yMax = yMax; + } + + @JsonProperty + public double getXMin() + { + return xMin; + } + + @JsonProperty + public double getYMin() + { + return yMin; + } + + @JsonProperty + public double getXMax() + { + return xMax; + } + + @JsonProperty + public double getYMax() + { + return yMax; + } + + public double getWidth() + { + return xMax - xMin; + } + public double getHeight() + { + return yMax - yMin; + } + + public boolean intersects(Rectangle other) + { + requireNonNull(other, "other is null"); + return this.xMin <= other.xMax && this.xMax >= other.xMin && this.yMin <= other.yMax && this.yMax >= other.yMin; + } + + public Rectangle intersection(Rectangle other) + { + requireNonNull(other, "other is null"); + if (!intersects(other)) { + return null; + } + + return new Rectangle(max(this.xMin, other.xMin), max(this.yMin, other.yMin), min(this.xMax, other.xMax), min(this.yMax, other.yMax)); + } + + public Rectangle merge(Rectangle other) + { + return new Rectangle(min(this.xMin, other.xMin), min(this.yMin, other.yMin), max(this.xMax, other.xMax), max(this.yMax, other.yMax)); + } + + public int estimateMemorySize() + { + return INSTANCE_SIZE; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) { + return false; + } + + if (!(obj instanceof Rectangle)) { + return false; + } + + Rectangle other = (Rectangle) obj; + return other.xMin == this.xMin && other.yMin == this.yMin && other.xMax == this.xMax && other.yMax == this.yMax; + } + + @Override + public int hashCode() + { + return Objects.hash(xMin, yMin, xMax, yMax); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("xMin", xMin) + .add("yMin", yMin) + .add("xMax", xMax) + .add("yMax", yMax) + .toString(); + } +} diff --git a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/serde/GeometrySerde.java b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/serde/GeometrySerde.java index 04fe91805e53a..1f51a909acbd2 100644 --- a/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/serde/GeometrySerde.java +++ b/presto-geospatial-toolkit/src/main/java/com/facebook/presto/geospatial/serde/GeometrySerde.java @@ -278,10 +278,7 @@ public static Envelope deserializeEnvelope(Slice shape) { requireNonNull(shape, "shape is null"); BasicSliceInput input = shape.getInput(); - - if (input.available() == 0) { - return null; - } + verify(input.available() > 0); int length = input.available() - 1; GeometrySerializationType type = GeometrySerializationType.getForCode(input.readByte()); diff --git a/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java b/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java new file mode 100644 index 0000000000000..16404fd1bb5ee --- /dev/null +++ b/presto-geospatial-toolkit/src/test/java/com/facebook/presto/geospatial/TestKdbTree.java @@ -0,0 +1,283 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.geospatial; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import org.testng.annotations.Test; + +import java.util.Map; +import java.util.Set; + +import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; +import static org.testng.Assert.assertEquals; + +public class TestKdbTree +{ + @Test + public void testSerde() + { + Rectangle extent = new Rectangle(0, 0, 9, 4); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (double x = 0; x < 10; x += 1) { + for (double y = 0; y < 5; y += 1) { + rectangles.add(new Rectangle(x, y, x + 0.1, y + 0.2)); + } + } + + testSerializationRoundtrip(buildKdbTree(100, extent, rectangles.build())); + testSerializationRoundtrip(buildKdbTree(20, extent, rectangles.build())); + testSerializationRoundtrip(buildKdbTree(10, extent, rectangles.build())); + } + + private void testSerializationRoundtrip(KdbTree tree) + { + KdbTree treeCopy = KdbTreeUtils.fromJson(KdbTreeUtils.toJson(tree)); + assertEquals(treeCopy, tree); + } + + @Test + public void testSinglePartition() + { + testSinglePartition(0, 0); + testSinglePartition(1, 2); + } + + private void testSinglePartition(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9, 4); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (double x = 0; x < 10; x += 1) { + for (double y = 0; y < 5; y += 1) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + KdbTree tree = buildKdbTree(100, extent, rectangles.build()); + + assertEquals(tree.getLeaves().size(), 1); + + Map.Entry entry = Iterables.getOnlyElement(tree.getLeaves().entrySet()); + assertEquals(entry.getKey().intValue(), 0); + assertEquals(entry.getValue(), extent); + } + + @Test + public void testSplitVertically() + { + testSplitVertically(0, 0); + testSplitVertically(1, 2); + } + + private void testSplitVertically(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9, 4); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 5; y++) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + KdbTree treeCopy = buildKdbTree(25, extent, rectangles.build()); + + Map leafNodes = treeCopy.getLeaves(); + assertEquals(leafNodes.size(), 2); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 4)); + assertEquals(leafNodes.get(1), new Rectangle(4.5, 0, 9, 4)); + + assertPartitions(treeCopy, new Rectangle(1, 1, 2, 2), ImmutableSet.of(0)); + assertPartitions(treeCopy, new Rectangle(1, 1, 5, 2), ImmutableSet.of(0, 1)); + } + + @Test + public void testSplitHorizontally() + { + testSplitHorizontally(0, 0); + testSplitHorizontally(1, 2); + } + + private void testSplitHorizontally(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 4, 9); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int x = 0; x < 5; x++) { + for (int y = 0; y < 10; y++) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + KdbTree tree = buildKdbTree(25, extent, rectangles.build()); + + Map leafNodes = tree.getLeaves(); + assertEquals(leafNodes.size(), 2); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4, 4.5)); + assertEquals(leafNodes.get(1), new Rectangle(0, 4.5, 4, 9)); + + // points inside and outside partitions + assertPartitions(tree, new Rectangle(1, 1, 1, 1), ImmutableSet.of(0)); + assertPartitions(tree, new Rectangle(1, 6, 1, 6), ImmutableSet.of(1)); + assertPartitions(tree, new Rectangle(5, 1, 5, 1), ImmutableSet.of()); + + // point on the border separating two partitions + assertPartitions(tree, new Rectangle(1, 4.5, 1, 4.5), ImmutableSet.of(0, 1)); + + // rectangles + assertPartitions(tree, new Rectangle(1, 1, 2, 2), ImmutableSet.of(0)); + assertPartitions(tree, new Rectangle(1, 6, 2, 7), ImmutableSet.of(1)); + assertPartitions(tree, new Rectangle(1, 1, 2, 5), ImmutableSet.of(0, 1)); + assertPartitions(tree, new Rectangle(5, 1, 6, 2), ImmutableSet.of()); + } + + private void assertPartitions(KdbTree kdbTree, Rectangle envelope, Set partitions) + { + Map matchingNodes = kdbTree.findIntersectingLeaves(envelope); + assertEquals(matchingNodes.size(), partitions.size()); + assertEquals(matchingNodes.keySet(), partitions); + } + + @Test + public void testEvenDistribution() + { + testEvenDistribution(0, 0); + testEvenDistribution(1, 2); + } + + private void testEvenDistribution(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9, 4); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 5; y++) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + + Map leafNodes = tree.getLeaves(); + assertEquals(leafNodes.size(), 6); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 2.5, 2.5)); + assertEquals(leafNodes.get(1), new Rectangle(0, 2.5, 2.5, 4)); + assertEquals(leafNodes.get(2), new Rectangle(2.5, 0, 4.5, 4)); + assertEquals(leafNodes.get(3), new Rectangle(4.5, 0, 7.5, 2.5)); + assertEquals(leafNodes.get(4), new Rectangle(4.5, 2.5, 7.5, 4)); + assertEquals(leafNodes.get(5), new Rectangle(7.5, 0, 9, 4)); + } + + @Test + public void testSkewedDistribution() + { + testSkewedDistribution(0, 0); + testSkewedDistribution(1, 2); + } + + private void testSkewedDistribution(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9, 4); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int x = 0; x < 10; x++) { + for (int y = 0; y < 5; y++) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + for (double x = 5; x < 6; x += 0.2) { + for (double y = 1; y < 2; y += 0.5) { + rectangles.add(new Rectangle(x, y, x + width, y + height)); + } + } + + KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + + Map leafNodes = tree.getLeaves(); + assertEquals(leafNodes.size(), 9); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5, 6, 7, 8)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 1.5, 2.5)); + assertEquals(leafNodes.get(1), new Rectangle(1.5, 0, 3.5, 2.5)); + assertEquals(leafNodes.get(2), new Rectangle(0, 2.5, 3.5, 4)); + assertEquals(leafNodes.get(3), new Rectangle(3.5, 0, 5.1, 1.75)); + assertEquals(leafNodes.get(4), new Rectangle(3.5, 1.75, 5.1, 4)); + assertEquals(leafNodes.get(5), new Rectangle(5.1, 0, 5.9, 1.75)); + assertEquals(leafNodes.get(6), new Rectangle(5.9, 0, 9, 1.75)); + assertEquals(leafNodes.get(7), new Rectangle(5.1, 1.75, 7.5, 4)); + assertEquals(leafNodes.get(8), new Rectangle(7.5, 1.75, 9, 4)); + } + + @Test + public void testCantSplitVertically() + { + testCantSplitVertically(0, 0); + testCantSplitVertically(1, 2); + } + + private void testCantSplitVertically(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9 + width, 4 + height); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int y = 0; y < 5; y++) { + for (int i = 0; i < 10; i++) { + rectangles.add(new Rectangle(0, y, width, y + height)); + rectangles.add(new Rectangle(9, y, 9 + width, y + height)); + } + } + + KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + + Map leafNodes = tree.getLeaves(); + assertEquals(leafNodes.size(), 10); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 0.5)); + assertEquals(leafNodes.get(1), new Rectangle(0, 0.5, 4.5, 1.5)); + assertEquals(leafNodes.get(2), new Rectangle(0, 1.5, 4.5, 2.5)); + assertEquals(leafNodes.get(3), new Rectangle(0, 2.5, 4.5, 3.5)); + assertEquals(leafNodes.get(4), new Rectangle(0, 3.5, 4.5, 4 + height)); + assertEquals(leafNodes.get(5), new Rectangle(4.5, 0, 9 + width, 0.5)); + assertEquals(leafNodes.get(6), new Rectangle(4.5, 0.5, 9 + width, 1.5)); + assertEquals(leafNodes.get(7), new Rectangle(4.5, 1.5, 9 + width, 2.5)); + assertEquals(leafNodes.get(8), new Rectangle(4.5, 2.5, 9 + width, 3.5)); + assertEquals(leafNodes.get(9), new Rectangle(4.5, 3.5, 9 + width, 4 + height)); + } + + @Test + public void testCantSplit() + { + testCantSplit(0, 0); + testCantSplit(1, 2); + } + + private void testCantSplit(double width, double height) + { + Rectangle extent = new Rectangle(0, 0, 9 + width, 4 + height); + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 5; j++) { + rectangles.add(new Rectangle(0, 0, width, height)); + rectangles.add(new Rectangle(9, 4, 9 + width, 4 + height)); + } + } + + KdbTree tree = buildKdbTree(10, extent, rectangles.build()); + + Map leafNodes = tree.getLeaves(); + assertEquals(leafNodes.size(), 2); + assertEquals(leafNodes.keySet(), ImmutableSet.of(0, 1)); + assertEquals(leafNodes.get(0), new Rectangle(0, 0, 4.5, 4 + height)); + assertEquals(leafNodes.get(1), new Rectangle(4.5, 0, 9 + width, 4 + height)); + } +} diff --git a/presto-geospatial/pom.xml b/presto-geospatial/pom.xml index a152be75eb00c..5ea84a3e02561 100644 --- a/presto-geospatial/pom.xml +++ b/presto-geospatial/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-geospatial @@ -31,6 +31,11 @@ jts-core + + com.fasterxml.jackson.core + jackson-databind + + com.facebook.presto presto-geospatial-toolkit @@ -77,21 +82,33 @@ + + com.facebook.presto.hadoop + hadoop-apache2 + test + + com.facebook.presto - presto-parser + presto-hive test com.facebook.presto - presto-memory + presto-tpch test - com.fasterxml.jackson.core - jackson-databind + com.facebook.presto + presto-parser + test + + + + com.facebook.presto + presto-memory test diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTile.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTile.java index 986cffb6e11bd..2d098271f6337 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTile.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTile.java @@ -51,9 +51,9 @@ public boolean equals(Object other) } BingTile otherTile = (BingTile) other; - return this.x == otherTile.x - && this.y == otherTile.y - && this.zoomLevel == otherTile.zoomLevel; + return this.x == otherTile.x && + this.y == otherTile.y && + this.zoomLevel == otherTile.zoomLevel; } @Override @@ -66,10 +66,10 @@ public int hashCode() public String toString() { return toStringHelper(this) - .add("x", x) - .add("y", y) - .add("zoom_level", zoomLevel) - .toString(); + .add("x", x) + .add("y", y) + .add("zoom_level", zoomLevel) + .toString(); } @JsonCreator diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java index d69d81259bd62..485a5a3ea1cd9 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileFunctions.java @@ -150,7 +150,7 @@ public static long toBingTile(@SqlType(StandardTypes.VARCHAR) Slice quadKey) return BingTile.fromQuadKey(quadKey.toStringUtf8()).encode(); } - @Description("Given a (longitude, latitude) point, returns the containing Bing tile at the specified zoom level") + @Description("Given a (latitude, longitude) point, returns the containing Bing tile at the specified zoom level") @ScalarFunction("bing_tile_at") @SqlType(BingTileType.NAME) public static long bingTileAt( @@ -243,7 +243,8 @@ public static Block bingTilesAround( int totalTileCount = tileCountX * tileCountY; checkCondition(totalTileCount <= 1_000_000, - "The number of input tiles is too large (more than 1M) to compute a set of covering Bing tiles."); + "The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: %d. Radius: %.1f km. Zoom level: %d.", + totalTileCount, radiusInKm, zoomLevel); BlockBuilder blockBuilder = BIGINT.createBlockBuilder(null, totalTileCount); @@ -371,7 +372,7 @@ public static Block geometryToBingTiles(@SqlType(GEOMETRY_TYPE_NAME) Slice input // XY coordinates start at (0,0) in the left upper corner and increase left to right and top to bottom long tileCount = (long) (rightLowerTile.getX() - leftUpperTile.getX() + 1) * (rightLowerTile.getY() - leftUpperTile.getY() + 1); - checkGeometryToBingTilesLimits(ogcGeometry, pointOrRectangle, tileCount); + checkGeometryToBingTilesLimits(ogcGeometry, envelope, pointOrRectangle, tileCount, zoomLevel); BlockBuilder blockBuilder = BIGINT.createBlockBuilder(null, toIntExact(tileCount)); if (pointOrRectangle || zoomLevel <= OPTIMIZED_TILING_MIN_ZOOM_LEVEL) { @@ -427,10 +428,12 @@ private static BingTile getTileCoveringLowerRightCorner(Envelope envelope, int z return tile; } - private static void checkGeometryToBingTilesLimits(OGCGeometry ogcGeometry, boolean pointOrRectangle, long tileCount) + private static void checkGeometryToBingTilesLimits(OGCGeometry ogcGeometry, Envelope envelope, boolean pointOrRectangle, long tileCount, int zoomLevel) { if (pointOrRectangle) { - checkCondition(tileCount <= 1_000_000, "The number of input tiles is too large (more than 1M) to compute a set of covering Bing tiles."); + checkCondition(tileCount <= 1_000_000, "The number of tiles covering input rectangle exceeds the limit of 1M. " + + "Number of tiles: %d. Rectangle: xMin=%.2f, yMin=%.2f, xMax=%.2f, yMax=%.2f. Zoom level: %d.", + tileCount, envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax(), zoomLevel); } else { checkCondition((int) tileCount == tileCount, "The zoom level is too high to compute a set of covering Bing tiles."); diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileOperators.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileOperators.java index 66032e2e95e5d..0e78714b4e0e1 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileOperators.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/BingTileOperators.java @@ -15,6 +15,7 @@ import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -30,14 +31,16 @@ private BingTileOperators() {} @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(BingTileType.NAME) long left, @SqlType(BingTileType.NAME) long right) + @SqlNullable + public static Boolean equal(@SqlType(BingTileType.NAME) long left, @SqlType(BingTileType.NAME) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(BingTileType.NAME) long left, @SqlType(BingTileType.NAME) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(BingTileType.NAME) long left, @SqlType(BingTileType.NAME) long right) { return left != right; } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoFunctions.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoFunctions.java index 47c44685fea59..29afe84d70f20 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoFunctions.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoFunctions.java @@ -15,16 +15,19 @@ import com.esri.core.geometry.Envelope; import com.esri.core.geometry.GeometryCursor; +import com.esri.core.geometry.ListeningGeometryCursor; import com.esri.core.geometry.MultiPath; import com.esri.core.geometry.MultiPoint; import com.esri.core.geometry.MultiVertexGeometry; import com.esri.core.geometry.NonSimpleResult; import com.esri.core.geometry.NonSimpleResult.Reason; import com.esri.core.geometry.OperatorSimplifyOGC; +import com.esri.core.geometry.OperatorUnion; import com.esri.core.geometry.Point; import com.esri.core.geometry.Polygon; import com.esri.core.geometry.Polyline; import com.esri.core.geometry.SpatialReference; +import com.esri.core.geometry.ogc.OGCConcreteGeometryCollection; import com.esri.core.geometry.ogc.OGCGeometry; import com.esri.core.geometry.ogc.OGCGeometryCollection; import com.esri.core.geometry.ogc.OGCLineString; @@ -32,6 +35,8 @@ import com.esri.core.geometry.ogc.OGCPoint; import com.esri.core.geometry.ogc.OGCPolygon; import com.facebook.presto.geospatial.GeometryType; +import com.facebook.presto.geospatial.KdbTree; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.geospatial.serde.GeometrySerde; import com.facebook.presto.geospatial.serde.GeometrySerializationType; import com.facebook.presto.geospatial.serde.JtsGeometrySerde; @@ -42,15 +47,23 @@ import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; -import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.IntegerType; import com.google.common.base.Joiner; +import com.google.common.base.VerifyException; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.slice.Slice; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.linearref.LengthIndexedLine; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; @@ -76,10 +89,18 @@ import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.type.StandardTypes.BIGINT; +import static com.facebook.presto.spi.type.StandardTypes.BOOLEAN; import static com.facebook.presto.spi.type.StandardTypes.DOUBLE; import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.spi.type.StandardTypes.TINYINT; +import static com.facebook.presto.spi.type.StandardTypes.VARBINARY; +import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; import static com.google.common.base.Preconditions.checkArgument; import static io.airlift.slice.Slices.utf8Slice; +import static io.airlift.slice.Slices.wrappedBuffer; +import static java.lang.Double.isInfinite; +import static java.lang.Double.isNaN; import static java.lang.Math.atan2; import static java.lang.Math.cos; import static java.lang.Math.sin; @@ -87,6 +108,8 @@ import static java.lang.Math.toIntExact; import static java.lang.Math.toRadians; import static java.lang.String.format; +import static java.util.Arrays.setAll; +import static java.util.Objects.requireNonNull; import static org.locationtech.jts.simplify.TopologyPreservingSimplifier.simplify; public final class GeoFunctions @@ -104,19 +127,61 @@ public final class GeoFunctions .put(OGCPolygonSelfTangency, "Self-tangency") .put(OGCDisconnectedInterior, "Disconnected interior") .build(); + private static final int NUMBER_OF_DIMENSIONS = 3; + private static final Block EMPTY_ARRAY_OF_INTS = IntegerType.INTEGER.createFixedSizeBlockBuilder(0).build(); private GeoFunctions() {} @Description("Returns a Geometry type LineString object from Well-Known Text representation (WKT)") @ScalarFunction("ST_LineFromText") @SqlType(GEOMETRY_TYPE_NAME) - public static Slice parseLine(@SqlType(StandardTypes.VARCHAR) Slice input) + public static Slice parseLine(@SqlType(VARCHAR) Slice input) { OGCGeometry geometry = geometryFromText(input); validateType("ST_LineFromText", geometry, EnumSet.of(LINE_STRING)); return serialize(geometry); } + @Description("Returns a LineString from an array of points") + @ScalarFunction("ST_LineString") + @SqlType(GEOMETRY_TYPE_NAME) + public static Slice stLineString(@SqlType("array(" + GEOMETRY_TYPE_NAME + ")") Block input) + { + MultiPath multipath = new Polyline(); + OGCPoint previousPoint = null; + for (int i = 0; i < input.getPositionCount(); i++) { + Slice slice = GEOMETRY.getSlice(input, i); + + if (slice.getInput().available() == 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid input to ST_LineString: null point at index %s", i + 1)); + } + + OGCGeometry geometry = deserialize(slice); + if (!(geometry instanceof OGCPoint)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("ST_LineString takes only an array of valid points, %s was passed", geometry.geometryType())); + } + OGCPoint point = (OGCPoint) geometry; + + if (point.isEmpty()) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid input to ST_LineString: empty point at index %s", i + 1)); + } + + if (previousPoint == null) { + multipath.startPath(point.X(), point.Y()); + } + else { + if (point.Equals(previousPoint)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, + format("Invalid input to ST_LineString: consecutive duplicate points at index %s", i + 1)); + } + multipath.lineTo(point.X(), point.Y()); + } + previousPoint = point; + } + OGCLineString linestring = new OGCLineString(multipath, 0, null); + return serialize(linestring); + } + @Description("Returns a Geometry type Point object with the given coordinate values") @ScalarFunction("ST_Point") @SqlType(GEOMETRY_TYPE_NAME) @@ -126,10 +191,40 @@ public static Slice stPoint(@SqlType(DOUBLE) double x, @SqlType(DOUBLE) double y return serialize(geometry); } + @SqlNullable + @Description("Returns a multi-point geometry formed from input points") + @ScalarFunction("ST_MultiPoint") + @SqlType(GEOMETRY_TYPE_NAME) + public static Slice stMultiPoint(@SqlType("array(" + GEOMETRY_TYPE_NAME + ")") Block input) + { + MultiPoint multipoint = new MultiPoint(); + for (int i = 0; i < input.getPositionCount(); i++) { + if (input.isNull(i)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid input to ST_MultiPoint: null at index %s", i + 1)); + } + + Slice slice = GEOMETRY.getSlice(input, i); + OGCGeometry geometry = deserialize(slice); + if (!(geometry instanceof OGCPoint)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid input to ST_MultiPoint: geometry is not a point: %s at index %s", geometry.geometryType(), i + 1)); + } + OGCPoint point = (OGCPoint) geometry; + if (point.isEmpty()) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid input to ST_MultiPoint: empty point at index %s", i + 1)); + } + + multipoint.add(point.X(), point.Y()); + } + if (multipoint.getPointCount() == 0) { + return null; + } + return serialize(createFromEsriGeometry(multipoint, null, true)); + } + @Description("Returns a Geometry type Polygon object from Well-Known Text representation (WKT)") @ScalarFunction("ST_Polygon") @SqlType(GEOMETRY_TYPE_NAME) - public static Slice stPolygon(@SqlType(StandardTypes.VARCHAR) Slice input) + public static Slice stPolygon(@SqlType(VARCHAR) Slice input) { OGCGeometry geometry = geometryFromText(input); validateType("ST_Polygon", geometry, EnumSet.of(POLYGON)); @@ -164,26 +259,42 @@ public static double stArea(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @Description("Returns a Geometry type object from Well-Known Text representation (WKT)") @ScalarFunction("ST_GeometryFromText") @SqlType(GEOMETRY_TYPE_NAME) - public static Slice stGeometryFromText(@SqlType(StandardTypes.VARCHAR) Slice input) + public static Slice stGeometryFromText(@SqlType(VARCHAR) Slice input) { return serialize(geometryFromText(input)); } + @Description("Returns a Geometry type object from Well-Known Binary representation (WKB)") + @ScalarFunction("ST_GeomFromBinary") + @SqlType(GEOMETRY_TYPE_NAME) + public static Slice stGeomFromBinary(@SqlType(VARBINARY) Slice input) + { + return serialize(geomFromBinary(input)); + } + @Description("Returns the Well-Known Text (WKT) representation of the geometry") @ScalarFunction("ST_AsText") - @SqlType(StandardTypes.VARCHAR) + @SqlType(VARCHAR) public static Slice stAsText(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { return utf8Slice(deserialize(input).asText()); } + @Description("Returns the Well-Known Binary (WKB) representation of the geometry") + @ScalarFunction("ST_AsBinary") + @SqlType(VARBINARY) + public static Slice stAsBinary(@SqlType(GEOMETRY_TYPE_NAME) Slice input) + { + return wrappedBuffer(deserialize(input).asBinary()); + } + @SqlNullable @Description("Returns the geometry that represents all points whose distance from the specified geometry is less than or equal to the specified distance") @ScalarFunction("ST_Buffer") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stBuffer(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @SqlType(DOUBLE) double distance) { - if (Double.isNaN(distance)) { + if (isNaN(distance)) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is NaN"); } @@ -246,32 +357,18 @@ public static Slice stCentroid(@SqlType(GEOMETRY_TYPE_NAME) Slice input) public static Slice stConvexHull(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); - validateType("ST_ConvexHull", geometry, EnumSet.of(POINT, MULTI_POINT, LINE_STRING, MULTI_LINE_STRING, POLYGON, MULTI_POLYGON)); if (geometry.isEmpty()) { return input; } if (GeometryType.getForEsriGeometryType(geometry.geometryType()) == POINT) { return input; } - OGCGeometry convexHull = geometry.convexHull(); - if (convexHull.isEmpty()) { - // This happens for a single-point multi-point because of a bug in ESRI library - https://github.com/Esri/geometry-api-java/issues/172 - return serialize(createFromEsriGeometry(((MultiVertexGeometry) geometry.getEsriGeometry()).getPoint(0), null)); - } - if (GeometryType.getForEsriGeometryType(convexHull.geometryType()) == MULTI_POLYGON) { - MultiVertexGeometry multiVertex = (MultiVertexGeometry) convexHull.getEsriGeometry(); - if (multiVertex.getPointCount() == 2) { - // This happens when all points of the input geometry are on the same line because of a bug in ESRI library - https://github.com/Esri/geometry-api-java/issues/172 - OGCGeometry linestring = createFromEsriGeometry(new Polyline(multiVertex.getPoint(0), multiVertex.getPoint(1)), null); - return serialize(linestring); - } - } - return serialize(convexHull); + return serialize(geometry.convexHull()); } @Description("Return the coordinate dimension of the Geometry") @ScalarFunction("ST_CoordDim") - @SqlType(StandardTypes.TINYINT) + @SqlType(TINYINT) public static long stCoordinateDimension(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { return deserialize(input).coordinateDimension(); @@ -279,7 +376,7 @@ public static long stCoordinateDimension(@SqlType(GEOMETRY_TYPE_NAME) Slice inpu @Description("Returns the inherent dimension of this Geometry object, which must be less than or equal to the coordinate dimension") @ScalarFunction("ST_Dimension") - @SqlType(StandardTypes.TINYINT) + @SqlType(TINYINT) public static long stDimension(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { return deserialize(input).dimension(); @@ -288,7 +385,7 @@ public static long stDimension(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @SqlNullable @Description("Returns TRUE if the LineString or Multi-LineString's start and end points are coincident") @ScalarFunction("ST_IsClosed") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stIsClosed(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); @@ -308,15 +405,16 @@ public static Boolean stIsClosed(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @SqlNullable @Description("Returns TRUE if this Geometry is an empty geometrycollection, polygon, point etc") @ScalarFunction("ST_IsEmpty") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stIsEmpty(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { - return deserialize(input).isEmpty(); + Envelope envelope = deserializeEnvelope(input); + return envelope == null || envelope.isEmpty(); } @Description("Returns TRUE if this Geometry has no anomalous geometric points, such as self intersection or self tangency") @ScalarFunction("ST_IsSimple") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static boolean stIsSimple(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); @@ -325,7 +423,7 @@ public static boolean stIsSimple(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @Description("Returns true if the input geometry is well formed") @ScalarFunction("ST_IsValid") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static boolean stIsValid(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { GeometryCursor cursor = deserialize(input).getEsriGeometryCursor(); @@ -343,7 +441,7 @@ public static boolean stIsValid(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @Description("Returns the reason for why the input geometry is not valid. Returns null if the input is valid.") @ScalarFunction("geometry_invalid_reason") - @SqlType(StandardTypes.VARCHAR) + @SqlType(VARCHAR) @SqlNullable public static Slice invalidReason(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { @@ -470,7 +568,7 @@ public static Double stYMin(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @SqlNullable @Description("Returns the cardinality of the collection of interior rings of a polygon") @ScalarFunction("ST_NumInteriorRing") - @SqlType(StandardTypes.BIGINT) + @SqlType(BIGINT) public static Long stNumInteriorRings(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); @@ -503,7 +601,7 @@ public static Block stInteriorRings(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @Description("Returns the cardinality of the geometry collection") @ScalarFunction("ST_NumGeometries") - @SqlType(StandardTypes.INTEGER) + @SqlType(INTEGER) public static long stNumGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); @@ -517,27 +615,66 @@ public static long stNumGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input) return ((OGCGeometryCollection) geometry).numGeometries(); } - @Description("Returns a geometry that represents the point set union of the input geometries. This function doesn't support geometry collections.") + @Description("Returns a geometry that represents the point set union of the input geometries.") @ScalarFunction("ST_Union") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stUnion(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { - // Only supports Geometry but not GeometryCollection due to ESRI library limitation - // https://github.com/Esri/geometry-api-java/issues/176 - // https://github.com/Esri/geometry-api-java/issues/177 - OGCGeometry leftGeometry = deserialize(left); - validateType("ST_Union", leftGeometry, EnumSet.of(POINT, MULTI_POINT, LINE_STRING, MULTI_LINE_STRING, POLYGON, MULTI_POLYGON)); - if (leftGeometry.isEmpty()) { - return right; + return stUnion(ImmutableList.of(left, right)); + } + + @Description("Returns a geometry that represents the point set union of the input geometries.") + @ScalarFunction("geometry_union") + @SqlType(GEOMETRY_TYPE_NAME) + public static Slice geometryUnion(@SqlType("array(" + GEOMETRY_TYPE_NAME + ")") Block input) + { + return stUnion(getGeometrySlicesFromBlock(input)); + } + + private static Slice stUnion(Iterable slices) + { + // The current state of Esri/geometry-api-java does not allow support for multiple dimensions being + // fed to the union operator without dropping the lower dimensions: + // https://github.com/Esri/geometry-api-java/issues/199 + // When operating over a collection of geometries, it is more efficient to reuse the same operator + // for the entire operation. Therefore, split the inputs and operators by dimension, and then union + // each dimension's result at the end. + ListeningGeometryCursor[] cursorsByDimension = new ListeningGeometryCursor[NUMBER_OF_DIMENSIONS]; + GeometryCursor[] operatorsByDimension = new GeometryCursor[NUMBER_OF_DIMENSIONS]; + + setAll(cursorsByDimension, i -> new ListeningGeometryCursor()); + setAll(operatorsByDimension, i -> OperatorUnion.local().execute(cursorsByDimension[i], null, null)); + + Iterator slicesIterator = slices.iterator(); + if (!slicesIterator.hasNext()) { + return null; } + while (slicesIterator.hasNext()) { + Slice slice = slicesIterator.next(); + // Ignore null inputs + if (slice.getInput().available() == 0) { + continue; + } - OGCGeometry rightGeometry = deserialize(right); - validateType("ST_Union", rightGeometry, EnumSet.of(POINT, MULTI_POINT, LINE_STRING, MULTI_LINE_STRING, POLYGON, MULTI_POLYGON)); - if (rightGeometry.isEmpty()) { - return left; + for (OGCGeometry geometry : flattenCollection(deserialize(slice))) { + int dimension = geometry.dimension(); + cursorsByDimension[dimension].tick(geometry.getEsriGeometry()); + operatorsByDimension[dimension].tock(); + } + } + + List outputs = new ArrayList<>(); + for (GeometryCursor operator : operatorsByDimension) { + OGCGeometry unionedGeometry = createFromEsriGeometry(operator.next(), null); + if (unionedGeometry != null) { + outputs.add(unionedGeometry); + } } - return serialize(leftGeometry.union(rightGeometry)); + if (outputs.size() == 1) { + return serialize(outputs.get(0)); + } + return serialize(new OGCConcreteGeometryCollection(outputs, null).flattenAndRemoveOverlaps().reduceFromMulti()); } @SqlNullable @@ -625,7 +762,7 @@ public static Slice stInteriorRingN(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @S @Description("Returns the number of points in a Geometry") @ScalarFunction("ST_NumPoints") - @SqlType(StandardTypes.BIGINT) + @SqlType(BIGINT) public static long stNumPoints(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { return getPointCount(deserialize(input)); @@ -634,7 +771,7 @@ public static long stNumPoints(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @SqlNullable @Description("Returns TRUE if and only if the line is closed and simple") @ScalarFunction("ST_IsRing") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stIsRing(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); @@ -662,10 +799,9 @@ public static Slice stStartPoint(@SqlType(GEOMETRY_TYPE_NAME) Slice input) @Description("Returns a \"simplified\" version of the given geometry") @ScalarFunction("simplify_geometry") @SqlType(GEOMETRY_TYPE_NAME) - public static Slice simplifyGeometry(@SqlType(GEOMETRY_TYPE_NAME) Slice input, - @SqlType(DOUBLE) double distanceTolerance) + public static Slice simplifyGeometry(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @SqlType(DOUBLE) double distanceTolerance) { - if (Double.isNaN(distanceTolerance)) { + if (isNaN(distanceTolerance)) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distanceTolerance is NaN"); } @@ -778,15 +914,16 @@ public static Slice stDifference(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTy return serialize(leftGeometry.difference(rightGeometry)); } + @SqlNullable @Description("Returns the 2-dimensional cartesian minimum distance (based on spatial ref) between two geometries in projected units") @ScalarFunction("ST_Distance") @SqlType(DOUBLE) - public static double stDistance(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) + public static Double stDistance(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { OGCGeometry leftGeometry = deserialize(left); OGCGeometry rightGeometry = deserialize(right); verifySameSpatialReference(leftGeometry, rightGeometry); - return leftGeometry.distance(rightGeometry); + return leftGeometry.isEmpty() || rightGeometry.isEmpty() ? null : leftGeometry.distance(rightGeometry); } @SqlNullable @@ -852,7 +989,7 @@ public static Slice stSymmetricDifference(@SqlType(GEOMETRY_TYPE_NAME) Slice lef @SqlNullable @Description("Returns TRUE if and only if no points of right lie in the exterior of left, and at least one point of the interior of left lies in the interior of right") @ScalarFunction("ST_Contains") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stContains(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::contains)) { @@ -867,7 +1004,7 @@ public static Boolean stContains(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTy @SqlNullable @Description("Returns TRUE if the supplied geometries have some, but not all, interior points in common") @ScalarFunction("ST_Crosses") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stCrosses(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::intersect)) { @@ -882,7 +1019,7 @@ public static Boolean stCrosses(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTyp @SqlNullable @Description("Returns TRUE if the Geometries do not spatially intersect - if they do not share any space together") @ScalarFunction("ST_Disjoint") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stDisjoint(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::intersect)) { @@ -897,7 +1034,7 @@ public static Boolean stDisjoint(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTy @SqlNullable @Description("Returns TRUE if the given geometries represent the same geometry") @ScalarFunction("ST_Equals") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stEquals(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { OGCGeometry leftGeometry = deserialize(left); @@ -909,7 +1046,7 @@ public static Boolean stEquals(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType @SqlNullable @Description("Returns TRUE if the Geometries spatially intersect in 2D - (share any portion of space) and FALSE if they don't (they are Disjoint)") @ScalarFunction("ST_Intersects") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stIntersects(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::intersect)) { @@ -924,7 +1061,7 @@ public static Boolean stIntersects(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @Sql @SqlNullable @Description("Returns TRUE if the Geometries share space, are of the same dimension, but are not completely contained by each other") @ScalarFunction("ST_Overlaps") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stOverlaps(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::intersect)) { @@ -939,8 +1076,8 @@ public static Boolean stOverlaps(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTy @SqlNullable @Description("Returns TRUE if this Geometry is spatially related to another Geometry") @ScalarFunction("ST_Relate") - @SqlType(StandardTypes.BOOLEAN) - public static Boolean stRelate(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right, @SqlType(StandardTypes.VARCHAR) Slice relation) + @SqlType(BOOLEAN) + public static Boolean stRelate(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right, @SqlType(VARCHAR) Slice relation) { OGCGeometry leftGeometry = deserialize(left); OGCGeometry rightGeometry = deserialize(right); @@ -951,7 +1088,7 @@ public static Boolean stRelate(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType @SqlNullable @Description("Returns TRUE if the geometries have at least one point in common, but their interiors do not intersect") @ScalarFunction("ST_Touches") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stTouches(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(left, right, Envelope::intersect)) { @@ -966,7 +1103,7 @@ public static Boolean stTouches(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTyp @SqlNullable @Description("Returns TRUE if the geometry A is completely inside geometry B") @ScalarFunction("ST_Within") - @SqlType(StandardTypes.BOOLEAN) + @SqlType(BOOLEAN) public static Boolean stWithin(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { if (!envelopes(right, left, Envelope::contains)) { @@ -980,20 +1117,93 @@ public static Boolean stWithin(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType @Description("Returns the type of the geometry") @ScalarFunction("ST_GeometryType") - @SqlType(StandardTypes.VARCHAR) + @SqlType(VARCHAR) public static Slice stGeometryType(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { return GeometrySerde.getGeometryType(input).standardName(); } + @ScalarFunction + @SqlNullable + @Description("Returns an array of spatial partition IDs for a given geometry") + @SqlType("array(int)") + public static Block spatialPartitions(@SqlType(KdbTreeType.NAME) Object kdbTree, @SqlType(GEOMETRY_TYPE_NAME) Slice geometry) + { + Envelope envelope = deserializeEnvelope(geometry); + if (envelope == null) { + // Empty geometry + return null; + } + + return spatialPartitions((KdbTree) kdbTree, new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax())); + } + + @ScalarFunction + @SqlNullable + @Description("Returns an array of spatial partition IDs for a geometry representing a set of points within specified distance from the input geometry") + @SqlType("array(int)") + public static Block spatialPartitions(@SqlType(KdbTreeType.NAME) Object kdbTree, @SqlType(GEOMETRY_TYPE_NAME) Slice geometry, @SqlType(DOUBLE) double distance) + { + if (isNaN(distance)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is NaN"); + } + + if (isInfinite(distance)) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is infinite"); + } + + if (distance < 0) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is negative"); + } + + Envelope envelope = deserializeEnvelope(geometry); + if (envelope == null) { + return null; + } + + Rectangle expandedEnvelope2D = new Rectangle(envelope.getXMin() - distance, envelope.getYMin() - distance, envelope.getXMax() + distance, envelope.getYMax() + distance); + return spatialPartitions((KdbTree) kdbTree, expandedEnvelope2D); + } + + private static Block spatialPartitions(KdbTree kdbTree, Rectangle envelope) + { + Map partitions = kdbTree.findIntersectingLeaves(envelope); + if (partitions.isEmpty()) { + return EMPTY_ARRAY_OF_INTS; + } + + // For input rectangles that represent a single point, return at most one partition + // by excluding right and upper sides of partition rectangles. The logic that builds + // KDB tree needs to make sure to add some padding to the right and upper sides of the + // overall extent of the tree to avoid missing right-most and top-most points. + boolean point = (envelope.getWidth() == 0 && envelope.getHeight() == 0); + if (point) { + for (Map.Entry partition : partitions.entrySet()) { + if (envelope.getXMin() < partition.getValue().getXMax() && envelope.getYMin() < partition.getValue().getYMax()) { + BlockBuilder blockBuilder = IntegerType.INTEGER.createFixedSizeBlockBuilder(1); + blockBuilder.writeInt(partition.getKey()); + return blockBuilder.build(); + } + } + throw new VerifyException(format("Cannot find half-open partition extent for a point: (%s, %s)", envelope.getXMin(), envelope.getYMin())); + } + + BlockBuilder blockBuilder = IntegerType.INTEGER.createFixedSizeBlockBuilder(partitions.size()); + for (int id : partitions.keySet()) { + blockBuilder.writeInt(id); + } + + return blockBuilder.build(); + } + @ScalarFunction @Description("Calculates the great-circle distance between two points on the Earth's surface in kilometers") - @SqlType(StandardTypes.DOUBLE) + @SqlType(DOUBLE) public static double greatCircleDistance( - @SqlType(StandardTypes.DOUBLE) double latitude1, - @SqlType(StandardTypes.DOUBLE) double longitude1, - @SqlType(StandardTypes.DOUBLE) double latitude2, - @SqlType(StandardTypes.DOUBLE) double longitude2) + @SqlType(DOUBLE) double latitude1, + @SqlType(DOUBLE) double longitude1, + @SqlType(DOUBLE) double latitude2, + @SqlType(DOUBLE) double longitude2) { checkLatitude(latitude1); checkLongitude(longitude1); @@ -1019,14 +1229,14 @@ public static double greatCircleDistance( private static void checkLatitude(double latitude) { - if (Double.isNaN(latitude) || Double.isInfinite(latitude) || latitude < -90 || latitude > 90) { + if (isNaN(latitude) || isInfinite(latitude) || latitude < -90 || latitude > 90) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Latitude must be between -90 and 90"); } } private static void checkLongitude(double longitude) { - if (Double.isNaN(longitude) || Double.isInfinite(longitude) || longitude < -180 || longitude > 180) { + if (isNaN(longitude) || isInfinite(longitude) || longitude < -180 || longitude > 180) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Longitude must be between -180 and 180"); } } @@ -1044,6 +1254,20 @@ private static OGCGeometry geometryFromText(Slice input) return geometry; } + private static OGCGeometry geomFromBinary(Slice input) + { + requireNonNull(input, "input is null"); + OGCGeometry geometry; + try { + geometry = OGCGeometry.fromBinary(input.toByteBuffer().slice()); + } + catch (IllegalArgumentException | IndexOutOfBoundsException e) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Invalid WKB", e); + } + geometry.setSpatialReference(null); + return geometry; + } + private static void validateType(String function, OGCGeometry geometry, Set validTypes) { GeometryType type = GeometryType.getForEsriGeometryType(geometry.geometryType()); @@ -1184,4 +1408,77 @@ private interface EnvelopesPredicate { boolean apply(Envelope left, Envelope right); } + + private static Iterable getGeometrySlicesFromBlock(Block block) + { + requireNonNull(block, "block is null"); + return () -> new Iterator() + { + private int iteratorPosition; + + @Override + public boolean hasNext() + { + return iteratorPosition != block.getPositionCount(); + } + + @Override + public Slice next() + { + if (!hasNext()) { + throw new NoSuchElementException("Slices have been consumed"); + } + return GEOMETRY.getSlice(block, iteratorPosition++); + } + }; + } + + private static Iterable flattenCollection(OGCGeometry geometry) + { + if (geometry == null) { + return ImmutableList.of(); + } + if (!(geometry instanceof OGCConcreteGeometryCollection)) { + return ImmutableList.of(geometry); + } + if (((OGCConcreteGeometryCollection) geometry).numGeometries() == 0) { + return ImmutableList.of(); + } + return () -> new GeometryCollectionIterator(geometry); + } + + private static class GeometryCollectionIterator + implements Iterator + { + private final Deque geometriesDeque = new ArrayDeque<>(); + + GeometryCollectionIterator(OGCGeometry geometries) + { + geometriesDeque.push(requireNonNull(geometries, "geometries is null")); + } + + @Override + public boolean hasNext() + { + if (geometriesDeque.isEmpty()) { + return false; + } + while (geometriesDeque.peek() instanceof OGCConcreteGeometryCollection) { + OGCGeometryCollection collection = (OGCGeometryCollection) geometriesDeque.pop(); + for (int i = 0; i < collection.numGeometries(); i++) { + geometriesDeque.push(collection.geometryN(i)); + } + } + return !geometriesDeque.isEmpty(); + } + + @Override + public OGCGeometry next() + { + if (!hasNext()) { + throw new NoSuchElementException("Geometries have been consumed"); + } + return geometriesDeque.pop(); + } + } } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoPlugin.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoPlugin.java index 6f7d8d4a32163..396a1ae5cec06 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoPlugin.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeoPlugin.java @@ -25,6 +25,7 @@ import static com.facebook.presto.plugin.geospatial.BingTileType.BING_TILE; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; +import static com.facebook.presto.plugin.geospatial.KdbTreeType.KDB_TREE; public class GeoPlugin implements Plugin @@ -32,7 +33,7 @@ public class GeoPlugin @Override public Iterable getTypes() { - return ImmutableList.of(GEOMETRY, BING_TILE); + return ImmutableList.of(GEOMETRY, BING_TILE, KDB_TREE); } @Override @@ -45,6 +46,9 @@ public Set> getFunctions() .add(BingTileCoordinatesFunction.class) .add(ConvexHullAggregation.class) .add(GeometryUnionAgg.class) + .add(KdbTreeCasts.class) + .add(SpatialPartitioningAggregateFunction.class) + .add(SpatialPartitioningInternalAggregateFunction.class) .build(); } } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeometryType.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeometryType.java index d92d3b462a558..2b007eb380739 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeometryType.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/GeometryType.java @@ -59,12 +59,20 @@ public Slice getSlice(Block block, int position) @Override public void writeSlice(BlockBuilder blockBuilder, Slice value) { + if (value == null) { + blockBuilder.appendNull(); + return; + } writeSlice(blockBuilder, value, 0, value.length()); } @Override public void writeSlice(BlockBuilder blockBuilder, Slice value, int offset, int length) { + if (value == null) { + blockBuilder.appendNull(); + return; + } blockBuilder.writeBytes(value, offset, length).closeEntry(); } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeCasts.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeCasts.java new file mode 100644 index 0000000000000..b92e85d367249 --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeCasts.java @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.LiteralParameters; +import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlType; +import io.airlift.slice.Slice; + +import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; +import static com.facebook.presto.spi.function.OperatorType.CAST; + +public final class KdbTreeCasts +{ + private KdbTreeCasts() {} + + @LiteralParameters("x") + @ScalarOperator(CAST) + @SqlType(KdbTreeType.NAME) + public static Object castVarcharToKdbTree(@SqlType("varchar(x)") Slice json) + { + try { + return KdbTreeUtils.fromJson(json.toStringUtf8()); + } + catch (IllegalArgumentException e) { + throw new PrestoException(INVALID_CAST_ARGUMENT, "Invalid JSON string for KDB tree", e); + } + } +} diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeType.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeType.java new file mode 100644 index 0000000000000..a44bd85acf427 --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/KdbTreeType.java @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.block.BlockBuilderStatus; +import com.facebook.presto.spi.type.AbstractType; +import com.facebook.presto.spi.type.TypeSignature; + +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; + +public final class KdbTreeType + extends AbstractType +{ + public static final KdbTreeType KDB_TREE = new KdbTreeType(); + public static final String NAME = "KdbTree"; + + private KdbTreeType() + { + super(new TypeSignature(NAME), Object.class); + } + + @Override + public Object getObjectValue(ConnectorSession session, Block block, int position) + { + throw new UnsupportedOperationException(); + } + + @Override + public void appendTo(Block block, int position, BlockBuilder blockBuilder) + { + throw new UnsupportedOperationException(); + } + + @Override + public BlockBuilder createBlockBuilder(BlockBuilderStatus blockBuilderStatus, int expectedEntries, int expectedBytesPerEntry) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "KdbTree type cannot be serialized"); + } + + @Override + public BlockBuilder createBlockBuilder(BlockBuilderStatus blockBuilderStatus, int expectedEntries) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "KdbTree type cannot be serialized"); + } +} diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningAggregateFunction.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningAggregateFunction.java new file mode 100644 index 0000000000000..3b79e882ea2b5 --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningAggregateFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.AggregationFunction; +import com.facebook.presto.spi.function.CombineFunction; +import com.facebook.presto.spi.function.InputFunction; +import com.facebook.presto.spi.function.OutputFunction; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.type.StandardTypes; +import io.airlift.slice.Slice; + +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; +import static com.facebook.presto.plugin.geospatial.SpatialPartitioningAggregateFunction.NAME; + +@AggregationFunction(value = NAME, decomposable = false) +public class SpatialPartitioningAggregateFunction +{ + public static final String NAME = "spatial_partitioning"; + + private SpatialPartitioningAggregateFunction() {} + + @InputFunction + public static void input(SpatialPartitioningState state, @SqlType(GEOMETRY_TYPE_NAME) Slice slice) + { + throw new UnsupportedOperationException("spatial_partitioning(geometry, samplingPercentage) aggregate function should be re-written into spatial_partitioning(geometry, samplingPercentage, partitionCount)"); + } + + @CombineFunction + public static void combine(SpatialPartitioningState state, SpatialPartitioningState otherState) + { + throw new UnsupportedOperationException("spatial_partitioning(geometry, samplingPercentage) aggregate function should be re-written into spatial_partitioning(geometry, samplingPercentage, partitionCount)"); + } + + @OutputFunction(StandardTypes.VARCHAR) + public static void output(SpatialPartitioningState state, BlockBuilder out) + { + throw new UnsupportedOperationException("spatial_partitioning(geometry, samplingPercentage) aggregate function should be re-written into spatial_partitioning(geometry, samplingPercentage, partitionCount)"); + } +} diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java new file mode 100644 index 0000000000000..9d4ad04b87f5c --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningInternalAggregateFunction.java @@ -0,0 +1,106 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.esri.core.geometry.Envelope; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.AggregationFunction; +import com.facebook.presto.spi.function.CombineFunction; +import com.facebook.presto.spi.function.InputFunction; +import com.facebook.presto.spi.function.OutputFunction; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.type.StandardTypes; +import io.airlift.slice.Slice; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; +import static com.facebook.presto.geospatial.serde.GeometrySerde.deserializeEnvelope; +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; +import static com.facebook.presto.plugin.geospatial.SpatialPartitioningAggregateFunction.NAME; +import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static java.lang.Math.toIntExact; + +@AggregationFunction(value = NAME, decomposable = false, hidden = true) +public class SpatialPartitioningInternalAggregateFunction +{ + private static final int MAX_SAMPLE_COUNT = 1_000_000; + + private SpatialPartitioningInternalAggregateFunction() {} + + @InputFunction + public static void input(SpatialPartitioningState state, @SqlType(GEOMETRY_TYPE_NAME) Slice slice, @SqlType(INTEGER) long partitionCount) + { + Envelope envelope = deserializeEnvelope(slice); + if (envelope == null) { + return; + } + + Rectangle extent = new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax()); + + if (state.getCount() == 0) { + state.setPartitionCount(toIntExact(partitionCount)); + state.setExtent(extent); + state.setSamples(new ArrayList<>()); + } + else { + state.setExtent(state.getExtent().merge(extent)); + } + + // use reservoir sampling + List samples = state.getSamples(); + if (samples.size() <= MAX_SAMPLE_COUNT) { + samples.add(extent); + } + else { + long sampleIndex = ThreadLocalRandom.current().nextLong(state.getCount()); + if (sampleIndex < MAX_SAMPLE_COUNT) { + samples.set(toIntExact(sampleIndex), extent); + } + } + + state.setCount(state.getCount() + 1); + } + + @CombineFunction + public static void combine(SpatialPartitioningState state, SpatialPartitioningState otherState) + { + throw new UnsupportedOperationException("spatial_partitioning must run on a single node"); + } + + @OutputFunction(StandardTypes.VARCHAR) + public static void output(SpatialPartitioningState state, BlockBuilder out) + { + if (state.getCount() == 0) { + out.appendNull(); + return; + } + + List samples = state.getSamples(); + + int partitionCount = state.getPartitionCount(); + int maxItemsPerNode = (samples.size() + partitionCount - 1) / partitionCount; + Rectangle envelope = state.getExtent(); + + // Add a small buffer on the right and upper sides + Rectangle paddedExtent = new Rectangle(envelope.getXMin(), envelope.getYMin(), Math.nextUp(envelope.getXMax()), Math.nextUp(envelope.getYMax())); + + VARCHAR.writeString(out, KdbTreeUtils.toJson(buildKdbTree(maxItemsPerNode, paddedExtent, samples))); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsState.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java similarity index 55% rename from presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsState.java rename to presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java index 2e50d57c24c76..fa54c2878f11b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsState.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningState.java @@ -11,24 +11,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.operator.aggregation.state; +package com.facebook.presto.plugin.geospatial; -import com.facebook.presto.operator.aggregation.MultiKeyValuePairs; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.spi.function.AccumulatorState; import com.facebook.presto.spi.function.AccumulatorStateMetadata; -import com.facebook.presto.spi.type.Type; -@AccumulatorStateMetadata(stateFactoryClass = MultiKeyValuePairsStateFactory.class, stateSerializerClass = MultiKeyValuePairStateSerializer.class) -public interface MultiKeyValuePairsState +import java.util.List; + +@AccumulatorStateMetadata(stateSerializerClass = SpatialPartitioningStateSerializer.class, stateFactoryClass = SpatialPartitioningStateFactory.class) +public interface SpatialPartitioningState extends AccumulatorState { - MultiKeyValuePairs get(); + int getPartitionCount(); + + void setPartitionCount(int partitionCount); + + long getCount(); + + void setCount(long count); - void set(MultiKeyValuePairs value); + Rectangle getExtent(); - void addMemoryUsage(long memory); + void setExtent(Rectangle envelope); - Type getKeyType(); + List getSamples(); - Type getValueType(); + void setSamples(List samples); } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java new file mode 100644 index 0000000000000..bc37ab6c3c747 --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateFactory.java @@ -0,0 +1,213 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.esri.core.geometry.Envelope; +import com.facebook.presto.array.IntBigArray; +import com.facebook.presto.array.LongBigArray; +import com.facebook.presto.array.ObjectBigArray; +import com.facebook.presto.geospatial.Rectangle; +import com.facebook.presto.spi.function.AccumulatorStateFactory; +import com.facebook.presto.spi.function.GroupedAccumulatorState; +import org.openjdk.jol.info.ClassLayout; + +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Math.toIntExact; + +public class SpatialPartitioningStateFactory + implements AccumulatorStateFactory +{ + @Override + public SpatialPartitioningState createSingleState() + { + return new SingleSpatialPartitioningState(); + } + + @Override + public Class getSingleStateClass() + { + return SpatialPartitioningState.class; + } + + @Override + public SpatialPartitioningState createGroupedState() + { + return new GroupedSpatialPartitioningState(); + } + + @Override + public Class getGroupedStateClass() + { + return GroupedSpatialPartitioningState.class; + } + + public static final class GroupedSpatialPartitioningState + implements GroupedAccumulatorState, SpatialPartitioningState + { + private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupedSpatialPartitioningState.class).instanceSize(); + private static final int ENVELOPE_SIZE = toIntExact(new Envelope(1, 2, 3, 4).estimateMemorySize()); + + private long groupId; + private final IntBigArray partitionCounts = new IntBigArray(); + private final LongBigArray counts = new LongBigArray(); + private final ObjectBigArray envelopes = new ObjectBigArray<>(); + private final ObjectBigArray> samples = new ObjectBigArray<>(); + private int envelopeCount; + private int samplesCount; + + @Override + public int getPartitionCount() + { + return partitionCounts.get(groupId); + } + + @Override + public void setPartitionCount(int partitionCount) + { + this.partitionCounts.set(groupId, partitionCount); + } + + @Override + public long getCount() + { + return counts.get(groupId); + } + + @Override + public void setCount(long count) + { + counts.set(groupId, count); + } + + @Override + public Rectangle getExtent() + { + return envelopes.get(groupId); + } + + @Override + public void setExtent(Rectangle envelope) + { + if (envelopes.get(groupId) == null) { + envelopeCount++; + } + envelopes.set(groupId, envelope); + } + + @Override + public List getSamples() + { + return samples.get(groupId); + } + + @Override + public void setSamples(List samples) + { + List currentSamples = this.samples.get(groupId); + if (currentSamples != null) { + samplesCount -= currentSamples.size(); + } + samplesCount += samples.size(); + this.samples.set(groupId, samples); + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + partitionCounts.sizeOf() + counts.sizeOf() + envelopes.sizeOf() + samples.sizeOf() + ENVELOPE_SIZE * (envelopeCount + samplesCount); + } + + @Override + public void setGroupId(long groupId) + { + this.groupId = groupId; + } + + @Override + public void ensureCapacity(long size) + { + partitionCounts.ensureCapacity(size); + counts.ensureCapacity(size); + envelopes.ensureCapacity(size); + samples.ensureCapacity(size); + } + } + + public static final class SingleSpatialPartitioningState + implements SpatialPartitioningState + { + private static final int INSTANCE_SIZE = ClassLayout.parseClass(SingleSpatialPartitioningState.class).instanceSize(); + + private int partitionCount; + private long count; + private Rectangle envelope; + private List samples = new ArrayList<>(); + + @Override + public int getPartitionCount() + { + return partitionCount; + } + + @Override + public void setPartitionCount(int partitionCount) + { + this.partitionCount = partitionCount; + } + + @Override + public long getCount() + { + return count; + } + + @Override + public void setCount(long count) + { + this.count = count; + } + + @Override + public Rectangle getExtent() + { + return envelope; + } + + @Override + public void setExtent(Rectangle envelope) + { + this.envelope = envelope; + } + + @Override + public List getSamples() + { + return samples; + } + + @Override + public void setSamples(List samples) + { + this.samples = samples; + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + (envelope != null ? envelope.estimateMemorySize() * (1 + samples.size()) : 0); + } + } +} diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateSerializer.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateSerializer.java new file mode 100644 index 0000000000000..39f62e0c779a8 --- /dev/null +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/SpatialPartitioningStateSerializer.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.AccumulatorStateSerializer; +import com.facebook.presto.spi.type.Type; + +import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; + +public class SpatialPartitioningStateSerializer + implements AccumulatorStateSerializer +{ + @Override + public Type getSerializedType() + { + // TODO: make serializer optional in case of non decomposable aggregation + return VARBINARY; + } + + @Override + public void serialize(SpatialPartitioningState state, BlockBuilder out) + { + throw new UnsupportedOperationException(); + } + + @Override + public void deserialize(Block block, int index, SpatialPartitioningState state) + { + throw new UnsupportedOperationException(); + } +} diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/ConvexHullAggregation.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/ConvexHullAggregation.java index 0270516c8de89..4e90d076885fb 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/ConvexHullAggregation.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/ConvexHullAggregation.java @@ -28,15 +28,8 @@ import com.google.common.base.Joiner; import io.airlift.slice.Slice; -import java.util.EnumSet; import java.util.Set; -import static com.facebook.presto.geospatial.GeometryType.LINE_STRING; -import static com.facebook.presto.geospatial.GeometryType.MULTI_LINE_STRING; -import static com.facebook.presto.geospatial.GeometryType.MULTI_POINT; -import static com.facebook.presto.geospatial.GeometryType.MULTI_POLYGON; -import static com.facebook.presto.geospatial.GeometryType.POINT; -import static com.facebook.presto.geospatial.GeometryType.POLYGON; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; @@ -51,6 +44,7 @@ public class ConvexHullAggregation { private static final Joiner OR_JOINER = Joiner.on(" or "); + private ConvexHullAggregation() {} @InputFunction @@ -58,8 +52,6 @@ public static void input(@AggregationState GeometryState state, @SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = GeometrySerde.deserialize(input); - // There is a bug when the input is of GEOMETRY_COLLECTION. see https://github.com/Esri/geometry-api-java/issues/194 - validateType("convex_hull_agg", geometry, EnumSet.of(POINT, MULTI_POINT, LINE_STRING, MULTI_LINE_STRING, POLYGON, MULTI_POLYGON)); if (state.getGeometry() == null) { state.setGeometry(geometry.convexHull()); } diff --git a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/GeometryUnionAgg.java b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/GeometryUnionAgg.java index 61bdf9086020c..6370100543457 100644 --- a/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/GeometryUnionAgg.java +++ b/presto-geospatial/src/main/java/com/facebook/presto/plugin/geospatial/aggregation/GeometryUnionAgg.java @@ -29,8 +29,8 @@ import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; /** - * Aggregate form of ST_Union which takes a set of geometries and unions them into a single geometry, resulting in no intersecting - * regions. The output may be a multi-geometry, a single geometry or a geometry collection. + * Aggregate form of ST_Union which takes a set of geometries and unions them into a single geometry using an iterative approach, + * resulting in no intersecting regions. The output may be a multi-geometry, a single geometry or a geometry collection. */ @Description("Returns a geometry that represents the point set union of the input geometries.") @AggregationFunction("geometry_union_agg") diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/BenchmarkGeometryAggregations.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/BenchmarkGeometryAggregations.java new file mode 100644 index 0000000000000..4539122606fa4 --- /dev/null +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/BenchmarkGeometryAggregations.java @@ -0,0 +1,145 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.plugin.memory.MemoryConnectorFactory; +import com.facebook.presto.testing.LocalQueryRunner; +import com.facebook.presto.testing.MaterializedResult; +import com.google.common.collect.ImmutableMap; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Collectors; + +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; +import static org.openjdk.jmh.annotations.Scope.Thread; + +@OutputTimeUnit(MILLISECONDS) +@BenchmarkMode(AverageTime) +@Fork(3) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +public class BenchmarkGeometryAggregations +{ + @State(Thread) + public static class Context + { + private LocalQueryRunner queryRunner; + + public LocalQueryRunner getQueryRunner() + { + return queryRunner; + } + + @Setup + public void setUp() + throws IOException + { + queryRunner = new LocalQueryRunner(testSessionBuilder() + .setCatalog("memory") + .setSchema("default") + .build()); + queryRunner.installPlugin(new GeoPlugin()); + queryRunner.createCatalog("memory", new MemoryConnectorFactory(), ImmutableMap.of()); + + Path path = Paths.get(BenchmarkGeometryAggregations.class.getClassLoader().getResource("us-states.tsv").getPath()); + String polygonValues = Files.lines(path) + .map(line -> line.split("\t")) + .map(parts -> format("('%s', '%s')", parts[0], parts[1])) + .collect(Collectors.joining(",")); + + queryRunner.execute( + format("CREATE TABLE memory.default.us_states AS SELECT ST_GeometryFromText(t.wkt) AS geom FROM (VALUES %s) as t (name, wkt)", + polygonValues)); + } + + @TearDown + public void tearDown() + { + queryRunner.close(); + queryRunner = null; + } + } + + @Benchmark + public MaterializedResult benchmarkArrayUnion(Context context) + { + return context.getQueryRunner() + .execute("SELECT geometry_union(array_agg(p.geom)) FROM us_states p"); + } + + @Benchmark + public MaterializedResult benchmarkUnion(Context context) + { + return context.getQueryRunner() + .execute("SELECT geometry_union_agg(p.geom) FROM us_states p"); + } + + @Benchmark + public MaterializedResult benchmarkConvexHull(Context context) + { + return context.getQueryRunner() + .execute("SELECT convex_hull_agg(p.geom) FROM us_states p"); + } + + @Test + public void verify() + throws IOException + { + Context context = new Context(); + try { + context.setUp(); + + BenchmarkGeometryAggregations benchmark = new BenchmarkGeometryAggregations(); + benchmark.benchmarkUnion(context); + benchmark.benchmarkArrayUnion(context); + benchmark.benchmarkConvexHull(context); + } + finally { + context.queryRunner.close(); + } + } + + public static void main(String[] args) + throws Exception + { + new BenchmarkGeometryAggregations().verify(); + + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkGeometryAggregations.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java index d56e032f48ff6..61bd55955100d 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestBingTileFunctions.java @@ -130,7 +130,7 @@ private void assertBingTilesAroundWithRadius( double longitude, int zoomLevel, double radius, - String...expectedQuadKeys) + String... expectedQuadKeys) { assertFunction( format("transform(bing_tiles_around(%s, %s, %s, %s), x -> bing_tile_quadkey(x))", @@ -239,7 +239,7 @@ public void testBingTilesWithRadiusBadInput() // Too many tiles assertInvalidFunction("bing_tiles_around(30.12, 60.0, 20, 100)", - "The number of input tiles is too large (more than 1M) to compute a set of covering Bing tiles."); + "The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 36699364. Radius: 100.0 km. Zoom level: 20."); } @Test @@ -454,7 +454,8 @@ public void testGeometryToBingTiles() assertInvalidFunction("geometry_to_bing_tiles(ST_Point(60, 30.12), 40)", "Zoom level must be <= 23"); // Input rectangle too large - assertInvalidFunction("geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 16)", "The number of input tiles is too large (more than 1M) to compute a set of covering Bing tiles."); + assertInvalidFunction("geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 16)", + "The number of tiles covering input rectangle exceeds the limit of 1M. Number of tiles: 370085804. Rectangle: xMin=0.00, yMin=0.00, xMax=80.00, yMax=80.00. Zoom level: 16."); assertFunction("cardinality(geometry_to_bing_tiles(ST_Envelope(ST_GeometryFromText('LINESTRING (0 0, 80 80)')), 5))", BIGINT, 104L); // Input polygon too complex diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToJoin.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialInnerJoin.java similarity index 98% rename from presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToJoin.java rename to presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialInnerJoin.java index ea8e54975bc4f..a6cd9f8389a96 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToJoin.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialInnerJoin.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.plugin.geospatial; -import com.facebook.presto.sql.planner.iterative.rule.TransformSpatialPredicates.TransformSpatialPredicateToJoin; +import com.facebook.presto.sql.planner.iterative.rule.ExtractSpatialJoins.ExtractSpatialInnerJoin; import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; import com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder; import com.facebook.presto.sql.planner.iterative.rule.test.RuleAssert; @@ -28,10 +28,10 @@ import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.values; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; -public class TestTransformSpatialPredicateToJoin +public class TestExtractSpatialInnerJoin extends BaseRuleTest { - public TestTransformSpatialPredicateToJoin() + public TestExtractSpatialInnerJoin() { super(new GeoPlugin()); } @@ -352,6 +352,6 @@ public void testPushDownAnd() private RuleAssert assertRuleApplication() { - return tester().assertThat(new TransformSpatialPredicateToJoin(tester().getMetadata())); + return tester().assertThat(new ExtractSpatialInnerJoin(tester().getMetadata(), tester().getSplitManager(), tester().getPageSourceManager())); } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToLeftJoin.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialLeftJoin.java similarity index 82% rename from presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToLeftJoin.java rename to presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialLeftJoin.java index c28e17aff5626..656e6ffdeaae9 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestTransformSpatialPredicateToLeftJoin.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestExtractSpatialLeftJoin.java @@ -14,7 +14,7 @@ package com.facebook.presto.plugin.geospatial; import com.facebook.presto.sql.planner.assertions.PlanMatchPattern; -import com.facebook.presto.sql.planner.iterative.rule.TransformSpatialPredicates.TransformSpatialPredicateToLeftJoin; +import com.facebook.presto.sql.planner.iterative.rule.ExtractSpatialJoins.ExtractSpatialLeftJoin; import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; import com.facebook.presto.sql.planner.iterative.rule.test.RuleAssert; import com.google.common.collect.ImmutableMap; @@ -28,10 +28,10 @@ import static com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder.expression; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; -public class TestTransformSpatialPredicateToLeftJoin +public class TestExtractSpatialLeftJoin extends BaseRuleTest { - public TestTransformSpatialPredicateToLeftJoin() + public TestExtractSpatialLeftJoin() { super(new GeoPlugin()); } @@ -48,15 +48,6 @@ public void testDoesNotFire() expression("ST_Contains(ST_GeometryFromText('POLYGON ...'), b)"))) .doesNotFire(); - // symbols - assertRuleApplication() - .on(p -> - p.join(LEFT, - p.values(p.symbol("a")), - p.values(p.symbol("b")), - expression("ST_Contains(a, b)"))) - .doesNotFire(); - // OR operand assertRuleApplication() .on(p -> @@ -85,6 +76,46 @@ public void testDoesNotFire() .doesNotFire(); } + @Test + public void testConvertToSpatialJoin() + { + // symbols + assertRuleApplication() + .on(p -> + p.join(LEFT, + p.values(p.symbol("a")), + p.values(p.symbol("b")), + p.expression("ST_Contains(a, b)"))) + .matches( + spatialLeftJoin("ST_Contains(a, b)", + values(ImmutableMap.of("a", 0)), + values(ImmutableMap.of("b", 0)))); + + // AND + assertRuleApplication() + .on(p -> + p.join(LEFT, + p.values(p.symbol("a"), p.symbol("name_1")), + p.values(p.symbol("b"), p.symbol("name_2")), + p.expression("name_1 != name_2 AND ST_Contains(a, b)"))) + .matches( + spatialLeftJoin("name_1 != name_2 AND ST_Contains(a, b)", + values(ImmutableMap.of("a", 0, "name_1", 1)), + values(ImmutableMap.of("b", 0, "name_2", 1)))); + + // AND + assertRuleApplication() + .on(p -> + p.join(LEFT, + p.values(p.symbol("a1"), p.symbol("a2")), + p.values(p.symbol("b1"), p.symbol("b2")), + p.expression("ST_Contains(a1, b1) AND ST_Contains(a2, b2)"))) + .matches( + spatialLeftJoin("ST_Contains(a1, b1) AND ST_Contains(a2, b2)", + values(ImmutableMap.of("a1", 0, "a2", 1)), + values(ImmutableMap.of("b1", 0, "b2", 1)))); + } + @Test public void testPushDownFirstArgument() { @@ -190,6 +221,6 @@ public void testPushDownAnd() private RuleAssert assertRuleApplication() { - return tester().assertThat(new TransformSpatialPredicateToLeftJoin(tester().getMetadata())); + return tester().assertThat(new ExtractSpatialLeftJoin(tester().getMetadata(), tester().getSplitManager(), tester().getPageSourceManager())); } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java index 907545680e9eb..5fca6d28dbe13 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestGeoFunctions.java @@ -15,18 +15,21 @@ import com.esri.core.geometry.Point; import com.esri.core.geometry.ogc.OGCPoint; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.operator.scalar.AbstractTestFunctions; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.ArrayType; -import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; -import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; +import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -35,8 +38,8 @@ import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; -import static org.testng.Assert.assertEquals; import static java.lang.String.format; +import static org.testng.Assert.assertEquals; public class TestGeoFunctions extends AbstractTestFunctions @@ -45,10 +48,64 @@ public class TestGeoFunctions protected void registerFunctions() { GeoPlugin plugin = new GeoPlugin(); - for (Type type : plugin.getTypes()) { - functionAssertions.getTypeRegistry().addType(type); + registerTypes(plugin); + registerFunctions(plugin); + } + + @Test + public void testSpatialPartitions() + { + String kdbTreeJson = makeKdbTreeJson(); + + assertSpatialPartitions(kdbTreeJson, "POINT EMPTY", null); + // points inside partitions + assertSpatialPartitions(kdbTreeJson, "POINT (0 0)", ImmutableList.of(0)); + assertSpatialPartitions(kdbTreeJson, "POINT (3 1)", ImmutableList.of(2)); + // point on the border between two partitions + assertSpatialPartitions(kdbTreeJson, "POINT (1 2.5)", ImmutableList.of(1)); + // point at the corner of three partitions + assertSpatialPartitions(kdbTreeJson, "POINT (4.5 2.5)", ImmutableList.of(4)); + // points outside + assertSpatialPartitions(kdbTreeJson, "POINT (2 6)", ImmutableList.of()); + assertSpatialPartitions(kdbTreeJson, "POINT (3 -1)", ImmutableList.of()); + assertSpatialPartitions(kdbTreeJson, "POINT (10 3)", ImmutableList.of()); + + // geometry within a partition + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", ImmutableList.of(3)); + // geometries spanning multiple partitions + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 5.5 3, 6 2)", ImmutableList.of(3, 4)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (3 2, 8 3)", ImmutableList.of(2, 3, 4, 5)); + // geometry outside + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 3 7)", ImmutableList.of()); + + // with distance + assertSpatialPartitions(kdbTreeJson, "POINT EMPTY", 1.2, null); + assertSpatialPartitions(kdbTreeJson, "POINT (1 1)", 1.2, ImmutableList.of(0)); + assertSpatialPartitions(kdbTreeJson, "POINT (1 1)", 2.3, ImmutableList.of(0, 1, 2)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", 0.2, ImmutableList.of(3)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (5 0.1, 6 2)", 1.2, ImmutableList.of(2, 3, 4)); + assertSpatialPartitions(kdbTreeJson, "MULTIPOINT (2 6, 3 7)", 1.2, ImmutableList.of()); + } + + private static String makeKdbTreeJson() + { + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (double x = 0; x < 10; x += 1) { + for (double y = 0; y < 5; y += 1) { + rectangles.add(new Rectangle(x, y, x + 1, y + 2)); + } } - functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); + return KdbTreeUtils.toJson(buildKdbTree(10, new Rectangle(0, 0, 9, 4), rectangles.build())); + } + + private void assertSpatialPartitions(String kdbTreeJson, String wkt, List expectedPartitions) + { + assertFunction(format("spatial_partitions(cast('%s' as KdbTree), ST_GeometryFromText('%s'))", kdbTreeJson, wkt), new ArrayType(INTEGER), expectedPartitions); + } + + private void assertSpatialPartitions(String kdbTreeJson, String wkt, double distance, List expectedPartitions) + { + assertFunction(format("spatial_partitions(cast('%s' as KdbTree), ST_GeometryFromText('%s'), %s)", kdbTreeJson, wkt, distance), new ArrayType(INTEGER), expectedPartitions); } @Test @@ -162,9 +219,9 @@ public void testSTConvexHull() assertConvexHull("MULTILINESTRING EMPTY", "MULTILINESTRING EMPTY"); assertConvexHull("POLYGON EMPTY", "POLYGON EMPTY"); assertConvexHull("MULTIPOLYGON EMPTY", "MULTIPOLYGON EMPTY"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION EMPTY", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POINT (1 1), POINT EMPTY)", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (1 1), GEOMETRYCOLLECTION (POINT (1 5), POINT (4 5), GEOMETRYCOLLECTION (POINT (3 4), POINT EMPTY))))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertConvexHull("GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION EMPTY"); + assertConvexHull("GEOMETRYCOLLECTION (POINT (1 1), POINT EMPTY)", "POINT (1 1)"); + assertConvexHull("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (1 1), GEOMETRYCOLLECTION (POINT (1 5), POINT (4 5), GEOMETRYCOLLECTION (POINT (3 4), POINT EMPTY))))", "POLYGON ((1 1, 4 5, 1 5, 1 1))"); // test single geometry assertConvexHull("POINT (1 1)", "POINT (1 1)"); @@ -182,15 +239,15 @@ public void testSTConvexHull() assertConvexHull("LINESTRING (20 20, 30 30)", "LINESTRING (20 20, 30 30)"); assertConvexHull("MULTILINESTRING ((0 0, 3 3), (1 1, 2 2), (2 2, 4 4), (5 5, 8 8))", "LINESTRING (0 0, 8 8)"); assertConvexHull("MULTIPOINT (0 1, 1 2, 2 3, 3 4, 4 5, 5 6)", "LINESTRING (0 1, 5 6)"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (1 1, 4 4, 2 2), POINT (10 10), POLYGON ((5 5, 7 7)), POINT (2 2), LINESTRING (6 6, 9 9), POLYGON ((1 1)))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (2 2), POINT (1 1)), POINT (3 3))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertConvexHull("GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (1 1, 4 4, 2 2), POINT (10 10), POLYGON ((5 5, 7 7)), POINT (2 2), LINESTRING (6 6, 9 9), POLYGON ((1 1)))", "LINESTRING (0 0, 10 10)"); + assertConvexHull("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (2 2), POINT (1 1)), POINT (3 3))", "LINESTRING (3 3, 1 1)"); // not all points are on the same line assertConvexHull("MULTILINESTRING ((1 1, 5 1, 6 6), (2 4, 4 0), (2 -4, 4 4), (3 -2, 4 -3))", "POLYGON ((1 1, 2 -4, 4 -3, 5 1, 6 6, 2 4, 1 1))"); assertConvexHull("MULTIPOINT (0 2, 1 0, 3 0, 4 0, 4 2, 2 2, 2 4)", "POLYGON ((0 2, 1 0, 4 0, 4 2, 2 4, 0 2))"); assertConvexHull("MULTIPOLYGON (((0 3, 2 0, 3 6), (2 1, 2 3, 5 3, 5 1), (1 7, 2 4, 4 2, 5 6, 3 8)))", "POLYGON ((0 3, 2 0, 5 1, 5 6, 3 8, 1 7, 0 3))"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POINT (2 3), LINESTRING (2 8, 7 10), POINT (8 10), POLYGON ((4 4, 4 8, 9 8, 6 6, 6 4, 8 3, 6 1)), POINT (4 2), LINESTRING (3 6, 5 5), POLYGON ((7 5, 7 6, 8 6, 8 5)))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (2 3), LINESTRING (2 8, 7 10), GEOMETRYCOLLECTION (POINT (8 10))), POLYGON ((4 4, 4 8, 9 8, 6 6, 6 4, 8 3, 6 1)), POINT (4 2), LINESTRING (3 6, 5 5), POLYGON ((7 5, 7 6, 8 6, 8 5)))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertConvexHull("GEOMETRYCOLLECTION (POINT (2 3), LINESTRING (2 8, 7 10), POINT (8 10), POLYGON ((4 4, 4 8, 9 8, 6 6, 6 4, 8 3, 6 1)), POINT (4 2), LINESTRING (3 6, 5 5), POLYGON ((7 5, 7 6, 8 6, 8 5)))", "POLYGON ((2 3, 6 1, 8 3, 9 8, 8 10, 7 10, 2 8, 2 3))"); + assertConvexHull("GEOMETRYCOLLECTION (GEOMETRYCOLLECTION (POINT (2 3), LINESTRING (2 8, 7 10), GEOMETRYCOLLECTION (POINT (8 10))), POLYGON ((4 4, 4 8, 9 8, 6 6, 6 4, 8 3, 6 1)), POINT (4 2), LINESTRING (3 6, 5 5), POLYGON ((7 5, 7 6, 8 6, 8 5)))", "POLYGON ((2 3, 6 1, 8 3, 9 8, 8 10, 7 10, 2 8, 2 3))"); // single-element multi-geometries and geometry collections assertConvexHull("MULTILINESTRING ((1 1, 5 1, 6 6))", "POLYGON ((1 1, 5 1, 6 6, 1 1))"); @@ -198,11 +255,11 @@ public void testSTConvexHull() assertConvexHull("MULTIPOINT (0 2)", "POINT (0 2)"); assertConvexHull("MULTIPOLYGON (((0 3, 2 0, 3 6)))", "POLYGON ((0 3, 2 0, 3 6, 0 3))"); assertConvexHull("MULTIPOLYGON (((0 0, 4 0, 4 4, 0 4, 2 2)))", "POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POINT (2 3))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (LINESTRING (1 1, 5 1, 6 6))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (LINESTRING (1 1, 5 1, 1 4, 5 4))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((0 3, 2 0, 3 6)))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 2 2)))", "ST_ConvexHull only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertConvexHull("GEOMETRYCOLLECTION (POINT (2 3))", "POINT (2 3)"); + assertConvexHull("GEOMETRYCOLLECTION (LINESTRING (1 1, 5 1, 6 6))", "POLYGON ((1 1, 5 1, 6 6, 1 1))"); + assertConvexHull("GEOMETRYCOLLECTION (LINESTRING (1 1, 5 1, 1 4, 5 4))", "POLYGON ((1 1, 5 1, 5 4, 1 4, 1 1))"); + assertConvexHull("GEOMETRYCOLLECTION (POLYGON ((0 3, 2 0, 3 6)))", "POLYGON ((0 3, 2 0, 3 6, 0 3))"); + assertConvexHull("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 2 2)))", "POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))"); } private void assertConvexHull(String inputWKT, String expectWKT) @@ -210,11 +267,6 @@ private void assertConvexHull(String inputWKT, String expectWKT) assertFunction(format("ST_AsText(ST_ConvexHull(ST_GeometryFromText('%s')))", inputWKT), VARCHAR, expectWKT); } - private void assertConvexHullInvalidFunction(String inputWKT, String errorMessage) - { - assertInvalidFunction(format("ST_ConvexHull(ST_GeometryFromText('%s'))", inputWKT), errorMessage); - } - @Test public void testSTCoordDim() { @@ -543,6 +595,15 @@ public void testSTDistance() assertFunction("ST_Distance(ST_GeometryFromText('MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))'), ST_GeometryFromText('LINESTRING (10 20, 20 50)'))", DOUBLE, 17.08800749063506); assertFunction("ST_Distance(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON ((4 4, 4 5, 5 5, 5 4))'))", DOUBLE, 1.4142135623730951); assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))'), ST_GeometryFromText('POLYGON ((10 100, 30 10))'))", DOUBLE, 27.892651361962706); + + assertFunction("ST_Distance(ST_GeometryFromText('POINT EMPTY'), ST_Point(150, 150))", DOUBLE, null); + assertFunction("ST_Distance(ST_Point(50, 100), ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('POINT EMPTY'), ST_GeometryFromText('POINT EMPTY'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOINT EMPTY'), ST_GeometryFromText('Point (50 100)'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('LINESTRING (50 100, 50 200)'), ST_GeometryFromText('LINESTRING EMPTY'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('MULTILINESTRING EMPTY'), ST_GeometryFromText('LINESTRING (10 20, 20 50)'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('POLYGON ((1 1, 1 3, 3 3, 3 1))'), ST_GeometryFromText('POLYGON EMPTY'))", DOUBLE, null); + assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON EMPTY'), ST_GeometryFromText('POLYGON ((10 100, 30 10))'))", DOUBLE, null); } @Test @@ -811,7 +872,8 @@ public void testSTUnion() "LINESTRING EMPTY", "MULTILINESTRING EMPTY", "POLYGON EMPTY", - "MULTIPOLYGON EMPTY"); + "MULTIPOLYGON EMPTY", + "GEOMETRYCOLLECTION EMPTY"); List simpleWkts = ImmutableList.of( "POINT (1 2)", @@ -819,12 +881,8 @@ public void testSTUnion() "LINESTRING (0 0, 2 2, 4 4)", "MULTILINESTRING ((0 0, 2 2, 4 4), (5 5, 7 7, 9 9))", "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", - "MULTIPOLYGON (((1 1, 3 1, 3 3, 1 3, 1 1)), ((2 4, 6 4, 6 6, 2 6, 2 4)))"); - - // invalid type GEOMETRYCOLLECTION - for (String simpleWkt : simpleWkts) { - assertInvalidGeometryCollectionUnion(simpleWkt); - } + "MULTIPOLYGON (((1 1, 3 1, 3 3, 1 3, 1 1)), ((2 4, 6 4, 6 6, 2 6, 2 4)))", + "GEOMETRYCOLLECTION (LINESTRING (0 5, 5 5), POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1)))"); // empty geometry for (String emptyWkt : emptyWkts) { @@ -845,19 +903,22 @@ public void testSTUnion() assertUnion("MULTILINESTRING ((0 0, 2 2, 4 4), (5 5, 7 7, 9 9))", "MULTILINESTRING ((5 5, 7 7, 9 9), (11 11, 13 13, 15 15))", "MULTILINESTRING ((0 0, 2 2, 4 4), (5 5, 7 7, 9 9), (11 11, 13 13, 15 15))"); assertUnion("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", "POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))", "POLYGON ((0 0, 1 0, 2 0, 2 1, 1 1, 0 1, 0 0))"); assertUnion("MULTIPOLYGON (((0 0, 1 0, 1 1, 0 1, 0 0)))", "MULTIPOLYGON (((1 0, 2 0, 2 1, 1 1, 1 0)))", "POLYGON ((0 0, 1 0, 2 0, 2 1, 1 1, 0 1, 0 0))"); + assertUnion("GEOMETRYCOLLECTION (POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)), POINT (1 2))", "GEOMETRYCOLLECTION (POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0)), MULTIPOINT ((1 2), (3 4)))", "GEOMETRYCOLLECTION (MULTIPOINT ((1 2), (3 4)), POLYGON ((0 0, 1 0, 2 0, 2 1, 1 1, 0 1, 0 0)))"); // within union assertUnion("MULTIPOINT ((20 20), (25 25))", "POINT (25 25)", "MULTIPOINT ((20 20), (25 25))"); assertUnion("LINESTRING (20 20, 30 30)", "POINT (25 25)", "LINESTRING (20 20, 25 25, 30 30)"); assertUnion("LINESTRING (20 20, 30 30)", "LINESTRING (25 25, 27 27)", "LINESTRING (20 20, 25 25, 27 27, 30 30)"); assertUnion("POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))", "POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))", "POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0))"); - assertUnion("MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))", "POLYGON ((2 2, 2 3, 3 3, 3 2))", "MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((2 2, 3 2, 4 2, 4 4, 2 4, 2 3, 2 2)))"); + assertUnion("MULTIPOLYGON (((0 0 , 0 2, 2 2, 2 0)), ((2 2, 2 4, 4 4, 4 2)))", "POLYGON ((2 2, 2 3, 3 3, 3 2))", "MULTIPOLYGON (((2 2, 3 2, 4 2, 4 4, 2 4, 2 3, 2 2)), ((0 0, 2 0, 2 2, 0 2, 0 0)))"); + assertUnion("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0)), MULTIPOINT ((20 20), (25 25)))", "GEOMETRYCOLLECTION (POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1)), POINT (25 25))", "GEOMETRYCOLLECTION (MULTIPOINT ((20 20), (25 25)), POLYGON ((0 0, 4 0, 4 4, 0 4, 0 0)))"); // overlap union assertUnion("LINESTRING (1 1, 3 1)", "LINESTRING (2 1, 4 1)", "LINESTRING (1 1, 2 1, 3 1, 4 1)"); assertUnion("MULTILINESTRING ((1 1, 3 1))", "MULTILINESTRING ((2 1, 4 1))", "LINESTRING (1 1, 2 1, 3 1, 4 1)"); assertUnion("POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1))", "POLYGON ((2 2, 4 2, 4 4, 2 4, 2 2))", "POLYGON ((1 1, 3 1, 3 2, 4 2, 4 4, 2 4, 2 3, 1 3, 1 1))"); assertUnion("MULTIPOLYGON (((1 1, 3 1, 3 3, 1 3, 1 1)))", "MULTIPOLYGON (((2 2, 4 2, 4 4, 2 4, 2 2)))", "POLYGON ((1 1, 3 1, 3 2, 4 2, 4 4, 2 4, 2 3, 1 3, 1 1))"); + assertUnion("GEOMETRYCOLLECTION (POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1)), LINESTRING (1 1, 3 1))", "GEOMETRYCOLLECTION (POLYGON ((2 2, 4 2, 4 4, 2 4, 2 2)), LINESTRING (2 1, 4 1))", "GEOMETRYCOLLECTION (LINESTRING (3 1, 4 1), POLYGON ((1 1, 2 1, 3 1, 3 2, 4 2, 4 4, 2 4, 2 3, 1 3, 1 1)))"); } private void assertUnion(String leftWkt, String rightWkt, String expectWkt) @@ -923,6 +984,99 @@ private void assertSTGeometryN(String wkt, int index, String expected) assertFunction("ST_ASText(ST_GeometryN(ST_GeometryFromText('" + wkt + "')," + index + "))", VARCHAR, expected); } + @Test + public void testSTLineString() + { + // General case, 2+ points + assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4)])", GEOMETRY, "LINESTRING (1 2, 3 4)"); + assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4), ST_Point(5, 6)])", GEOMETRY, "LINESTRING (1 2, 3 4, 5 6)"); + assertFunction("ST_LineString(array[ST_Point(1,2), ST_Point(3,4), ST_Point(5,6), ST_Point(7,8)])", GEOMETRY, "LINESTRING (1 2, 3 4, 5 6, 7 8)"); + + // Other ways of creating points + assertFunction("ST_LineString(array[ST_GeometryFromText('POINT (1 2)'), ST_GeometryFromText('POINT (3 4)')])", GEOMETRY, "LINESTRING (1 2, 3 4)"); + + // Duplicate consecutive points throws exception + assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), ST_Point(1, 2)])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: consecutive duplicate points at index 2"); + assertFunction("ST_LineString(array[ST_Point(1, 2), ST_Point(3, 4), ST_Point(1, 2)])", GEOMETRY, "LINESTRING (1 2, 3 4, 1 2)"); + + // Single point + assertFunction("ST_LineString(array[ST_Point(9,10)])", GEOMETRY, "LINESTRING EMPTY"); + + // Zero points + assertFunction("ST_LineString(array[])", GEOMETRY, "LINESTRING EMPTY"); + + // Only points can be passed + assertInvalidFunction("ST_LineString(array[ST_Point(7,8), ST_GeometryFromText('LINESTRING (1 2, 3 4)')])", INVALID_FUNCTION_ARGUMENT, "ST_LineString takes only an array of valid points, LineString was passed"); + + // Nulls points are invalid + assertInvalidFunction("ST_LineString(array[NULL])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: null point at index 1"); + assertInvalidFunction("ST_LineString(array[ST_Point(1,2), NULL])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: null point at index 2"); + assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), NULL, ST_Point(3, 4)])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: null point at index 2"); + assertInvalidFunction("ST_LineString(array[ST_Point(1, 2), NULL, ST_Point(3, 4), NULL])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: null point at index 2"); + + // Empty points are invalid + assertInvalidFunction("ST_LineString(array[ST_GeometryFromText('POINT EMPTY')])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: empty point at index 1"); + assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY')])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: empty point at index 2"); + assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4)])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: empty point at index 2"); + assertInvalidFunction("ST_LineString(array[ST_Point(1,2), ST_GeometryFromText('POINT EMPTY'), ST_Point(3,4), ST_GeometryFromText('POINT EMPTY')])", INVALID_FUNCTION_ARGUMENT, "Invalid input to ST_LineString: empty point at index 2"); + } + + @Test + public void testMultiPoint() + { + // General case, 2+ points + assertMultiPoint("MULTIPOINT ((1 2), (3 4))", "POINT (1 2)", "POINT (3 4)"); + assertMultiPoint("MULTIPOINT ((1 2), (3 4), (5 6))", "POINT (1 2)", "POINT (3 4)", "POINT (5 6)"); + assertMultiPoint("MULTIPOINT ((1 2), (3 4), (5 6), (7 8))", "POINT (1 2)", "POINT (3 4)", "POINT (5 6)", "POINT (7 8)"); + + // Duplicate points work + assertMultiPoint("MULTIPOINT ((1 2), (1 2))", "POINT (1 2)", "POINT (1 2)"); + assertMultiPoint("MULTIPOINT ((1 2), (3 4), (1 2))", "POINT (1 2)", "POINT (3 4)", "POINT (1 2)"); + + // Single point + assertMultiPoint("MULTIPOINT ((1 2))", "POINT (1 2)"); + + // Empty array + assertFunction("ST_MultiPoint(array[])", GEOMETRY, null); + + // Only points can be passed + assertInvalidMultiPoint("geometry is not a point: LineString at index 2", "POINT (7 8)", "LINESTRING (1 2, 3 4)"); + + // Null point raises exception + assertInvalidFunction("ST_MultiPoint(array[null])", "Invalid input to ST_MultiPoint: null at index 1"); + assertInvalidMultiPoint("null at index 3", "POINT (1 2)", "POINT (1 2)", null); + assertInvalidMultiPoint("null at index 2", "POINT (1 2)", null, "POINT (3 4)"); + assertInvalidMultiPoint("null at index 2", "POINT (1 2)", null, "POINT (3 4)", null); + + // Empty point raises exception + assertInvalidMultiPoint("empty point at index 1", "POINT EMPTY"); + assertInvalidMultiPoint("empty point at index 2", "POINT (1 2)", "POINT EMPTY"); + } + + private void assertMultiPoint(String expectedWkt, String... pointWkts) + { + assertFunction( + format( + "ST_MultiPoint(array[%s])", + Arrays.stream(pointWkts) + .map(wkt -> wkt == null ? "null" : format("ST_GeometryFromText('%s')", wkt)) + .collect(Collectors.joining(","))), + GEOMETRY, + expectedWkt); + } + + private void assertInvalidMultiPoint(String errorMessage, String... pointWkts) + { + assertInvalidFunction( + format( + "ST_MultiPoint(array[%s])", + Arrays.stream(pointWkts) + .map(wkt -> wkt == null ? "null" : format("ST_GeometryFromText('%s')", wkt)) + .collect(Collectors.joining(","))), + INVALID_FUNCTION_ARGUMENT, + format("Invalid input to ST_MultiPoint: %s", errorMessage)); + } + @Test public void testSTPointN() { @@ -970,6 +1124,7 @@ private void assertSTGeometries(String wkt, String... expected) assertFunction(String.format("transform(ST_Geometries(ST_GeometryFromText('%s')), x -> ST_ASText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.copyOf(expected)); } + @Test public void testSTInteriorRingN() { assertInvalidInteriorRingN("POINT EMPTY", 0, "POINT"); @@ -997,6 +1152,7 @@ private void assertInvalidInteriorRingN(String wkt, int index, String geometryTy assertInvalidFunction(format("ST_InteriorRingN(ST_GeometryFromText('%s'), %d)", wkt, index), format("ST_InteriorRingN only applies to POLYGON. Input type is: %s", geometryType)); } + @Test public void testSTGeometryType() { assertFunction("ST_GeometryType(ST_Point(1, 4))", VARCHAR, "ST_Point"); @@ -1008,4 +1164,44 @@ public void testSTGeometryType() assertFunction("ST_GeometryType(ST_GeometryFromText('GEOMETRYCOLLECTION(POINT(4 6),LINESTRING(4 6, 7 10))'))", VARCHAR, "ST_GeomCollection"); assertFunction("ST_GeometryType(ST_Envelope(ST_GeometryFromText('LINESTRING (1 1, 2 2)')))", VARCHAR, "ST_Polygon"); } + + @Test + public void testSTGeometryFromBinary() + { + assertFunction("ST_GeomFromBinary(null)", GEOMETRY, null); + + // empty geometries + assertGeomFromBinary("POINT EMPTY"); + assertGeomFromBinary("MULTIPOINT EMPTY"); + assertGeomFromBinary("LINESTRING EMPTY"); + assertGeomFromBinary("MULTILINESTRING EMPTY"); + assertGeomFromBinary("POLYGON EMPTY"); + assertGeomFromBinary("MULTIPOLYGON EMPTY"); + assertGeomFromBinary("GEOMETRYCOLLECTION EMPTY"); + + // valid nonempty geometries + assertGeomFromBinary("POINT (1 2)"); + assertGeomFromBinary("MULTIPOINT ((1 2), (3 4))"); + assertGeomFromBinary("LINESTRING (0 0, 1 2, 3 4)"); + assertGeomFromBinary("MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))"); + assertGeomFromBinary("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))"); + assertGeomFromBinary("POLYGON ((0 0, 3 0, 3 3, 0 3, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))"); + assertGeomFromBinary("MULTIPOLYGON (((1 1, 3 1, 3 3, 1 3, 1 1)), ((2 4, 6 4, 6 6, 2 6, 2 4)))"); + assertGeomFromBinary("GEOMETRYCOLLECTION (POINT (1 2), LINESTRING (0 0, 1 2, 3 4), POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0)))"); + + // array of geometries + assertFunction("transform(array[ST_AsBinary(ST_Point(1, 2)), ST_AsBinary(ST_Point(3, 4))], wkb -> ST_AsText(ST_GeomFromBinary(wkb)))", new ArrayType(VARCHAR), ImmutableList.of("POINT (1 2)", "POINT (3 4)")); + + // invalid geometries + assertGeomFromBinary("MULTIPOINT ((0 0), (0 1), (1 1), (0 1))"); + assertGeomFromBinary("LINESTRING (0 0, 0 1, 0 1, 1 1, 1 0, 0 0)"); + + // invalid binary + assertInvalidFunction("ST_GeomFromBinary(from_hex('deadbeef'))", "Invalid WKB"); + } + + private void assertGeomFromBinary(String wkt) + { + assertFunction(format("ST_AsText(ST_GeomFromBinary(ST_AsBinary(ST_GeometryFromText('%s'))))", wkt), VARCHAR, wkt); + } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java new file mode 100644 index 0000000000000..916e3016edd51 --- /dev/null +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestKdbTreeCasts.java @@ -0,0 +1,60 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; +import com.facebook.presto.operator.scalar.AbstractTestFunctions; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static java.lang.String.format; + +public class TestKdbTreeCasts + extends AbstractTestFunctions +{ + @BeforeClass + protected void registerFunctions() + { + GeoPlugin plugin = new GeoPlugin(); + registerTypes(plugin); + registerFunctions(plugin); + } + + @Test + public void test() + { + String kdbTreeJson = makeKdbTreeJson(); + assertFunction(format("typeof(cast('%s' AS KdbTree))", kdbTreeJson), VARCHAR, "KdbTree"); + assertFunction(format("typeof(cast('%s' AS KDBTree))", kdbTreeJson), VARCHAR, "KdbTree"); + assertFunction(format("typeof(cast('%s' AS kdbTree))", kdbTreeJson), VARCHAR, "KdbTree"); + assertFunction(format("typeof(cast('%s' AS kdbtree))", kdbTreeJson), VARCHAR, "KdbTree"); + + assertInvalidCast("typeof(cast('' AS KdbTree))", "Invalid JSON string for KDB tree"); + } + + private String makeKdbTreeJson() + { + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (double x = 0; x < 10; x += 1) { + for (double y = 0; y < 5; y += 1) { + rectangles.add(new Rectangle(x, y, x + 1, y + 2)); + } + } + return KdbTreeUtils.toJson(buildKdbTree(100, new Rectangle(0, 0, 9, 4), rectangles.build())); + } +} diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestRewriteSpatialPartitioningAggregation.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestRewriteSpatialPartitioningAggregation.java new file mode 100644 index 0000000000000..189d4ee7e7ca6 --- /dev/null +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestRewriteSpatialPartitioningAggregation.java @@ -0,0 +1,88 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.facebook.presto.sql.planner.iterative.rule.RewriteSpatialPartitioningAggregation; +import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; +import com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder; +import com.facebook.presto.sql.planner.iterative.rule.test.RuleAssert; +import com.facebook.presto.sql.planner.plan.AggregationNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.aggregation; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.expression; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.functionCall; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.project; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.values; + +public class TestRewriteSpatialPartitioningAggregation + extends BaseRuleTest +{ + public TestRewriteSpatialPartitioningAggregation() + { + super(new GeoPlugin()); + } + + @Test + public void testDoesNotFire() + { + assertRuleApplication() + .on(p -> p.aggregation(a -> + a.globalGrouping() + .step(AggregationNode.Step.FINAL) + .addAggregation(p.symbol("sp"), PlanBuilder.expression("spatial_partitioning(geometry, 10)"), ImmutableList.of(GEOMETRY)) + .source(p.values(p.symbol("geometry"))))) + .doesNotFire(); + } + + @Test + public void test() + { + assertRuleApplication() + .on(p -> p.aggregation(a -> + a.globalGrouping() + .step(AggregationNode.Step.FINAL) + .addAggregation(p.symbol("sp"), PlanBuilder.expression("spatial_partitioning(geometry)"), ImmutableList.of(GEOMETRY)) + .source(p.values(p.symbol("geometry"))))) + .matches( + aggregation( + ImmutableMap.of("sp", functionCall("spatial_partitioning", ImmutableList.of("envelope", "partition_count"))), + project( + ImmutableMap.of("partition_count", expression("100"), + "envelope", expression("ST_Envelope(geometry)")), + values("geometry")))); + + assertRuleApplication() + .on(p -> p.aggregation(a -> + a.globalGrouping() + .step(AggregationNode.Step.FINAL) + .addAggregation(p.symbol("sp"), PlanBuilder.expression("spatial_partitioning(ST_Envelope(geometry))"), ImmutableList.of(GEOMETRY)) + .source(p.values(p.symbol("geometry"))))) + .matches( + aggregation( + ImmutableMap.of("sp", functionCall("spatial_partitioning", ImmutableList.of("envelope", "partition_count"))), + project( + ImmutableMap.of("partition_count", expression("100"), + "envelope", expression("ST_Envelope(geometry)")), + values("geometry")))); + } + + private RuleAssert assertRuleApplication() + { + return tester().assertThat(new RewriteSpatialPartitioningAggregation(tester().getMetadata())); + } +} diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinOperator.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinOperator.java index 9e45698839eaf..309c8160aacb3 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinOperator.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinOperator.java @@ -14,6 +14,9 @@ package com.facebook.presto.plugin.geospatial; import com.facebook.presto.RowPagesBuilder; +import com.facebook.presto.geospatial.KdbTree; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.operator.Driver; import com.facebook.presto.operator.DriverContext; import com.facebook.presto.operator.InternalJoinFilterFunction; @@ -31,8 +34,8 @@ import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.gen.JoinFilterFunctionCompiler; -import com.facebook.presto.sql.planner.plan.JoinNode.Type; import com.facebook.presto.sql.planner.plan.PlanNodeId; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type; import com.facebook.presto.testing.MaterializedResult; import com.facebook.presto.testing.TestingTaskContext; import com.google.common.collect.ImmutableList; @@ -54,14 +57,17 @@ import static com.facebook.presto.RowPagesBuilder.rowPagesBuilder; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.geospatial.KdbTree.Node.newInternal; +import static com.facebook.presto.geospatial.KdbTree.Node.newLeaf; import static com.facebook.presto.operator.OperatorAssertion.assertOperatorEquals; import static com.facebook.presto.plugin.geospatial.GeoFunctions.stGeometryFromText; import static com.facebook.presto.plugin.geospatial.GeoFunctions.stPoint; import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; +import static com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type.INNER; +import static com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type.LEFT; import static com.facebook.presto.testing.MaterializedResult.resultBuilder; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.util.concurrent.Executors.newScheduledThreadPool; @@ -74,6 +80,13 @@ @Test(singleThreaded = true) public class TestSpatialJoinOperator { + private static final String KDB_TREE_JSON = KdbTreeUtils.toJson( + new KdbTree(newInternal(new Rectangle(-2, -2, 15, 15), + newInternal(new Rectangle(-2, -2, 6, 15), + newLeaf(new Rectangle(-2, -2, 6, 1), 1), + newLeaf(new Rectangle(-2, 1, 6, 15), 2)), + newLeaf(new Rectangle(6, -2, 15, 15), 0)))); + // 2 intersecting polygons: A and B private static final Slice POLYGON_A = stGeometryFromText(Slices.utf8Slice("POLYGON ((0 0, -0.5 2.5, 0 5, 2.5 5.5, 5 5, 5.5 2.5, 5 0, 2.5 -0.5, 0 0))")); private static final Slice POLYGON_B = stGeometryFromText(Slices.utf8Slice("POLYGON ((4 4, 3.5 7, 4 10, 7 10.5, 10 10, 10.5 7, 10 4, 7 3.5, 4 4))")); @@ -175,9 +188,9 @@ public void testSpatialLeftJoin() private void assertSpatialJoin(TaskContext taskContext, Type joinType, RowPagesBuilder buildPages, RowPagesBuilder probePages, MaterializedResult expected) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); PagesSpatialIndexFactory pagesSpatialIndexFactory = buildIndex(driverContext, (build, probe, r) -> build.contains(probe), Optional.empty(), Optional.empty(), buildPages); - OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), joinType, probePages.getTypes(), Ints.asList(1), 0, pagesSpatialIndexFactory); + OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), joinType, probePages.getTypes(), Ints.asList(1), 0, Optional.empty(), pagesSpatialIndexFactory); assertOperatorEquals(joinOperatorFactory, driverContext, probePages.build(), expected); } @@ -251,7 +264,7 @@ public void testYield() // verify we will yield #match times totally TaskContext taskContext = createTaskContext(); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // force a yield for every match AtomicInteger filterFunctionCalls = new AtomicInteger(); @@ -284,7 +297,7 @@ public void testYield() } List probeInput = probePages.build(); - OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, probePages.getTypes(), Ints.asList(1), 0, pagesSpatialIndexFactory); + OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, probePages.getTypes(), Ints.asList(1), 0, Optional.empty(), pagesSpatialIndexFactory); Operator operator = joinOperatorFactory.createOperator(driverContext); assertTrue(operator.needsInput()); @@ -311,7 +324,7 @@ public void testYield() public void testDistanceQuery() { TaskContext taskContext = createTaskContext(); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); RowPagesBuilder buildPages = rowPagesBuilder(ImmutableList.of(GEOMETRY, VARCHAR, DOUBLE)) .row(stPoint(0, 0), "0_0", 1.5) @@ -331,7 +344,7 @@ public void testDistanceQuery() .row(stPoint(3, 1), "3_1") .pageBreak() .row(stPoint(10, 1), "10_1"); - OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, probePages.getTypes(), Ints.asList(1), 0, pagesSpatialIndexFactory); + OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, probePages.getTypes(), Ints.asList(1), 0, Optional.empty(), pagesSpatialIndexFactory); MaterializedResult expected = resultBuilder(taskContext.getSession(), ImmutableList.of(VARCHAR, VARCHAR)) .row("0_1", "0_0") @@ -345,10 +358,74 @@ public void testDistanceQuery() assertOperatorEquals(joinOperatorFactory, driverContext, probePages.build(), expected); } + @Test + public void testDistributedSpatialJoin() + { + TaskContext taskContext = createTaskContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, true).addDriverContext(); + + RowPagesBuilder buildPages = rowPagesBuilder(ImmutableList.of(GEOMETRY, VARCHAR, INTEGER)) + .row(POLYGON_A, "A", 1) + .row(POLYGON_A, "A", 2) + .row(null, "null", null) + .pageBreak() + .row(POLYGON_B, "B", 0) + .row(POLYGON_B, "B", 2); + + RowPagesBuilder probePages = rowPagesBuilder(ImmutableList.of(GEOMETRY, VARCHAR, INTEGER)) + .row(POINT_X, "x", 2) + .row(null, "null", null) + .row(POINT_Y, "y", 2) + .pageBreak() + .row(POINT_Z, "z", 0); + + MaterializedResult expected = resultBuilder(taskContext.getSession(), ImmutableList.of(VARCHAR, VARCHAR)) + .row("x", "A") + .row("y", "A") + .row("y", "B") + .row("z", "B") + .build(); + + PagesSpatialIndexFactory pagesSpatialIndexFactory = buildIndex(driverContext, (build, probe, r) -> build.contains(probe), Optional.empty(), Optional.of(2), Optional.of(KDB_TREE_JSON), Optional.empty(), buildPages); + OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, probePages.getTypes(), Ints.asList(1), 0, Optional.of(2), pagesSpatialIndexFactory); + assertOperatorEquals(joinOperatorFactory, driverContext, probePages.build(), expected); + } + + @Test + public void testDistributedSpatialSelfJoin() + { + TaskContext taskContext = createTaskContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, true).addDriverContext(); + + RowPagesBuilder pages = rowPagesBuilder(ImmutableList.of(GEOMETRY, VARCHAR, INTEGER)) + .row(POLYGON_A, "A", 1) + .row(POLYGON_A, "A", 2) + .row(null, "null", null) + .pageBreak() + .row(POLYGON_B, "B", 0) + .row(POLYGON_B, "B", 2); + + MaterializedResult expected = resultBuilder(taskContext.getSession(), ImmutableList.of(VARCHAR, VARCHAR)) + .row("A", "A") + .row("A", "B") + .row("B", "A") + .row("B", "B") + .build(); + + PagesSpatialIndexFactory pagesSpatialIndexFactory = buildIndex(driverContext, (build, probe, r) -> build.intersects(probe), Optional.empty(), Optional.of(2), Optional.of(KDB_TREE_JSON), Optional.empty(), pages); + OperatorFactory joinOperatorFactory = new SpatialJoinOperatorFactory(2, new PlanNodeId("test"), INNER, pages.getTypes(), Ints.asList(1), 0, Optional.of(2), pagesSpatialIndexFactory); + assertOperatorEquals(joinOperatorFactory, driverContext, pages.build(), expected); + } + private PagesSpatialIndexFactory buildIndex(DriverContext driverContext, SpatialPredicate spatialRelationshipTest, Optional radiusChannel, Optional filterFunction, RowPagesBuilder buildPages) + { + return buildIndex(driverContext, spatialRelationshipTest, radiusChannel, Optional.empty(), Optional.empty(), filterFunction, buildPages); + } + + private PagesSpatialIndexFactory buildIndex(DriverContext driverContext, SpatialPredicate spatialRelationshipTest, Optional radiusChannel, Optional partitionChannel, Optional kdbTreeJson, Optional filterFunction, RowPagesBuilder buildPages) { Optional filterFunctionFactory = filterFunction - .map(function -> (session, addresses, channels) -> new StandardJoinFilterFunction(function, addresses, channels)); + .map(function -> (session, addresses, pages) -> new StandardJoinFilterFunction(function, addresses, pages)); ValuesOperator.ValuesOperatorFactory valuesOperatorFactory = new ValuesOperator.ValuesOperatorFactory(0, new PlanNodeId("test"), buildPages.build()); SpatialIndexBuilderOperatorFactory buildOperatorFactory = new SpatialIndexBuilderOperatorFactory( @@ -358,7 +435,9 @@ private PagesSpatialIndexFactory buildIndex(DriverContext driverContext, Spatial Ints.asList(1), 0, radiusChannel, + partitionChannel, spatialRelationshipTest, + kdbTreeJson, filterFunctionFactory, 10_000, new TestingFactory(false)); diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinPlanning.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinPlanning.java index 70ff8d3cf886f..9f78fe075ec53 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinPlanning.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoinPlanning.java @@ -13,32 +13,53 @@ */ package com.facebook.presto.plugin.geospatial; +import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.geospatial.KdbTree; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; +import com.facebook.presto.plugin.memory.MemoryConnectorFactory; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.sql.planner.LogicalPlanner; import com.facebook.presto.sql.planner.assertions.BasePlanTest; +import com.facebook.presto.sql.planner.plan.ExchangeNode; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.testing.LocalQueryRunner; +import com.facebook.presto.tpch.TpchConnectorFactory; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.testng.annotations.Test; import java.util.Optional; +import static com.facebook.presto.SystemSessionProperties.SPATIAL_PARTITIONING_TABLE_NAME; +import static com.facebook.presto.geospatial.KdbTree.Node.newLeaf; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_SPATIAL_PARTITIONING; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.anyTree; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.equiJoinClause; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.exchange; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.expression; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.filter; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.join; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.project; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.spatialJoin; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.spatialLeftJoin; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.tableScan; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.unnest; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.values; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.google.common.base.Strings.nullToEmpty; +import static java.lang.String.format; import static java.util.Collections.emptyList; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; public class TestSpatialJoinPlanning extends BasePlanTest { private static final String POINTS_SQL = "(VALUES (2.1e0, 2.1e0, 'x')) AS a (lng, lat, name)"; private static final String POLYGONS_SQL = "(VALUES ('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'a')) AS b (wkt, name)"; + private static final String KDB_TREE_JSON = KdbTreeUtils.toJson(new KdbTree(newLeaf(new Rectangle(0, 0, 10, 10), 0))); public TestSpatialJoinPlanning() { @@ -47,14 +68,21 @@ public TestSpatialJoinPlanning() private static LocalQueryRunner createQueryRunner() { - LocalQueryRunner queryRunner = new LocalQueryRunner(testSessionBuilder().build()); + LocalQueryRunner queryRunner = new LocalQueryRunner(testSessionBuilder() + .setCatalog("memory") + .setSchema("default") + .build()); queryRunner.installPlugin(new GeoPlugin()); + queryRunner.createCatalog("tpch", new TpchConnectorFactory(1), ImmutableMap.of()); + queryRunner.createCatalog("memory", new MemoryConnectorFactory(), ImmutableMap.of()); + queryRunner.execute(format("CREATE TABLE kdb_tree AS SELECT '%s' AS v", KDB_TREE_JSON)); return queryRunner; } @Test public void testSpatialJoinContains() { + // broadcast assertPlan("SELECT b.name, a.name " + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", @@ -63,7 +91,7 @@ public void testSpatialJoinContains() project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), anyTree(values(ImmutableMap.of("lng", 0, "lat", 1)))), anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), anyTree(values(ImmutableMap.of("wkt", 0)))))))); - // Verify that projections generated by the TransformSpatialPredicateToJoin rule + // Verify that projections generated by the ExtractSpatialJoins rule // get merged with other projections under the join assertPlan("SELECT * " + "FROM (SELECT length(name), * FROM " + POINTS_SQL + "), (SELECT length(name), * FROM " + POLYGONS_SQL + ") " + @@ -74,11 +102,135 @@ public void testSpatialJoinContains() anyTree(values(ImmutableMap.of("lng", 0, "lat", 1, "name", 2)))), anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))"), "length_2", expression("length(name_2)")), anyTree(values(ImmutableMap.of("wkt", 0, "name_2", 1)))))))); + + // distributed + assertDistributedPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + withSpatialPartitioning("kdb_tree"), + anyTree( + spatialJoin("st_contains(st_geometryfromtext, st_point)", Optional.of(KDB_TREE_JSON), + anyTree(unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_point)", KDB_TREE_JSON))), + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), anyTree(values(ImmutableMap.of("lng", 0, "lat", 1))))))), + anyTree(unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_geometryfromtext)", KDB_TREE_JSON))), + project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), anyTree(values(ImmutableMap.of("wkt", 0)))))))))); + } + + @Test + public void testSpatialJoinWithin() + { + // broadcast + assertPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Within(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + anyTree( + spatialJoin("st_within(st_geometryfromtext, st_point)", + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), anyTree(values(ImmutableMap.of("lng", 0, "lat", 1)))), + anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), anyTree(values(ImmutableMap.of("wkt", 0)))))))); + + // Verify that projections generated by the ExtractSpatialJoins rule + // get merged with other projections under the join + assertPlan("SELECT * " + + "FROM (SELECT length(name), * FROM " + POINTS_SQL + "), (SELECT length(name), * FROM " + POLYGONS_SQL + ") " + + "WHERE ST_Within(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + anyTree( + spatialJoin("st_within(st_geometryfromtext, st_point)", + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)"), "length", expression("length(name)")), + anyTree(values(ImmutableMap.of("lng", 0, "lat", 1, "name", 2)))), + anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))"), "length_2", expression("length(name_2)")), + anyTree(values(ImmutableMap.of("wkt", 0, "name_2", 1)))))))); + + // distributed + assertDistributedPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Within(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + withSpatialPartitioning("kdb_tree"), + anyTree( + spatialJoin("st_within(st_geometryfromtext, st_point)", Optional.of(KDB_TREE_JSON), + anyTree(unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_point)", KDB_TREE_JSON))), + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), anyTree(values(ImmutableMap.of("lng", 0, "lat", 1))))))), + anyTree(unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_geometryfromtext)", KDB_TREE_JSON))), + project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), anyTree(values(ImmutableMap.of("wkt", 0)))))))))); + } + + @Test + public void testInvalidKdbTree() + { + // table doesn't exist + assertInvalidSpatialPartitioning( + withSpatialPartitioning("non_existent_table"), + "SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + "Table not found: memory.default.non_existent_table"); + + // empty table + getQueryRunner().execute("CREATE TABLE empty_table AS SELECT 'a' AS v WHERE false"); + + assertInvalidSpatialPartitioning( + withSpatialPartitioning("empty_table"), + "SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + "Expected exactly one row for table memory.default.empty_table, but got none"); + + // invalid JSON + getQueryRunner().execute("CREATE TABLE invalid_kdb_tree AS SELECT 'invalid-json' AS v"); + + assertInvalidSpatialPartitioning( + withSpatialPartitioning("invalid_kdb_tree"), + "SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + "Invalid JSON string for KDB tree: .*"); + + // more than one row + getQueryRunner().execute(format("CREATE TABLE too_many_rows AS SELECT * FROM (VALUES '%s', '%s') AS t(v)", KDB_TREE_JSON, KDB_TREE_JSON)); + + assertInvalidSpatialPartitioning( + withSpatialPartitioning("too_many_rows"), + "SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + "Expected exactly one row for table memory.default.too_many_rows, but found 2 rows"); + + // more than one column + getQueryRunner().execute("CREATE TABLE too_many_columns AS SELECT '%s' as c1, 100 as c2"); + + assertInvalidSpatialPartitioning( + withSpatialPartitioning("too_many_columns"), + "SELECT b.name, a.name " + + "FROM " + POINTS_SQL + ", " + POLYGONS_SQL + " " + + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat))", + "Expected single column for table memory.default.too_many_columns, but found 2 columns"); + } + + private void assertInvalidSpatialPartitioning(Session session, String sql, String expectedMessageRegExp) + { + LocalQueryRunner queryRunner = getQueryRunner(); + try { + queryRunner.inTransaction(session, transactionSession -> { + queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, false, WarningCollector.NOOP); + return null; + }); + fail(format("Expected query to fail: %s", sql)); + } + catch (PrestoException ex) { + assertEquals(ex.getErrorCode(), INVALID_SPATIAL_PARTITIONING.toErrorCode()); + if (!nullToEmpty(ex.getMessage()).matches(expectedMessageRegExp)) { + fail(format("Expected exception message '%s' to match '%s' for query: %s", ex.getMessage(), expectedMessageRegExp, sql), ex); + } + } } @Test public void testSpatialJoinIntersects() { + // broadcast assertPlan("SELECT b.name, a.name " + "FROM (VALUES ('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'a')) AS a (wkt, name), (VALUES ('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'a')) AS b (wkt, name) " + "WHERE ST_Intersects(ST_GeometryFromText(a.wkt), ST_GeometryFromText(b.wkt))", @@ -86,11 +238,26 @@ public void testSpatialJoinIntersects() spatialJoin("st_intersects(geometry_a, geometry_b)", project(ImmutableMap.of("geometry_a", expression("ST_GeometryFromText(cast(wkt_a as varchar))")), anyTree(values(ImmutableMap.of("wkt_a", 0)))), anyTree(project(ImmutableMap.of("geometry_b", expression("ST_GeometryFromText(cast(wkt_b as varchar))")), anyTree(values(ImmutableMap.of("wkt_b", 0)))))))); + + // distributed + assertDistributedPlan("SELECT b.name, a.name " + + "FROM (VALUES ('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'a')) AS a (wkt, name), (VALUES ('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))', 'a')) AS b (wkt, name) " + + "WHERE ST_Intersects(ST_GeometryFromText(a.wkt), ST_GeometryFromText(b.wkt))", + withSpatialPartitioning("default.kdb_tree"), + anyTree( + spatialJoin("st_intersects(geometry_a, geometry_b)", Optional.of(KDB_TREE_JSON), + anyTree(unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), geometry_a)", KDB_TREE_JSON))), + project(ImmutableMap.of("geometry_a", expression("ST_GeometryFromText(cast(wkt_a as varchar))")), anyTree(values(ImmutableMap.of("wkt_a", 0))))))), + anyTree( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), geometry_b)", KDB_TREE_JSON))), + project(ImmutableMap.of("geometry_b", expression("ST_GeometryFromText(cast(wkt_b as varchar))")), anyTree(values(ImmutableMap.of("wkt_b", 0))))))))); } @Test public void testDistanceQuery() { + // broadcast assertPlan("SELECT b.name, a.name " + "FROM (VALUES (2.1, 2.1, 'x')) AS a (lng, lat, name), (VALUES (2.1, 2.1, 'x')) AS b (lng, lat, name) " + "WHERE ST_Distance(ST_Point(a.lng, a.lat), ST_Point(b.lng, b.lat)) <= 3.1", @@ -106,6 +273,24 @@ public void testDistanceQuery() spatialJoin("st_distance(st_point_a, st_point_b) <= radius", project(ImmutableMap.of("st_point_a", expression("ST_Point(cast(a_lng as double), cast(a_lat as double))")), anyTree(values(ImmutableMap.of("a_lng", 0, "a_lat", 1)))), anyTree(project(ImmutableMap.of("st_point_b", expression("ST_Point(cast(b_lng as double), cast(b_lat as double))"), "radius", expression("3e2 / (111.321e3 * cos(radians(cast(b_lat as double))))")), anyTree(values(ImmutableMap.of("b_lng", 0, "b_lat", 1)))))))); + + // distributed + assertDistributedPlan("SELECT b.name, a.name " + + "FROM (VALUES (2.1, 2.1, 'x')) AS a (lng, lat, name), (VALUES (2.1, 2.1, 'x')) AS b (lng, lat, name) " + + "WHERE ST_Distance(ST_Point(a.lng, a.lat), ST_Point(b.lng, b.lat)) <= 3.1", + withSpatialPartitioning("memory.default.kdb_tree"), + anyTree( + spatialJoin("st_distance(st_point_a, st_point_b) <= radius", Optional.of(KDB_TREE_JSON), + anyTree( + unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_point_a)", KDB_TREE_JSON))), + project(ImmutableMap.of("st_point_a", expression("ST_Point(cast(a_lng as double), cast(a_lat as double))")), + anyTree(values(ImmutableMap.of("a_lng", 0, "a_lat", 1))))))), + anyTree( + unnest( + project(ImmutableMap.of("partitions", expression(format("spatial_partitions(cast('%s' as kdbtree), st_point_b, 3.1e0)", KDB_TREE_JSON)), "radius", expression("3.1e0")), + project(ImmutableMap.of("st_point_b", expression("ST_Point(cast(b_lng as double), cast(b_lat as double))")), + anyTree(values(ImmutableMap.of("b_lng", 0, "b_lat", 1)))))))))); } @Test @@ -161,7 +346,7 @@ public void testIntersectsWithEquiClause() } @Test - public void testLeftJoin() + public void testSpatialLeftJoins() { assertPlan("SELECT b.name, a.name " + "FROM " + POINTS_SQL + " LEFT JOIN " + POLYGONS_SQL + " " + @@ -170,5 +355,91 @@ public void testLeftJoin() spatialLeftJoin("st_contains(st_geometryfromtext, st_point)", project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), anyTree(values(ImmutableMap.of("lng", 0, "lat", 1)))), anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), anyTree(values(ImmutableMap.of("wkt", 0)))))))); + + // deterministic extra join predicate + assertPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + " LEFT JOIN " + POLYGONS_SQL + " " + + "ON ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat)) AND a.name <> b.name", + anyTree( + spatialLeftJoin("st_contains(st_geometryfromtext, st_point) AND name_a <> name_b", + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), + anyTree(values(ImmutableMap.of("lng", 0, "lat", 1, "name_a", 2)))), + anyTree( + project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), + anyTree(values(ImmutableMap.of("wkt", 0, "name_b", 1)))))))); + + // non-deterministic extra join predicate + assertPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + " LEFT JOIN " + POLYGONS_SQL + " " + + "ON ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat)) AND rand() < 0.5", + anyTree( + spatialLeftJoin("st_contains(st_geometryfromtext, st_point) AND rand() < 5e-1", + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), + anyTree(values(ImmutableMap.of("lng", 0, "lat", 1)))), + anyTree( + project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), + anyTree(values(ImmutableMap.of("wkt", 0)))))))); + + // filter over join + assertPlan("SELECT b.name, a.name " + + "FROM " + POINTS_SQL + " LEFT JOIN " + POLYGONS_SQL + " " + + " ON ST_Contains(ST_GeometryFromText(wkt), ST_Point(lng, lat)) " + + "WHERE concat(a.name, b.name) is null", + anyTree( + filter("concat(cast(name_a as varchar), cast(name_b as varchar)) is null", + spatialLeftJoin("st_contains(st_geometryfromtext, st_point)", + project(ImmutableMap.of("st_point", expression("ST_Point(lng, lat)")), + anyTree(values(ImmutableMap.of("lng", 0, "lat", 1, "name_a", 2)))), + anyTree(project(ImmutableMap.of("st_geometryfromtext", expression("ST_GeometryFromText(cast(wkt as varchar))")), + anyTree(values(ImmutableMap.of("wkt", 0, "name_b", 1))))))))); + } + + @Test + public void testDistributedSpatialJoinOverUnion() + { + // union on the left side + assertDistributedPlan("SELECT a.name, b.name " + + "FROM (SELECT name FROM tpch.tiny.region UNION ALL SELECT name FROM tpch.tiny.nation) a, tpch.tiny.customer b " + + "WHERE ST_Contains(ST_GeometryFromText(a.name), ST_GeometryFromText(b.name))", + withSpatialPartitioning("kdb_tree"), + anyTree( + spatialJoin("st_contains(g1, g3)", Optional.of(KDB_TREE_JSON), + anyTree(unnest(exchange(ExchangeNode.Scope.REMOTE, ExchangeNode.Type.REPARTITION, + project(ImmutableMap.of("p1", expression(format("spatial_partitions(cast('%s' as kdbtree), g1)", KDB_TREE_JSON))), + project(ImmutableMap.of("g1", expression("ST_GeometryFromText(cast(name_a1 as varchar))")), + tableScan("region", ImmutableMap.of("name_a1", "name")))), + project(ImmutableMap.of("p2", expression(format("spatial_partitions(cast('%s' as kdbtree), g2)", KDB_TREE_JSON))), + project(ImmutableMap.of("g2", expression("ST_GeometryFromText(cast(name_a2 as varchar))")), + tableScan("nation", ImmutableMap.of("name_a2", "name"))))))), + anyTree(unnest( + project(ImmutableMap.of("p3", expression(format("spatial_partitions(cast('%s' as kdbtree), g3)", KDB_TREE_JSON))), + project(ImmutableMap.of("g3", expression("ST_GeometryFromText(cast(name_b as varchar))")), + tableScan("customer", ImmutableMap.of("name_b", "name"))))))))); + + // union on the right side + assertDistributedPlan("SELECT a.name, b.name " + + "FROM tpch.tiny.customer a, (SELECT name FROM tpch.tiny.region UNION ALL SELECT name FROM tpch.tiny.nation) b " + + "WHERE ST_Contains(ST_GeometryFromText(a.name), ST_GeometryFromText(b.name))", + withSpatialPartitioning("kdb_tree"), + anyTree( + spatialJoin("st_contains(g1, g2)", Optional.of(KDB_TREE_JSON), + anyTree(unnest( + project(ImmutableMap.of("p1", expression(format("spatial_partitions(cast('%s' as kdbtree), g1)", KDB_TREE_JSON))), + project(ImmutableMap.of("g1", expression("ST_GeometryFromText(cast(name_a as varchar))")), + tableScan("customer", ImmutableMap.of("name_a", "name")))))), + anyTree(unnest(exchange(ExchangeNode.Scope.REMOTE, ExchangeNode.Type.REPARTITION, + project(ImmutableMap.of("p2", expression(format("spatial_partitions(cast('%s' as kdbtree), g2)", KDB_TREE_JSON))), + project(ImmutableMap.of("g2", expression("ST_GeometryFromText(cast(name_b1 as varchar))")), + tableScan("region", ImmutableMap.of("name_b1", "name")))), + project(ImmutableMap.of("p3", expression(format("spatial_partitions(cast('%s' as kdbtree), g3)", KDB_TREE_JSON))), + project(ImmutableMap.of("g3", expression("ST_GeometryFromText(cast(name_b2 as varchar))")), + tableScan("nation", ImmutableMap.of("name_b2", "name")))))))))); + } + + private Session withSpatialPartitioning(String tableName) + { + return Session.builder(this.getQueryRunner().getDefaultSession()) + .setSystemProperty(SPATIAL_PARTITIONING_TABLE_NAME, tableName) + .build(); } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoins.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoins.java index 7091c47d402b0..4f940d03854aa 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoins.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialJoins.java @@ -13,10 +13,25 @@ */ package com.facebook.presto.plugin.geospatial; +import com.facebook.presto.Session; +import com.facebook.presto.hive.HdfsConfiguration; +import com.facebook.presto.hive.HdfsConfigurationUpdater; +import com.facebook.presto.hive.HdfsEnvironment; +import com.facebook.presto.hive.HiveClientConfig; +import com.facebook.presto.hive.HiveHdfsConfiguration; +import com.facebook.presto.hive.HivePlugin; +import com.facebook.presto.hive.authentication.NoHdfsAuthentication; +import com.facebook.presto.hive.metastore.Database; +import com.facebook.presto.hive.metastore.PrincipalType; +import com.facebook.presto.hive.metastore.file.FileHiveMetastore; import com.facebook.presto.tests.AbstractTestQueryFramework; import com.facebook.presto.tests.DistributedQueryRunner; import org.testng.annotations.Test; +import java.io.File; +import java.util.Optional; + +import static com.facebook.presto.SystemSessionProperties.SPATIAL_PARTITIONING_TABLE_NAME; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static java.lang.String.format; @@ -52,42 +67,80 @@ public TestSpatialJoins() private static DistributedQueryRunner createQueryRunner() throws Exception { - DistributedQueryRunner queryRunner = new DistributedQueryRunner(testSessionBuilder().build(), 4); + DistributedQueryRunner queryRunner = new DistributedQueryRunner(testSessionBuilder() + .setSource(TestSpatialJoins.class.getSimpleName()) + .setCatalog("hive") + .setSchema("default") + .build(), 4); queryRunner.installPlugin(new GeoPlugin()); + + File baseDir = queryRunner.getCoordinator().getBaseDataDir().resolve("hive_data").toFile(); + + HiveClientConfig hiveClientConfig = new HiveClientConfig(); + HdfsConfiguration hdfsConfiguration = new HiveHdfsConfiguration(new HdfsConfigurationUpdater(hiveClientConfig)); + HdfsEnvironment hdfsEnvironment = new HdfsEnvironment(hdfsConfiguration, hiveClientConfig, new NoHdfsAuthentication()); + + FileHiveMetastore metastore = new FileHiveMetastore(hdfsEnvironment, baseDir.toURI().toString(), "test"); + metastore.createDatabase(Database.builder() + .setDatabaseName("default") + .setOwnerName("public") + .setOwnerType(PrincipalType.ROLE) + .build()); + queryRunner.installPlugin(new HivePlugin("hive", Optional.of(metastore))); + + queryRunner.createCatalog("hive", "hive"); return queryRunner; } @Test public void testBroadcastSpatialJoinContains() + { + testSpatialJoinContains(getSession()); + } + + @Test + public void testDistributedSpatialJoinContains() + { + assertUpdate(format("CREATE TABLE contains_partitioning AS " + + "SELECT spatial_partitioning(ST_GeometryFromText(wkt)) as v " + + "FROM (%s) as a (wkt, name, id)", POLYGONS_SQL), 1); + + Session session = Session.builder(getSession()) + .setSystemProperty(SPATIAL_PARTITIONING_TABLE_NAME, "contains_partitioning") + .build(); + testSpatialJoinContains(session); + } + + private void testSpatialJoinContains(Session session) { // Test ST_Contains(build, probe) - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POINTS_SQL + ") AS a (latitude, longitude, name, id), (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(longitude, latitude))", "SELECT * FROM (VALUES ('a', 'x'), ('b', 'y'), ('c', 'y'), ('d', 'z'))"); - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POINTS_SQL + ") AS a (latitude, longitude, name, id) JOIN (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "ON ST_Contains(ST_GeometryFromText(wkt), ST_Point(longitude, latitude))", "SELECT * FROM (VALUES ('a', 'x'), ('b', 'y'), ('c', 'y'), ('d', 'z'))"); - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id), (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "WHERE ST_Contains(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", "SELECT * FROM (VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('c', 'b'))"); - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id) JOIN (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "ON ST_Contains(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", "SELECT * FROM (VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('c', 'b'))"); // Test ST_Contains(probe, build) - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POLYGONS_SQL + ") AS b (wkt, name, id), (" + POINTS_SQL + ") AS a (latitude, longitude, name, id) " + "WHERE ST_Contains(ST_GeometryFromText(wkt), ST_Point(longitude, latitude))", "SELECT * FROM (VALUES ('a', 'x'), ('b', 'y'), ('c', 'y'), ('d', 'z'))"); - assertQuery("SELECT b.name, a.name " + + assertQuery(session, "SELECT b.name, a.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id), (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "WHERE ST_Contains(ST_GeometryFromText(a.wkt), ST_GeometryFromText(b.wkt))", "SELECT * FROM (VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), ('b', 'c'))"); @@ -146,22 +199,40 @@ public void testBroadcastSpatialJoinContainsWithEmptyProbeSide() @Test public void testBroadcastSpatialJoinIntersects() + { + testSpatialJoinIntersects(getSession()); + } + + @Test + public void tesDistributedSpatialJoinIntersects() + { + assertUpdate(format("CREATE TABLE intersects_partitioning AS " + + "SELECT spatial_partitioning(ST_GeometryFromText(wkt)) as v " + + "FROM (%s) as a (wkt, name, id)", POLYGONS_SQL), 1); + + Session session = Session.builder(getSession()) + .setSystemProperty(SPATIAL_PARTITIONING_TABLE_NAME, "intersects_partitioning") + .build(); + testSpatialJoinIntersects(session); + } + + private void testSpatialJoinIntersects(Session session) { // Test ST_Intersects(build, probe) - assertQuery("SELECT a.name, b.name " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id), (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "WHERE ST_Intersects(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", "SELECT * FROM VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), " + "('a', 'c'), ('c', 'a'), ('c', 'b'), ('b', 'c')"); - assertQuery("SELECT a.name, b.name " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id) JOIN (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "ON ST_Intersects(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", "SELECT * FROM VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), " + "('a', 'c'), ('c', 'a'), ('c', 'b'), ('b', 'c')"); // Test ST_Intersects(probe, build) - assertQuery("SELECT a.name, b.name " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id), (" + POLYGONS_SQL + ") AS b (wkt, name, id) " + "WHERE ST_Intersects(ST_GeometryFromText(a.wkt), ST_GeometryFromText(b.wkt))", "SELECT * FROM VALUES ('a', 'a'), ('b', 'b'), ('c', 'c'), ('d', 'd'), " + @@ -192,24 +263,40 @@ public void testBroadcastSpatialJoinIntersectsWithExtraConditions() @Test public void testBroadcastDistanceQuery() + { + testDistanceQuery(getSession()); + } + + @Test + public void testDistributedDistanceQuery() + { + assertUpdate(format("CREATE TABLE distance_partitioning AS SELECT spatial_partitioning(ST_Point(x, y)) as v " + + "FROM (VALUES (0, 0, '0_0'), (1, 0, '1_0'), (3, 0, '3_0'), (10, 0, '10_0')) as a (x, y, name)"), 1); + + Session session = Session.builder(getSession()) + .setSystemProperty(SPATIAL_PARTITIONING_TABLE_NAME, "distance_partitioning") + .build(); + testDistanceQuery(session); + } + + private void testDistanceQuery(Session session) { // ST_Distance(probe, build) - assertQuery("SELECT a.name, b.name " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (VALUES (0, 0, '0_0'), (1, 0, '1_0'), (3, 0, '3_0'), (10, 0, '10_0')) as a (x, y, name), " + - "(VALUES (0, 1, '0_1'), (1, 1, '1_1'), (3, 1, '3_1'), (10, 1, '10_1')) as b (x, y, name) " + + "(VALUES (0, 1, '0_1'), (1, 1, '1_1'), (3, 1, '3_1'), (10, 1, '10_1')) as b (x, y, name) " + "WHERE ST_Distance(ST_Point(a.x, a.y), ST_Point(b.x, b.y)) <= 1.5", - "SELECT * FROM VALUES ('0_0', '0_1'), ('0_0', '1_1'), ('1_0', '0_1'), ('1_0', '1_1'), ('3_0', '3_1'), ('10_0', '10_1')"); + "SELECT * FROM VALUES ('0_0', '0_1'), ('0_0', '1_1'), ('1_0', '0_1'), ('1_0', '1_1'), ('3_0', '3_1'), ('10_0', '10_1')"); // ST_Distance(build, probe) - assertQuery("SELECT a.name, b.name " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (VALUES (0, 0, '0_0'), (1, 0, '1_0'), (3, 0, '3_0'), (10, 0, '10_0')) as a (x, y, name), " + "(VALUES (0, 1, '0_1'), (1, 1, '1_1'), (3, 1, '3_1'), (10, 1, '10_1')) as b (x, y, name) " + "WHERE ST_Distance(ST_Point(b.x, b.y), ST_Point(a.x, a.y)) <= 1.5", "SELECT * FROM VALUES ('0_0', '0_1'), ('0_0', '1_1'), ('1_0', '0_1'), ('1_0', '1_1'), ('3_0', '3_1'), ('10_0', '10_1')"); // radius expression - assertQuery("SELECT a.name, b.name " + - "FROM (VALUES (0, 0, '0_0'), (1, 0, '1_0'), (3, 0, '3_0'), (10, 0, '10_0')) as a (x, y, name), " + + assertQuery(session, "SELECT a.name, b.name " + "FROM (VALUES (0, 0, '0_0'), (1, 0, '1_0'), (3, 0, '3_0'), (10, 0, '10_0')) as a (x, y, name), " + "(VALUES (0, 1, '0_1'), (1, 1, '1_1'), (3, 1, '3_1'), (10, 1, '10_1')) as b (x, y, name) " + "WHERE ST_Distance(ST_Point(a.x, a.y), ST_Point(b.x, b.y)) <= sqrt(b.x * b.x + b.y * b.y)", "SELECT * FROM VALUES ('0_0', '0_1'), ('0_0', '1_1'), ('0_0', '3_1'), ('0_0', '10_1'), ('1_0', '1_1'), ('1_0', '3_1'), ('1_0', '10_1'), ('3_0', '3_1'), ('3_0', '10_1'), ('10_0', '10_1')"); @@ -227,8 +314,8 @@ public void testBroadcastSpatialLeftJoin() // Empty build side assertQuery("SELECT a.name, b.name " + - "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id) LEFT JOIN (VALUES (null, 'null', 1)) AS b (wkt, name, id) " + - "ON ST_Intersects(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", + "FROM (" + POLYGONS_SQL + ") AS a (wkt, name, id) LEFT JOIN (VALUES (null, 'null', 1)) AS b (wkt, name, id) " + + "ON ST_Intersects(ST_GeometryFromText(b.wkt), ST_GeometryFromText(a.wkt))", "SELECT * FROM VALUES ('a', null), ('b', null), ('c', null), ('d', null), ('empty', null), ('null', null)"); // Extra condition diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java new file mode 100644 index 0000000000000..2a9761662851f --- /dev/null +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/TestSpatialPartitioningInternalAggregation.java @@ -0,0 +1,162 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.geospatial; + +import com.esri.core.geometry.Envelope; +import com.esri.core.geometry.Point; +import com.esri.core.geometry.ogc.OGCGeometry; +import com.esri.core.geometry.ogc.OGCPoint; +import com.facebook.presto.block.BlockAssertions; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; +import com.facebook.presto.metadata.FunctionKind; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.aggregation.Accumulator; +import com.facebook.presto.operator.aggregation.AccumulatorFactory; +import com.facebook.presto.operator.aggregation.GroupedAccumulator; +import com.facebook.presto.operator.aggregation.InternalAggregationFunction; +import com.facebook.presto.operator.scalar.AbstractTestFunctions; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.TypeSignature; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.geospatial.KdbTree.buildKdbTree; +import static com.facebook.presto.geospatial.serde.GeometrySerde.serialize; +import static com.facebook.presto.operator.aggregation.AggregationTestUtils.createGroupByIdBlock; +import static com.facebook.presto.operator.aggregation.AggregationTestUtils.getFinalBlock; +import static com.facebook.presto.operator.aggregation.AggregationTestUtils.getGroupValue; +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY_TYPE_NAME; +import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; +import static com.google.common.math.DoubleMath.roundToInt; +import static java.math.RoundingMode.CEILING; +import static org.testng.Assert.assertEquals; + +public class TestSpatialPartitioningInternalAggregation + extends AbstractTestFunctions +{ + @BeforeClass + public void setup() + { + GeoPlugin plugin = new GeoPlugin(); + registerTypes(plugin); + registerFunctions(plugin); + } + + @DataProvider(name = "partitionCount") + public static Object[][] partitionCountProvider() + { + return new Object[][] {{100}, {10}}; + } + + @Test(dataProvider = "partitionCount") + public void test(int partitionCount) + { + InternalAggregationFunction function = getFunction(); + List geometries = makeGeometries(); + Block geometryBlock = makeGeometryBlock(geometries); + + Block partitionCountBlock = BlockAssertions.createRLEBlock(partitionCount, geometries.size()); + + Rectangle expectedExtent = new Rectangle(-10, -10, Math.nextUp(10.0), Math.nextUp(10.0)); + String expectedValue = getSpatialPartitioning(expectedExtent, geometries, partitionCount); + + AccumulatorFactory accumulatorFactory = function.bind(Ints.asList(0, 1, 2), Optional.empty()); + Page page = new Page(geometryBlock, partitionCountBlock); + + Accumulator accumulator = accumulatorFactory.createAccumulator(); + accumulator.addInput(page); + String aggregation = (String) BlockAssertions.getOnlyValue(accumulator.getFinalType(), getFinalBlock(accumulator)); + assertEquals(aggregation, expectedValue); + + GroupedAccumulator groupedAggregation = accumulatorFactory.createGroupedAccumulator(); + groupedAggregation.addInput(createGroupByIdBlock(0, page.getPositionCount()), page); + String groupValue = (String) getGroupValue(groupedAggregation, 0); + assertEquals(groupValue, expectedValue); + } + + private InternalAggregationFunction getFunction() + { + return functionAssertions + .getMetadata() + .getFunctionRegistry() + .getAggregateFunctionImplementation( + new Signature("spatial_partitioning", + FunctionKind.AGGREGATE, + TypeSignature.parseTypeSignature(VARCHAR), + TypeSignature.parseTypeSignature(GEOMETRY_TYPE_NAME), + TypeSignature.parseTypeSignature(INTEGER))); + } + + private List makeGeometries() + { + ImmutableList.Builder geometries = ImmutableList.builder(); + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + geometries.add(new OGCPoint(new Point(-10 + i, -10 + j), null)); + } + } + + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + geometries.add(new OGCPoint(new Point(-10 + 2 * i, 2 * j), null)); + } + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + geometries.add(new OGCPoint(new Point(2.5 * i, -10 + 2.5 * j), null)); + } + } + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + geometries.add(new OGCPoint(new Point(5 * i, 5 * j), null)); + } + } + + return geometries.build(); + } + + private Block makeGeometryBlock(List geometries) + { + BlockBuilder builder = GEOMETRY.createBlockBuilder(null, geometries.size()); + for (OGCGeometry geometry : geometries) { + GEOMETRY.writeSlice(builder, serialize(geometry)); + } + return builder.build(); + } + + private String getSpatialPartitioning(Rectangle extent, List geometries, int partitionCount) + { + ImmutableList.Builder rectangles = ImmutableList.builder(); + for (OGCGeometry geometry : geometries) { + Envelope envelope = new Envelope(); + geometry.getEsriGeometry().queryEnvelope(envelope); + rectangles.add(new Rectangle(envelope.getXMin(), envelope.getYMin(), envelope.getXMax(), envelope.getYMax())); + } + + return KdbTreeUtils.toJson(buildKdbTree(roundToInt(geometries.size() * 1.0 / partitionCount, CEILING), extent, rectangles.build())); + } +} diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java index 515135d397cf9..80a303d74512d 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/AbstractTestGeoAggregationFunctions.java @@ -24,7 +24,6 @@ import com.facebook.presto.plugin.geospatial.GeometryType; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.type.Type; -import com.facebook.presto.spi.type.TypeSignature; import io.airlift.slice.Slice; import org.testng.annotations.BeforeClass; @@ -36,6 +35,7 @@ import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; public abstract class AbstractTestGeoAggregationFunctions extends AbstractTestFunctions @@ -53,10 +53,11 @@ public void registerFunctions() function = functionAssertions .getMetadata() .getFunctionRegistry() - .getAggregateFunctionImplementation(new Signature(getFunctionName(), - FunctionKind.AGGREGATE, - TypeSignature.parseTypeSignature(GeometryType.GEOMETRY_TYPE_NAME), - TypeSignature.parseTypeSignature(GeometryType.GEOMETRY_TYPE_NAME))); + .getAggregateFunctionImplementation(new Signature( + getFunctionName(), + FunctionKind.AGGREGATE, + parseTypeSignature(GeometryType.GEOMETRY_TYPE_NAME), + parseTypeSignature(GeometryType.GEOMETRY_TYPE_NAME))); } protected void assertAggregatedGeometries(String testDescription, String expectedWkt, String... wkts) diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryConvexHullGeoAggregation.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryConvexHullGeoAggregation.java index 9821bcf754f82..76b4b30a7aec4 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryConvexHullGeoAggregation.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryConvexHullGeoAggregation.java @@ -22,8 +22,6 @@ import java.nio.file.Paths; import java.util.List; -import static java.lang.String.format; - public class TestGeometryConvexHullGeoAggregation extends AbstractTestGeoAggregationFunctions { @@ -354,6 +352,53 @@ public Object[][] points1000() }; } + @DataProvider(name = "geometryCollection") + public Object[][] geometryCollection() + { + return new Object[][] { + { + "identity", + "POLYGON ((0 0, 5 0, 5 2, 0 2, 0 0))", + new String[] {"MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((3 0, 5 0, 5 2, 3 2, 3 0)))", + "GEOMETRYCOLLECTION ( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((3 0, 5 0, 5 2, 3 2, 3 0)))", + "GEOMETRYCOLLECTION ( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((3 0, 5 0, 5 2, 3 2, 3 0)))"}, + }, + { + "empty with non-empty", + "POLYGON ((0 0, 5 0, 5 2, 0 2, 0 0))", + new String[] {"GEOMETRYCOLLECTION EMPTY", + "GEOMETRYCOLLECTION ( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((3 0, 5 0, 5 2, 3 2, 3 0)))"}, + }, + { + "overlapping geometry collections", + "POLYGON ((1 1, 5 1, 4 2, 2 2, 1 1))", + new String[] {"GEOMETRYCOLLECTION ( POLYGON ((2 2, 3 1, 1 1, 2 2)), POLYGON ((3 2, 4 1, 2 1, 3 2)) )", + "GEOMETRYCOLLECTION ( POLYGON ((4 2, 5 1, 3 1, 4 2)) )"}, + }, + { + "disjoint geometry collection of polygons", + "POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))", + new String[] {"GEOMETRYCOLLECTION ( POLYGON (( 0 0, 0 2, 2 2, 2 0, 0 0 )), POLYGON (( 0 3, 0 5, 2 5, 2 3, 0 3 )) )", + "GEOMETRYCOLLECTION ( POLYGON (( 3 0, 3 2, 5 2, 5 0, 3 0 )), POLYGON (( 3 3, 3 5, 5 5, 5 3, 3 3 )) )"}, + }, + { + "square with a line crossed", + "POLYGON ((0 2, 1 1, 3 1, 5 2, 3 3, 1 3, 0 2))", + new String[] {"POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1))", "LINESTRING (0 2, 5 2)"}, + }, + { + "square with adjacent line", + "POLYGON ((0 5, 1 1, 3 1, 5 5, 0 5))", + new String[] {"POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1))", "LINESTRING (0 5, 5 5)"}, + }, + { + "square with adjacent point", + "POLYGON ((5 2, 3 3, 1 3, 1 1, 3 1, 5 2))", + new String[] {"POLYGON ((1 1, 3 1, 3 3, 1 3, 1 1))", "POINT (5 2)"}, + }, + }; + } + @Test(dataProvider = "point") public void testPoint(String testDescription, String expectedWkt, String... wkts) { @@ -396,13 +441,10 @@ public void test1000Points(String testDescription, String expectedWkt, String... assertAggregatedGeometries(testDescription, expectedWkt, wkt); } - @Test - public void testGeometrycollection() + @Test(dataProvider = "geometryCollection") + public void testGeometryCollection(String testDescription, String expectedWkt, String... wkt) { - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 2 2)))", "convex_hull_agg only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 2 2)), POINT(1 1))", "convex_hull_agg only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((0 0, 4 0, 4 4, 0 4, 2 2)), LINESTRING(1 1, 3 4))", "convex_hull_agg only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); - assertConvexHullInvalidFunction("GEOMETRYCOLLECTION (POLYGON ((2 2, 3 1, 1 1, 2 2)), POLYGON ((3 2, 4 1, 2 1, 3 2)))", "convex_hull_agg only applies to POINT or MULTI_POINT or LINE_STRING or MULTI_LINE_STRING or POLYGON or MULTI_POLYGON. Input type is: GEOMETRY_COLLECTION"); + assertAggregatedGeometries(testDescription, expectedWkt, wkt); } @Override @@ -410,9 +452,4 @@ protected String getFunctionName() { return "convex_hull_agg"; } - - private void assertConvexHullInvalidFunction(String inputWKT, String errorMessage) - { - assertInvalidFunction(format("convex_hull_agg(ST_GeometryFromText('%s'))", inputWKT), errorMessage); - } } diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateFactory.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateFactory.java index 30d702126029f..b8a6756b43b90 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateFactory.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateFactory.java @@ -31,6 +31,7 @@ public void init() { factory = new GeometryStateFactory(); } + @Test public void testCreateSingleStateEmpty() { diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateSerializer.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateSerializer.java index 2332ca99ed852..f296412586d46 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateSerializer.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryStateSerializer.java @@ -48,6 +48,7 @@ public void testSerializeDeserialize() assertEquals(state.getGeometry().asText(), "POINT (1 2)"); } + @Test public void testSerializeDeserializeGrouped() { diff --git a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java index 9c352a6ef8e51..f396eba5988b5 100644 --- a/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java +++ b/presto-geospatial/src/test/java/com/facebook/presto/plugin/geospatial/aggregation/TestGeometryUnionGeoAggregation.java @@ -13,12 +13,23 @@ */ package com.facebook.presto.plugin.geospatial.aggregation; +import com.google.common.base.Joiner; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.Arrays; +import java.util.List; + +import static com.facebook.presto.plugin.geospatial.GeometryType.GEOMETRY; +import static java.lang.String.format; +import static java.util.Collections.reverse; +import static java.util.stream.Collectors.toList; + public class TestGeometryUnionGeoAggregation extends AbstractTestGeoAggregationFunctions { + private static final Joiner COMMA_JOINER = Joiner.on(","); + @DataProvider(name = "point") public Object[][] point() { @@ -33,11 +44,6 @@ public Object[][] point() null, new String[] {}, }, - { - "null before value yields the value", - "POINT (1 2)", - new String[] {null, "POINT (1 2)"}, - }, { "empty with non-empty", "POINT (1 2)", @@ -213,13 +219,14 @@ public Object[][] multipolygon() return new Object[][] { { "identity", - "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))", - new String[] {"MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))", "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))", "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))"}, + "MULTIPOLYGON (((4 2, 3 1, 5 1, 4 2)), ((14 12, 13 11, 15 11, 14 12)))", + new String[] {"MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)), ((14 12, 15 11, 13 11, 14 12)))", + "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)), ((14 12, 15 11, 13 11, 14 12)))"}, }, { "empty with non-empty", - "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))", - new String[] {"MULTIPOLYGON EMPTY", "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))"}, + "MULTIPOLYGON (((4 2, 3 1, 5 1, 4 2)), ((14 12, 13 11, 15 11, 14 12)))", + new String[] {"MULTIPOLYGON EMPTY", "MULTIPOLYGON (((4 2, 5 1, 3 1, 4 2)), ((14 12, 15 11, 13 11, 14 12)))"}, }, { "disjoint", @@ -229,7 +236,7 @@ public Object[][] multipolygon() }, { "overlapping multipolygons are simplified", - "POLYGON ((2 2, 1 1, 2 1, 3 1, 4 1, 5 1, 4 2, 3.5 1.5, 3 2, 2.5 1.5, 2 2))", + "POLYGON ((1 1, 2 1, 3 1, 4 1, 5 1, 4 2, 3.5 1.5, 3 2, 2.5 1.5, 2 2, 1 1))", new String[] {"MULTIPOLYGON (((2 2, 3 1, 1 1, 2 2)), ((3 2, 4 1, 2 1, 3 2)))", "MULTIPOLYGON(((4 2, 5 1, 3 1, 4 2)))"}, }, { @@ -252,6 +259,12 @@ public Object[][] geometryCollection() "GEOMETRYCOLLECTION ( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((3 0, 5 0, 5 2, 3 2, 3 0)))", "GEOMETRYCOLLECTION ( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((3 0, 5 0, 5 2, 3 2, 3 0)))"}, }, + { + "empty collection with empty collection", + "GEOMETRYCOLLECTION EMPTY", + new String[] {"GEOMETRYCOLLECTION EMPTY", + "GEOMETRYCOLLECTION EMPTY"}, + }, { "empty with non-empty", "MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((3 0, 5 0, 5 2, 3 2, 3 0)))", @@ -292,42 +305,49 @@ public Object[][] geometryCollection() public void testPoint(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "linestring") public void testLineString(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "polygon") public void testPolygon(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "multipoint") public void testMultiPoint(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "multilinestring") public void testMultiLineString(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "multipolygon") public void testMultiPolygon(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Test(dataProvider = "geometrycollection") public void testGeometryCollection(String testDescription, String expectedWkt, String... wkts) { assertAggregatedGeometries(testDescription, expectedWkt, wkts); + assertArrayAggAndGeometryUnion(testDescription, expectedWkt, wkts); } @Override @@ -335,4 +355,16 @@ protected String getFunctionName() { return "geometry_union_agg"; } + + private void assertArrayAggAndGeometryUnion(String testDescription, String expectedWkt, String[] wkts) + { + List wktList = Arrays.stream(wkts).map(wkt -> format("ST_GeometryFromText('%s')", wkt)).collect(toList()); + String wktArray = format("ARRAY[%s]", COMMA_JOINER.join(wktList)); + // ST_Union(ARRAY[ST_GeometryFromText('...'), ...]) + assertFunction(format("geometry_union(%s)", wktArray), GEOMETRY, expectedWkt); + + reverse(wktList); + wktArray = format("ARRAY[%s]", COMMA_JOINER.join(wktList)); + assertFunction(format("geometry_union(%s)", wktArray), GEOMETRY, expectedWkt); + } } diff --git a/presto-hive-hadoop2/bin/common.sh b/presto-hive-hadoop2/bin/common.sh index 776ae9b429911..56f4f0d42516c 100755 --- a/presto-hive-hadoop2/bin/common.sh +++ b/presto-hive-hadoop2/bin/common.sh @@ -26,7 +26,7 @@ function hadoop_master_container(){ function hadoop_master_ip() { HADOOP_MASTER_CONTAINER=$(hadoop_master_container) - docker inspect --format '{{ .NetworkSettings.IPAddress }}' $HADOOP_MASTER_CONTAINER + docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $HADOOP_MASTER_CONTAINER } function check_hadoop() { diff --git a/presto-hive-hadoop2/bin/run_hive_s3_tests.sh b/presto-hive-hadoop2/bin/run_hive_s3_tests.sh index 37809e51567ab..47a23830e9ef2 100755 --- a/presto-hive-hadoop2/bin/run_hive_s3_tests.sh +++ b/presto-hive-hadoop2/bin/run_hive_s3_tests.sh @@ -19,6 +19,9 @@ exec_in_hadoop_master_container sed -i \ table_path="s3a://${S3_BUCKET}/presto_test_external_fs/" exec_in_hadoop_master_container hadoop fs -mkdir -p "${table_path}" exec_in_hadoop_master_container hadoop fs -copyFromLocal -f /tmp/test1.csv "${table_path}" +exec_in_hadoop_master_container hadoop fs -copyFromLocal -f /tmp/test1.csv.gz "${table_path}" +exec_in_hadoop_master_container hadoop fs -copyFromLocal -f /tmp/test1.csv.lz4 "${table_path}" +exec_in_hadoop_master_container hadoop fs -copyFromLocal -f /tmp/test1.csv.bz2 "${table_path}" exec_in_hadoop_master_container /usr/bin/hive -e "CREATE EXTERNAL TABLE presto_test_external_fs(t_bigint bigint) LOCATION '${table_path}'" stop_unnecessary_hadoop_services diff --git a/presto-hive-hadoop2/bin/run_hive_tests.sh b/presto-hive-hadoop2/bin/run_hive_tests.sh index c84ea427b34cb..2b7a4b6eb667b 100755 --- a/presto-hive-hadoop2/bin/run_hive_tests.sh +++ b/presto-hive-hadoop2/bin/run_hive_tests.sh @@ -13,6 +13,8 @@ exec_in_hadoop_master_container su hive -s /usr/bin/hive -f /files/sql/create-te stop_unnecessary_hadoop_services +HADOOP_MASTER_IP=$(hadoop_master_ip) + # run product tests pushd ${PROJECT_ROOT} set +e @@ -25,11 +27,7 @@ set +e -Dhive.hadoop2.metastoreHost=hadoop-master \ -Dhive.hadoop2.timeZone=Asia/Kathmandu \ -Dhive.metastore.thrift.client.socks-proxy=${PROXY}:1180 \ - -Dsun.net.spi.nameservice.provider.1=default \ - -Dsun.net.spi.nameservice.provider.2=dns,dnsjava \ - -Ddns.server=${PROXY} \ - -Ddns.port=55353 \ - -Ddns.search=. + -Dhadoop-master-ip=${HADOOP_MASTER_IP} EXIT_CODE=$? set -e popd diff --git a/presto-hive-hadoop2/conf/docker-compose.yml b/presto-hive-hadoop2/conf/docker-compose.yml index e13735913d303..aa75a1727ecfa 100644 --- a/presto-hive-hadoop2/conf/docker-compose.yml +++ b/presto-hive-hadoop2/conf/docker-compose.yml @@ -18,10 +18,6 @@ services: - ./files/words:/usr/share/dict/words:ro - ./files/core-site.xml.s3-template:/etc/hadoop/conf/core-site.xml.s3-template:ro - ./files/test1.csv:/tmp/test1.csv:ro - dnsmasq: - hostname: dnsmasq - image: 'prestodb/dns:5' - cap_add: - - NET_ADMIN - ports: - - '55353:53/udp' + - ./files/test1.csv.gz:/tmp/test1.csv.gz:ro + - ./files/test1.csv.lz4:/tmp/test1.csv.lz4:ro + - ./files/test1.csv.bz2:/tmp/test1.csv.bz2:ro diff --git a/presto-hive-hadoop2/conf/files/test1.csv.bz2 b/presto-hive-hadoop2/conf/files/test1.csv.bz2 new file mode 100644 index 0000000000000..583c3dec15413 Binary files /dev/null and b/presto-hive-hadoop2/conf/files/test1.csv.bz2 differ diff --git a/presto-hive-hadoop2/conf/files/test1.csv.gz b/presto-hive-hadoop2/conf/files/test1.csv.gz new file mode 100644 index 0000000000000..2343915f27bcb Binary files /dev/null and b/presto-hive-hadoop2/conf/files/test1.csv.gz differ diff --git a/presto-hive-hadoop2/conf/files/test1.csv.lz4 b/presto-hive-hadoop2/conf/files/test1.csv.lz4 new file mode 100644 index 0000000000000..bfc2c59cbf5a8 Binary files /dev/null and b/presto-hive-hadoop2/conf/files/test1.csv.lz4 differ diff --git a/presto-hive-hadoop2/pom.xml b/presto-hive-hadoop2/pom.xml index 1f202f935e800..53a4ff7de769a 100644 --- a/presto-hive-hadoop2/pom.xml +++ b/presto-hive-hadoop2/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-hive-hadoop2 @@ -95,12 +95,6 @@ presto-main test - - - dnsjava - dnsjava - test - @@ -117,7 +111,8 @@ **/TestHiveClient.java - **/TestHiveClientS3.java + **/TestHiveFileSystemS3.java + **/TestHiveFileSystemS3SelectPushdown.java @@ -149,7 +144,8 @@ maven-surefire-plugin - **/TestHiveClientS3.java + **/TestHiveFileSystemS3.java + **/TestHiveFileSystemS3SelectPushdown.java diff --git a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClientS3.java b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystemS3.java similarity index 77% rename from presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClientS3.java rename to presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystemS3.java index 1b2254afdc5eb..2a7bbf2a7b24b 100644 --- a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClientS3.java +++ b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystemS3.java @@ -17,44 +17,33 @@ import com.facebook.presto.hive.s3.PrestoS3ConfigurationUpdater; import com.facebook.presto.hive.s3.S3ConfigurationUpdater; import org.apache.hadoop.fs.Path; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Parameters; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; import static org.testng.util.Strings.isNullOrEmpty; -public class TestHiveClientS3 +public abstract class AbstractTestHiveFileSystemS3 extends AbstractTestHiveFileSystem { private String awsAccessKey; private String awsSecretKey; private String writableBucket; - @Parameters({ - "hive.hadoop2.metastoreHost", - "hive.hadoop2.metastorePort", - "hive.hadoop2.databaseName", - "hive.hadoop2.s3.awsAccessKey", - "hive.hadoop2.s3.awsSecretKey", - "hive.hadoop2.s3.writableBucket"}) - @BeforeClass - public void setup(String host, int port, String databaseName, String awsAccessKey, String awsSecretKey, String writableBucket) + protected void setup(String host, int port, String databaseName, String awsAccessKey, String awsSecretKey, String writableBucket, boolean s3SelectPushdownEnabled) { checkArgument(!isNullOrEmpty(host), "Expected non empty host"); checkArgument(!isNullOrEmpty(databaseName), "Expected non empty databaseName"); checkArgument(!isNullOrEmpty(awsAccessKey), "Expected non empty awsAccessKey"); checkArgument(!isNullOrEmpty(awsSecretKey), "Expected non empty awsSecretKey"); checkArgument(!isNullOrEmpty(writableBucket), "Expected non empty writableBucket"); - this.awsAccessKey = awsAccessKey; this.awsSecretKey = awsSecretKey; this.writableBucket = writableBucket; - super.setup(host, port, databaseName, this::createHdfsConfiguration); + super.setup(host, port, databaseName, this::createHdfsConfiguration, s3SelectPushdownEnabled); } - private HdfsConfiguration createHdfsConfiguration(HiveClientConfig config) + HdfsConfiguration createHdfsConfiguration(HiveClientConfig config) { S3ConfigurationUpdater s3Config = new PrestoS3ConfigurationUpdater(new HiveS3Config() .setS3AwsAccessKey(awsAccessKey) diff --git a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClient.java b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClient.java index 115ce862d7d97..2cb97ef1ead1d 100644 --- a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClient.java +++ b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveClient.java @@ -13,22 +13,26 @@ */ package com.facebook.presto.hive; +import org.apache.hadoop.net.NetUtils; import org.testng.annotations.BeforeClass; -import org.testng.annotations.Optional; import org.testng.annotations.Parameters; -import org.xbill.DNS.Lookup; + +import java.net.UnknownHostException; public class TestHiveClient extends AbstractTestHiveClient { - @Parameters({"hive.hadoop2.metastoreHost", "hive.hadoop2.metastorePort", "hive.hadoop2.databaseName", "hive.hadoop2.timeZone", "dns.port"}) + @Parameters({"hive.hadoop2.metastoreHost", "hive.hadoop2.metastorePort", "hive.hadoop2.databaseName", "hive.hadoop2.timeZone"}) @BeforeClass - public void initialize(String host, int port, String databaseName, String timeZone, @Optional Integer dnsPort) + public void initialize(String host, int port, String databaseName, String timeZone) + throws UnknownHostException { - // Connecting to Hive cluster (e.g. dockerized) over SOCKS proxy requires using a DNS server, which resolves cluster IP addresses (e.g. hadoop-master). - // The DNS server might listen on custom port. Therefore we need to configure the name resolver to use that port. - if (dnsPort != null) { - Lookup.getDefaultResolver().setPort(dnsPort); + String hadoopMasterIp = System.getProperty("hadoop-master-ip"); + if (hadoopMasterIp != null) { + // Even though Hadoop is accessed by proxy, Hadoop still tries to resolve hadoop-master + // (e.g: in: NameNodeProxies.createProxy) + // This adds a static resolution for hadoop-master to docker container internal ip + NetUtils.addStaticResolution("hadoop-master", hadoopMasterIp); } setup(host, port, databaseName, timeZone); diff --git a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3.java b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3.java new file mode 100644 index 0000000000000..0c805cefa456c --- /dev/null +++ b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3.java @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Parameters; + +public class TestHiveFileSystemS3 + extends AbstractTestHiveFileSystemS3 +{ + @Parameters({ + "hive.hadoop2.metastoreHost", + "hive.hadoop2.metastorePort", + "hive.hadoop2.databaseName", + "hive.hadoop2.s3.awsAccessKey", + "hive.hadoop2.s3.awsSecretKey", + "hive.hadoop2.s3.writableBucket"}) + @BeforeClass + public void setup(String host, int port, String databaseName, String awsAccessKey, String awsSecretKey, String writableBucket) + { + super.setup(host, port, databaseName, awsAccessKey, awsSecretKey, writableBucket, false); + } +} diff --git a/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3SelectPushdown.java b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3SelectPushdown.java new file mode 100644 index 0000000000000..e713ce3929228 --- /dev/null +++ b/presto-hive-hadoop2/src/test/java/com/facebook/presto/hive/TestHiveFileSystemS3SelectPushdown.java @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Parameters; + +public class TestHiveFileSystemS3SelectPushdown + extends AbstractTestHiveFileSystemS3 +{ + @Parameters({ + "hive.hadoop2.metastoreHost", + "hive.hadoop2.metastorePort", + "hive.hadoop2.databaseName", + "hive.hadoop2.s3.awsAccessKey", + "hive.hadoop2.s3.awsSecretKey", + "hive.hadoop2.s3.writableBucket"}) + @BeforeClass + public void setup(String host, int port, String databaseName, String awsAccessKey, String awsSecretKey, String writableBucket) + { + super.setup(host, port, databaseName, awsAccessKey, awsSecretKey, writableBucket, true); + } +} diff --git a/presto-hive/pom.xml b/presto-hive/pom.xml index 3f5ae715bc236..72741b0632d00 100644 --- a/presto-hive/pom.xml +++ b/presto-hive/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-hive @@ -32,6 +32,11 @@ presto-orc + + com.facebook.presto + presto-parquet + + com.facebook.presto presto-memory-context @@ -139,6 +144,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + com.amazonaws aws-java-sdk-core @@ -204,12 +215,25 @@ + + com.facebook.presto + presto-spi + test-jar + test + + com.facebook.presto presto-main test + + com.facebook.presto + presto-parser + test + + com.facebook.presto presto-tests diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/BackgroundHiveSplitLoader.java b/presto-hive/src/main/java/com/facebook/presto/hive/BackgroundHiveSplitLoader.java index a40ee3aaa3349..f3593e54811ec 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/BackgroundHiveSplitLoader.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/BackgroundHiveSplitLoader.java @@ -75,6 +75,7 @@ import static com.facebook.presto.hive.HiveUtil.getFooterCount; import static com.facebook.presto.hive.HiveUtil.getHeaderCount; import static com.facebook.presto.hive.HiveUtil.getInputFormat; +import static com.facebook.presto.hive.S3SelectPushdown.shouldEnablePushdownForTable; import static com.facebook.presto.hive.metastore.MetastoreUtil.getHiveSchema; import static com.facebook.presto.hive.util.ConfigurationUtils.toJobConf; import static com.facebook.presto.hive.util.HiveFileIterator.NestedDirectoryPolicy.FAIL; @@ -280,6 +281,7 @@ private ListenableFuture loadPartition(HivePartitionMetadata partition) Configuration configuration = hdfsEnvironment.getConfiguration(hdfsContext, path); InputFormat inputFormat = getInputFormat(configuration, schema, false); FileSystem fs = hdfsEnvironment.getFileSystem(hdfsContext, path); + boolean s3SelectPushdownEnabled = shouldEnablePushdownForTable(session, table, path.toString(), partition.getPartition()); if (inputFormat instanceof SymlinkTextInputFormat) { if (tableBucketInfo.isPresent()) { @@ -300,7 +302,17 @@ private ListenableFuture loadPartition(HivePartitionMetadata partition) FileInputFormat.setInputPaths(targetJob, targetPath); InputSplit[] targetSplits = targetInputFormat.getSplits(targetJob, 0); - InternalHiveSplitFactory splitFactory = new InternalHiveSplitFactory(targetFilesystem, partitionName, inputFormat, schema, partitionKeys, effectivePredicate, partition.getColumnCoercions(), Optional.empty(), isForceLocalScheduling(session)); + InternalHiveSplitFactory splitFactory = new InternalHiveSplitFactory( + targetFilesystem, + partitionName, + inputFormat, + schema, + partitionKeys, + effectivePredicate, + partition.getColumnCoercions(), + Optional.empty(), + isForceLocalScheduling(session), + s3SelectPushdownEnabled); lastResult = addSplitsToSource(targetSplits, splitFactory); if (stopped) { return COMPLETED_FUTURE; @@ -314,13 +326,13 @@ private ListenableFuture loadPartition(HivePartitionMetadata partition) if (partition.getPartition().isPresent()) { Optional partitionBucketProperty = partition.getPartition().get().getStorage().getBucketProperty(); if (tableBucketInfo.isPresent() && partitionBucketProperty.isPresent()) { - int tableBucketCount = tableBucketInfo.get().getBucketCount(); + int readBucketCount = tableBucketInfo.get().getReadBucketCount(); int partitionBucketCount = partitionBucketProperty.get().getBucketCount(); // Validation was done in HiveSplitManager#getPartitionMetadata. // Here, it's just trying to see if its needs the BucketConversion. - if (tableBucketCount != partitionBucketCount) { - bucketConversion = Optional.of(new BucketConversion(tableBucketCount, partitionBucketCount, tableBucketInfo.get().getBucketColumns())); - if (tableBucketCount > partitionBucketCount) { + if (readBucketCount != partitionBucketCount) { + bucketConversion = Optional.of(new BucketConversion(readBucketCount, partitionBucketCount, tableBucketInfo.get().getBucketColumns())); + if (readBucketCount > partitionBucketCount) { bucketConversionRequiresWorkerParticipation = true; } } @@ -335,7 +347,8 @@ private ListenableFuture loadPartition(HivePartitionMetadata partition) effectivePredicate, partition.getColumnCoercions(), bucketConversionRequiresWorkerParticipation ? bucketConversion : Optional.empty(), - isForceLocalScheduling(session)); + isForceLocalScheduling(session), + s3SelectPushdownEnabled); // To support custom input formats, we want to call getSplits() // on the input format to obtain file splits. @@ -355,7 +368,9 @@ private ListenableFuture loadPartition(HivePartitionMetadata partition) return hiveSplitSource.addToQueue(getBucketedSplits(path, fs, splitFactory, tableBucketInfo.get(), bucketConversion)); } - boolean splittable = getHeaderCount(schema) == 0 && getFooterCount(schema) == 0; + // S3 Select pushdown works at the granularity of individual S3 objects, + // therefore we must not split files when it is enabled. + boolean splittable = getHeaderCount(schema) == 0 && getFooterCount(schema) == 0 && !s3SelectPushdownEnabled; fileIterators.addLast(createInternalHiveSplitIterator(path, fs, splitFactory, splittable)); return COMPLETED_FUTURE; } @@ -395,7 +410,8 @@ private Iterator createInternalHiveSplitIterator(Path path, F private List getBucketedSplits(Path path, FileSystem fileSystem, InternalHiveSplitFactory splitFactory, BucketSplitInfo bucketSplitInfo, Optional bucketConversion) { - int tableBucketCount = bucketSplitInfo.getBucketCount(); + int readBucketCount = bucketSplitInfo.getReadBucketCount(); + int tableBucketCount = bucketSplitInfo.getTableBucketCount(); int partitionBucketCount = bucketConversion.isPresent() ? bucketConversion.get().getPartitionBucketCount() : tableBucketCount; // list all files in the partition @@ -428,12 +444,37 @@ private List getBucketedSplits(Path path, FileSystem fileSyst // convert files internal splits List splitList = new ArrayList<>(); - for (int bucketNumber = 0; bucketNumber < Math.max(tableBucketCount, partitionBucketCount); bucketNumber++) { - int partitionBucketNumber = bucketNumber % partitionBucketCount; // physical - int tableBucketNumber = bucketNumber % tableBucketCount; // logical - if (bucketSplitInfo.isBucketEnabled(tableBucketNumber)) { + for (int bucketNumber = 0; bucketNumber < Math.max(readBucketCount, partitionBucketCount); bucketNumber++) { + // Physical bucket #. This determine file name. It also determines the order of splits in the result. + int partitionBucketNumber = bucketNumber % partitionBucketCount; + // Logical bucket #. Each logical bucket corresponds to a "bucket" from engine's perspective. + int readBucketNumber = bucketNumber % readBucketCount; + + boolean containsEligibleTableBucket = false; + boolean containsIneligibleTableBucket = false; + for (int tableBucketNumber = bucketNumber % tableBucketCount; tableBucketNumber < tableBucketCount; tableBucketNumber += Math.max(readBucketCount, partitionBucketCount)) { + // table bucket number: this is used for evaluating "$bucket" filters. + if (bucketSplitInfo.isTableBucketEnabled(tableBucketNumber)) { + containsEligibleTableBucket = true; + } + else { + containsIneligibleTableBucket = true; + } + } + + if (containsEligibleTableBucket && containsIneligibleTableBucket) { + throw new PrestoException( + NOT_SUPPORTED, + "The bucket filter cannot be satisfied. There are restrictions on the bucket filter when all the following is true: " + + "1. a table has a different buckets count as at least one of its partitions that is read in this query; " + + "2. the table has a different but compatible bucket number with another table in the query; " + + "3. some buckets of the table is filtered out from the query, most likely using a filter on \"$bucket\". " + + "(table name: " + table.getTableName() + ", table bucket count: " + tableBucketCount + ", " + + "partition bucket count: " + partitionBucketCount + ", effective reading bucket count: " + readBucketCount + ")"); + } + if (containsEligibleTableBucket) { LocatedFileStatus file = files.get(partitionBucketNumber); - splitFactory.createInternalHiveSplit(file, tableBucketNumber) + splitFactory.createInternalHiveSplit(file, readBucketNumber) .ifPresent(splitList::add); } } @@ -501,7 +542,8 @@ private static String getPartitionLocation(Table table, Optional part public static class BucketSplitInfo { private final List bucketColumns; - private final int bucketCount; + private final int tableBucketCount; + private final int readBucketCount; private final IntPredicate bucketFilter; public static Optional createBucketSplitInfo(Optional bucketHandle, Optional bucketFilter) @@ -514,18 +556,20 @@ public static Optional createBucketSplitInfo(Optional bucketColumns = bucketHandle.get().getColumns(); - if (bucketFilter.isPresent()) { - return Optional.of(new BucketSplitInfo(bucketColumns, bucketCount, bucketFilter.get().getBucketsToKeep()::contains)); - } - return Optional.of(new BucketSplitInfo(bucketColumns, bucketCount, bucketNumber -> true)); + IntPredicate predicate = bucketFilter + .map(filter -> filter.getBucketsToKeep()::contains) + .orElse(bucket -> true); + return Optional.of(new BucketSplitInfo(bucketColumns, tableBucketCount, readBucketCount, predicate)); } - private BucketSplitInfo(List bucketColumns, int bucketCount, IntPredicate bucketFilter) + private BucketSplitInfo(List bucketColumns, int tableBucketCount, int readBucketCount, IntPredicate bucketFilter) { this.bucketColumns = ImmutableList.copyOf(requireNonNull(bucketColumns, "bucketColumns is null")); - this.bucketCount = bucketCount; + this.tableBucketCount = tableBucketCount; + this.readBucketCount = readBucketCount; this.bucketFilter = requireNonNull(bucketFilter, "bucketFilter is null"); } @@ -534,14 +578,28 @@ public List getBucketColumns() return bucketColumns; } - public int getBucketCount() + public int getTableBucketCount() + { + return tableBucketCount; + } + + public int getReadBucketCount() { - return bucketCount; + return readBucketCount; } - public boolean isBucketEnabled(int value) + /** + * Evaluates whether the provided table bucket number passes the bucket predicate. + * A bucket predicate can be present in two cases: + *

    + *
  • Filter on "$bucket" column. e.g. {@code "$bucket" between 0 and 100} + *
  • Single-value equality filter on all bucket columns. e.g. for a table with two bucketing columns, + * {@code bucketCol1 = 'a' AND bucketCol2 = 123} + *
+ */ + public boolean isTableBucketEnabled(int tableBucketNumber) { - return bucketFilter.test(value); + return bucketFilter.test(tableBucketNumber); } } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/CreateEmptyPartitionProcedure.java b/presto-hive/src/main/java/com/facebook/presto/hive/CreateEmptyPartitionProcedure.java new file mode 100644 index 0000000000000..af01f74af6b3b --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/CreateEmptyPartitionProcedure.java @@ -0,0 +1,128 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.hive.LocationService.WriteInfo; +import com.facebook.presto.hive.PartitionUpdate.UpdateMode; +import com.facebook.presto.hive.metastore.ExtendedHiveMetastore; +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.procedure.Procedure; +import com.facebook.presto.spi.procedure.Procedure.Argument; +import com.google.common.collect.ImmutableList; +import io.airlift.json.JsonCodec; +import io.airlift.slice.Slice; +import io.airlift.slice.Slices; +import org.apache.hadoop.hive.common.FileUtils; + +import javax.inject.Inject; +import javax.inject.Provider; + +import java.lang.invoke.MethodHandle; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +import static com.facebook.presto.spi.StandardErrorCode.ALREADY_EXISTS; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_PROCEDURE_ARGUMENT; +import static com.facebook.presto.spi.block.MethodHandleUtil.methodHandle; +import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.Objects.requireNonNull; + +public class CreateEmptyPartitionProcedure + implements Provider +{ + private static final MethodHandle CREATE_EMPTY_PARTITION = methodHandle( + CreateEmptyPartitionProcedure.class, + "createEmptyPartition", + ConnectorSession.class, + String.class, + String.class, + List.class, + List.class); + + private final Supplier hiveMetadataFactory; + private final ExtendedHiveMetastore metastore; + private final LocationService locationService; + private final JsonCodec partitionUpdateJsonCodec; + + @Inject + public CreateEmptyPartitionProcedure(Supplier hiveMetadataFactory, ExtendedHiveMetastore metastore, LocationService locationService, JsonCodec partitionUpdateCodec) + { + this.hiveMetadataFactory = requireNonNull(hiveMetadataFactory, "hiveMetadataFactory is null"); + this.metastore = requireNonNull(metastore, "metastore is null"); + this.locationService = requireNonNull(locationService, "locationService is null"); + this.partitionUpdateJsonCodec = requireNonNull(partitionUpdateCodec, "partitionUpdateCodec is null"); + } + + @Override + public Procedure get() + { + return new Procedure( + "system", + "create_empty_partition", + ImmutableList.of( + new Argument("schema_name", VARCHAR), + new Argument("table_name", VARCHAR), + new Argument("partition_columns", "array(varchar)"), + new Argument("partition_values", "array(varchar)")), + CREATE_EMPTY_PARTITION.bindTo(this)); + } + + public void createEmptyPartition(ConnectorSession session, String schema, String table, List partitionColumnNames, List partitionValues) + { + TransactionalMetadata hiveMetadata = hiveMetadataFactory.get(); + + HiveInsertTableHandle hiveInsertTableHandle = (HiveInsertTableHandle) hiveMetadata.beginInsert(session, new HiveTableHandle(schema, table)); + + List actualPartitionColumnNames = hiveInsertTableHandle.getInputColumns().stream() + .filter(HiveColumnHandle::isPartitionKey) + .map(HiveColumnHandle::getName) + .collect(toImmutableList()); + if (!Objects.equals(partitionColumnNames, actualPartitionColumnNames)) { + throw new PrestoException(INVALID_PROCEDURE_ARGUMENT, "input partition column names doesn't match actual partition column names"); + } + + List partitionStringValues = partitionValues.stream() + .map(String.class::cast) + .collect(toImmutableList()); + + if (metastore.getPartition(schema, table, partitionStringValues).isPresent()) { + throw new PrestoException(ALREADY_EXISTS, "Partition already exists"); + } + String partitionName = FileUtils.makePartName(actualPartitionColumnNames, partitionStringValues); + + WriteInfo writeInfo = locationService.getPartitionWriteInfo(hiveInsertTableHandle.getLocationHandle(), Optional.empty(), partitionName); + Slice serializedPartitionUpdate = Slices.wrappedBuffer( + partitionUpdateJsonCodec.toJsonBytes( + new PartitionUpdate( + partitionName, + UpdateMode.NEW, + writeInfo.getWritePath(), + writeInfo.getTargetPath(), + ImmutableList.of(), + 0, + 0, + 0))); + + hiveMetadata.finishInsert( + session, + hiveInsertTableHandle, + ImmutableList.of(serializedPartitionUpdate), + ImmutableList.of()); + hiveMetadata.commit(); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/ForRecordingHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/ForRecordingHiveMetastore.java new file mode 100644 index 0000000000000..3777fa7969377 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/ForRecordingHiveMetastore.java @@ -0,0 +1,31 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import javax.inject.Qualifier; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +@Target({FIELD, PARAMETER, METHOD}) +@Qualifier +public @interface ForRecordingHiveMetastore +{ +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursor.java b/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursor.java index e533ab771d1d9..6387fd3541dea 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursor.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursor.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.hive; +import com.facebook.presto.hadoop.TextLineLengthLimitExceededException; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.RecordCursor; import com.facebook.presto.spi.block.Block; @@ -22,6 +23,8 @@ import com.facebook.presto.spi.type.TypeManager; import io.airlift.slice.Slice; import io.airlift.slice.Slices; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.common.type.HiveDecimal; import org.apache.hadoop.hive.serde2.Deserializer; import org.apache.hadoop.hive.serde2.SerDeException; @@ -49,6 +52,7 @@ import java.util.concurrent.TimeUnit; import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA; import static com.facebook.presto.hive.HiveErrorCode.HIVE_CURSOR_ERROR; import static com.facebook.presto.hive.HiveUtil.closeWithSuppression; import static com.facebook.presto.hive.HiveUtil.getDeserializer; @@ -80,6 +84,7 @@ class GenericHiveRecordCursor implements RecordCursor { + private final Path path; private final RecordReader recordReader; private final K key; private final V value; @@ -110,6 +115,8 @@ class GenericHiveRecordCursor private boolean closed; public GenericHiveRecordCursor( + Configuration configuration, + Path path, RecordReader recordReader, long totalBytes, Properties splitSchema, @@ -117,19 +124,21 @@ public GenericHiveRecordCursor( DateTimeZone hiveStorageTimeZone, TypeManager typeManager) { + requireNonNull(path, "path is null"); requireNonNull(recordReader, "recordReader is null"); checkArgument(totalBytes >= 0, "totalBytes is negative"); requireNonNull(splitSchema, "splitSchema is null"); requireNonNull(columns, "columns is null"); requireNonNull(hiveStorageTimeZone, "hiveStorageTimeZone is null"); + this.path = path; this.recordReader = recordReader; this.totalBytes = totalBytes; this.key = recordReader.createKey(); this.value = recordReader.createValue(); this.hiveStorageTimeZone = hiveStorageTimeZone; - this.deserializer = getDeserializer(splitSchema); + this.deserializer = getDeserializer(configuration, splitSchema); this.rowInspector = getTableObjectInspector(deserializer); int size = columns.size(); @@ -212,6 +221,9 @@ public boolean advanceNextPosition() } catch (IOException | SerDeException | RuntimeException e) { closeWithSuppression(this, e); + if (e instanceof TextLineLengthLimitExceededException) { + throw new PrestoException(HIVE_BAD_DATA, "Line too long in text file: " + path, e); + } throw new PrestoException(HIVE_CURSOR_ERROR, e); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursorProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursorProvider.java index f5a216f380ccb..81a3dae489c3e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursorProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/GenericHiveRecordCursorProvider.java @@ -57,7 +57,8 @@ public Optional createRecordCursor( List columns, TupleDomain effectivePredicate, DateTimeZone hiveStorageTimeZone, - TypeManager typeManager) + TypeManager typeManager, + boolean s3SelectPushdownEnabled) { // make sure the FileSystem is created with the proper Configuration object try { @@ -71,6 +72,8 @@ public Optional createRecordCursor( () -> HiveUtil.createRecordReader(configuration, path, start, length, schema, columns)); return Optional.of(new GenericHiveRecordCursor<>( + configuration, + path, genericRecordReader(recordReader), length, schema, diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HdfsConfigurationUpdater.java b/presto-hive/src/main/java/com/facebook/presto/hive/HdfsConfigurationUpdater.java index 6b20c34f8ca7d..4cef800996f5a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HdfsConfigurationUpdater.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HdfsConfigurationUpdater.java @@ -21,6 +21,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.ql.io.orc.OrcFile.OrcTableProperties; +import org.apache.hadoop.mapreduce.lib.input.LineRecordReader; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.net.DNSToSwitchMapping; import org.apache.hadoop.net.SocksSocketFactory; @@ -63,6 +64,7 @@ public class HdfsConfigurationUpdater private final int fileSystemMaxCacheSize; private final S3ConfigurationUpdater s3ConfigurationUpdater; private final boolean isHdfsWireEncryptionEnabled; + private int textMaxLineLength; @VisibleForTesting public HdfsConfigurationUpdater(HiveClientConfig config) @@ -75,6 +77,7 @@ public HdfsConfigurationUpdater(HiveClientConfig config, S3ConfigurationUpdater { requireNonNull(config, "config is null"); checkArgument(config.getDfsTimeout().toMillis() >= 1, "dfsTimeout must be at least 1 ms"); + checkArgument(toIntExact(config.getTextMaxLineLength().toBytes()) >= 1, "textMaxLineLength must be at least 1 byte"); this.socksProxy = config.getMetastoreSocksProxy(); this.ipcPingInterval = config.getIpcPingInterval(); @@ -86,6 +89,7 @@ public HdfsConfigurationUpdater(HiveClientConfig config, S3ConfigurationUpdater this.compressionCodec = config.getHiveCompressionCodec(); this.fileSystemMaxCacheSize = config.getFileSystemMaxCacheSize(); this.isHdfsWireEncryptionEnabled = config.isHdfsWireEncryptionEnabled(); + this.textMaxLineLength = toIntExact(config.getTextMaxLineLength().toBytes()); this.s3ConfigurationUpdater = requireNonNull(s3ConfigurationUpdater, "s3ConfigurationUpdater is null"); } @@ -93,9 +97,6 @@ public HdfsConfigurationUpdater(HiveClientConfig config, S3ConfigurationUpdater private static Configuration readConfiguration(List resourcePaths) { Configuration result = new Configuration(false); - if (resourcePaths == null) { - return result; - } for (String resourcePath : resourcePaths) { Configuration resourceProperties = new Configuration(false); @@ -139,6 +140,8 @@ public void updateConfiguration(Configuration config) config.setInt("fs.cache.max-size", fileSystemMaxCacheSize); + config.setInt(LineRecordReader.MAX_LINE_LENGTH, textMaxLineLength); + configureCompression(config, compressionCodec); s3ConfigurationUpdater.updateConfiguration(config); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBasicStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBasicStatistics.java index 941b2ca271bc6..4d8112fe73173 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBasicStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBasicStatistics.java @@ -13,13 +13,18 @@ */ package com.facebook.presto.hive; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.concurrent.Immutable; + import java.util.Objects; import java.util.OptionalLong; import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +@Immutable public class HiveBasicStatistics { private final OptionalLong fileCount; @@ -42,37 +47,38 @@ public HiveBasicStatistics(long fileCount, long rowCount, long inMemoryDataSizeI this(OptionalLong.of(fileCount), OptionalLong.of(rowCount), OptionalLong.of(inMemoryDataSizeInBytes), OptionalLong.of(onDiskDataSizeInBytes)); } + @JsonCreator public HiveBasicStatistics( - OptionalLong fileCount, - OptionalLong rowCount, - OptionalLong inMemoryDataSizeInBytes, - OptionalLong onDiskDataSizeInBytes) + @JsonProperty("fileCount") OptionalLong fileCount, + @JsonProperty("rowCount") OptionalLong rowCount, + @JsonProperty("inMemoryDataSizeInBytes") OptionalLong inMemoryDataSizeInBytes, + @JsonProperty("onDiskDataSizeInBytes") OptionalLong onDiskDataSizeInBytes) { this.fileCount = requireNonNull(fileCount, "fileCount is null"); - fileCount.ifPresent(count -> checkArgument(count >= 0, "fileCount is negative: %d", count)); this.rowCount = requireNonNull(rowCount, "rowCount is null"); - rowCount.ifPresent(count -> checkArgument(count >= 0, "rowCount is negative: %d", count)); this.inMemoryDataSizeInBytes = requireNonNull(inMemoryDataSizeInBytes, "inMemoryDataSizeInBytes is null"); - inMemoryDataSizeInBytes.ifPresent(size -> checkArgument(size >= 0, "inMemoryDataSizeInBytes is negative: %d", size)); this.onDiskDataSizeInBytes = requireNonNull(onDiskDataSizeInBytes, "onDiskDataSizeInBytes is null"); - onDiskDataSizeInBytes.ifPresent(size -> checkArgument(size >= 0, "onDiskDataSizeInBytes is negative: %d", size)); } + @JsonProperty public OptionalLong getFileCount() { return fileCount; } + @JsonProperty public OptionalLong getRowCount() { return rowCount; } + @JsonProperty public OptionalLong getInMemoryDataSizeInBytes() { return inMemoryDataSizeInBytes; } + @JsonProperty public OptionalLong getOnDiskDataSizeInBytes() { return onDiskDataSizeInBytes; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketHandle.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketHandle.java index 06f7f342906d4..d66218d72ed5f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketHandle.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketHandle.java @@ -25,13 +25,21 @@ public class HiveBucketHandle { private final List columns; - private final int bucketCount; + // Number of buckets in the table, as specified in table metadata + private final int tableBucketCount; + // Number of buckets the table will appear to have when the Hive connector + // presents the table to the engine for read. + private final int readBucketCount; @JsonCreator - public HiveBucketHandle(@JsonProperty("columns") List columns, @JsonProperty("bucketCount") int bucketCount) + public HiveBucketHandle( + @JsonProperty("columns") List columns, + @JsonProperty("tableBucketCount") int tableBucketCount, + @JsonProperty("readBucketCount") int readBucketCount) { this.columns = requireNonNull(columns, "columns is null"); - this.bucketCount = requireNonNull(bucketCount, "bucketCount is null"); + this.tableBucketCount = tableBucketCount; + this.readBucketCount = readBucketCount; } @JsonProperty @@ -41,18 +49,24 @@ public List getColumns() } @JsonProperty - public int getBucketCount() + public int getTableBucketCount() { - return bucketCount; + return tableBucketCount; } - public HiveBucketProperty toBucketProperty() + @JsonProperty + public int getReadBucketCount() + { + return readBucketCount; + } + + public HiveBucketProperty toTableBucketProperty() { return new HiveBucketProperty( columns.stream() .map(HiveColumnHandle::getName) .collect(toList()), - bucketCount, + tableBucketCount, ImmutableList.of()); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketProperty.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketProperty.java index fb9e91664b7a3..ed0671cd1cc3a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketProperty.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketProperty.java @@ -60,7 +60,7 @@ public static Optional fromStorageDescriptor(StorageDescript List sortedBy = ImmutableList.of(); if (storageDescriptor.isSetSortCols()) { sortedBy = storageDescriptor.getSortCols().stream() - .map(order-> SortingColumn.fromMetastoreApiOrder(order, tablePartitionName)) + .map(order -> SortingColumn.fromMetastoreApiOrder(order, tablePartitionName)) .collect(toImmutableList()); } return Optional.of(new HiveBucketProperty(storageDescriptor.getBucketCols(), storageDescriptor.getNumBuckets(), sortedBy)); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketing.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketing.java index 3e9cabfc56f3d..53f774437cf24 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketing.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveBucketing.java @@ -24,6 +24,7 @@ import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.predicate.ValueSet; import com.facebook.presto.spi.type.Type; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -272,7 +273,8 @@ public static Optional getHiveBucketHandle(Table table) bucketColumns.add(bucketColumnHandle); } - return Optional.of(new HiveBucketHandle(bucketColumns.build(), hiveBucketProperty.get().getBucketCount())); + int bucketCount = hiveBucketProperty.get().getBucketCount(); + return Optional.of(new HiveBucketHandle(bucketColumns.build(), bucketCount, bucketCount)); } public static Optional getHiveBucketFilter(Table table, TupleDomain effectivePredicate) @@ -360,6 +362,7 @@ public static class HiveBucketFilter { private final Set bucketsToKeep; + @JsonCreator public HiveBucketFilter(@JsonProperty("bucketsToKeep") Set bucketsToKeep) { this.bucketsToKeep = bucketsToKeep; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientConfig.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientConfig.java index 895a5e53c1a0c..3870dcee0fd20 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientConfig.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientConfig.java @@ -29,6 +29,8 @@ import io.airlift.units.MinDuration; import org.joda.time.DateTimeZone; +import javax.validation.constraints.DecimalMax; +import javax.validation.constraints.DecimalMin; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; @@ -38,6 +40,7 @@ import java.util.concurrent.TimeUnit; import static io.airlift.units.DataSize.Unit.MEGABYTE; +import static java.util.concurrent.TimeUnit.MINUTES; @DefunctConfig({ "hive.file-system-cache-ttl", @@ -47,8 +50,6 @@ "hive.optimized-reader.enabled"}) public class HiveClientConfig { - private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); - private String timeZone = TimeZone.getDefault().getID(); private DataSize maxSplitSize = new DataSize(64, MEGABYTE); @@ -87,7 +88,7 @@ public class HiveClientConfig private S3FileSystemType s3FileSystemType = S3FileSystemType.PRESTO; - private HiveStorageFormat hiveStorageFormat = HiveStorageFormat.RCBINARY; + private HiveStorageFormat hiveStorageFormat = HiveStorageFormat.ORC; private HiveCompressionCodec hiveCompressionCodec = HiveCompressionCodec.GZIP; private boolean respectTableFormat = true; private boolean immutablePartitions; @@ -95,11 +96,12 @@ public class HiveClientConfig private int maxOpenSortFiles = 50; private int writeValidationThreads = 16; - private List resourceConfigFiles; + private List resourceConfigFiles = ImmutableList.of(); + + private DataSize textMaxLineLength = new DataSize(100, MEGABYTE); private boolean useParquetColumnNames; - private boolean parquetOptimizedReaderEnabled = true; - private boolean parquetPredicatePushdownEnabled = true; + private boolean failOnCorruptedParquetStatistics = true; private boolean assumeCanonicalPartitionKeys; @@ -112,8 +114,8 @@ public class HiveClientConfig private DataSize orcStreamBufferSize = new DataSize(8, MEGABYTE); private DataSize orcMaxReadBlockSize = new DataSize(16, MEGABYTE); private boolean orcLazyReadSmallRanges = true; - private boolean orcOptimizedWriterEnabled; - private double orcWriterValidationPercentage = 100.0; + private boolean orcOptimizedWriterEnabled = true; + private double orcWriterValidationPercentage; private OrcWriteValidationMode orcWriterValidationMode = OrcWriteValidationMode.BOTH; private boolean rcfileOptimizedWriterEnabled = true; @@ -131,13 +133,21 @@ public class HiveClientConfig private int fileSystemMaxCacheSize = 1000; + private boolean optimizeMismatchedBucketCount; private boolean writesToNonManagedTablesEnabled; private boolean createsOfNonManagedTablesEnabled = true; private boolean tableStatisticsEnabled = true; private int partitionStatisticsSampleSize = 100; + private boolean ignoreCorruptedStatistics; private boolean collectColumnStatisticsOnWrite; + private String recordingPath; + private boolean replay; + private Duration recordingDuration = new Duration(0, MINUTES); + private boolean s3SelectPushdownEnabled; + private int s3SelectPushdownMaxConnections = 500; + public int getMaxInitialSplits() { return maxInitialSplits; @@ -462,6 +472,7 @@ public HiveClientConfig setMaxPartitionBatchSize(int maxPartitionBatchSize) return this; } + @NotNull public List getResourceConfigFiles() { return resourceConfigFiles; @@ -470,13 +481,13 @@ public List getResourceConfigFiles() @Config("hive.config.resources") public HiveClientConfig setResourceConfigFiles(String files) { - this.resourceConfigFiles = (files == null) ? null : SPLITTER.splitToList(files); + this.resourceConfigFiles = Splitter.on(',').trimResults().omitEmptyStrings().splitToList(files); return this; } public HiveClientConfig setResourceConfigFiles(List files) { - this.resourceConfigFiles = (files == null) ? null : ImmutableList.copyOf(files); + this.resourceConfigFiles = ImmutableList.copyOf(files); return this; } @@ -665,34 +676,6 @@ public HiveClientConfig setVerifyChecksum(boolean verifyChecksum) return this; } - @Deprecated - public boolean isParquetPredicatePushdownEnabled() - { - return parquetPredicatePushdownEnabled; - } - - @Deprecated - @Config("hive.parquet-predicate-pushdown.enabled") - public HiveClientConfig setParquetPredicatePushdownEnabled(boolean parquetPredicatePushdownEnabled) - { - this.parquetPredicatePushdownEnabled = parquetPredicatePushdownEnabled; - return this; - } - - @Deprecated - public boolean isParquetOptimizedReaderEnabled() - { - return parquetOptimizedReaderEnabled; - } - - @Deprecated - @Config("hive.parquet-optimized-reader.enabled") - public HiveClientConfig setParquetOptimizedReaderEnabled(boolean parquetOptimizedReaderEnabled) - { - this.parquetOptimizedReaderEnabled = parquetOptimizedReaderEnabled; - return this; - } - public boolean isUseOrcColumnNames() { return useOrcColumnNames; @@ -826,6 +809,8 @@ public HiveClientConfig setOrcOptimizedWriterEnabled(boolean orcOptimizedWriterE return this; } + @DecimalMin("0.0") + @DecimalMax("100.0") public double getOrcWriterValidationPercentage() { return orcWriterValidationPercentage; @@ -892,6 +877,22 @@ public HiveClientConfig setAssumeCanonicalPartitionKeys(boolean assumeCanonicalP return this; } + @MinDataSize("1B") + @MaxDataSize("1GB") + @NotNull + public DataSize getTextMaxLineLength() + { + return textMaxLineLength; + } + + @Config("hive.text.max-line-length") + @ConfigDescription("Maximum line length for text files") + public HiveClientConfig setTextMaxLineLength(DataSize textMaxLineLength) + { + this.textMaxLineLength = textMaxLineLength; + return this; + } + public boolean isUseParquetColumnNames() { return useParquetColumnNames; @@ -905,6 +906,31 @@ public HiveClientConfig setUseParquetColumnNames(boolean useParquetColumnNames) return this; } + public boolean isFailOnCorruptedParquetStatistics() + { + return failOnCorruptedParquetStatistics; + } + + @Config("hive.parquet.fail-on-corrupted-statistics") + @ConfigDescription("Fail when scanning Parquet files with corrupted statistics") + public HiveClientConfig setFailOnCorruptedParquetStatistics(boolean failOnCorruptedParquetStatistics) + { + this.failOnCorruptedParquetStatistics = failOnCorruptedParquetStatistics; + return this; + } + + public boolean isOptimizeMismatchedBucketCount() + { + return optimizeMismatchedBucketCount; + } + + @Config("hive.optimize-mismatched-bucket-count") + public HiveClientConfig setOptimizeMismatchedBucketCount(boolean optimizeMismatchedBucketCount) + { + this.optimizeMismatchedBucketCount = optimizeMismatchedBucketCount; + return this; + } + public enum HiveMetastoreAuthenticationType { NONE, @@ -1076,6 +1102,19 @@ public HiveClientConfig setPartitionStatisticsSampleSize(int partitionStatistics return this; } + public boolean isIgnoreCorruptedStatistics() + { + return ignoreCorruptedStatistics; + } + + @Config("hive.ignore-corrupted-statistics") + @ConfigDescription("Ignore corrupted statistics rather than failing") + public HiveClientConfig setIgnoreCorruptedStatistics(boolean ignoreCorruptedStatistics) + { + this.ignoreCorruptedStatistics = ignoreCorruptedStatistics; + return this; + } + public boolean isCollectColumnStatisticsOnWrite() { return collectColumnStatisticsOnWrite; @@ -1088,4 +1127,67 @@ public HiveClientConfig setCollectColumnStatisticsOnWrite(boolean collectColumnS this.collectColumnStatisticsOnWrite = collectColumnStatisticsOnWrite; return this; } + + @Config("hive.metastore-recording-path") + public HiveClientConfig setRecordingPath(String recordingPath) + { + this.recordingPath = recordingPath; + return this; + } + + public String getRecordingPath() + { + return recordingPath; + } + + @Config("hive.replay-metastore-recording") + public HiveClientConfig setReplay(boolean replay) + { + this.replay = replay; + return this; + } + + public boolean isReplay() + { + return replay; + } + + @Config("hive.metastore-recoding-duration") + public HiveClientConfig setRecordingDuration(Duration recordingDuration) + { + this.recordingDuration = recordingDuration; + return this; + } + + @NotNull + public Duration getRecordingDuration() + { + return recordingDuration; + } + + public boolean isS3SelectPushdownEnabled() + { + return s3SelectPushdownEnabled; + } + + @Config("hive.s3select-pushdown.enabled") + @ConfigDescription("Enable query pushdown to AWS S3 Select service") + public HiveClientConfig setS3SelectPushdownEnabled(boolean s3SelectPushdownEnabled) + { + this.s3SelectPushdownEnabled = s3SelectPushdownEnabled; + return this; + } + + @Min(1) + public int getS3SelectPushdownMaxConnections() + { + return s3SelectPushdownMaxConnections; + } + + @Config("hive.s3select-pushdown.max-connections") + public HiveClientConfig setS3SelectPushdownMaxConnections(int s3SelectPushdownMaxConnections) + { + this.s3SelectPushdownMaxConnections = s3SelectPushdownMaxConnections; + return this; + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientModule.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientModule.java index b0bb04cb410a3..63ae156fc0075 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientModule.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveClientModule.java @@ -17,8 +17,8 @@ import com.facebook.presto.hive.orc.DwrfPageSourceFactory; import com.facebook.presto.hive.orc.OrcPageSourceFactory; import com.facebook.presto.hive.parquet.ParquetPageSourceFactory; -import com.facebook.presto.hive.parquet.ParquetRecordCursorProvider; import com.facebook.presto.hive.rcfile.RcFilePageSourceFactory; +import com.facebook.presto.hive.s3.PrestoS3ClientFactory; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPageSinkProvider; import com.facebook.presto.spi.connector.ConnectorPageSourceProvider; @@ -27,6 +27,7 @@ import com.google.inject.Module; import com.google.inject.Provides; import com.google.inject.Scopes; +import com.google.inject.TypeLiteral; import com.google.inject.multibindings.Multibinder; import io.airlift.event.client.EventClient; @@ -34,6 +35,7 @@ import java.util.concurrent.ExecutorService; import java.util.function.Function; +import java.util.function.Supplier; import static com.google.inject.multibindings.Multibinder.newSetBinder; import static io.airlift.concurrent.Threads.daemonThreadsNamed; @@ -73,8 +75,10 @@ public void configure(Binder binder) binder.bind(NamenodeStats.class).in(Scopes.SINGLETON); newExporter(binder).export(NamenodeStats.class).as(generatedNameOf(NamenodeStats.class, connectorId)); + binder.bind(PrestoS3ClientFactory.class).in(Scopes.SINGLETON); + Multibinder recordCursorProviderBinder = newSetBinder(binder, HiveRecordCursorProvider.class); - recordCursorProviderBinder.addBinding().to(ParquetRecordCursorProvider.class).in(Scopes.SINGLETON); + recordCursorProviderBinder.addBinding().to(S3SelectRecordCursorProvider.class).in(Scopes.SINGLETON); recordCursorProviderBinder.addBinding().to(GenericHiveRecordCursorProvider.class).in(Scopes.SINGLETON); binder.bind(HiveWriterStats.class).in(Scopes.SINGLETON); @@ -85,6 +89,7 @@ public void configure(Binder binder) binder.bind(LocationService.class).to(HiveLocationService.class).in(Scopes.SINGLETON); binder.bind(TableParameterCodec.class).in(Scopes.SINGLETON); binder.bind(HiveMetadataFactory.class).in(Scopes.SINGLETON); + binder.bind(new TypeLiteral>() {}).to(HiveMetadataFactory.class).in(Scopes.SINGLETON); binder.bind(HiveTransactionManager.class).in(Scopes.SINGLETON); binder.bind(ConnectorSplitManager.class).to(HiveSplitManager.class).in(Scopes.SINGLETON); newExporter(binder).export(ConnectorSplitManager.class).as(generatedNameOf(HiveSplitManager.class, connectorId)); @@ -109,6 +114,8 @@ public void configure(Binder binder) configBinder(binder).bindConfig(OrcFileWriterConfig.class); fileWriterFactoryBinder.addBinding().to(OrcFileWriterFactory.class).in(Scopes.SINGLETON); fileWriterFactoryBinder.addBinding().to(RcFileFileWriterFactory.class).in(Scopes.SINGLETON); + + configBinder(binder).bindConfig(ParquetFileWriterConfig.class); } @ForHiveClient diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveColumnHandle.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveColumnHandle.java index 0b153e7f71814..41dd09150d83b 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveColumnHandle.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveColumnHandle.java @@ -29,7 +29,6 @@ import static com.facebook.presto.hive.HiveType.HIVE_LONG; import static com.facebook.presto.hive.HiveType.HIVE_STRING; import static com.facebook.presto.spi.type.BigintType.BIGINT; -import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; @@ -157,14 +156,7 @@ public boolean equals(Object obj) @Override public String toString() { - return toStringHelper(this) - .add("name", name) - .add("hiveType", hiveType) - .add("hiveColumnIndex", hiveColumnIndex) - .add("columnType", columnType) - .add("comment", comment.orElse(null)) - .omitNullValues() - .toString(); + return name + ":" + hiveType + ":" + hiveColumnIndex + ":" + columnType; } public static HiveColumnHandle updateRowIdHandle() diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnector.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnector.java index 9754e6147cd30..296f6034430df 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnector.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnector.java @@ -24,6 +24,7 @@ import com.facebook.presto.spi.connector.ConnectorSplitManager; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.connector.classloader.ClassLoaderSafeConnectorMetadata; +import com.facebook.presto.spi.procedure.Procedure; import com.facebook.presto.spi.session.PropertyMetadata; import com.facebook.presto.spi.transaction.IsolationLevel; import com.google.common.collect.ImmutableList; @@ -33,6 +34,7 @@ import java.util.List; import java.util.Set; +import java.util.function.Supplier; import static com.facebook.presto.spi.transaction.IsolationLevel.READ_UNCOMMITTED; import static com.facebook.presto.spi.transaction.IsolationLevel.checkConnectorSupports; @@ -45,12 +47,13 @@ public class HiveConnector private static final Logger log = Logger.get(HiveConnector.class); private final LifeCycleManager lifeCycleManager; - private final HiveMetadataFactory metadataFactory; + private final Supplier metadataFactory; private final ConnectorSplitManager splitManager; private final ConnectorPageSourceProvider pageSourceProvider; private final ConnectorPageSinkProvider pageSinkProvider; private final ConnectorNodePartitioningProvider nodePartitioningProvider; private final Set systemTables; + private final Set procedures; private final List> sessionProperties; private final List> schemaProperties; private final List> tableProperties; @@ -61,13 +64,14 @@ public class HiveConnector public HiveConnector( LifeCycleManager lifeCycleManager, - HiveMetadataFactory metadataFactory, + Supplier metadataFactory, HiveTransactionManager transactionManager, ConnectorSplitManager splitManager, ConnectorPageSourceProvider pageSourceProvider, ConnectorPageSinkProvider pageSinkProvider, ConnectorNodePartitioningProvider nodePartitioningProvider, Set systemTables, + Set procedures, List> sessionProperties, List> schemaProperties, List> tableProperties, @@ -82,6 +86,7 @@ public HiveConnector( this.pageSinkProvider = requireNonNull(pageSinkProvider, "pageSinkProvider is null"); this.nodePartitioningProvider = requireNonNull(nodePartitioningProvider, "nodePartitioningProvider is null"); this.systemTables = ImmutableSet.copyOf(requireNonNull(systemTables, "systemTables is null")); + this.procedures = ImmutableSet.copyOf(requireNonNull(procedures, "procedures is null")); this.sessionProperties = ImmutableList.copyOf(requireNonNull(sessionProperties, "sessionProperties is null")); this.schemaProperties = ImmutableList.copyOf(requireNonNull(schemaProperties, "schemaProperties is null")); this.tableProperties = ImmutableList.copyOf(requireNonNull(tableProperties, "tableProperties is null")); @@ -127,6 +132,12 @@ public Set getSystemTables() return systemTables; } + @Override + public Set getProcedures() + { + return procedures; + } + @Override public List> getSessionProperties() { @@ -163,7 +174,7 @@ public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel checkConnectorSupports(READ_UNCOMMITTED, isolationLevel); ConnectorTransactionHandle transaction = new HiveTransactionHandle(); try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { - transactionManager.put(transaction, metadataFactory.create()); + transactionManager.put(transaction, metadataFactory.get()); } return transaction; } @@ -171,7 +182,7 @@ public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel @Override public void commit(ConnectorTransactionHandle transaction) { - HiveMetadata metadata = (HiveMetadata) transactionManager.remove(transaction); + TransactionalMetadata metadata = transactionManager.remove(transaction); checkArgument(metadata != null, "no such transaction: %s", transaction); try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { metadata.commit(); @@ -181,7 +192,7 @@ public void commit(ConnectorTransactionHandle transaction) @Override public void rollback(ConnectorTransactionHandle transaction) { - HiveMetadata metadata = (HiveMetadata) transactionManager.remove(transaction); + TransactionalMetadata metadata = transactionManager.remove(transaction); checkArgument(metadata != null, "no such transaction: %s", transaction); try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { metadata.rollback(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnectorFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnectorFactory.java index 25ec10f6ea32e..43e75dcbcbe5f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnectorFactory.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveConnectorFactory.java @@ -36,9 +36,12 @@ import com.facebook.presto.spi.connector.classloader.ClassLoaderSafeConnectorPageSourceProvider; import com.facebook.presto.spi.connector.classloader.ClassLoaderSafeConnectorSplitManager; import com.facebook.presto.spi.connector.classloader.ClassLoaderSafeNodePartitioningProvider; +import com.facebook.presto.spi.procedure.Procedure; import com.facebook.presto.spi.type.TypeManager; import com.google.common.collect.ImmutableSet; import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; import io.airlift.bootstrap.Bootstrap; import io.airlift.bootstrap.LifeCycleManager; import io.airlift.event.client.EventModule; @@ -50,6 +53,7 @@ import java.lang.management.ManagementFactory; import java.util.Map; import java.util.Optional; +import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; @@ -61,14 +65,14 @@ public class HiveConnectorFactory { private final String name; private final ClassLoader classLoader; - private final ExtendedHiveMetastore metastore; + private final Optional metastore; - public HiveConnectorFactory(String name, ClassLoader classLoader, ExtendedHiveMetastore metastore) + public HiveConnectorFactory(String name, ClassLoader classLoader, Optional metastore) { checkArgument(!isNullOrEmpty(name), "name is null or empty"); this.name = name; this.classLoader = requireNonNull(classLoader, "classLoader is null"); - this.metastore = metastore; + this.metastore = requireNonNull(metastore, "metastore is null"); } @Override @@ -84,7 +88,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { requireNonNull(config, "config is null"); @@ -93,11 +97,12 @@ public Connector create(String connectorId, Map config, Connecto new EventModule(), new MBeanModule(), new JsonModule(), - new HiveClientModule(connectorId), - new HiveS3Module(connectorId), - new HiveMetastoreModule(connectorId, Optional.ofNullable(metastore)), + new HiveClientModule(catalogName), + new HiveS3Module(catalogName), + new HiveMetastoreModule(catalogName, metastore), new HiveSecurityModule(), new HiveAuthenticationModule(), + new HiveProcedureModule(), binder -> { MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); binder.bind(MBeanServer.class).toInstance(new RebindSafeMBeanServer(platformMBeanServer)); @@ -124,6 +129,7 @@ public Connector create(String connectorId, Map config, Connecto HiveSessionProperties hiveSessionProperties = injector.getInstance(HiveSessionProperties.class); HiveTableProperties hiveTableProperties = injector.getInstance(HiveTableProperties.class); ConnectorAccessControl accessControl = new PartitionsAwareAccessControl(injector.getInstance(ConnectorAccessControl.class)); + Set procedures = injector.getInstance(Key.get(new TypeLiteral>() {})); return new HiveConnector( lifeCycleManager, @@ -134,6 +140,7 @@ public Connector create(String connectorId, Map config, Connecto new ClassLoaderSafeConnectorPageSinkProvider(pageSinkProvider, classLoader), new ClassLoaderSafeNodePartitioningProvider(connectorDistributionProvider, classLoader), ImmutableSet.of(), + procedures, hiveSessionProperties.getSessionProperties(), HiveSchemaProperties.SCHEMA_PROPERTIES, hiveTableProperties.getTableProperties(), diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveErrorCode.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveErrorCode.java index d9505e8caa7f9..4f5d3ee40220b 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveErrorCode.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveErrorCode.java @@ -61,6 +61,8 @@ public enum HiveErrorCode HIVE_TABLE_NOT_READABLE(34, USER_ERROR), HIVE_TABLE_DROPPED_DURING_QUERY(35, EXTERNAL), // HIVE_TOO_MANY_BUCKET_SORT_FILES(36) is deprecated + HIVE_CORRUPTED_COLUMN_STATISTICS(37, EXTERNAL), + HIVE_EXCEEDED_SPLIT_BUFFERING_LIMIT(38, USER_ERROR), /**/; private final ErrorCode errorCode; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveFileWriter.java index 89f30dcc4bef8..e052bf826a0e7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveFileWriter.java @@ -29,6 +29,8 @@ public interface HiveFileWriter void rollback(); + long getValidationCpuNanos(); + default Optional getVerificationTask() { return Optional.empty(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveHdfsConfiguration.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveHdfsConfiguration.java index e791629f4e86d..ee060d06a01c3 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveHdfsConfiguration.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveHdfsConfiguration.java @@ -21,23 +21,13 @@ import java.net.URI; import static com.facebook.presto.hive.util.ConfigurationUtils.copy; +import static com.facebook.presto.hive.util.ConfigurationUtils.getInitialConfiguration; import static java.util.Objects.requireNonNull; public class HiveHdfsConfiguration implements HdfsConfiguration { - private static final Configuration INITIAL_CONFIGURATION; - - static { - Configuration.addDefaultResource("hdfs-default.xml"); - Configuration.addDefaultResource("hdfs-site.xml"); - - // must not be transitively reloaded during the future loading of various Hadoop modules - // all the required default resources must be declared above - INITIAL_CONFIGURATION = new Configuration(false); - Configuration defaultConfiguration = new Configuration(); - copy(defaultConfiguration, INITIAL_CONFIGURATION); - } + private static final Configuration INITIAL_CONFIGURATION = getInitialConfiguration(); @SuppressWarnings("ThreadLocalNotStaticFinal") private final ThreadLocal hadoopConfiguration = new ThreadLocal() diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadata.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadata.java index d41568db76162..cd0b7b2357a71 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadata.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadata.java @@ -52,8 +52,8 @@ import com.facebook.presto.spi.TableNotFoundException; import com.facebook.presto.spi.ViewNotFoundException; import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorOutputMetadata; +import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.NullableValue; @@ -88,7 +88,10 @@ import org.apache.hadoop.mapred.JobConf; import org.joda.time.DateTimeZone; +import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -96,6 +99,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.OptionalInt; import java.util.OptionalLong; import java.util.Properties; import java.util.Set; @@ -121,13 +125,15 @@ import static com.facebook.presto.hive.HiveErrorCode.HIVE_UNKNOWN_ERROR; import static com.facebook.presto.hive.HiveErrorCode.HIVE_UNSUPPORTED_FORMAT; import static com.facebook.presto.hive.HiveErrorCode.HIVE_WRITER_CLOSE_ERROR; -import static com.facebook.presto.hive.HivePartitionManager.extractPartitionKeyValues; +import static com.facebook.presto.hive.HivePartitionManager.extractPartitionValues; import static com.facebook.presto.hive.HiveSessionProperties.getHiveStorageFormat; import static com.facebook.presto.hive.HiveSessionProperties.isBucketExecutionEnabled; import static com.facebook.presto.hive.HiveSessionProperties.isCollectColumnStatisticsOnWrite; +import static com.facebook.presto.hive.HiveSessionProperties.isOptimizedMismatchedBucketCount; import static com.facebook.presto.hive.HiveSessionProperties.isRespectTableFormat; import static com.facebook.presto.hive.HiveSessionProperties.isSortedWritingEnabled; import static com.facebook.presto.hive.HiveSessionProperties.isStatisticsEnabled; +import static com.facebook.presto.hive.HiveTableProperties.AVRO_SCHEMA_URL; import static com.facebook.presto.hive.HiveTableProperties.BUCKETED_BY_PROPERTY; import static com.facebook.presto.hive.HiveTableProperties.BUCKET_COUNT_PROPERTY; import static com.facebook.presto.hive.HiveTableProperties.EXTERNAL_LOCATION_PROPERTY; @@ -136,6 +142,7 @@ import static com.facebook.presto.hive.HiveTableProperties.PARTITIONED_BY_PROPERTY; import static com.facebook.presto.hive.HiveTableProperties.SORTED_BY_PROPERTY; import static com.facebook.presto.hive.HiveTableProperties.STORAGE_FORMAT_PROPERTY; +import static com.facebook.presto.hive.HiveTableProperties.getAvroSchemaUrl; import static com.facebook.presto.hive.HiveTableProperties.getBucketProperty; import static com.facebook.presto.hive.HiveTableProperties.getExternalLocation; import static com.facebook.presto.hive.HiveTableProperties.getHiveStorageFormat; @@ -176,7 +183,6 @@ import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.StandardErrorCode.SCHEMA_NOT_EMPTY; import static com.facebook.presto.spi.predicate.TupleDomain.withColumnDomains; -import static com.facebook.presto.spi.statistics.TableStatistics.EMPTY_STATISTICS; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; @@ -196,7 +202,7 @@ import static org.apache.hadoop.hive.metastore.TableType.MANAGED_TABLE; public class HiveMetadata - implements ConnectorMetadata + implements TransactionalMetadata { public static final String PRESTO_VERSION_NAME = "presto_version"; public static final String PRESTO_QUERY_ID_NAME = "presto_query_id"; @@ -206,6 +212,7 @@ public class HiveMetadata private static final String ORC_BLOOM_FILTER_FPP_KEY = "orc.bloom.filter.fpp"; private static final String PARTITIONS_TABLE_SUFFIX = "$partitions"; + public static final String AVRO_SCHEMA_URL_KEY = "avro.schema.url"; private final boolean allowCorruptWritesForTesting; private final SemiTransactionalHiveMetastore metastore; @@ -432,6 +439,12 @@ private ConnectorTableMetadata getTableMetadata(SchemaTableName tableName) properties.put(ORC_BLOOM_FILTER_FPP, Double.parseDouble(orcBloomFilterFfp)); } + // Avro specfic property + String avroSchemaUrl = table.get().getParameters().get(AVRO_SCHEMA_URL_KEY); + if (avroSchemaUrl != null) { + properties.put(AVRO_SCHEMA_URL, avroSchemaUrl); + } + // Hook point for extended versions of the Hive Plugin properties.putAll(tableParameterCodec.decode(table.get().getParameters())); @@ -514,14 +527,16 @@ public Map> listTableColumns(ConnectorSess public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTableHandle tableHandle, Constraint constraint) { if (!isStatisticsEnabled(session)) { - return EMPTY_STATISTICS; + return TableStatistics.empty(); } - List hivePartitions = getPartitionsAsList(tableHandle, constraint); - Map tableColumns = getColumnHandles(session, tableHandle) + Map columns = getColumnHandles(session, tableHandle) .entrySet().stream() .filter(entry -> !((HiveColumnHandle) entry.getValue()).isHidden()) .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); - return hiveStatisticsProvider.getTableStatistics(session, tableHandle, hivePartitions, tableColumns); + Map columnTypes = columns.entrySet().stream() + .collect(toImmutableMap(Map.Entry::getKey, entry -> getColumnMetadata(session, tableHandle, entry.getValue()).getType())); + List partitions = getPartitionsAsList(tableHandle, constraint); + return hiveStatisticsProvider.getTableStatistics(session, ((HiveTableHandle) tableHandle).getSchemaTableName(), columns, columnTypes, partitions); } private List getPartitionsAsList(ConnectorTableHandle tableHandle, Constraint constraint) @@ -614,9 +629,14 @@ public void createTable(ConnectorSession session, ConnectorTableMetadata tableMe String tableName = schemaTableName.getTableName(); List partitionedBy = getPartitionedBy(tableMetadata.getProperties()); Optional bucketProperty = getBucketProperty(tableMetadata.getProperties()); + + if ((bucketProperty.isPresent() || !partitionedBy.isEmpty()) && getAvroSchemaUrl(tableMetadata.getProperties()) != null) { + throw new PrestoException(NOT_SUPPORTED, "Bucketing/Partitioning columns not supported when Avro schema url is set"); + } + List columnHandles = getColumnHandles(tableMetadata, ImmutableSet.copyOf(partitionedBy), typeTranslator); HiveStorageFormat hiveStorageFormat = getHiveStorageFormat(tableMetadata.getProperties()); - Map tableProperties = getEmptyTableProperties(tableMetadata); + Map tableProperties = getEmptyTableProperties(tableMetadata, !partitionedBy.isEmpty(), new HdfsContext(session, schemaName, tableName)); hiveStorageFormat.validateColumns(columnHandles); @@ -668,7 +688,7 @@ public void createTable(ConnectorSession session, ConnectorTableMetadata tableMe new PartitionStatistics(basicStatistics, ImmutableMap.of())); } - private Map getEmptyTableProperties(ConnectorTableMetadata tableMetadata) + private Map getEmptyTableProperties(ConnectorTableMetadata tableMetadata, boolean partitioned, HdfsContext hdfsContext) { Builder tableProperties = ImmutableMap.builder(); @@ -682,12 +702,50 @@ private Map getEmptyTableProperties(ConnectorTableMetadata table tableProperties.put(ORC_BLOOM_FILTER_FPP_KEY, String.valueOf(getOrcBloomFilterFpp(tableMetadata.getProperties()))); } + // Avro specific properties + String avroSchemaUrl = getAvroSchemaUrl(tableMetadata.getProperties()); + if (avroSchemaUrl != null) { + HiveStorageFormat hiveStorageFormat = getHiveStorageFormat(tableMetadata.getProperties()); + if (hiveStorageFormat != HiveStorageFormat.AVRO) { + throw new PrestoException(INVALID_TABLE_PROPERTY, format("Cannot specify %s table property for storage format: %s", AVRO_SCHEMA_URL, hiveStorageFormat)); + } + tableProperties.put(AVRO_SCHEMA_URL_KEY, validateAndNormalizeAvroSchemaUrl(avroSchemaUrl, hdfsContext)); + } + // Table comment property tableMetadata.getComment().ifPresent(value -> tableProperties.put(TABLE_COMMENT, value)); return tableProperties.build(); } + private String validateAndNormalizeAvroSchemaUrl(String url, HdfsContext context) + { + try { + new URL(url).openStream().close(); + return url; + } + catch (MalformedURLException e) { + // try locally + if (new File(url).exists()) { + // hive needs url to have a protocol + return new File(url).toURI().toString(); + } + // try hdfs + try { + if (!hdfsEnvironment.getFileSystem(context, new Path(url)).exists(new Path(url))) { + throw new PrestoException(INVALID_TABLE_PROPERTY, "Cannot locate Avro schema file: " + url); + } + return url; + } + catch (IOException ex) { + throw new PrestoException(INVALID_TABLE_PROPERTY, "Avro schema file is not a valid file system URI: " + url, ex); + } + } + catch (IOException e) { + throw new PrestoException(INVALID_TABLE_PROPERTY, "Cannot open Avro schema file: " + url, e); + } + } + private Path getExternalPath(HdfsContext context, String location) { try { @@ -787,6 +845,7 @@ private static PrincipalPrivileges buildInitialPrivilegeSet(String tableOwner) public void addColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnMetadata column) { HiveTableHandle handle = (HiveTableHandle) tableHandle; + failIfAvroSchemaIsSet(handle); metastore.addColumn(handle.getSchemaName(), handle.getTableName(), column.getName(), toHiveType(typeTranslator, column.getType()), column.getComment()); } @@ -795,6 +854,7 @@ public void addColumn(ConnectorSession session, ConnectorTableHandle tableHandle public void renameColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle source, String target) { HiveTableHandle hiveTableHandle = (HiveTableHandle) tableHandle; + failIfAvroSchemaIsSet(hiveTableHandle); HiveColumnHandle sourceHandle = (HiveColumnHandle) source; metastore.renameColumn(hiveTableHandle.getSchemaName(), hiveTableHandle.getTableName(), sourceHandle.getName(), target); @@ -804,11 +864,27 @@ public void renameColumn(ConnectorSession session, ConnectorTableHandle tableHan public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column) { HiveTableHandle hiveTableHandle = (HiveTableHandle) tableHandle; + failIfAvroSchemaIsSet(hiveTableHandle); HiveColumnHandle columnHandle = (HiveColumnHandle) column; metastore.dropColumn(hiveTableHandle.getSchemaName(), hiveTableHandle.getTableName(), columnHandle.getName()); } + private void failIfAvroSchemaIsSet(HiveTableHandle handle) + { + String tableName = handle.getTableName(); + String schemaName = handle.getSchemaName(); + Optional
table = metastore.getTable(schemaName, tableName); + + if (!table.isPresent()) { + throw new TableNotFoundException(new SchemaTableName(schemaName, tableName)); + } + + if (table.get().getParameters().get(AVRO_SCHEMA_URL_KEY) != null) { + throw new PrestoException(NOT_SUPPORTED, "ALTER TABLE not supported when Avro schema url is set"); + } + } + @Override public void renameTable(ConnectorSession session, ConnectorTableHandle tableHandle, SchemaTableName newTableName) { @@ -838,17 +914,20 @@ public HiveOutputTableHandle beginCreateTable(ConnectorSession session, Connecto throw new PrestoException(NOT_SUPPORTED, "External tables cannot be created using CREATE TABLE AS"); } + if (getAvroSchemaUrl(tableMetadata.getProperties()) != null) { + throw new PrestoException(NOT_SUPPORTED, "CREATE TABLE AS not supported when Avro schema url is set"); + } + HiveStorageFormat tableStorageFormat = getHiveStorageFormat(tableMetadata.getProperties()); List partitionedBy = getPartitionedBy(tableMetadata.getProperties()); Optional bucketProperty = getBucketProperty(tableMetadata.getProperties()); - Map tableProperties = getEmptyTableProperties(tableMetadata); - // get the root directory for the database SchemaTableName schemaTableName = tableMetadata.getTable(); String schemaName = schemaTableName.getSchemaName(); String tableName = schemaTableName.getTableName(); + Map tableProperties = getEmptyTableProperties(tableMetadata, !partitionedBy.isEmpty(), new HdfsContext(session, schemaName, tableName)); List columnHandles = getColumnHandles(tableMetadata, ImmutableSet.copyOf(partitionedBy), typeTranslator); HiveStorageFormat partitionStorageFormat = isRespectTableFormat(session) ? tableStorageFormat : getHiveStorageFormat(session); @@ -1040,17 +1119,17 @@ private void createEmptyFile(ConnectorSession session, Path path, Table table, O } for (String fileName : fileNames) { - writeEmptyFile(new Path(path, fileName), conf, schema, format.getSerDe(), format.getOutputFormat()); + writeEmptyFile(session, new Path(path, fileName), conf, schema, format.getSerDe(), format.getOutputFormat()); } } - private static void writeEmptyFile(Path target, JobConf conf, Properties properties, String serDe, String outputFormatName) + private static void writeEmptyFile(ConnectorSession session, Path target, JobConf conf, Properties properties, String serDe, String outputFormatName) { // Some serializers such as Avro set a property in the schema. initializeSerializer(conf, properties, serDe); // The code below is not a try with resources because RecordWriter is not Closeable. - FileSinkOperator.RecordWriter recordWriter = HiveWriteUtils.createRecordWriter(target, conf, properties, outputFormatName); + FileSinkOperator.RecordWriter recordWriter = HiveWriteUtils.createRecordWriter(target, conf, properties, outputFormatName, session); try { recordWriter.close(false); } @@ -1197,7 +1276,7 @@ private Partition buildPartitionObject(ConnectorSession session, Table table, Pa .setDatabaseName(table.getDatabaseName()) .setTableName(table.getTableName()) .setColumns(table.getDataColumns()) - .setValues(extractPartitionKeyValues(partitionUpdate.getName())) + .setValues(extractPartitionValues(partitionUpdate.getName())) .setParameters(ImmutableMap.builder() .put(PRESTO_VERSION_NAME, prestoVersion) .put(PRESTO_QUERY_ID_NAME, session.getQueryId()) @@ -1430,14 +1509,15 @@ public ConnectorTableLayout getTableLayout(ConnectorSession session, ConnectorTa discretePredicates = Optional.of(new DiscretePredicates(partitionColumns, partitionDomains)); } - Optional nodePartitioning = Optional.empty(); + Optional tablePartitioning = Optional.empty(); if (isBucketExecutionEnabled(session) && hiveLayoutHandle.getBucketHandle().isPresent()) { - nodePartitioning = hiveLayoutHandle.getBucketHandle().map(hiveBucketHandle -> new ConnectorTablePartitioning( + tablePartitioning = hiveLayoutHandle.getBucketHandle().map(hiveBucketHandle -> new ConnectorTablePartitioning( new HivePartitioningHandle( - hiveBucketHandle.getBucketCount(), + hiveBucketHandle.getReadBucketCount(), hiveBucketHandle.getColumns().stream() .map(HiveColumnHandle::getHiveType) - .collect(Collectors.toList())), + .collect(Collectors.toList()), + OptionalInt.empty()), hiveBucketHandle.getColumns().stream() .map(ColumnHandle.class::cast) .collect(toList()))); @@ -1447,12 +1527,93 @@ public ConnectorTableLayout getTableLayout(ConnectorSession session, ConnectorTa hiveLayoutHandle, Optional.empty(), predicate, - nodePartitioning, + tablePartitioning, Optional.empty(), discretePredicates, ImmutableList.of()); } + @Override + public Optional getCommonPartitioningHandle(ConnectorSession session, ConnectorPartitioningHandle left, ConnectorPartitioningHandle right) + { + HivePartitioningHandle leftHandle = (HivePartitioningHandle) left; + HivePartitioningHandle rightHandle = (HivePartitioningHandle) right; + + if (!leftHandle.getHiveTypes().equals(rightHandle.getHiveTypes())) { + return Optional.empty(); + } + if (leftHandle.getBucketCount() == rightHandle.getBucketCount()) { + return Optional.of(leftHandle); + } + if (!isOptimizedMismatchedBucketCount(session)) { + return Optional.empty(); + } + + int largerBucketCount = Math.max(leftHandle.getBucketCount(), rightHandle.getBucketCount()); + int smallerBucketCount = Math.min(leftHandle.getBucketCount(), rightHandle.getBucketCount()); + if (largerBucketCount % smallerBucketCount != 0) { + // must be evenly divisible + return Optional.empty(); + } + if (Integer.bitCount(largerBucketCount / smallerBucketCount) != 1) { + // ratio must be power of two + return Optional.empty(); + } + + OptionalInt maxCompatibleBucketCount = min(leftHandle.getMaxCompatibleBucketCount(), rightHandle.getMaxCompatibleBucketCount()); + if (maxCompatibleBucketCount.isPresent() && maxCompatibleBucketCount.getAsInt() < smallerBucketCount) { + // maxCompatibleBucketCount must be larger than or equal to smallerBucketCount + // because the current code uses the smallerBucketCount as the common partitioning handle. + return Optional.empty(); + } + + return Optional.of(new HivePartitioningHandle( + smallerBucketCount, + leftHandle.getHiveTypes(), + maxCompatibleBucketCount)); + } + + private static OptionalInt min(OptionalInt left, OptionalInt right) + { + if (!left.isPresent()) { + return right; + } + if (!right.isPresent()) { + return left; + } + return OptionalInt.of(Math.min(left.getAsInt(), right.getAsInt())); + } + + @Override + public ConnectorTableLayoutHandle getAlternativeLayoutHandle(ConnectorSession session, ConnectorTableLayoutHandle tableLayoutHandle, ConnectorPartitioningHandle partitioningHandle) + { + HiveTableLayoutHandle hiveLayoutHandle = (HiveTableLayoutHandle) tableLayoutHandle; + HivePartitioningHandle hivePartitioningHandle = (HivePartitioningHandle) partitioningHandle; + + checkArgument(hiveLayoutHandle.getBucketHandle().isPresent(), "Hive connector only provides alternative layout for bucketed table"); + HiveBucketHandle bucketHandle = hiveLayoutHandle.getBucketHandle().get(); + ImmutableList bucketTypes = bucketHandle.getColumns().stream().map(HiveColumnHandle::getHiveType).collect(toImmutableList()); + checkArgument( + hivePartitioningHandle.getHiveTypes().equals(bucketTypes), + "Types from the new PartitioningHandle (%s) does not match the TableLayoutHandle (%s)", + hivePartitioningHandle.getHiveTypes(), + bucketTypes); + int largerBucketCount = Math.max(bucketHandle.getTableBucketCount(), hivePartitioningHandle.getBucketCount()); + int smallerBucketCount = Math.min(bucketHandle.getTableBucketCount(), hivePartitioningHandle.getBucketCount()); + checkArgument( + largerBucketCount % smallerBucketCount == 0 && Integer.bitCount(largerBucketCount / smallerBucketCount) == 1, + "The requested partitioning is not a valid alternative for the table layout"); + + return new HiveTableLayoutHandle( + hiveLayoutHandle.getSchemaTableName(), + hiveLayoutHandle.getPartitionColumns(), + hiveLayoutHandle.getPartitions().get(), + hiveLayoutHandle.getCompactEffectivePredicate(), + hiveLayoutHandle.getPromisedPredicate(), + Optional.of(new HiveBucketHandle(bucketHandle.getColumns(), bucketHandle.getTableBucketCount(), hivePartitioningHandle.getBucketCount())), + hiveLayoutHandle.getBucketFilter()); + } + @VisibleForTesting static TupleDomain createPredicate(List partitionColumns, List partitions) { @@ -1524,10 +1685,11 @@ public Optional getInsertLayout(ConnectorSession sessio } HivePartitioningHandle partitioningHandle = new HivePartitioningHandle( - hiveBucketHandle.get().getBucketCount(), + hiveBucketHandle.get().getTableBucketCount(), hiveBucketHandle.get().getColumns().stream() .map(HiveColumnHandle::getHiveType) - .collect(Collectors.toList())); + .collect(Collectors.toList()), + OptionalInt.of(hiveBucketHandle.get().getTableBucketCount())); List partitionColumns = hiveBucketHandle.get().getColumns().stream() .map(HiveColumnHandle::getName) .collect(Collectors.toList()); @@ -1555,7 +1717,8 @@ public Optional getNewTableLayout(ConnectorSession sess bucketProperty.get().getBucketCount(), bucketedBy.stream() .map(hiveTypeMap::get) - .collect(toList())), + .collect(toList()), + OptionalInt.of(bucketProperty.get().getBucketCount())), bucketedBy)); } @@ -1774,11 +1937,13 @@ private static Function columnMetadataGetter(T handle.isHidden()); } + @Override public void rollback() { metastore.rollback(); } + @Override public void commit() { metastore.commit(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadataFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadataFactory.java index 60259b9ae3871..5fd8d754293ff 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadataFactory.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveMetadataFactory.java @@ -26,10 +26,12 @@ import javax.inject.Inject; import java.util.concurrent.ExecutorService; +import java.util.function.Supplier; import static java.util.Objects.requireNonNull; public class HiveMetadataFactory + implements Supplier { private static final Logger log = Logger.get(HiveMetadataFactory.class); @@ -135,7 +137,8 @@ public HiveMetadataFactory( renameExecution = new BoundedExecutor(executorService, maxConcurrentFileRenames); } - public HiveMetadata create() + @Override + public HiveMetadata get() { SemiTransactionalHiveMetastore metastore = new SemiTransactionalHiveMetastore( hdfsEnvironment, @@ -157,7 +160,7 @@ public HiveMetadata create() partitionUpdateCodec, typeTranslator, prestoVersion, - new MetastoreHiveStatisticsProvider(typeManager, metastore, timeZone), + new MetastoreHiveStatisticsProvider(metastore), maxPartitions); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveNodePartitioningProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveNodePartitioningProvider.java index bbde1dcc14580..488223d6a87da 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveNodePartitioningProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveNodePartitioningProvider.java @@ -16,39 +16,23 @@ import com.facebook.presto.spi.BucketFunction; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorSplit; -import com.facebook.presto.spi.Node; -import com.facebook.presto.spi.NodeManager; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; -import com.google.common.collect.ImmutableMap; -import javax.inject.Inject; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.function.ToIntFunction; import java.util.stream.IntStream; +import static com.facebook.presto.spi.connector.ConnectorBucketNodeMap.createBucketNodeMap; import static com.google.common.collect.ImmutableList.toImmutableList; -import static java.util.Objects.requireNonNull; public class HiveNodePartitioningProvider implements ConnectorNodePartitioningProvider { - private final NodeManager nodeManager; - - @Inject - public HiveNodePartitioningProvider(NodeManager nodeManager) - { - this.nodeManager = requireNonNull(nodeManager, "nodeManager is null"); - } - @Override public BucketFunction getBucketFunction( ConnectorTransactionHandle transactionHandle, @@ -63,18 +47,10 @@ public BucketFunction getBucketFunction( } @Override - public Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { HivePartitioningHandle handle = (HivePartitioningHandle) partitioningHandle; - - List nodes = shuffle(nodeManager.getRequiredWorkerNodes()); - - int bucketCount = handle.getBucketCount(); - ImmutableMap.Builder distribution = ImmutableMap.builder(); - for (int i = 0; i < bucketCount; i++) { - distribution.put(i, nodes.get(i % nodes.size())); - } - return distribution.build(); + return createBucketNodeMap(handle.getBucketCount()); } @Override @@ -83,7 +59,8 @@ public ToIntFunction getSplitBucketFunction( ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { - return value -> ((HiveSplit) value).getBucketNumber().getAsInt(); + return value -> ((HiveSplit) value).getBucketNumber() + .orElseThrow(() -> new IllegalArgumentException("Bucket number not set in split")); } @Override @@ -93,11 +70,4 @@ public List listPartitionHandles(ConnectorTransactionH int bucketCount = handle.getBucketCount(); return IntStream.range(0, bucketCount).mapToObj(HivePartitionHandle::new).collect(toImmutableList()); } - - private static List shuffle(Collection items) - { - List list = new ArrayList<>(items); - Collections.shuffle(list); - return list; - } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSink.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSink.java index a4edbfdafcfa3..14188db9ed453 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSink.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSink.java @@ -85,6 +85,7 @@ public class HivePageSink private long writtenBytes; private long systemMemoryUsage; + private long validationCpuNanos; public HivePageSink( HiveWriterFactory writerFactory, @@ -167,6 +168,12 @@ public long getSystemMemoryUsage() return systemMemoryUsage; } + @Override + public long getValidationCpuNanos() + { + return validationCpuNanos; + } + @Override public CompletableFuture> finish() { @@ -193,6 +200,9 @@ private ListenableFuture> doFinish() writtenBytes = writers.stream() .mapToLong(HiveWriter::getWrittenBytes) .sum(); + validationCpuNanos = writers.stream() + .mapToLong(HiveWriter::getValidationCpuNanos) + .sum(); if (verificationTasks.isEmpty()) { return Futures.immediateFuture(result); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSinkProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSinkProvider.java index 95716f8945a67..6ae83ea98afee 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSinkProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSinkProvider.java @@ -64,6 +64,7 @@ public class HivePageSinkProvider private final EventClient eventClient; private final HiveSessionProperties hiveSessionProperties; private final HiveWriterStats hiveWriterStats; + private final OrcFileWriterFactory orcFileWriterFactory; @Inject public HivePageSinkProvider( @@ -79,7 +80,8 @@ public HivePageSinkProvider( NodeManager nodeManager, EventClient eventClient, HiveSessionProperties hiveSessionProperties, - HiveWriterStats hiveWriterStats) + HiveWriterStats hiveWriterStats, + OrcFileWriterFactory orcFileWriterFactory) { this.fileWriterFactories = ImmutableSet.copyOf(requireNonNull(fileWriterFactories, "fileWriterFactories is null")); this.hdfsEnvironment = requireNonNull(hdfsEnvironment, "hdfsEnvironment is null"); @@ -100,6 +102,7 @@ public HivePageSinkProvider( this.eventClient = requireNonNull(eventClient, "eventClient is null"); this.hiveSessionProperties = requireNonNull(hiveSessionProperties, "hiveSessionProperties is null"); this.hiveWriterStats = requireNonNull(hiveWriterStats, "stats is null"); + this.orcFileWriterFactory = requireNonNull(orcFileWriterFactory, "orcFileWriterFactory is null"); } @Override @@ -150,7 +153,8 @@ private ConnectorPageSink createPageSink(HiveWritableTableHandle handle, boolean nodeManager, eventClient, hiveSessionProperties, - hiveWriterStats); + hiveWriterStats, + orcFileWriterFactory); return new HivePageSink( writerFactory, diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSource.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSource.java index 8094f886b5239..80912e2dc83d6 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSource.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSource.java @@ -514,7 +514,7 @@ public Block apply(Block block) valueIsNull[i] = arrayBlock.isNull(i); offsets[i + 1] = offsets[i] + arrayBlock.getLength(i); } - return ArrayBlock.fromElementBlock(arrayBlock.getPositionCount(), valueIsNull, offsets, elementsBlock); + return ArrayBlock.fromElementBlock(arrayBlock.getPositionCount(), Optional.of(valueIsNull), offsets, elementsBlock); } } @@ -550,7 +550,7 @@ public Block apply(Block block) valueIsNull[i] = mapBlock.isNull(i); offsets[i + 1] = offsets[i] + mapBlock.getEntryCount(i); } - return ((MapType) toType).createBlockFromKeyValue(valueIsNull, offsets, keysBlock, valuesBlock); + return ((MapType) toType).createBlockFromKeyValue(Optional.of(valueIsNull), offsets, keysBlock, valuesBlock); } } @@ -600,7 +600,7 @@ else if (i < rowBlock.getFieldCount()) { for (int i = 0; i < rowBlock.getPositionCount(); i++) { valueIsNull[i] = rowBlock.isNull(i); } - return RowBlock.fromFieldBlocks(valueIsNull, fields); + return RowBlock.fromFieldBlocks(valueIsNull.length, Optional.of(valueIsNull), fields); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSourceProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSourceProvider.java index a8ea96480e6d9..930958ac74ee4 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSourceProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePageSourceProvider.java @@ -90,10 +90,12 @@ public ConnectorPageSource createPageSource(ConnectorTransactionHandle transacti HiveSplit hiveSplit = (HiveSplit) split; Path path = new Path(hiveSplit.getPath()); + Configuration configuration = hdfsEnvironment.getConfiguration(new HdfsContext(session, hiveSplit.getDatabase(), hiveSplit.getTable()), path); + Optional pageSource = createHivePageSource( cursorProviders, pageSourceFactories, - hdfsEnvironment.getConfiguration(new HdfsContext(session, hiveSplit.getDatabase(), hiveSplit.getTable()), path), + configuration, session, path, hiveSplit.getBucketNumber(), @@ -107,7 +109,8 @@ public ConnectorPageSource createPageSource(ConnectorTransactionHandle transacti hiveStorageTimeZone, typeManager, hiveSplit.getColumnCoercions(), - hiveSplit.getBucketConversion()); + hiveSplit.getBucketConversion(), + hiveSplit.isS3SelectPushdownEnabled()); if (pageSource.isPresent()) { return pageSource.get(); } @@ -131,7 +134,8 @@ public static Optional createHivePageSource( DateTimeZone hiveStorageTimeZone, TypeManager typeManager, Map columnCoercions, - Optional bucketConversion) + Optional bucketConversion, + boolean s3SelectPushdownEnabled) { List columnMappings = ColumnMapping.buildColumnMappings( partitionKeys, @@ -191,7 +195,8 @@ public static Optional createHivePageSource( toColumnHandles(regularAndInterimColumnMappings, doCoercion), effectivePredicate, hiveStorageTimeZone, - typeManager); + typeManager, + s3SelectPushdownEnabled); if (cursor.isPresent()) { RecordCursor delegate = cursor.get(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionManager.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionManager.java index 976a4586b0d87..de0c970bd861d 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionManager.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionManager.java @@ -181,27 +181,22 @@ private Optional parseValuesAndFilterPartition( List partitionColumnTypes, Constraint constraint) { - List keys = extractPartitionKeyValues(partitionId); + HivePartition partition = parsePartition(tableName, partitionId, partitionColumns, partitionColumnTypes, timeZone); Map domains = constraint.getSummary().getDomains().get(); - ImmutableMap.Builder builder = ImmutableMap.builder(); - for (int i = 0; i < partitionColumns.size(); i++) { - HiveColumnHandle column = partitionColumns.get(i); - NullableValue parsedValue = parsePartitionValue(partitionId, keys.get(i), partitionColumnTypes.get(i), timeZone); - + for (HiveColumnHandle column : partitionColumns) { + NullableValue value = partition.getKeys().get(column); Domain allowedDomain = domains.get(column); - if (allowedDomain != null && !allowedDomain.includesNullableValue(parsedValue.getValue())) { + if (allowedDomain != null && !allowedDomain.includesNullableValue(value.getValue())) { return Optional.empty(); } - builder.put(column, parsedValue); } - Map values = builder.build(); - if (constraint.predicate().isPresent() && !constraint.predicate().get().test(values)) { + if (constraint.predicate().isPresent() && !constraint.predicate().get().test(partition.getKeys())) { return Optional.empty(); } - return Optional.of(new HivePartition(tableName, partitionId, values)); + return Optional.of(partition); } private Table getTable(SemiTransactionalHiveMetastore metastore, SchemaTableName tableName) @@ -282,7 +277,25 @@ else if (type instanceof TinyintType .orElseThrow(() -> new TableNotFoundException(tableName)); } - public static List extractPartitionKeyValues(String partitionName) + public static HivePartition parsePartition( + SchemaTableName tableName, + String partitionName, + List partitionColumns, + List partitionColumnTypes, + DateTimeZone timeZone) + { + List partitionValues = extractPartitionValues(partitionName); + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (int i = 0; i < partitionColumns.size(); i++) { + HiveColumnHandle column = partitionColumns.get(i); + NullableValue parsedValue = parsePartitionValue(partitionName, partitionValues.get(i), partitionColumnTypes.get(i), timeZone); + builder.put(column, parsedValue); + } + Map values = builder.build(); + return new HivePartition(tableName, partitionName, values); + } + + public static List extractPartitionValues(String partitionName) { ImmutableList.Builder values = ImmutableList.builder(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionMetadata.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionMetadata.java index 1690461f90a65..40314d3e560d3 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionMetadata.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitionMetadata.java @@ -26,7 +26,10 @@ public class HivePartitionMetadata private final HivePartition hivePartition; private final Map columnCoercions; - HivePartitionMetadata(HivePartition hivePartition, Optional partition, Map columnCoercions) + HivePartitionMetadata( + HivePartition hivePartition, + Optional partition, + Map columnCoercions) { this.partition = requireNonNull(partition, "partition is null"); this.hivePartition = requireNonNull(hivePartition, "hivePartition is null"); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitioningHandle.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitioningHandle.java index aaae1fe688290..46cc4889b483a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitioningHandle.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePartitioningHandle.java @@ -19,8 +19,9 @@ import java.util.List; import java.util.Objects; +import java.util.OptionalInt; -import static com.google.common.base.MoreObjects.toStringHelper; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; public class HivePartitioningHandle @@ -28,14 +29,17 @@ public class HivePartitioningHandle { private final int bucketCount; private final List hiveTypes; + private final OptionalInt maxCompatibleBucketCount; @JsonCreator public HivePartitioningHandle( @JsonProperty("bucketCount") int bucketCount, - @JsonProperty("hiveTypes") List hiveTypes) + @JsonProperty("hiveTypes") List hiveTypes, + @JsonProperty("maxCompatibleBucketCount") OptionalInt maxCompatibleBucketCount) { this.bucketCount = bucketCount; this.hiveTypes = requireNonNull(hiveTypes, "hiveTypes is null"); + this.maxCompatibleBucketCount = maxCompatibleBucketCount; } @JsonProperty @@ -50,13 +54,16 @@ public List getHiveTypes() return hiveTypes; } + @JsonProperty + public OptionalInt getMaxCompatibleBucketCount() + { + return maxCompatibleBucketCount; + } + @Override public String toString() { - return toStringHelper(this) - .add("bucketCount", bucketCount) - .add("hiveTypes", hiveTypes) - .toString(); + return format("buckets=%s, hiveTypes=%s", bucketCount, hiveTypes); } @Override diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HivePlugin.java b/presto-hive/src/main/java/com/facebook/presto/hive/HivePlugin.java index cb0aba8ce5022..90295056b1b7e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HivePlugin.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HivePlugin.java @@ -18,25 +18,28 @@ import com.facebook.presto.spi.connector.ConnectorFactory; import com.google.common.collect.ImmutableList; +import java.util.Optional; + import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.Objects.requireNonNull; public class HivePlugin implements Plugin { private final String name; - private ExtendedHiveMetastore metastore; + private final Optional metastore; public HivePlugin(String name) { - this(name, null); + this(name, Optional.empty()); } - public HivePlugin(String name, ExtendedHiveMetastore metastore) + public HivePlugin(String name, Optional metastore) { checkArgument(!isNullOrEmpty(name), "name is null or empty"); this.name = name; - this.metastore = metastore; + this.metastore = requireNonNull(metastore, "metastore is null"); } @Override diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveProcedureModule.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveProcedureModule.java new file mode 100644 index 0000000000000..708d22bc29cc0 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveProcedureModule.java @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.procedure.Procedure; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Scopes; +import com.google.inject.multibindings.Multibinder; + +import static com.google.inject.multibindings.Multibinder.newSetBinder; + +public class HiveProcedureModule + implements Module +{ + @Override + public void configure(Binder binder) + { + Multibinder procedures = newSetBinder(binder, Procedure.class); + procedures.addBinding().toProvider(CreateEmptyPartitionProcedure.class).in(Scopes.SINGLETON); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveRecordCursorProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveRecordCursorProvider.java index cca7d89f254be..bf178e7959eac 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveRecordCursorProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveRecordCursorProvider.java @@ -38,5 +38,6 @@ Optional createRecordCursor( List columns, TupleDomain effectivePredicate, DateTimeZone hiveStorageTimeZone, - TypeManager typeManager); + TypeManager typeManager, + boolean s3SelectPushdownEnabled); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSessionProperties.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSessionProperties.java index 62988901396a5..f9e884d3b8d90 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSessionProperties.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSessionProperties.java @@ -29,9 +29,9 @@ import static com.facebook.presto.hive.HiveSessionProperties.InsertExistingPartitionsBehavior.ERROR; import static com.facebook.presto.spi.StandardErrorCode.INVALID_SESSION_PROPERTY; import static com.facebook.presto.spi.session.PropertyMetadata.booleanProperty; -import static com.facebook.presto.spi.session.PropertyMetadata.doubleProperty; import static com.facebook.presto.spi.session.PropertyMetadata.integerProperty; import static com.facebook.presto.spi.session.PropertyMetadata.stringProperty; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; import static com.google.common.base.Preconditions.checkArgument; @@ -61,9 +61,10 @@ public final class HiveSessionProperties private static final String ORC_OPTIMIZED_WRITER_MAX_DICTIONARY_MEMORY = "orc_optimized_writer_max_dictionary_memory"; private static final String HIVE_STORAGE_FORMAT = "hive_storage_format"; private static final String RESPECT_TABLE_FORMAT = "respect_table_format"; - private static final String PARQUET_PREDICATE_PUSHDOWN_ENABLED = "parquet_predicate_pushdown_enabled"; - private static final String PARQUET_OPTIMIZED_READER_ENABLED = "parquet_optimized_reader_enabled"; private static final String PARQUET_USE_COLUMN_NAME = "parquet_use_column_names"; + private static final String PARQUET_FAIL_WITH_CORRUPTED_STATISTICS = "parquet_fail_with_corrupted_statistics"; + private static final String PARQUET_WRITER_BLOCK_SIZE = "parquet_writer_block_size"; + private static final String PARQUET_WRITER_PAGE_SIZE = "parquet_writer_page_size"; private static final String MAX_SPLIT_SIZE = "max_split_size"; private static final String MAX_INITIAL_SPLIT_SIZE = "max_initial_split_size"; public static final String RCFILE_OPTIMIZED_WRITER_ENABLED = "rcfile_optimized_writer_enabled"; @@ -71,7 +72,10 @@ public final class HiveSessionProperties private static final String SORTED_WRITING_ENABLED = "sorted_writing_enabled"; private static final String STATISTICS_ENABLED = "statistics_enabled"; private static final String PARTITION_STATISTICS_SAMPLE_SIZE = "partition_statistics_sample_size"; + private static final String IGNORE_CORRUPTED_STATISTICS = "ignore_corrupted_statistics"; private static final String COLLECT_COLUMN_STATISTICS_ON_WRITE = "collect_column_statistics_on_write"; + private static final String OPTIMIZE_MISMATCHED_BUCKET_COUNT = "optimize_mismatched_bucket_count"; + private static final String S3_SELECT_PUSHDOWN_ENABLED = "s3_select_pushdown_enabled"; private final List> sessionProperties; @@ -94,7 +98,7 @@ public static InsertExistingPartitionsBehavior valueOf(String value, boolean imm } @Inject - public HiveSessionProperties(HiveClientConfig hiveClientConfig, OrcFileWriterConfig orcFileWriterConfig) + public HiveSessionProperties(HiveClientConfig hiveClientConfig, OrcFileWriterConfig orcFileWriterConfig, ParquetFileWriterConfig parquetFileWriterConfig) { sessionProperties = ImmutableList.of( booleanProperty( @@ -166,11 +170,23 @@ public HiveSessionProperties(HiveClientConfig hiveClientConfig, OrcFileWriterCon "Experimental: ORC: Force all validation for files", hiveClientConfig.getOrcWriterValidationPercentage() > 0.0, false), - doubleProperty( + new PropertyMetadata<>( ORC_OPTIMIZED_WRITER_VALIDATE_PERCENTAGE, "Experimental: ORC: sample percentage for validation for files", + DOUBLE, + Double.class, hiveClientConfig.getOrcWriterValidationPercentage(), - false), + false, + value -> { + double doubleValue = ((Number) value).doubleValue(); + if (doubleValue < 0.0 || doubleValue > 100.0) { + throw new PrestoException( + INVALID_SESSION_PROPERTY, + format("%s must be between 0.0 and 100.0 inclusive: %s", ORC_OPTIMIZED_WRITER_VALIDATE_PERCENTAGE, doubleValue)); + } + return doubleValue; + }, + value -> value), stringProperty( ORC_OPTIMIZED_WRITER_VALIDATE_MODE, "Experimental: ORC: Level of detail in ORC validation", @@ -206,21 +222,26 @@ public HiveSessionProperties(HiveClientConfig hiveClientConfig, OrcFileWriterCon "Write new partitions using table format rather than default storage format", hiveClientConfig.isRespectTableFormat(), false), - booleanProperty( - PARQUET_OPTIMIZED_READER_ENABLED, - "Experimental: Parquet: Enable optimized reader", - hiveClientConfig.isParquetOptimizedReaderEnabled(), - false), - booleanProperty( - PARQUET_PREDICATE_PUSHDOWN_ENABLED, - "Experimental: Parquet: Enable predicate pushdown for Parquet", - hiveClientConfig.isParquetPredicatePushdownEnabled(), - false), booleanProperty( PARQUET_USE_COLUMN_NAME, "Experimental: Parquet: Access Parquet columns using names from the file", hiveClientConfig.isUseParquetColumnNames(), false), + booleanProperty( + PARQUET_FAIL_WITH_CORRUPTED_STATISTICS, + "Parquet: Fail when scanning Parquet files with corrupted statistics", + hiveClientConfig.isFailOnCorruptedParquetStatistics(), + false), + dataSizeSessionProperty( + PARQUET_WRITER_BLOCK_SIZE, + "Parquet: Writer block size", + parquetFileWriterConfig.getBlockSize(), + false), + dataSizeSessionProperty( + PARQUET_WRITER_PAGE_SIZE, + "Parquet: Writer page size", + parquetFileWriterConfig.getPageSize(), + false), dataSizeSessionProperty( MAX_SPLIT_SIZE, "Max split size", @@ -256,10 +277,25 @@ public HiveSessionProperties(HiveClientConfig hiveClientConfig, OrcFileWriterCon "Maximum sample size of the partitions column statistics", hiveClientConfig.getPartitionStatisticsSampleSize(), false), + booleanProperty( + IGNORE_CORRUPTED_STATISTICS, + "Experimental: Ignore corrupted statistics rather than failing", + hiveClientConfig.isIgnoreCorruptedStatistics(), + false), booleanProperty( COLLECT_COLUMN_STATISTICS_ON_WRITE, "Experimental: Enables automatic column level statistics collection on write", hiveClientConfig.isCollectColumnStatisticsOnWrite(), + false), + booleanProperty( + OPTIMIZE_MISMATCHED_BUCKET_COUNT, + "Experimenal: Enable optimization to avoid shuffle when bucket count is compatible but not the same", + hiveClientConfig.isOptimizeMismatchedBucketCount(), + false), + booleanProperty( + S3_SELECT_PUSHDOWN_ENABLED, + "S3 Select pushdown enabled", + hiveClientConfig.isS3SelectPushdownEnabled(), false)); } @@ -283,11 +319,6 @@ public static InsertExistingPartitionsBehavior getInsertExistingPartitionsBehavi return session.getProperty(INSERT_EXISTING_PARTITIONS_BEHAVIOR, InsertExistingPartitionsBehavior.class); } - public static boolean isParquetOptimizedReaderEnabled(ConnectorSession session) - { - return session.getProperty(PARQUET_OPTIMIZED_READER_ENABLED, Boolean.class); - } - public static boolean isOrcBloomFiltersEnabled(ConnectorSession session) { return session.getProperty(ORC_BLOOM_FILTERS_ENABLED, Boolean.class); @@ -338,10 +369,7 @@ public static boolean isOrcOptimizedWriterValidate(ConnectorSession session) boolean validate = session.getProperty(ORC_OPTIMIZED_WRITER_VALIDATE, Boolean.class); double percentage = session.getProperty(ORC_OPTIMIZED_WRITER_VALIDATE_PERCENTAGE, Double.class); - // if validation sampling is disabled, just use the session property value - if (percentage <= 0.0) { - return validate; - } + checkArgument(percentage >= 0.0 && percentage <= 100.0); // session property can disabled validation if (!validate) { @@ -388,14 +416,24 @@ public static boolean isRespectTableFormat(ConnectorSession session) return session.getProperty(RESPECT_TABLE_FORMAT, Boolean.class); } - public static boolean isParquetPredicatePushdownEnabled(ConnectorSession session) + public static boolean isUseParquetColumnNames(ConnectorSession session) { - return session.getProperty(PARQUET_PREDICATE_PUSHDOWN_ENABLED, Boolean.class); + return session.getProperty(PARQUET_USE_COLUMN_NAME, Boolean.class); } - public static boolean isUseParquetColumnNames(ConnectorSession session) + public static boolean isFailOnCorruptedParquetStatistics(ConnectorSession session) { - return session.getProperty(PARQUET_USE_COLUMN_NAME, Boolean.class); + return session.getProperty(PARQUET_FAIL_WITH_CORRUPTED_STATISTICS, Boolean.class); + } + + public static DataSize getParquetWriterBlockSize(ConnectorSession session) + { + return session.getProperty(PARQUET_WRITER_BLOCK_SIZE, DataSize.class); + } + + public static DataSize getParquetWriterPageSize(ConnectorSession session) + { + return session.getProperty(PARQUET_WRITER_PAGE_SIZE, DataSize.class); } public static DataSize getMaxSplitSize(ConnectorSession session) @@ -423,6 +461,11 @@ public static boolean isSortedWritingEnabled(ConnectorSession session) return session.getProperty(SORTED_WRITING_ENABLED, Boolean.class); } + public static boolean isS3SelectPushdownEnabled(ConnectorSession session) + { + return session.getProperty(S3_SELECT_PUSHDOWN_ENABLED, Boolean.class); + } + public static boolean isStatisticsEnabled(ConnectorSession session) { return session.getProperty(STATISTICS_ENABLED, Boolean.class); @@ -437,11 +480,21 @@ public static int getPartitionStatisticsSampleSize(ConnectorSession session) return size; } + public static boolean isIgnoreCorruptedStatistics(ConnectorSession session) + { + return session.getProperty(IGNORE_CORRUPTED_STATISTICS, Boolean.class); + } + public static boolean isCollectColumnStatisticsOnWrite(ConnectorSession session) { return session.getProperty(COLLECT_COLUMN_STATISTICS_ON_WRITE, Boolean.class); } + public static boolean isOptimizedMismatchedBucketCount(ConnectorSession session) + { + return session.getProperty(OPTIMIZE_MISMATCHED_BUCKET_COUNT, Boolean.class); + } + public static PropertyMetadata dataSizeSessionProperty(String name, String description, DataSize defaultValue, boolean hidden) { return new PropertyMetadata<>( diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplit.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplit.java index b95c4294c9b46..68dfec7ef2346 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplit.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplit.java @@ -50,6 +50,7 @@ public class HiveSplit private final boolean forceLocalScheduling; private final Map columnCoercions; // key: hiveColumnIndex private final Optional bucketConversion; + private final boolean s3SelectPushdownEnabled; @JsonCreator public HiveSplit( @@ -67,7 +68,8 @@ public HiveSplit( @JsonProperty("forceLocalScheduling") boolean forceLocalScheduling, @JsonProperty("effectivePredicate") TupleDomain effectivePredicate, @JsonProperty("columnCoercions") Map columnCoercions, - @JsonProperty("bucketConversion") Optional bucketConversion) + @JsonProperty("bucketConversion") Optional bucketConversion, + @JsonProperty("s3SelectPushdownEnabled") boolean s3SelectPushdownEnabled) { checkArgument(start >= 0, "start must be positive"); checkArgument(length >= 0, "length must be positive"); @@ -99,6 +101,7 @@ public HiveSplit( this.effectivePredicate = effectivePredicate; this.columnCoercions = columnCoercions; this.bucketConversion = bucketConversion; + this.s3SelectPushdownEnabled = s3SelectPushdownEnabled; } @JsonProperty @@ -198,6 +201,12 @@ public boolean isRemotelyAccessible() return !forceLocalScheduling; } + @JsonProperty + public boolean isS3SelectPushdownEnabled() + { + return s3SelectPushdownEnabled; + } + @Override public Object getInfo() { @@ -211,6 +220,7 @@ public Object getInfo() .put("table", table) .put("forceLocalScheduling", forceLocalScheduling) .put("partitionName", partitionName) + .put("s3SelectPushdownEnabled", s3SelectPushdownEnabled) .build(); } @@ -223,6 +233,7 @@ public String toString() .addValue(length) .addValue(fileSize) .addValue(effectivePredicate) + .addValue(s3SelectPushdownEnabled) .toString(); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitManager.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitManager.java index 0588bbbd4b5f1..8689acc3e59f2 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitManager.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitManager.java @@ -190,7 +190,7 @@ public ConnectorSplitSource getSplits(ConnectorTransactionHandle transaction, Co // sort partitions partitions = Ordering.natural().onResultOf(HivePartition::getPartitionId).reverse().sortedCopy(partitions); - Iterable hivePartitions = getPartitionMetadata(metastore, table, tableName, partitions, bucketHandle.map(HiveBucketHandle::toBucketProperty)); + Iterable hivePartitions = getPartitionMetadata(metastore, table, tableName, partitions, bucketHandle.map(HiveBucketHandle::toTableBucketProperty), session); HiveSplitLoader hiveSplitLoader = new BackgroundHiveSplitLoader( table, @@ -248,7 +248,7 @@ public CounterStat getHighMemorySplitSource() return highMemorySplitSourceCounter; } - private Iterable getPartitionMetadata(SemiTransactionalHiveMetastore metastore, Table table, SchemaTableName tableName, List hivePartitions, Optional bucketProperty) + private Iterable getPartitionMetadata(SemiTransactionalHiveMetastore metastore, Table table, SchemaTableName tableName, List hivePartitions, Optional bucketProperty, ConnectorSession session) { if (hivePartitions.isEmpty()) { return ImmutableList.of(); @@ -360,7 +360,7 @@ private Iterable getPartitionMetadata(SemiTransactionalHi return concat(partitionBatches); } - private static boolean isBucketCountCompatible(int tableBucketCount, int partitionBucketCount) + static boolean isBucketCountCompatible(int tableBucketCount, int partitionBucketCount) { checkArgument(tableBucketCount > 0 && partitionBucketCount > 0); int larger = Math.max(tableBucketCount, partitionBucketCount); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitSource.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitSource.java index 28eaf6071f7d5..c1c7fe89d9b61 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitSource.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveSplitSource.java @@ -45,6 +45,7 @@ import java.util.function.Function; import java.util.function.Predicate; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_EXCEEDED_SPLIT_BUFFERING_LIMIT; import static com.facebook.presto.hive.HiveErrorCode.HIVE_FILE_NOT_FOUND; import static com.facebook.presto.hive.HiveErrorCode.HIVE_UNKNOWN_ERROR; import static com.facebook.presto.hive.HiveSessionProperties.getMaxInitialSplitSize; @@ -53,7 +54,6 @@ import static com.facebook.presto.hive.HiveSplitSource.StateKind.FAILED; import static com.facebook.presto.hive.HiveSplitSource.StateKind.INITIAL; import static com.facebook.presto.hive.HiveSplitSource.StateKind.NO_MORE_SPLITS; -import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -286,7 +286,7 @@ ListenableFuture addToQueue(InternalHiveSplit split) log.warn("Split buffering for %s.%s in query %s exceeded memory limit (%s). %s splits are buffered.", databaseName, tableName, queryId, succinctBytes(maxOutstandingSplitsBytes), getBufferedInternalSplitCount()); } - throw new PrestoException(GENERIC_INTERNAL_ERROR, format( + throw new PrestoException(HIVE_EXCEEDED_SPLIT_BUFFERING_LIMIT, format( "Split buffering for %s.%s exceeded memory limit (%s). %s splits are buffered.", databaseName, tableName, succinctBytes(maxOutstandingSplitsBytes), getBufferedInternalSplitCount())); } @@ -376,7 +376,9 @@ public CompletableFuture getNextBatch(ConnectorPartitionHan internalSplit.isForceLocalScheduling(), (TupleDomain) compactEffectivePredicate, transformValues(internalSplit.getColumnCoercions(), HiveTypeName::toHiveType), - internalSplit.getBucketConversion())); + internalSplit.getBucketConversion(), + internalSplit.isS3SelectPushdownEnabled())); + internalSplit.increaseStart(splitBytes); if (internalSplit.isDone()) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableLayoutHandle.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableLayoutHandle.java index ad1993c5670b6..4813edd617001 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableLayoutHandle.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableLayoutHandle.java @@ -125,6 +125,11 @@ public Optional getBucketFilter() @Override public String toString() { - return schemaTableName.toString(); + StringBuilder result = new StringBuilder(); + result.append(schemaTableName.toString()); + if (bucketHandle.isPresent()) { + result.append(" bucket=").append(bucketHandle.get().getReadBucketCount()); + } + return result.toString(); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableProperties.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableProperties.java index 1b473c9344820..3263ce93b97a6 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableProperties.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTableProperties.java @@ -49,6 +49,7 @@ public class HiveTableProperties public static final String SORTED_BY_PROPERTY = "sorted_by"; public static final String ORC_BLOOM_FILTER_COLUMNS = "orc_bloom_filter_columns"; public static final String ORC_BLOOM_FILTER_FPP = "orc_bloom_filter_fpp"; + public static final String AVRO_SCHEMA_URL = "avro_schema_url"; private final List> tableProperties; @@ -124,7 +125,8 @@ public HiveTableProperties(TypeManager typeManager, HiveClientConfig config) "ORC Bloom filter false positive probability", config.getOrcDefaultBloomFilterFpp(), false), - integerProperty(BUCKET_COUNT_PROPERTY, "Number of buckets", 0, false)); + integerProperty(BUCKET_COUNT_PROPERTY, "Number of buckets", 0, false), + stringProperty(AVRO_SCHEMA_URL, "URI pointing to Avro schema for the table", null, false)); } public List> getTableProperties() @@ -137,6 +139,11 @@ public static String getExternalLocation(Map tableProperties) return (String) tableProperties.get(EXTERNAL_LOCATION_PROPERTY); } + public static String getAvroSchemaUrl(Map tableProperties) + { + return (String) tableProperties.get(AVRO_SCHEMA_URL); + } + public static HiveStorageFormat getHiveStorageFormat(Map tableProperties) { return (HiveStorageFormat) tableProperties.get(STORAGE_FORMAT_PROPERTY); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionHandle.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionHandle.java index db3c159cddd0d..7fee00687630f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionHandle.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionHandle.java @@ -20,7 +20,6 @@ import java.util.Objects; import java.util.UUID; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; public class HiveTransactionHandle @@ -67,8 +66,6 @@ public int hashCode() @Override public String toString() { - return toStringHelper(this) - .add("uuid", uuid) - .toString(); + return uuid.toString(); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionManager.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionManager.java index 5807b2d8c98fa..6b2c20871154d 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionManager.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveTransactionManager.java @@ -25,24 +25,24 @@ public class HiveTransactionManager { - private final ConcurrentMap transactions = new ConcurrentHashMap<>(); + private final ConcurrentMap transactions = new ConcurrentHashMap<>(); @Inject public HiveTransactionManager() { } - public ConnectorMetadata get(ConnectorTransactionHandle transactionHandle) + public TransactionalMetadata get(ConnectorTransactionHandle transactionHandle) { return transactions.get(transactionHandle); } - public ConnectorMetadata remove(ConnectorTransactionHandle transactionHandle) + public TransactionalMetadata remove(ConnectorTransactionHandle transactionHandle) { return transactions.remove(transactionHandle); } - public void put(ConnectorTransactionHandle transactionHandle, ConnectorMetadata metadata) + public void put(ConnectorTransactionHandle transactionHandle, TransactionalMetadata metadata) { ConnectorMetadata previousValue = transactions.putIfAbsent(transactionHandle, metadata); checkState(previousValue == null); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveType.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveType.java index 75532dd6c6e39..ea4fbc9cf99f0 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveType.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveType.java @@ -33,8 +33,6 @@ import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo; import org.apache.hadoop.hive.serde2.typeinfo.VarcharTypeInfo; -import javax.annotation.Nonnull; - import java.util.List; import java.util.Locale; import java.util.Optional; @@ -178,14 +176,12 @@ public static boolean isSupportedType(TypeInfo typeInfo) } @JsonCreator - @Nonnull public static HiveType valueOf(String hiveTypeName) { requireNonNull(hiveTypeName, "hiveTypeName is null"); return toHiveType(getTypeInfoFromTypeString(hiveTypeName)); } - @Nonnull public static List toHiveTypes(String hiveTypes) { requireNonNull(hiveTypes, "hiveTypes is null"); @@ -194,14 +190,12 @@ public static List toHiveTypes(String hiveTypes) .collect(toList())); } - @Nonnull private static HiveType toHiveType(TypeInfo typeInfo) { requireNonNull(typeInfo, "typeInfo is null"); return new HiveType(typeInfo); } - @Nonnull public static HiveType toHiveType(TypeTranslator typeTranslator, Type type) { requireNonNull(typeTranslator, "typeTranslator is null"); @@ -209,7 +203,6 @@ public static HiveType toHiveType(TypeTranslator typeTranslator, Type type) return new HiveType(typeTranslator.translate(type)); } - @Nonnull private static TypeSignature getTypeSignature(TypeInfo typeInfo) { switch (typeInfo.getCategory()) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveUtil.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveUtil.java index 8c774ebd8cb14..5d970c7223a2e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveUtil.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveUtil.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.hive; +import com.facebook.presto.hadoop.TextLineLengthLimitExceededException; +import com.facebook.presto.hive.avro.PrestoAvroSerDe; import com.facebook.presto.hive.metastore.Column; import com.facebook.presto.hive.metastore.Table; import com.facebook.presto.hive.util.FooterAwareRecordReader; @@ -45,14 +47,16 @@ import org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat; import org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat; import org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe; +import org.apache.hadoop.hive.serde2.AbstractSerDe; import org.apache.hadoop.hive.serde2.Deserializer; import org.apache.hadoop.hive.serde2.SerDeException; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; -import org.apache.hadoop.hive.serde2.objectinspector.StructField; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.typeinfo.StructTypeInfo; import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.WritableComparable; +import org.apache.hadoop.io.compress.CompressionCodec; +import org.apache.hadoop.io.compress.CompressionCodecFactory; import org.apache.hadoop.mapred.FileSplit; import org.apache.hadoop.mapred.InputFormat; import org.apache.hadoop.mapred.JobConf; @@ -71,6 +75,7 @@ import javax.annotation.Nullable; import java.io.IOException; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; @@ -90,6 +95,7 @@ import static com.facebook.presto.hive.HiveColumnHandle.isBucketColumnHandle; import static com.facebook.presto.hive.HiveColumnHandle.isPathColumnHandle; import static com.facebook.presto.hive.HiveColumnHandle.pathColumnHandle; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA; import static com.facebook.presto.hive.HiveErrorCode.HIVE_CANNOT_OPEN_SPLIT; import static com.facebook.presto.hive.HiveErrorCode.HIVE_INVALID_METADATA; import static com.facebook.presto.hive.HiveErrorCode.HIVE_INVALID_PARTITION_VALUE; @@ -97,8 +103,9 @@ import static com.facebook.presto.hive.HiveErrorCode.HIVE_SERDE_NOT_FOUND; import static com.facebook.presto.hive.HiveErrorCode.HIVE_UNSUPPORTED_FORMAT; import static com.facebook.presto.hive.HivePartitionKey.HIVE_DEFAULT_DYNAMIC_PARTITION; -import static com.facebook.presto.hive.metastore.MetastoreUtil.getHiveSchema; +import static com.facebook.presto.hive.util.ConfigurationUtils.copy; import static com.facebook.presto.hive.util.ConfigurationUtils.toJobConf; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -148,6 +155,7 @@ public final class HiveUtil private static final DateTimeFormatter HIVE_DATE_PARSER = ISODateTimeFormat.date().withZoneUTC(); private static final DateTimeFormatter HIVE_TIMESTAMP_PARSER; + private static final Field COMPRESSION_CODECS_FIELD; private static final Pattern SUPPORTED_DECIMAL_TYPE = Pattern.compile(DECIMAL_TYPE_NAME + "\\((\\d+),(\\d+)\\)"); private static final int DECIMAL_PRECISION_GROUP = 1; @@ -166,6 +174,14 @@ public final class HiveUtil }; DateTimePrinter timestampWithoutTimeZonePrinter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS").getPrinter(); HIVE_TIMESTAMP_PARSER = new DateTimeFormatterBuilder().append(timestampWithoutTimeZonePrinter, timestampWithoutTimeZoneParser).toFormatter().withZoneUTC(); + + try { + COMPRESSION_CODECS_FIELD = TextInputFormat.class.getDeclaredField("compressionCodecs"); + COMPRESSION_CODECS_FIELD.setAccessible(true); + } + catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } } private HiveUtil() @@ -216,6 +232,10 @@ private HiveUtil() return recordReader; } catch (IOException e) { + if (e instanceof TextLineLengthLimitExceededException) { + throw new PrestoException(HIVE_BAD_DATA, "Line too long in text file: " + path, e); + } + throw new PrestoException(HIVE_CANNOT_OPEN_SPLIT, format("Error opening Hive split %s (offset=%s, length=%s) using %s: %s", path, start, @@ -232,6 +252,24 @@ public static void setReadColumns(Configuration configuration, List rea configuration.setBoolean(READ_ALL_COLUMNS, false); } + public static Optional getCompressionCodec(TextInputFormat inputFormat, Path file) + { + CompressionCodecFactory compressionCodecFactory; + + try { + compressionCodecFactory = (CompressionCodecFactory) COMPRESSION_CODECS_FIELD.get(inputFormat); + } + catch (IllegalAccessException e) { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "Failed to find compressionCodec for inputFormat: " + inputFormat.getClass().getName(), e); + } + + if (compressionCodecFactory == null) { + return Optional.empty(); + } + + return Optional.ofNullable(compressionCodecFactory.getCodec(file)); + } + static InputFormat getInputFormat(Configuration configuration, Properties schema, boolean symlinkTarget) { String inputFormatName = getInputFormatName(schema); @@ -313,11 +351,6 @@ public static boolean isSplittable(InputFormat inputFormat, FileSystem fil } } - public static StructObjectInspector getTableObjectInspector(Properties schema) - { - return getTableObjectInspector(getDeserializer(schema)); - } - public static StructObjectInspector getTableObjectInspector(@SuppressWarnings("deprecation") Deserializer deserializer) { try { @@ -330,11 +363,6 @@ public static StructObjectInspector getTableObjectInspector(@SuppressWarnings("d } } - public static List getTableStructFields(Table table) - { - return getTableObjectInspector(getHiveSchema(table)).getAllStructFieldRefs(); - } - public static boolean isDeserializerClass(Properties schema, Class deserializerClass) { return getDeserializerClassName(schema).equals(deserializerClass.getName()); @@ -348,12 +376,12 @@ public static String getDeserializerClassName(Properties schema) } @SuppressWarnings("deprecation") - public static Deserializer getDeserializer(Properties schema) + public static Deserializer getDeserializer(Configuration configuration, Properties schema) { String name = getDeserializerClassName(schema); Deserializer deserializer = createDeserializer(getDeserializerClass(name)); - initializeDeserializer(deserializer, schema); + initializeDeserializer(configuration, deserializer, schema); return deserializer; } @@ -365,6 +393,10 @@ private static Class getDeserializerClass(String name) return ParquetHiveSerDe.class; } + if ("org.apache.hadoop.hive.serde2.avro.AvroSerDe".equals(name)) { + return PrestoAvroSerDe.class; + } + try { return Class.forName(name, true, JavaUtils.getClassLoader()).asSubclass(Deserializer.class); } @@ -388,13 +420,23 @@ private static Deserializer createDeserializer(Class cla } @SuppressWarnings("deprecation") - private static void initializeDeserializer(Deserializer deserializer, Properties schema) + private static void initializeDeserializer(Configuration configuration, Deserializer deserializer, Properties schema) { try { - deserializer.initialize(new Configuration(false), schema); + configuration = copy(configuration); // Some SerDes (e.g. Avro) modify passed configuration + deserializer.initialize(configuration, schema); + validate(deserializer); } - catch (SerDeException e) { - throw new RuntimeException("error initializing deserializer: " + deserializer.getClass().getName()); + catch (SerDeException | RuntimeException e) { + throw new RuntimeException("error initializing deserializer: " + deserializer.getClass().getName(), e); + } + } + + @SuppressWarnings("deprecation") + private static void validate(Deserializer deserializer) + { + if (deserializer instanceof AbstractSerDe && !((AbstractSerDe) deserializer).getConfigurationErrors().isEmpty()) { + throw new RuntimeException("There are configuration errors: " + ((AbstractSerDe) deserializer).getConfigurationErrors()); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriteUtils.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriteUtils.java index 8a161a9d468be..042d0368669f0 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriteUtils.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriteUtils.java @@ -21,6 +21,7 @@ import com.facebook.presto.hive.metastore.Storage; import com.facebook.presto.hive.metastore.Table; import com.facebook.presto.hive.s3.PrestoS3FileSystem; +import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaNotFoundException; @@ -172,7 +173,7 @@ private HiveWriteUtils() { } - public static RecordWriter createRecordWriter(Path target, JobConf conf, Properties properties, String outputFormatName) + public static RecordWriter createRecordWriter(Path target, JobConf conf, Properties properties, String outputFormatName, ConnectorSession session) { try { boolean compress = HiveConf.getBoolVar(conf, COMPRESSRESULT); @@ -180,7 +181,7 @@ public static RecordWriter createRecordWriter(Path target, JobConf conf, Propert return createRcFileWriter(target, conf, properties, compress); } if (outputFormatName.equals(MapredParquetOutputFormat.class.getName())) { - return createParquetWriter(target, conf, properties, compress); + return createParquetWriter(target, conf, properties, compress, session); } Object writer = Class.forName(outputFormatName).getConstructor().newInstance(); return ((HiveOutputFormat) writer).getHiveRecordWriter(conf, target, Text.class, compress, properties, Reporter.NULL); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriter.java index f7eb6fa32bf94..7311728a5dd84 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriter.java @@ -87,6 +87,11 @@ public void commit() onCommit.accept(this); } + long getValidationCpuNanos() + { + return fileWriter.getValidationCpuNanos(); + } + public Optional getVerificationTask() { return fileWriter.getVerificationTask(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriterFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriterFactory.java index dcb946a3bf212..5ec1ac377e03c 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriterFactory.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/HiveWriterFactory.java @@ -135,6 +135,8 @@ public class HiveWriterFactory private final HiveWriterStats hiveWriterStats; + private final OrcFileWriterFactory orcFileWriterFactory; + public HiveWriterFactory( Set fileWriterFactories, String schemaName, @@ -159,7 +161,8 @@ public HiveWriterFactory( NodeManager nodeManager, EventClient eventClient, HiveSessionProperties hiveSessionProperties, - HiveWriterStats hiveWriterStats) + HiveWriterStats hiveWriterStats, + OrcFileWriterFactory orcFileWriterFactory) { this.fileWriterFactories = ImmutableSet.copyOf(requireNonNull(fileWriterFactories, "fileWriterFactories is null")); this.schemaName = requireNonNull(schemaName, "schemaName is null"); @@ -248,6 +251,8 @@ public HiveWriterFactory( } this.hiveWriterStats = requireNonNull(hiveWriterStats, "hiveWriterStats is null"); + + this.orcFileWriterFactory = requireNonNull(orcFileWriterFactory, "orcFileWriterFactory is null"); } public HiveWriter createWriter(Page partitionColumns, int position, OptionalInt bucketNumber) @@ -449,7 +454,8 @@ else if (insertExistingPartitionsBehavior == InsertExistingPartitionsBehavior.ER schema, partitionStorageFormat.getEstimatedWriterSystemMemoryUsage(), conf, - typeManager); + typeManager, + session); } String writerImplementation = hiveFileWriter.getClass().getName(); @@ -519,7 +525,8 @@ else if (insertExistingPartitionsBehavior == InsertExistingPartitionsBehavior.ER types, sortFields, sortOrders, - pageSorter); + pageSorter, + (fs, p) -> orcFileWriterFactory.createOrcDataSink(session, fs, p)); } return new HiveWriter( diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/InternalHiveSplit.java b/presto-hive/src/main/java/com/facebook/presto/hive/InternalHiveSplit.java index 1b5b6f9bb1c02..6d8f65ab33445 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/InternalHiveSplit.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/InternalHiveSplit.java @@ -57,6 +57,7 @@ public class InternalHiveSplit private final boolean forceLocalScheduling; private final Map columnCoercions; private final Optional bucketConversion; + private final boolean s3SelectPushdownEnabled; private long start; private int currentBlockIndex; @@ -74,7 +75,8 @@ public InternalHiveSplit( boolean splittable, boolean forceLocalScheduling, Map columnCoercions, - Optional bucketConversion) + Optional bucketConversion, + boolean s3SelectPushdownEnabled) { checkArgument(start >= 0, "start must be positive"); checkArgument(end >= 0, "length must be positive"); @@ -101,6 +103,7 @@ public InternalHiveSplit( this.forceLocalScheduling = forceLocalScheduling; this.columnCoercions = ImmutableMap.copyOf(columnCoercions); this.bucketConversion = bucketConversion; + this.s3SelectPushdownEnabled = s3SelectPushdownEnabled; } public String getPath() @@ -123,6 +126,11 @@ public long getFileSize() return fileSize; } + public boolean isS3SelectPushdownEnabled() + { + return s3SelectPushdownEnabled; + } + public Properties getSchema() { return schema; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/IonSqlQueryBuilder.java b/presto-hive/src/main/java/com/facebook/presto/hive/IonSqlQueryBuilder.java new file mode 100644 index 0000000000000..63644c628ba8d --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/IonSqlQueryBuilder.java @@ -0,0 +1,268 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.predicate.Domain; +import com.facebook.presto.spi.predicate.Range; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.Decimals; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.spi.type.VarcharType; +import com.google.common.base.Joiner; +import io.airlift.slice.Slice; +import org.joda.time.format.DateTimeFormatter; + +import java.util.ArrayList; +import java.util.List; + +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.SmallintType.SMALLINT; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.ImmutableList.Builder; +import static com.google.common.collect.ImmutableList.builder; +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.stream.Collectors.joining; +import static org.joda.time.chrono.ISOChronology.getInstanceUTC; +import static org.joda.time.format.ISODateTimeFormat.date; + +/** + * S3 Select uses Ion SQL++ query language. This class is used to construct a valid Ion SQL++ query + * to be evaluated with S3 Select on an S3 object. + */ +public class IonSqlQueryBuilder +{ + private static final DateTimeFormatter FORMATTER = date().withChronology(getInstanceUTC()); + private static final String DATA_SOURCE = "S3Object s"; + private final TypeManager typeManager; + + public IonSqlQueryBuilder(TypeManager typeManager) + { + this.typeManager = requireNonNull(typeManager, "typeManager is null"); + } + + public String buildSql(List columns, TupleDomain tupleDomain) + { + StringBuilder sql = new StringBuilder("SELECT "); + + if (columns.isEmpty()) { + sql.append("' '"); + } + else { + String columnNames = columns.stream() + .map(column -> format("s._%d", column.getHiveColumnIndex() + 1)) + .collect(joining(", ")); + sql.append(columnNames); + } + + sql.append(" FROM "); + sql.append(DATA_SOURCE); + + List clauses = toConjuncts(columns, tupleDomain); + if (!clauses.isEmpty()) { + sql.append(" WHERE ") + .append(Joiner.on(" AND ").join(clauses)); + } + + return sql.toString(); + } + + private List toConjuncts(List columns, TupleDomain tupleDomain) + { + Builder builder = builder(); + for (HiveColumnHandle column : columns) { + Type type = column.getHiveType().getType(typeManager); + if (tupleDomain.getDomains().isPresent() && isSupported(type)) { + Domain domain = tupleDomain.getDomains().get().get(column); + if (domain != null) { + builder.add(toPredicate(domain, type, column.getHiveColumnIndex())); + } + } + } + return builder.build(); + } + + private static boolean isSupported(Type type) + { + Type validType = requireNonNull(type, "type is null"); + return validType.equals(BIGINT) || + validType.equals(TINYINT) || + validType.equals(SMALLINT) || + validType.equals(INTEGER) || + validType instanceof DecimalType || + validType.equals(BOOLEAN) || + validType.equals(DATE) || + validType instanceof VarcharType; + } + + private String toPredicate(Domain domain, Type type, int position) + { + checkArgument(domain.getType().isOrderable(), "Domain type must be orderable"); + + if (domain.getValues().isNone()) { + if (domain.isNullAllowed()) { + return format("s._%d", position + 1) + " = '' "; + } + return "FALSE"; + } + + if (domain.getValues().isAll()) { + if (domain.isNullAllowed()) { + return "TRUE"; + } + return format("s._%d", position + 1) + " <> '' "; + } + + List disjuncts = new ArrayList<>(); + List singleValues = new ArrayList<>(); + for (Range range : domain.getValues().getRanges().getOrderedRanges()) { + checkState(!range.isAll()); + if (range.isSingleValue()) { + singleValues.add(range.getLow().getValue()); + continue; + } + List rangeConjuncts = new ArrayList<>(); + if (!range.getLow().isLowerUnbounded()) { + switch (range.getLow().getBound()) { + case ABOVE: + rangeConjuncts.add(toPredicate(">", range.getLow().getValue(), type, position)); + break; + case EXACTLY: + rangeConjuncts.add(toPredicate(">=", range.getLow().getValue(), type, position)); + break; + case BELOW: + throw new IllegalArgumentException("Low marker should never use BELOW bound"); + default: + throw new AssertionError("Unhandled bound: " + range.getLow().getBound()); + } + } + if (!range.getHigh().isUpperUnbounded()) { + switch (range.getHigh().getBound()) { + case ABOVE: + throw new IllegalArgumentException("High marker should never use ABOVE bound"); + case EXACTLY: + rangeConjuncts.add(toPredicate("<=", range.getHigh().getValue(), type, position)); + break; + case BELOW: + rangeConjuncts.add(toPredicate("<", range.getHigh().getValue(), type, position)); + break; + default: + throw new AssertionError("Unhandled bound: " + range.getHigh().getBound()); + } + } + // If rangeConjuncts is null, then the range was ALL, which should already have been checked for + checkState(!rangeConjuncts.isEmpty()); + disjuncts.add("(" + Joiner.on(" AND ").join(rangeConjuncts) + ")"); + } + + // Add back all of the possible single values either as an equality or an IN predicate + if (singleValues.size() == 1) { + disjuncts.add(toPredicate("=", getOnlyElement(singleValues), type, position)); + } + else if (singleValues.size() > 1) { + List values = new ArrayList<>(); + for (Object value : singleValues) { + checkType(type); + values.add(valueToQuery(type, value)); + } + disjuncts.add(createColumn(type, position) + " IN (" + Joiner.on(",").join(values) + ")"); + } + + // Add nullability disjuncts + checkState(!disjuncts.isEmpty()); + if (domain.isNullAllowed()) { + disjuncts.add(format("s._%d", position + 1) + " = '' "); + } + + return "(" + Joiner.on(" OR ").join(disjuncts) + ")"; + } + + private String toPredicate(String operator, Object value, Type type, int position) + { + checkType(type); + + return format("%s %s %s", createColumn(type, position), operator, valueToQuery(type, value)); + } + + private static void checkType(Type type) + { + checkArgument(isSupported(type), "Type not supported: %s", type); + } + + private static String valueToQuery(Type type, Object value) + { + if (type.equals(BIGINT)) { + return String.valueOf(((Number) value).longValue()); + } + if (type.equals(INTEGER)) { + return String.valueOf(((Number) value).intValue()); + } + if (type.equals(SMALLINT)) { + return String.valueOf(((Number) value).shortValue()); + } + if (type.equals(TINYINT)) { + return String.valueOf(((Number) value).byteValue()); + } + if (type.equals(BOOLEAN)) { + return String.valueOf(value); + } + if (type.equals(DATE)) { + return "`" + FORMATTER.print(DAYS.toMillis((long) value)) + "`"; + } + if (type.equals(VarcharType.VARCHAR)) { + return "'" + ((Slice) value).toStringUtf8() + "'"; + } + if (type instanceof DecimalType) { + if (Decimals.isLongDecimal(type)) { + return Decimals.toString((Slice) value, ((DecimalType) type).getScale()); + } + return Decimals.toString((long) value, ((DecimalType) type).getScale()); + } + return "'" + ((Slice) value).toStringUtf8() + "'"; + } + + private String createColumn(Type type, int position) + { + String column = format("s._%d", position + 1); + + if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(SMALLINT) || type.equals(TINYINT)) { + return formatPredicate(column, "INT"); + } + if (type.equals(BOOLEAN)) { + return formatPredicate(column, "BOOL"); + } + if (type.equals(DATE)) { + return formatPredicate(column, "TIMESTAMP"); + } + if (type instanceof DecimalType) { + DecimalType decimalType = (DecimalType) type; + return formatPredicate(column, format("DECIMAL(%s,%s)", decimalType.getPrecision(), decimalType.getScale())); + } + return column; + } + + private String formatPredicate(String column, String type) + { + return format("case %s when '' then null else CAST(%s AS %s) end", column, column, type); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/OrcFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/OrcFileWriter.java index c5196e5370e9a..207b16c73fc0f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/OrcFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/OrcFileWriter.java @@ -33,6 +33,8 @@ import java.io.IOException; import java.io.UncheckedIOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; import java.util.List; import java.util.Map; import java.util.Optional; @@ -49,6 +51,7 @@ public class OrcFileWriter implements HiveFileWriter { private static final int INSTANCE_SIZE = ClassLayout.parseClass(OrcFileWriter.class).instanceSize(); + private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); private final OrcWriter orcWriter; private final Callable rollbackAction; @@ -56,6 +59,8 @@ public class OrcFileWriter private final List nullBlocks; private final Optional> validationInputFactory; + private long validationCpuNanos; + public OrcFileWriter( OrcDataSink orcDataSink, Callable rollbackAction, @@ -152,7 +157,9 @@ public void commit() if (validationInputFactory.isPresent()) { try { try (OrcDataSource input = validationInputFactory.get().get()) { + long startThreadCpuTime = THREAD_MX_BEAN.getCurrentThreadCpuTime(); orcWriter.validate(input); + validationCpuNanos += THREAD_MX_BEAN.getCurrentThreadCpuTime() - startThreadCpuTime; } } catch (IOException | UncheckedIOException e) { @@ -177,6 +184,12 @@ public void rollback() } } + @Override + public long getValidationCpuNanos() + { + return validationCpuNanos; + } + @Override public String toString() { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/ParquetFileWriterConfig.java b/presto-hive/src/main/java/com/facebook/presto/hive/ParquetFileWriterConfig.java new file mode 100644 index 0000000000000..f2945a7d91cc7 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/ParquetFileWriterConfig.java @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import io.airlift.configuration.Config; +import io.airlift.units.DataSize; +import parquet.hadoop.ParquetWriter; + +import static io.airlift.units.DataSize.Unit.BYTE; + +public class ParquetFileWriterConfig +{ + private DataSize blockSize = new DataSize(ParquetWriter.DEFAULT_BLOCK_SIZE, BYTE); + private DataSize pageSize = new DataSize(ParquetWriter.DEFAULT_PAGE_SIZE, BYTE); + + public DataSize getBlockSize() + { + return blockSize; + } + + @Config("hive.parquet.writer.block-size") + public ParquetFileWriterConfig setBlockSize(DataSize blockSize) + { + this.blockSize = blockSize; + return this; + } + + public DataSize getPageSize() + { + return pageSize; + } + + @Config("hive.parquet.writer.page-size") + public ParquetFileWriterConfig setPageSize(DataSize pageSize) + { + this.pageSize = pageSize; + return this; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/ParquetRecordWriterUtil.java b/presto-hive/src/main/java/com/facebook/presto/hive/ParquetRecordWriterUtil.java index f9585c426d51b..7750b58bd9499 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/ParquetRecordWriterUtil.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/ParquetRecordWriterUtil.java @@ -14,6 +14,7 @@ package com.facebook.presto.hive; import com.facebook.presto.hive.RecordFileWriter.ExtendedRecordWriter; +import com.facebook.presto.spi.ConnectorSession; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.ql.exec.FileSinkOperator.RecordWriter; import org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat; @@ -23,12 +24,16 @@ import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.Reporter; import parquet.hadoop.ParquetFileWriter; +import parquet.hadoop.ParquetOutputFormat; import parquet.hadoop.ParquetRecordWriter; import java.io.IOException; import java.lang.reflect.Field; import java.util.Properties; +import static com.facebook.presto.hive.HiveSessionProperties.getParquetWriterBlockSize; +import static com.facebook.presto.hive.HiveSessionProperties.getParquetWriterPageSize; + public final class ParquetRecordWriterUtil { private static final Field REAL_WRITER_FIELD; @@ -52,9 +57,12 @@ public final class ParquetRecordWriterUtil private ParquetRecordWriterUtil() {} - public static RecordWriter createParquetWriter(Path target, JobConf conf, Properties properties, boolean compress) + public static RecordWriter createParquetWriter(Path target, JobConf conf, Properties properties, boolean compress, ConnectorSession session) throws IOException, ReflectiveOperationException { + conf.setLong(ParquetOutputFormat.BLOCK_SIZE, getParquetWriterBlockSize(session).toBytes()); + conf.setLong(ParquetOutputFormat.PAGE_SIZE, getParquetWriterPageSize(session).toBytes()); + RecordWriter recordWriter = new MapredParquetOutputFormat() .getHiveRecordWriter(conf, target, Text.class, compress, properties, Reporter.NULL); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/PartitionStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/PartitionStatistics.java index 3e1bfb078ee0b..2b0dbb0d9e492 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/PartitionStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/PartitionStatistics.java @@ -15,14 +15,19 @@ package com.facebook.presto.hive; import com.facebook.presto.hive.metastore.HiveColumnStatistics; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; +import javax.annotation.concurrent.Immutable; + import java.util.Map; import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class PartitionStatistics { private static final PartitionStatistics EMPTY = new PartitionStatistics(HiveBasicStatistics.createEmptyStatistics(), ImmutableMap.of()); @@ -35,19 +40,22 @@ public static PartitionStatistics empty() return EMPTY; } + @JsonCreator public PartitionStatistics( - HiveBasicStatistics basicStatistics, - Map columnStatistics) + @JsonProperty("basicStatistics") HiveBasicStatistics basicStatistics, + @JsonProperty("columnStatistics") Map columnStatistics) { this.basicStatistics = requireNonNull(basicStatistics, "basicStatistics is null"); this.columnStatistics = ImmutableMap.copyOf(requireNonNull(columnStatistics, "columnStatistics can not be null")); } + @JsonProperty public HiveBasicStatistics getBasicStatistics() { return basicStatistics; } + @JsonProperty public Map getColumnStatistics() { return columnStatistics; @@ -81,4 +89,32 @@ public String toString() .add("columnStatistics", columnStatistics) .toString(); } + + public static Builder builder() + { + return new Builder(); + } + + public static class Builder + { + private HiveBasicStatistics basicStatistics = HiveBasicStatistics.createEmptyStatistics(); + private Map columnStatistics = ImmutableMap.of(); + + public Builder setBasicStatistics(HiveBasicStatistics basicStatistics) + { + this.basicStatistics = requireNonNull(basicStatistics, "basicStatistics is null"); + return this; + } + + public Builder setColumnStatistics(Map columnStatistics) + { + this.columnStatistics = ImmutableMap.copyOf(requireNonNull(columnStatistics, "columnStatistics is null")); + return this; + } + + public PartitionStatistics build() + { + return new PartitionStatistics(basicStatistics, columnStatistics); + } + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/PartitionUpdate.java b/presto-hive/src/main/java/com/facebook/presto/hive/PartitionUpdate.java index d320f7c916855..1bfa2ba6b05fd 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/PartitionUpdate.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/PartitionUpdate.java @@ -14,6 +14,7 @@ package com.facebook.presto.hive; import com.facebook.presto.spi.PrestoException; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimaps; @@ -39,6 +40,7 @@ public class PartitionUpdate private final long inMemoryDataSizeInBytes; private final long onDiskDataSizeInBytes; + @JsonCreator public PartitionUpdate( @JsonProperty("name") String name, @JsonProperty("updateMode") UpdateMode updateMode, diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/RcFileFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/RcFileFileWriter.java index c8da4e932cf58..1d3e696c3a567 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/RcFileFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/RcFileFileWriter.java @@ -32,6 +32,8 @@ import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; import java.util.List; import java.util.Map; import java.util.Optional; @@ -48,6 +50,7 @@ public class RcFileFileWriter implements HiveFileWriter { private static final int INSTANCE_SIZE = ClassLayout.parseClass(RcFileFileWriter.class).instanceSize(); + private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); private final CountingOutputStream outputStream; private final RcFileWriter rcFileWriter; @@ -56,6 +59,8 @@ public class RcFileFileWriter private final List nullBlocks; private final Optional> validationInputFactory; + private long validationCpuNanos; + public RcFileFileWriter( OutputStream outputStream, Callable rollbackAction, @@ -143,7 +148,9 @@ public void commit() if (validationInputFactory.isPresent()) { try { try (RcFileDataSource input = validationInputFactory.get().get()) { + long startThreadCpuTime = THREAD_MX_BEAN.getCurrentThreadCpuTime(); rcFileWriter.validate(input); + validationCpuNanos += THREAD_MX_BEAN.getCurrentThreadCpuTime() - startThreadCpuTime; } } catch (IOException | UncheckedIOException e) { @@ -168,6 +175,12 @@ public void rollback() } } + @Override + public long getValidationCpuNanos() + { + return validationCpuNanos; + } + @Override public String toString() { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/RecordFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/RecordFileWriter.java index 65c6771a86c3f..16349af52c343 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/RecordFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/RecordFileWriter.java @@ -15,6 +15,7 @@ import com.facebook.presto.hive.HiveWriteUtils.FieldSetter; import com.facebook.presto.hive.metastore.StorageFormat; +import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; @@ -80,7 +81,8 @@ public RecordFileWriter( Properties schema, DataSize estimatedWriterSystemMemoryUsage, JobConf conf, - TypeManager typeManager) + TypeManager typeManager, + ConnectorSession session) { this.path = requireNonNull(path, "path is null"); this.conf = requireNonNull(conf, "conf is null"); @@ -98,7 +100,7 @@ public RecordFileWriter( serDe = OptimizedLazyBinaryColumnarSerde.class.getName(); } serializer = initializeSerializer(conf, schema, serDe); - recordWriter = createRecordWriter(path, conf, schema, storageFormat.getOutputFormat()); + recordWriter = createRecordWriter(path, conf, schema, storageFormat.getOutputFormat(), session); List objectInspectors = getRowColumnInspectors(fileColumnTypes); tableInspector = getStandardStructObjectInspector(fileColumnNames, objectInspectors); @@ -201,6 +203,13 @@ public void rollback() } } + @Override + public long getValidationCpuNanos() + { + // RecordFileWriter delegates to Hive RecordWriter and there is no validation + return 0; + } + @Override public String toString() { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/RetryDriver.java b/presto-hive/src/main/java/com/facebook/presto/hive/RetryDriver.java index 911ee8f3712ba..c454754612266 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/RetryDriver.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/RetryDriver.java @@ -17,6 +17,7 @@ import io.airlift.log.Logger; import io.airlift.units.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -124,6 +125,7 @@ public V run(String callableName, Callable callable) requireNonNull(callableName, "callableName is null"); requireNonNull(callable, "callable is null"); + List suppressedExceptions = new ArrayList<>(); long startTime = System.nanoTime(); int attempt = 0; while (true) { @@ -140,14 +142,18 @@ public V run(String callableName, Callable callable) e = exceptionMapper.apply(e); for (Class clazz : exceptionWhiteList) { if (clazz.isInstance(e)) { + addSuppressed(e, suppressedExceptions); throw e; } } if (attempt >= maxAttempts || Duration.nanosSince(startTime).compareTo(maxRetryTime) >= 0) { + addSuppressed(e, suppressedExceptions); throw e; } log.debug("Failed on executing %s with attempt %d, will retry. Exception: %s", callableName, attempt, e.getMessage()); + suppressedExceptions.add(e); + int delayInMs = (int) Math.min(minSleepTime.toMillis() * Math.pow(scaleFactor, attempt - 1), maxSleepTime.toMillis()); int jitter = ThreadLocalRandom.current().nextInt(Math.max(1, (int) (delayInMs * 0.1))); try { @@ -155,9 +161,20 @@ public V run(String callableName, Callable callable) } catch (InterruptedException ie) { Thread.currentThread().interrupt(); - throw new RuntimeException(ie); + Exception exception = new RuntimeException(ie); + addSuppressed(new RuntimeException(ie), suppressedExceptions); + throw exception; } } } } + + private static void addSuppressed(Exception exception, List suppressedExceptions) + { + for (Throwable suppressedException : suppressedExceptions) { + if (exception != suppressedException) { + exception.addSuppressed(suppressedException); + } + } + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectCsvRecordReader.java b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectCsvRecordReader.java new file mode 100644 index 0000000000000..bce0117a073f7 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectCsvRecordReader.java @@ -0,0 +1,112 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.amazonaws.services.s3.model.CSVInput; +import com.amazonaws.services.s3.model.CSVOutput; +import com.amazonaws.services.s3.model.CompressionType; +import com.amazonaws.services.s3.model.ExpressionType; +import com.amazonaws.services.s3.model.InputSerialization; +import com.amazonaws.services.s3.model.OutputSerialization; +import com.amazonaws.services.s3.model.SelectObjectContentRequest; +import com.facebook.presto.hive.s3.PrestoS3ClientFactory; +import com.facebook.presto.hive.s3.PrestoS3FileSystem; +import com.facebook.presto.spi.PrestoException; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.compress.BZip2Codec; +import org.apache.hadoop.io.compress.CompressionCodec; +import org.apache.hadoop.io.compress.GzipCodec; + +import java.net.URI; +import java.util.Properties; + +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static org.apache.hadoop.hive.serde.serdeConstants.ESCAPE_CHAR; +import static org.apache.hadoop.hive.serde.serdeConstants.QUOTE_CHAR; + +public class S3SelectCsvRecordReader + extends S3SelectLineRecordReader +{ + /* + * Sentinel unicode comment character (http://www.unicode.org/faq/private_use.html#nonchar_codes). + * It is expected that \uFDD0 sentinel comment character is not the first character in any row of user's CSV S3 object. + * The rows starting with \uFDD0 will be skipped by S3Select and will not be a part of the result set or aggregations. + * To process CSV objects that may contain \uFDD0 as first row character please disable S3SelectPushdown. + * TODO: Remove this proxy logic when S3Select API supports disabling of row level comments. + */ + + private static final String COMMENTS_CHAR_STR = "\uFDD0"; + + S3SelectCsvRecordReader( + Configuration configuration, + HiveClientConfig clientConfig, + Path path, + long start, + long length, + Properties schema, + String ionSqlQuery, + PrestoS3ClientFactory s3ClientFactory) + { + super(configuration, clientConfig, path, start, length, schema, ionSqlQuery, s3ClientFactory); + } + + @Override + public SelectObjectContentRequest buildSelectObjectRequest(Properties schema, String query, Path path) + { + SelectObjectContentRequest selectObjectRequest = new SelectObjectContentRequest(); + URI uri = path.toUri(); + selectObjectRequest.setBucketName(PrestoS3FileSystem.getBucketName(uri)); + selectObjectRequest.setKey(PrestoS3FileSystem.keyFromPath(path)); + selectObjectRequest.setExpression(query); + selectObjectRequest.setExpressionType(ExpressionType.SQL); + + String fieldDelimiter = getFieldDelimiter(schema); + String quoteChar = schema.getProperty(QUOTE_CHAR, null); + String escapeChar = schema.getProperty(ESCAPE_CHAR, null); + + CSVInput selectObjectCSVInputSerialization = new CSVInput(); + selectObjectCSVInputSerialization.setRecordDelimiter(lineDelimiter); + selectObjectCSVInputSerialization.setFieldDelimiter(fieldDelimiter); + selectObjectCSVInputSerialization.setComments(COMMENTS_CHAR_STR); + selectObjectCSVInputSerialization.setQuoteCharacter(quoteChar); + selectObjectCSVInputSerialization.setQuoteEscapeCharacter(escapeChar); + InputSerialization selectObjectInputSerialization = new InputSerialization(); + + CompressionCodec codec = compressionCodecFactory.getCodec(path); + if (codec instanceof GzipCodec) { + selectObjectInputSerialization.setCompressionType(CompressionType.GZIP); + } + else if (codec instanceof BZip2Codec) { + selectObjectInputSerialization.setCompressionType(CompressionType.BZIP2); + } + else if (codec != null) { + throw new PrestoException(NOT_SUPPORTED, "Compression extension not supported for S3 Select: " + path); + } + + selectObjectInputSerialization.setCsv(selectObjectCSVInputSerialization); + selectObjectRequest.setInputSerialization(selectObjectInputSerialization); + + OutputSerialization selectObjectOutputSerialization = new OutputSerialization(); + CSVOutput selectObjectCSVOutputSerialization = new CSVOutput(); + selectObjectCSVOutputSerialization.setRecordDelimiter(lineDelimiter); + selectObjectCSVOutputSerialization.setFieldDelimiter(fieldDelimiter); + selectObjectCSVOutputSerialization.setQuoteCharacter(quoteChar); + selectObjectCSVOutputSerialization.setQuoteEscapeCharacter(escapeChar); + selectObjectOutputSerialization.setCsv(selectObjectCSVOutputSerialization); + selectObjectRequest.setOutputSerialization(selectObjectOutputSerialization); + + return selectObjectRequest; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectLineRecordReader.java b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectLineRecordReader.java new file mode 100644 index 0000000000000..1ac8d1d884757 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectLineRecordReader.java @@ -0,0 +1,229 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.SelectObjectContentRequest; +import com.facebook.presto.hive.s3.HiveS3Config; +import com.facebook.presto.hive.s3.PrestoS3ClientFactory; +import com.facebook.presto.hive.s3.PrestoS3SelectClient; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.Closer; +import io.airlift.units.Duration; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.LongWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.compress.CompressionCodecFactory; +import org.apache.hadoop.mapred.RecordReader; +import org.apache.hadoop.util.LineReader; + +import javax.annotation.concurrent.ThreadSafe; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +import static com.facebook.presto.hive.RetryDriver.retry; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_MAX_BACKOFF_TIME; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_MAX_CLIENT_RETRIES; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_MAX_RETRY_TIME; +import static com.google.common.base.Throwables.throwIfInstanceOf; +import static com.google.common.base.Throwables.throwIfUnchecked; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.hadoop.hive.serde.serdeConstants.FIELD_DELIM; +import static org.apache.hadoop.hive.serde.serdeConstants.LINE_DELIM; +import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_FORMAT; +import static org.apache.http.HttpStatus.SC_BAD_REQUEST; +import static org.apache.http.HttpStatus.SC_FORBIDDEN; +import static org.apache.http.HttpStatus.SC_NOT_FOUND; + +@ThreadSafe +public abstract class S3SelectLineRecordReader + implements RecordReader +{ + private InputStream selectObjectContent; + private long processedRecords; + private long recordsFromS3; + private long position; + private LineReader reader; + private boolean isFirstLine; + private static final Duration BACKOFF_MIN_SLEEP = new Duration(1, SECONDS); + private final PrestoS3SelectClient selectClient; + private final long start; + private final long end; + private final int maxAttempts; + private final Duration maxBackoffTime; + private final Duration maxRetryTime; + private final Closer closer = Closer.create(); + private final SelectObjectContentRequest selectObjectContentRequest; + protected final CompressionCodecFactory compressionCodecFactory; + protected final String lineDelimiter; + + S3SelectLineRecordReader( + Configuration configuration, + HiveClientConfig clientConfig, + Path path, + long start, + long length, + Properties schema, + String ionSqlQuery, + PrestoS3ClientFactory s3ClientFactory) + { + requireNonNull(configuration, "configuration is null"); + requireNonNull(clientConfig, "clientConfig is null"); + requireNonNull(schema, "schema is null"); + requireNonNull(path, "path is null"); + requireNonNull(ionSqlQuery, "ionSqlQuery is null"); + requireNonNull(s3ClientFactory, "s3ClientFactory is null"); + this.lineDelimiter = (schema).getProperty(LINE_DELIM, "\n"); + this.processedRecords = 0; + this.recordsFromS3 = 0; + this.start = start; + this.position = this.start; + this.end = this.start + length; + this.isFirstLine = true; + + this.compressionCodecFactory = new CompressionCodecFactory(configuration); + this.selectObjectContentRequest = buildSelectObjectRequest(schema, ionSqlQuery, path); + + HiveS3Config defaults = new HiveS3Config(); + this.maxAttempts = configuration.getInt(S3_MAX_CLIENT_RETRIES, defaults.getS3MaxClientRetries()) + 1; + this.maxBackoffTime = Duration.valueOf(configuration.get(S3_MAX_BACKOFF_TIME, defaults.getS3MaxBackoffTime().toString())); + this.maxRetryTime = Duration.valueOf(configuration.get(S3_MAX_RETRY_TIME, defaults.getS3MaxRetryTime().toString())); + + this.selectClient = new PrestoS3SelectClient(configuration, clientConfig, s3ClientFactory); + closer.register(selectClient); + } + + public abstract SelectObjectContentRequest buildSelectObjectRequest(Properties schema, String query, Path path); + + private int readLine(Text value) + throws IOException + { + try { + return retry() + .maxAttempts(maxAttempts) + .exponentialBackoff(BACKOFF_MIN_SLEEP, maxBackoffTime, maxRetryTime, 2.0) + .stopOn(InterruptedException.class, UnrecoverableS3OperationException.class) + .run("readRecordsContentStream", () -> { + if (isFirstLine) { + recordsFromS3 = 0; + selectObjectContent = selectClient.getRecordsContent(selectObjectContentRequest); + closer.register(selectObjectContent); + reader = new LineReader(selectObjectContent, lineDelimiter.getBytes(StandardCharsets.UTF_8)); + closer.register(reader); + isFirstLine = false; + } + try { + return reader.readLine(value); + } + catch (RuntimeException e) { + isFirstLine = true; + recordsFromS3 = 0; + if (e instanceof AmazonS3Exception) { + switch (((AmazonS3Exception) e).getStatusCode()) { + case SC_FORBIDDEN: + case SC_NOT_FOUND: + case SC_BAD_REQUEST: + throw new UnrecoverableS3OperationException(selectClient.getBucketName(), selectClient.getKeyName(), e); + } + } + throw e; + } + }); + } + catch (Exception e) { + throwIfInstanceOf(e, IOException.class); + throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + @Override + public synchronized boolean next(LongWritable key, Text value) + throws IOException + { + while (true) { + int bytes = readLine(value); + if (bytes <= 0) { + if (!selectClient.isRequestComplete()) { + throw new IOException("S3 Select request was incomplete as End Event was not received"); + } + return false; + } + recordsFromS3++; + if (recordsFromS3 > processedRecords) { + position += bytes; + processedRecords++; + key.set(processedRecords); + return true; + } + } + } + + @Override + public LongWritable createKey() + { + return new LongWritable(); + } + + @Override + public Text createValue() + { + return new Text(); + } + + @Override + public long getPos() + { + return position; + } + + @Override + public void close() + throws IOException + { + closer.close(); + } + + @Override + public float getProgress() + { + return ((float) (position - start)) / (end - start); + } + + String getFieldDelimiter(Properties schema) + { + return schema.getProperty(FIELD_DELIM, schema.getProperty(SERIALIZATION_FORMAT)); + } + + /** + * This exception is for stopping retries for S3 Select calls that shouldn't be retried. + * For example, "Caused by: com.amazonaws.services.s3.model.AmazonS3Exception: Forbidden (Service: Amazon S3; Status Code: 403 ..." + */ + @VisibleForTesting + static class UnrecoverableS3OperationException + extends RuntimeException + { + public UnrecoverableS3OperationException(String bucket, String key, Throwable cause) + { + // append bucket and key to the message + super(format("%s (Bucket: %s, Key: %s)", cause, bucket, key)); + } + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectPushdown.java b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectPushdown.java new file mode 100644 index 0000000000000..77988f8a3599e --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectPushdown.java @@ -0,0 +1,153 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.hive.metastore.Column; +import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.Table; +import com.facebook.presto.spi.ConnectorSession; +import com.google.common.collect.ImmutableSet; +import io.airlift.log.Logger; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe; +import org.apache.hadoop.hive.serde2.typeinfo.DecimalTypeInfo; +import org.apache.hadoop.io.compress.BZip2Codec; +import org.apache.hadoop.io.compress.GzipCodec; +import org.apache.hadoop.mapred.InputFormat; +import org.apache.hadoop.mapred.TextInputFormat; + +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; + +import static com.facebook.presto.hive.HiveSessionProperties.isS3SelectPushdownEnabled; +import static com.facebook.presto.hive.HiveUtil.getCompressionCodec; +import static com.facebook.presto.hive.HiveUtil.getDeserializerClassName; +import static com.facebook.presto.hive.HiveUtil.getInputFormatName; +import static com.facebook.presto.hive.metastore.MetastoreUtil.getHiveSchema; +import static org.apache.hadoop.hive.serde.serdeConstants.BIGINT_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.BOOLEAN_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.DATE_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.DECIMAL_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.INT_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.SMALLINT_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.STRING_TYPE_NAME; +import static org.apache.hadoop.hive.serde.serdeConstants.TINYINT_TYPE_NAME; + +/** + * S3SelectPushdown uses Amazon S3 Select to push down queries to Amazon S3. This allows Presto to retrieve only a + * subset of data rather than retrieving the full S3 object thus improving Presto query performance. + */ +public class S3SelectPushdown +{ + private static final Logger LOG = Logger.get(S3SelectPushdown.class); + private static final Set SUPPORTED_S3_PREFIXES = ImmutableSet.of("s3://", "s3a://", "s3n://"); + private static final Set SUPPORTED_SERDES = ImmutableSet.of(LazySimpleSerDe.class.getName()); + private static final Set SUPPORTED_INPUT_FORMATS = ImmutableSet.of(TextInputFormat.class.getName()); + + /* + * Double and Real Types lose precision. Thus, they are not pushed down to S3. Please use Decimal Type if push down is desired. + * + * Pushing down timestamp to s3select is problematic due to following reasons: + * 1) Presto bug: TIMESTAMP behaviour does not match sql standard (https://github.com/prestodb/presto/issues/7122) + * 2) Presto uses the timezone from client to convert the timestamp if no timezone is provided, however, s3select is a different service and this could lead to unexpected results. + * 3) ION SQL compare timestamps using precision, timestamps with different precisions are not equal even actually they present the same instant of time. This could lead to unexpected results. + */ + private static final Set SUPPORTED_COLUMN_TYPES = ImmutableSet.of( + BOOLEAN_TYPE_NAME, + INT_TYPE_NAME, + TINYINT_TYPE_NAME, + SMALLINT_TYPE_NAME, + BIGINT_TYPE_NAME, + STRING_TYPE_NAME, + DECIMAL_TYPE_NAME, + DATE_TYPE_NAME); + + private S3SelectPushdown() {} + + private static boolean isSerdeSupported(Properties schema) + { + String serdeName = getDeserializerClassName(schema); + return SUPPORTED_SERDES.contains(serdeName); + } + + private static boolean isInputFormatSupported(Properties schema) + { + String inputFormat = getInputFormatName(schema); + return SUPPORTED_INPUT_FORMATS.contains(inputFormat); + } + + public static boolean isCompressionCodecSupported(InputFormat inputFormat, Path path) + { + if (inputFormat instanceof TextInputFormat) { + return getCompressionCodec((TextInputFormat) inputFormat, path) + .map(codec -> (codec instanceof GzipCodec) || (codec instanceof BZip2Codec)) + .orElse(true); + } + + return false; + } + + private static boolean areColumnTypesSupported(List columns) + { + if (columns == null || columns.isEmpty()) { + return false; + } + + for (Column column : columns) { + String type = column.getType().getHiveTypeName().toString(); + if (column.getType().getTypeInfo() instanceof DecimalTypeInfo) { + // skip precision and scale when check decimal type + type = DECIMAL_TYPE_NAME; + } + if (!SUPPORTED_COLUMN_TYPES.contains(type)) { + return false; + } + } + + return true; + } + + private static boolean isS3Storage(String path) + { + return SUPPORTED_S3_PREFIXES.stream().anyMatch(path::startsWith); + } + + static boolean shouldEnablePushdownForTable(ConnectorSession session, Table table, String path, Optional optionalPartition) + { + if (!isS3SelectPushdownEnabled(session)) { + return false; + } + + if (path == null) { + return false; + } + + // Hive table partitions could be on different storages, + // as a result, we have to check each individual optionalPartition + Properties schema = optionalPartition + .map(partition -> getHiveSchema(partition, table)) + .orElseGet(() -> getHiveSchema(table)); + return shouldEnablePushdownForTable(table, path, schema); + } + + private static boolean shouldEnablePushdownForTable(Table table, String path, Properties schema) + { + return isS3Storage(path) && + isSerdeSupported(schema) && + isInputFormatSupported(schema) && + areColumnTypesSupported(table.getDataColumns()); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursor.java b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursor.java new file mode 100644 index 0000000000000..f6e88e12bf890 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursor.java @@ -0,0 +1,233 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.type.TypeManager; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.Writable; +import org.apache.hadoop.mapred.RecordReader; +import org.joda.time.DateTimeZone; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; +import static org.apache.hadoop.hive.serde.serdeConstants.LIST_COLUMNS; +import static org.apache.hadoop.hive.serde.serdeConstants.LIST_COLUMN_TYPES; +import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_DDL; + +class S3SelectRecordCursor + extends GenericHiveRecordCursor +{ + private static final String THRIFT_STRUCT = "struct"; + private static final String START_STRUCT = "{"; + private static final String END_STRUCT = "}"; + private static final String FIELD_SEPARATOR = ","; + + public S3SelectRecordCursor( + Configuration configuration, + Path path, + RecordReader recordReader, + long totalBytes, + Properties splitSchema, + List columns, + DateTimeZone hiveStorageTimeZone, + TypeManager typeManager) + { + super(configuration, path, recordReader, totalBytes, updateSplitSchema(splitSchema, columns), columns, hiveStorageTimeZone, typeManager); + } + + // since s3select only returns the required column, not the whole columns + // we need to update the split schema to include only the required columns + // otherwise, Serde could not deserialize output from s3select to row data correctly + static Properties updateSplitSchema(Properties splitSchema, List columns) + { + requireNonNull(splitSchema, "splitSchema is null"); + requireNonNull(columns, "columns is null"); + // clone split properties for update so as not to affect the original one + Properties updatedSchema = new Properties(); + updatedSchema.putAll(splitSchema); + updatedSchema.setProperty(LIST_COLUMNS, buildColumns(columns)); + updatedSchema.setProperty(LIST_COLUMN_TYPES, buildColumnTypes(columns)); + ThriftTable thriftTable = parseThriftDdl(splitSchema.getProperty(SERIALIZATION_DDL)); + updatedSchema.setProperty(SERIALIZATION_DDL, + thriftTableToDdl(pruneThriftTable(thriftTable, columns))); + return updatedSchema; + } + + private static String buildColumns(List columns) + { + if (columns == null || columns.isEmpty()) { + return ""; + } + return columns.stream() + .map(HiveColumnHandle::getName) + .collect(Collectors.joining(",")); + } + + private static String buildColumnTypes(List columns) + { + if (columns == null || columns.isEmpty()) { + return ""; + } + return columns.stream() + .map(column -> column.getHiveType().getTypeInfo().getTypeName()) + .collect(Collectors.joining(",")); + } + + /** + * Parse Thrift description of a table schema. Examples: + *
    + *
  • struct article { varchar article varchar author date date_pub int quantity}
  • + *
  • struct article { varchar article, varchar author, date date_pub, int quantity }
  • + *
  • struct article { varchar article, varchar author, date date_pub, int quantity}
  • + *
+ */ + private static ThriftTable parseThriftDdl(String ddl) + { + if (isNullOrEmpty(ddl)) { + return null; + } + String[] parts = ddl.trim().split("\\s+"); + checkArgument(parts.length >= 5, "Invalid Thrift DDL " + ddl); + checkArgument(THRIFT_STRUCT.equals(parts[0]), "Thrift DDL should start with " + THRIFT_STRUCT); + ThriftTable thriftTable = new ThriftTable(); + thriftTable.setTableName(parts[1]); + checkArgument(START_STRUCT.equals(parts[2]), "Invalid Thrift DDL " + ddl); + checkArgument(parts[parts.length - 1].endsWith(END_STRUCT), "Invalid Thrift DDL " + ddl); + String lastColumnNameWithEndStruct = parts[parts.length - 1]; + parts[parts.length - 1] = lastColumnNameWithEndStruct.substring(0, lastColumnNameWithEndStruct.length() - 1); + List fields = new ArrayList<>(); + for (int i = 3; i < parts.length - 1; i += 2) { + ThriftField thriftField = new ThriftField(); + thriftField.setType(parts[i]); + String columnNameWithFieldSeparator = parts[i + 1]; + if (columnNameWithFieldSeparator.endsWith(FIELD_SEPARATOR)) { + parts[i + 1] = columnNameWithFieldSeparator.substring(0, columnNameWithFieldSeparator.length() - 1); + } + thriftField.setName(parts[i + 1]); + fields.add(thriftField); + } + thriftTable.setFields(fields); + + return thriftTable; + } + + private static ThriftTable pruneThriftTable(ThriftTable thriftTable, List columns) + { + if (thriftTable == null) { + return null; + } + List fields = thriftTable.getFields(); + if (fields == null || fields.isEmpty()) { + return thriftTable; + } + Set columnNames = columns.stream() + .map(HiveColumnHandle::getName) + .collect(toImmutableSet()); + List filteredFields = fields.stream() + .filter(field -> columnNames.contains(field.getName())) + .collect(toList()); + thriftTable.setFields(filteredFields); + + return thriftTable; + } + + private static String thriftTableToDdl(ThriftTable thriftTable) + { + if (thriftTable == null) { + return ""; + } + List fields = thriftTable.getFields(); + if (fields == null || fields.isEmpty()) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(THRIFT_STRUCT) + .append(" ") + .append(thriftTable.getTableName()) + .append(" ") + .append(START_STRUCT); + stringBuilder.append(fields.stream() + .map(field -> " " + field.getType() + " " + field.getName()) + .collect(Collectors.joining(","))); + stringBuilder.append(END_STRUCT); + + return stringBuilder.toString(); + } + + private static class ThriftField + { + private String type; + private String name; + + private String getType() + { + return type; + } + + private void setType(String type) + { + checkArgument(!isNullOrEmpty(type), "type is null or empty string"); + this.type = type; + } + + private String getName() + { + return name; + } + + private void setName(String name) + { + requireNonNull(name, "name is null"); + this.name = name; + } + } + + private static class ThriftTable + { + private String tableName; + private List fields; + + private String getTableName() + { + return tableName; + } + + private void setTableName(String tableName) + { + checkArgument(!isNullOrEmpty(tableName), "tableName is null or empty string"); + this.tableName = tableName; + } + + private List getFields() + { + return fields; + } + + private void setFields(List fields) + { + requireNonNull(fields, "fields is null"); + this.fields = fields; + } + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursorProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursorProvider.java new file mode 100644 index 0000000000000..cd899ee0e8b28 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/S3SelectRecordCursorProvider.java @@ -0,0 +1,96 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.hive.s3.PrestoS3ClientFactory; +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.RecordCursor; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.TypeManager; +import com.google.common.collect.ImmutableSet; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe; +import org.joda.time.DateTimeZone; + +import javax.inject.Inject; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; + +import static com.facebook.presto.hive.HiveErrorCode.HIVE_FILESYSTEM_ERROR; +import static com.facebook.presto.hive.HiveUtil.getDeserializerClassName; +import static java.util.Objects.requireNonNull; + +public class S3SelectRecordCursorProvider + implements HiveRecordCursorProvider +{ + private static final Set CSV_SERDES = ImmutableSet.of(LazySimpleSerDe.class.getName()); + private final HdfsEnvironment hdfsEnvironment; + private final HiveClientConfig clientConfig; + private final PrestoS3ClientFactory s3ClientFactory; + + @Inject + public S3SelectRecordCursorProvider( + HdfsEnvironment hdfsEnvironment, + HiveClientConfig clientConfig, + PrestoS3ClientFactory s3ClientFactory) + { + this.hdfsEnvironment = requireNonNull(hdfsEnvironment, "hdfsEnvironment is null"); + this.clientConfig = requireNonNull(clientConfig, "clientConfig is null"); + this.s3ClientFactory = requireNonNull(s3ClientFactory, "s3ClientFactory is null"); + } + + @Override + public Optional createRecordCursor( + Configuration configuration, + ConnectorSession session, + Path path, + long start, + long length, + long fileSize, + Properties schema, + List columns, + TupleDomain effectivePredicate, + DateTimeZone hiveStorageTimeZone, + TypeManager typeManager, + boolean s3SelectPushdownEnabled) + { + if (!s3SelectPushdownEnabled) { + return Optional.empty(); + } + + try { + this.hdfsEnvironment.getFileSystem(session.getUser(), path, configuration); + } + catch (IOException e) { + throw new PrestoException(HIVE_FILESYSTEM_ERROR, "Failed getting FileSystem: " + path, e); + } + + String serdeName = getDeserializerClassName(schema); + if (CSV_SERDES.contains(serdeName)) { + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(typeManager); + String ionSqlQuery = queryBuilder.buildSql(columns, effectivePredicate); + S3SelectLineRecordReader recordReader = new S3SelectCsvRecordReader(configuration, clientConfig, path, start, length, schema, ionSqlQuery, s3ClientFactory); + return Optional.of(new S3SelectRecordCursor(configuration, path, recordReader, length, schema, columns, hiveStorageTimeZone, typeManager)); + } + + // unsupported serdes + return Optional.empty(); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/SortingFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/SortingFileWriter.java index 5ce2942f68d97..f7523599b3c2a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/SortingFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/SortingFileWriter.java @@ -18,6 +18,7 @@ import com.facebook.presto.hive.util.SortBuffer; import com.facebook.presto.hive.util.TempFileReader; import com.facebook.presto.hive.util.TempFileWriter; +import com.facebook.presto.orc.OrcDataSink; import com.facebook.presto.orc.OrcDataSource; import com.facebook.presto.orc.OrcDataSourceId; import com.facebook.presto.spi.Page; @@ -39,10 +40,9 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import java.util.NavigableSet; -import java.util.Objects; import java.util.Optional; -import java.util.TreeSet; +import java.util.PriorityQueue; +import java.util.Queue; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.stream.IntStream; @@ -72,7 +72,8 @@ public class SortingFileWriter private final List sortOrders; private final HiveFileWriter outputWriter; private final SortBuffer sortBuffer; - private final NavigableSet tempFiles = new TreeSet<>(comparing(TempFile::getSize)); + private final TempFileSinkFactory tempFileSinkFactory; + private final Queue tempFiles = new PriorityQueue<>(comparing(TempFile::getSize)); private final AtomicLong nextFileId = new AtomicLong(); public SortingFileWriter( @@ -84,7 +85,8 @@ public SortingFileWriter( List types, List sortFields, List sortOrders, - PageSorter pageSorter) + PageSorter pageSorter, + TempFileSinkFactory tempFileSinkFactory) { checkArgument(maxOpenTempFiles >= 2, "maxOpenTempFiles must be at least two"); this.fileSystem = requireNonNull(fileSystem, "fileSystem is null"); @@ -95,6 +97,7 @@ public SortingFileWriter( this.sortOrders = ImmutableList.copyOf(requireNonNull(sortOrders, "sortOrders is null")); this.outputWriter = requireNonNull(outputWriter, "outputWriter is null"); this.sortBuffer = new SortBuffer(maxMemory, types, sortFields, sortOrders, pageSorter); + this.tempFileSinkFactory = tempFileSinkFactory; } @Override @@ -135,16 +138,8 @@ public void commit() try { writeSorted(); outputWriter.commit(); - - for (TempFile tempFile : tempFiles) { - Path file = tempFile.getPath(); - fileSystem.delete(file, false); - if (fileSystem.exists(file)) { - throw new IOException("Failed to delete temporary file: " + file); - } - } } - catch (IOException | UncheckedIOException e) { + catch (UncheckedIOException e) { throw new PrestoException(HIVE_WRITER_CLOSE_ERROR, "Error committing write to Hive", e); } } @@ -159,6 +154,12 @@ public void rollback() outputWriter.rollback(); } + @Override + public long getValidationCpuNanos() + { + return outputWriter.getValidationCpuNanos(); + } + @Override public String toString() { @@ -193,7 +194,7 @@ private void combineFiles() int count = min(maxOpenTempFiles, tempFiles.size() - (maxOpenTempFiles - 1)); List smallestFiles = IntStream.range(0, count) - .mapToObj(i -> tempFiles.pollFirst()) + .mapToObj(i -> tempFiles.poll()) .collect(toImmutableList()); writeTempFile(writer -> mergeFiles(smallestFiles, writer::writePage)); @@ -222,6 +223,14 @@ private void mergeFiles(Iterable files, Consumer consumer) new MergingPageIterator(iterators, types, sortFields, sortOrders) .forEachRemaining(consumer); + + for (TempFile tempFile : files) { + Path file = tempFile.getPath(); + fileSystem.delete(file, false); + if (fileSystem.exists(file)) { + throw new IOException("Failed to delete temporary file: " + file); + } + } } catch (IOException e) { throw new UncheckedIOException(e); @@ -232,7 +241,7 @@ private void writeTempFile(Consumer consumer) { Path tempFile = getTempFileName(); - try (TempFileWriter writer = new TempFileWriter(types, fileSystem.create(tempFile))) { + try (TempFileWriter writer = new TempFileWriter(types, tempFileSinkFactory.createSink(fileSystem, tempFile))) { consumer.accept(writer); writer.close(); tempFiles.add(new TempFile(tempFile, writer.getWrittenBytes())); @@ -283,25 +292,6 @@ public long getSize() return size; } - @Override - public boolean equals(Object obj) - { - if (this == obj) { - return true; - } - if ((obj == null) || (getClass() != obj.getClass())) { - return false; - } - TempFile other = (TempFile) obj; - return Objects.equals(path, other.path); - } - - @Override - public int hashCode() - { - return Objects.hash(path); - } - @Override public String toString() { @@ -311,4 +301,10 @@ public String toString() .toString(); } } + + public interface TempFileSinkFactory + { + OrcDataSink createSink(FileSystem fileSystem, Path path) + throws IOException; + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/TransactionalMetadata.java b/presto-hive/src/main/java/com/facebook/presto/hive/TransactionalMetadata.java new file mode 100644 index 0000000000000..66c3498419c97 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/TransactionalMetadata.java @@ -0,0 +1,24 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.connector.ConnectorMetadata; + +public interface TransactionalMetadata + extends ConnectorMetadata +{ + void commit(); + + void rollback(); +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/authentication/AuthenticationModules.java b/presto-hive/src/main/java/com/facebook/presto/hive/authentication/AuthenticationModules.java index 0059c118b6d54..8b2ca75883f6c 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/authentication/AuthenticationModules.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/authentication/AuthenticationModules.java @@ -15,6 +15,7 @@ import com.facebook.presto.hive.ForHdfs; import com.facebook.presto.hive.ForHiveMetastore; +import com.facebook.presto.hive.HdfsConfigurationUpdater; import com.google.inject.Binder; import com.google.inject.Key; import com.google.inject.Module; @@ -23,6 +24,7 @@ import javax.inject.Inject; +import static com.facebook.presto.hive.authentication.KerberosHadoopAuthentication.createKerberosHadoopAuthentication; import static com.google.inject.Scopes.SINGLETON; import static io.airlift.configuration.ConfigBinder.configBinder; @@ -51,14 +53,15 @@ public void configure(Binder binder) configBinder(binder).bindConfig(MetastoreKerberosConfig.class); } + @Inject @Provides @Singleton @ForHiveMetastore - HadoopAuthentication createHadoopAuthentication(MetastoreKerberosConfig config) + HadoopAuthentication createHadoopAuthentication(MetastoreKerberosConfig config, HdfsConfigurationUpdater updater) { String principal = config.getHiveMetastoreClientPrincipal(); String keytabLocation = config.getHiveMetastoreClientKeytab(); - return createCachingKerberosHadoopAuthentication(principal, keytabLocation); + return createCachingKerberosHadoopAuthentication(principal, keytabLocation, updater); } }; } @@ -99,11 +102,11 @@ public void configure(Binder binder) @Provides @Singleton @ForHdfs - HadoopAuthentication createHadoopAuthentication(HdfsKerberosConfig config) + HadoopAuthentication createHadoopAuthentication(HdfsKerberosConfig config, HdfsConfigurationUpdater updater) { String principal = config.getHdfsPrestoPrincipal(); String keytabLocation = config.getHdfsPrestoKeytab(); - return createCachingKerberosHadoopAuthentication(principal, keytabLocation); + return createCachingKerberosHadoopAuthentication(principal, keytabLocation, updater); } }; } @@ -125,19 +128,19 @@ public void configure(Binder binder) @Provides @Singleton @ForHdfs - HadoopAuthentication createHadoopAuthentication(HdfsKerberosConfig config) + HadoopAuthentication createHadoopAuthentication(HdfsKerberosConfig config, HdfsConfigurationUpdater updater) { String principal = config.getHdfsPrestoPrincipal(); String keytabLocation = config.getHdfsPrestoKeytab(); - return createCachingKerberosHadoopAuthentication(principal, keytabLocation); + return createCachingKerberosHadoopAuthentication(principal, keytabLocation, updater); } }; } - private static HadoopAuthentication createCachingKerberosHadoopAuthentication(String principal, String keytabLocation) + private static HadoopAuthentication createCachingKerberosHadoopAuthentication(String principal, String keytabLocation, HdfsConfigurationUpdater updater) { KerberosAuthentication kerberosAuthentication = new KerberosAuthentication(principal, keytabLocation); - KerberosHadoopAuthentication kerberosHadoopAuthentication = new KerberosHadoopAuthentication(kerberosAuthentication); + KerberosHadoopAuthentication kerberosHadoopAuthentication = createKerberosHadoopAuthentication(kerberosAuthentication, updater); return new CachingKerberosHadoopAuthentication(kerberosHadoopAuthentication); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/authentication/KerberosHadoopAuthentication.java b/presto-hive/src/main/java/com/facebook/presto/hive/authentication/KerberosHadoopAuthentication.java index abd7904d6c1f6..b627461e7fca6 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/authentication/KerberosHadoopAuthentication.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/authentication/KerberosHadoopAuthentication.java @@ -13,34 +13,37 @@ */ package com.facebook.presto.hive.authentication; +import com.facebook.presto.hive.HdfsConfigurationUpdater; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; -import org.apache.hadoop.security.authentication.util.KerberosName; import javax.security.auth.Subject; +import static com.facebook.presto.hive.util.ConfigurationUtils.getInitialConfiguration; import static java.util.Objects.requireNonNull; import static org.apache.hadoop.security.UserGroupInformationShim.createUserGroupInformationForSubject; public class KerberosHadoopAuthentication implements HadoopAuthentication { - static { + private final KerberosAuthentication kerberosAuthentication; + + public static KerberosHadoopAuthentication createKerberosHadoopAuthentication(KerberosAuthentication kerberosAuthentication, HdfsConfigurationUpdater updater) + { + Configuration configuration = getInitialConfiguration(); + updater.updateConfiguration(configuration); + // In order to enable KERBEROS authentication method for HDFS // UserGroupInformation.authenticationMethod static field must be set to KERBEROS // It is further used in many places in DfsClient - Configuration configuration = new Configuration(false); configuration.set("hadoop.security.authentication", "kerberos"); + UserGroupInformation.setConfiguration(configuration); - // KerberosName#rules static field must be initialized - // It is used in KerberosName#getShortName which is used in User constructor invoked by UserGroupInformation#getUGIFromSubject - KerberosName.setRules("DEFAULT"); + return new KerberosHadoopAuthentication(kerberosAuthentication); } - private final KerberosAuthentication kerberosAuthentication; - - public KerberosHadoopAuthentication(KerberosAuthentication kerberosAuthentication) + private KerberosHadoopAuthentication(KerberosAuthentication kerberosAuthentication) { this.kerberosAuthentication = requireNonNull(kerberosAuthentication, "kerberosAuthentication is null"); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/avro/PrestoAvroSerDe.java b/presto-hive/src/main/java/com/facebook/presto/hive/avro/PrestoAvroSerDe.java new file mode 100644 index 0000000000000..b3f9d90e6ce27 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/avro/PrestoAvroSerDe.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.avro; + +import org.apache.avro.Schema; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hive.serde2.avro.AvroSerDe; +import org.apache.hadoop.hive.serde2.avro.AvroSerdeException; +import org.apache.hadoop.hive.serde2.avro.AvroSerdeUtils; + +import java.io.IOException; +import java.util.Properties; + +public class PrestoAvroSerDe + extends AvroSerDe +{ + @Override + public Schema determineSchemaOrReturnErrorSchema(Configuration conf, Properties props) + { + // AvroSerDe does not propagate initialization exceptions. Instead, it stores just an exception's message in + // this.configErrors (see https://issues.apache.org/jira/browse/HIVE-7868). In Presto, such behavior is not + // at all useful, as silenced exception usually carries important information which may be otherwise unavailable. + try { + return AvroSerdeUtils.determineSchemaOrThrowException(conf, props); + } + catch (IOException | AvroSerdeException e) { + throw new RuntimeException(e); + } + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/BooleanStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/BooleanStatistics.java index d82e0a932ca3e..bbd624698cdaa 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/BooleanStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/BooleanStatistics.java @@ -16,12 +16,15 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.util.Objects; import java.util.OptionalLong; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class BooleanStatistics { private final OptionalLong trueCount; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/CachingHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/CachingHiveMetastore.java index 0db75b132c5ef..fa5cf2ef32dee 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/CachingHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/CachingHiveMetastore.java @@ -23,7 +23,6 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -39,7 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; @@ -48,8 +46,9 @@ import java.util.function.Function; import static com.facebook.presto.hive.HiveErrorCode.HIVE_PARTITION_DROPPED_DURING_QUERY; -import static com.facebook.presto.hive.HiveUtil.toPartitionValues; -import static com.google.common.base.MoreObjects.toStringHelper; +import static com.facebook.presto.hive.metastore.HivePartitionName.hivePartitionName; +import static com.facebook.presto.hive.metastore.HiveTableName.hiveTableName; +import static com.facebook.presto.hive.metastore.PartitionFilter.partitionFilter; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.base.Throwables.throwIfUnchecked; @@ -256,7 +255,7 @@ private List loadAllDatabases() @Override public Optional
getTable(String databaseName, String tableName) { - return get(tableCache, HiveTableName.table(databaseName, tableName)); + return get(tableCache, hiveTableName(databaseName, tableName)); } @Override @@ -273,7 +272,7 @@ private Optional
loadTable(HiveTableName hiveTableName) @Override public PartitionStatistics getTableStatistics(String databaseName, String tableName) { - return get(tableStatisticsCache, new HiveTableName(databaseName, tableName)); + return get(tableStatisticsCache, hiveTableName(databaseName, tableName)); } private PartitionStatistics loadTableColumnStatistics(HiveTableName hiveTableName) @@ -285,24 +284,25 @@ private PartitionStatistics loadTableColumnStatistics(HiveTableName hiveTableNam public Map getPartitionStatistics(String databaseName, String tableName, Set partitionNames) { List partitions = partitionNames.stream() - .map(partitionName -> HivePartitionName.partition(databaseName, tableName, partitionName)) + .map(partitionName -> HivePartitionName.hivePartitionName(databaseName, tableName, partitionName)) .collect(toImmutableList()); Map statistics = getAll(partitionStatisticsCache, partitions); return statistics.entrySet() .stream() - .collect(toImmutableMap(entry -> entry.getKey().getPartitionName(), Entry::getValue)); + .collect(toImmutableMap(entry -> entry.getKey().getPartitionName().get(), Entry::getValue)); } private PartitionStatistics loadPartitionColumnStatistics(HivePartitionName partition) { + String partitionName = partition.getPartitionName().get(); Map partitionStatistics = delegate.getPartitionStatistics( partition.getHiveTableName().getDatabaseName(), partition.getHiveTableName().getTableName(), - ImmutableSet.of(partition.getPartitionName())); - if (!partitionStatistics.containsKey(partition.getPartitionName())) { + ImmutableSet.of(partitionName)); + if (!partitionStatistics.containsKey(partitionName)) { throw new PrestoException(HIVE_PARTITION_DROPPED_DURING_QUERY, "Statistics result does not contain entry for partition: " + partition.getPartitionName()); } - return partitionStatistics.get(partition.getPartitionName()); + return partitionStatistics.get(partitionName); } private Map loadPartitionColumnStatistics(Iterable keys) @@ -312,14 +312,14 @@ private Map loadPartitionColumnStatistic ImmutableMap.Builder result = ImmutableMap.builder(); tablePartitions.keySet().forEach(table -> { Set partitionNames = tablePartitions.get(table).stream() - .map(HivePartitionName::getPartitionName) + .map(partitionName -> partitionName.getPartitionName().get()) .collect(toImmutableSet()); Map partitionStatistics = delegate.getPartitionStatistics(table.getDatabaseName(), table.getTableName(), partitionNames); for (String partitionName : partitionNames) { if (!partitionStatistics.containsKey(partitionName)) { throw new PrestoException(HIVE_PARTITION_DROPPED_DURING_QUERY, "Statistics result does not contain entry for partition: " + partitionName); } - result.put(HivePartitionName.partition(table, partitionName), partitionStatistics.get(partitionName)); + result.put(HivePartitionName.hivePartitionName(table, partitionName), partitionStatistics.get(partitionName)); } }); return result.build(); @@ -332,7 +332,7 @@ public void updateTableStatistics(String databaseName, String tableName, Functio delegate.updateTableStatistics(databaseName, tableName, update); } finally { - tableStatisticsCache.invalidate(HiveTableName.table(databaseName, tableName)); + tableStatisticsCache.invalidate(hiveTableName(databaseName, tableName)); } } @@ -343,7 +343,7 @@ public void updatePartitionStatistics(String databaseName, String tableName, Str delegate.updatePartitionStatistics(databaseName, tableName, partitionName, update); } finally { - partitionStatisticsCache.invalidate(HivePartitionName.partition(databaseName, tableName, partitionName)); + partitionStatisticsCache.invalidate(HivePartitionName.hivePartitionName(databaseName, tableName, partitionName)); } } @@ -455,17 +455,6 @@ public void renameTable(String databaseName, String tableName, String newDatabas } } - @Override - public void updateTableParameters(String databaseName, String tableName, Function, Map> update) - { - try { - delegate.updateTableParameters(databaseName, tableName, update); - } - finally { - invalidateTable(databaseName, tableName); - } - } - @Override public void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) { @@ -501,27 +490,27 @@ public void dropColumn(String databaseName, String tableName, String columnName) protected void invalidateTable(String databaseName, String tableName) { - tableCache.invalidate(new HiveTableName(databaseName, tableName)); + tableCache.invalidate(hiveTableName(databaseName, tableName)); tableNamesCache.invalidate(databaseName); viewNamesCache.invalidate(databaseName); userTablePrivileges.asMap().keySet().stream() .filter(userTableKey -> userTableKey.matches(databaseName, tableName)) .forEach(userTablePrivileges::invalidate); - tableStatisticsCache.invalidate(new HiveTableName(databaseName, tableName)); + tableStatisticsCache.invalidate(hiveTableName(databaseName, tableName)); invalidatePartitionCache(databaseName, tableName); } @Override public Optional getPartition(String databaseName, String tableName, List partitionValues) { - HivePartitionName name = HivePartitionName.partition(databaseName, tableName, partitionValues); + HivePartitionName name = hivePartitionName(databaseName, tableName, partitionValues); return get(partitionCache, name); } @Override public Optional> getPartitionNames(String databaseName, String tableName) { - return get(partitionNamesCache, HiveTableName.table(databaseName, tableName)); + return get(partitionNamesCache, hiveTableName(databaseName, tableName)); } private Optional> loadPartitionNames(HiveTableName hiveTableName) @@ -532,7 +521,7 @@ private Optional> loadPartitionNames(HiveTableName hiveTableName) @Override public Optional> getPartitionNamesByParts(String databaseName, String tableName, List parts) { - return get(partitionFilterCache, PartitionFilter.partitionFilter(databaseName, tableName, parts)); + return get(partitionFilterCache, partitionFilter(databaseName, tableName, parts)); } private Optional> loadPartitionNamesByParts(PartitionFilter partitionFilter) @@ -546,12 +535,12 @@ private Optional> loadPartitionNamesByParts(PartitionFilter partiti @Override public Map> getPartitionsByNames(String databaseName, String tableName, List partitionNames) { - Iterable names = transform(partitionNames, name -> HivePartitionName.partition(databaseName, tableName, name)); + Iterable names = transform(partitionNames, name -> HivePartitionName.hivePartitionName(databaseName, tableName, name)); Map> all = getAll(partitionCache, names); ImmutableMap.Builder> partitionsByName = ImmutableMap.builder(); for (Entry> entry : all.entrySet()) { - partitionsByName.put(entry.getKey().getPartitionName(), entry.getValue()); + partitionsByName.put(entry.getKey().getPartitionName().get(), entry.getValue()); } return partitionsByName.build(); } @@ -578,19 +567,19 @@ private Map> loadPartitionsByNames(Iterab List partitionsToFetch = new ArrayList<>(); for (HivePartitionName partitionName : partitionNames) { checkArgument(partitionName.getHiveTableName().equals(hiveTableName), "Expected table name %s but got %s", hiveTableName, partitionName.getHiveTableName()); - partitionsToFetch.add(partitionName.getPartitionName()); + partitionsToFetch.add(partitionName.getPartitionName().get()); } ImmutableMap.Builder> partitions = ImmutableMap.builder(); Map> partitionsByNames = delegate.getPartitionsByNames(databaseName, tableName, partitionsToFetch); for (Entry> entry : partitionsByNames.entrySet()) { - partitions.put(HivePartitionName.partition(hiveTableName, entry.getKey()), entry.getValue()); + partitions.put(HivePartitionName.hivePartitionName(hiveTableName, entry.getKey()), entry.getValue()); } return partitions.build(); } @Override - public void addPartitions(String databaseName, String tableName, List partitions) + public void addPartitions(String databaseName, String tableName, List partitions) { try { delegate.addPartitions(databaseName, tableName, partitions); @@ -613,7 +602,7 @@ public void dropPartition(String databaseName, String tableName, List pa } @Override - public void alterPartition(String databaseName, String tableName, Partition partition) + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition) { try { delegate.alterPartition(databaseName, tableName, partition); @@ -623,20 +612,9 @@ public void alterPartition(String databaseName, String tableName, Partition part } } - @Override - public void updatePartitionParameters(String databaseName, String tableName, List partitionValues, Function, Map> update) - { - try { - delegate.updatePartitionParameters(databaseName, tableName, partitionValues, update); - } - finally { - invalidatePartitionCache(databaseName, tableName); - } - } - private void invalidatePartitionCache(String databaseName, String tableName) { - HiveTableName hiveTableName = HiveTableName.table(databaseName, tableName); + HiveTableName hiveTableName = hiveTableName(databaseName, tableName); partitionNamesCache.invalidate(hiveTableName); partitionCache.asMap().keySet().stream() .filter(partitionName -> partitionName.getHiveTableName().equals(hiveTableName)) @@ -711,258 +689,4 @@ private static CacheBuilder newCacheBuilder(OptionalLong expires cacheBuilder = cacheBuilder.maximumSize(maximumSize); return cacheBuilder; } - - private static class HiveTableName - { - private final String databaseName; - private final String tableName; - - private HiveTableName(String databaseName, String tableName) - { - this.databaseName = databaseName; - this.tableName = tableName; - } - - public static HiveTableName table(String databaseName, String tableName) - { - return new HiveTableName(databaseName, tableName); - } - - public String getDatabaseName() - { - return databaseName; - } - - public String getTableName() - { - return tableName; - } - - @Override - public String toString() - { - return toStringHelper(this) - .add("databaseName", databaseName) - .add("tableName", tableName) - .toString(); - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - HiveTableName other = (HiveTableName) o; - return Objects.equals(databaseName, other.databaseName) && - Objects.equals(tableName, other.tableName); - } - - @Override - public int hashCode() - { - return Objects.hash(databaseName, tableName); - } - } - - private static class HivePartitionName - { - private final HiveTableName hiveTableName; - private final List partitionValues; - private final String partitionName; // does not participate in hashCode/equals - - private HivePartitionName(HiveTableName hiveTableName, List partitionValues, String partitionName) - { - this.hiveTableName = requireNonNull(hiveTableName, "hiveTableName is null"); - this.partitionValues = requireNonNull(partitionValues, "partitionValues is null"); - this.partitionName = partitionName; - } - - public static HivePartitionName partition(HiveTableName hiveTableName, String partitionName) - { - return new HivePartitionName(hiveTableName, toPartitionValues(partitionName), partitionName); - } - - public static HivePartitionName partition(String databaseName, String tableName, String partitionName) - { - return partition(HiveTableName.table(databaseName, tableName), partitionName); - } - - public static HivePartitionName partition(String databaseName, String tableName, List partitionValues) - { - return new HivePartitionName(HiveTableName.table(databaseName, tableName), partitionValues, null); - } - - public HiveTableName getHiveTableName() - { - return hiveTableName; - } - - public List getPartitionValues() - { - return partitionValues; - } - - public String getPartitionName() - { - return requireNonNull(partitionName, "partitionName is null"); - } - - @Override - public String toString() - { - return toStringHelper(this) - .add("hiveTableName", hiveTableName) - .add("partitionValues", partitionValues) - .add("partitionName", partitionName) - .toString(); - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - HivePartitionName other = (HivePartitionName) o; - return Objects.equals(hiveTableName, other.hiveTableName) && - Objects.equals(partitionValues, other.partitionValues); - } - - @Override - public int hashCode() - { - return Objects.hash(hiveTableName, partitionValues); - } - } - - private static class PartitionFilter - { - private final HiveTableName hiveTableName; - private final List parts; - - private PartitionFilter(HiveTableName hiveTableName, List parts) - { - this.hiveTableName = hiveTableName; - this.parts = ImmutableList.copyOf(parts); - } - - public static PartitionFilter partitionFilter(String databaseName, String tableName, List parts) - { - return new PartitionFilter(HiveTableName.table(databaseName, tableName), parts); - } - - public HiveTableName getHiveTableName() - { - return hiveTableName; - } - - public List getParts() - { - return parts; - } - - @Override - public String toString() - { - return toStringHelper(this) - .add("hiveTableName", hiveTableName) - .add("parts", parts) - .toString(); - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PartitionFilter other = (PartitionFilter) o; - return Objects.equals(hiveTableName, other.hiveTableName) && - Objects.equals(parts, other.parts); - } - - @Override - public int hashCode() - { - return Objects.hash(hiveTableName, parts); - } - } - - private static class UserTableKey - { - private final String user; - private final String database; - private final String table; - - public UserTableKey(String user, String table, String database) - { - this.user = requireNonNull(user, "principalName is null"); - this.table = requireNonNull(table, "table is null"); - this.database = requireNonNull(database, "database is null"); - } - - public String getUser() - { - return user; - } - - public String getDatabase() - { - return database; - } - - public String getTable() - { - return table; - } - - public boolean matches(String databaseName, String tableName) - { - return this.database.equals(databaseName) && this.table.equals(tableName); - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - UserTableKey that = (UserTableKey) o; - return Objects.equals(user, that.user) && - Objects.equals(table, that.table) && - Objects.equals(database, that.database); - } - - @Override - public int hashCode() - { - return Objects.hash(user, table, database); - } - - @Override - public String toString() - { - return toStringHelper(this) - .add("principalName", user) - .add("table", table) - .add("database", database) - .toString(); - } - } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Column.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Column.java index 18dc3e6880649..950554c8709c9 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Column.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Column.java @@ -19,6 +19,7 @@ import javax.annotation.concurrent.Immutable; +import java.util.Objects; import java.util.Optional; import static com.google.common.base.MoreObjects.toStringHelper; @@ -68,4 +69,26 @@ public String toString() .add("type", type) .toString(); } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Column column = (Column) o; + return Objects.equals(name, column.name) && + Objects.equals(type, column.type) && + Objects.equals(comment, column.comment); + } + + @Override + public int hashCode() + { + return Objects.hash(name, type, comment); + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Database.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Database.java index c85c79fa09fdc..7a32723b508c5 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Database.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Database.java @@ -13,14 +13,18 @@ */ package com.facebook.presto.hive.metastore; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; import javax.annotation.concurrent.Immutable; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @Immutable @@ -35,12 +39,14 @@ public class Database private final Optional comment; private final Map parameters; - public Database(String databaseName, - Optional location, - String ownerName, - PrincipalType ownerType, - Optional comment, - Map parameters) + @JsonCreator + public Database( + @JsonProperty("databaseName") String databaseName, + @JsonProperty("location") Optional location, + @JsonProperty("ownerName") String ownerName, + @JsonProperty("ownerType") PrincipalType ownerType, + @JsonProperty("comment") Optional comment, + @JsonProperty("parameters") Map parameters) { this.databaseName = requireNonNull(databaseName, "databaseName is null"); this.location = requireNonNull(location, "location is null"); @@ -50,31 +56,37 @@ public Database(String databaseName, this.parameters = ImmutableMap.copyOf(requireNonNull(parameters, "parameters is null")); } + @JsonProperty public String getDatabaseName() { return databaseName; } + @JsonProperty public Optional getLocation() { return location; } + @JsonProperty public String getOwnerName() { return ownerName; } + @JsonProperty public PrincipalType getOwnerType() { return ownerType; } + @JsonProperty public Optional getComment() { return comment; } + @JsonProperty public Map getParameters() { return parameters; @@ -164,4 +176,42 @@ public Database build() parameters); } } + + @Override + public String toString() + { + return toStringHelper(this) + .add("databaseName", databaseName) + .add("location", location) + .add("ownerName", ownerName) + .add("ownerType", ownerType) + .add("comment", comment) + .add("parameters", parameters) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Database database = (Database) o; + return Objects.equals(databaseName, database.databaseName) && + Objects.equals(location, database.location) && + Objects.equals(ownerName, database.ownerName) && + ownerType == database.ownerType && + Objects.equals(comment, database.comment) && + Objects.equals(parameters, database.parameters); + } + + @Override + public int hashCode() + { + return Objects.hash(databaseName, location, ownerName, ownerType, comment, parameters); + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DateStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DateStatistics.java index cd65d81107336..8b422ad4f0f85 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DateStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DateStatistics.java @@ -16,6 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.time.LocalDate; import java.util.Objects; import java.util.Optional; @@ -23,6 +25,7 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class DateStatistics { private final Optional min; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DecimalStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DecimalStatistics.java index e9521e804e6a4..ada80c2c38944 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DecimalStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DecimalStatistics.java @@ -16,6 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.math.BigDecimal; import java.util.Objects; import java.util.Optional; @@ -23,6 +25,7 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class DecimalStatistics { private final Optional min; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DoubleStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DoubleStatistics.java index d779b681be771..c70ff272a93b8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DoubleStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/DoubleStatistics.java @@ -16,12 +16,15 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.util.Objects; import java.util.OptionalDouble; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class DoubleStatistics { private final OptionalDouble min; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/ExtendedHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/ExtendedHiveMetastore.java index 487511e8b9381..c2f3e9a0853ea 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/ExtendedHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/ExtendedHiveMetastore.java @@ -65,8 +65,6 @@ public interface ExtendedHiveMetastore void renameTable(String databaseName, String tableName, String newDatabaseName, String newTableName); - void updateTableParameters(String databaseName, String tableName, Function, Map> update); - void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment); void renameColumn(String databaseName, String tableName, String oldColumnName, String newColumnName); @@ -81,18 +79,11 @@ public interface ExtendedHiveMetastore Map> getPartitionsByNames(String databaseName, String tableName, List partitionNames); - /** - * Adds partitions to the table in a single atomic task. The implementation - * must either add all partitions and return normally, or add no partitions and - * throw an exception. - */ - void addPartitions(String databaseName, String tableName, List partitions); + void addPartitions(String databaseName, String tableName, List partitions); void dropPartition(String databaseName, String tableName, List parts, boolean deleteData); - void alterPartition(String databaseName, String tableName, Partition partition); - - void updatePartitionParameters(String databaseName, String tableName, List partitionValues, Function, Map> update); + void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition); Set getRoles(String user); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveColumnStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveColumnStatistics.java index 22364babd224d..df9fcf7ee7782 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveColumnStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveColumnStatistics.java @@ -16,6 +16,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; @@ -29,6 +31,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; +@Immutable public class HiveColumnStatistics { private static final HiveColumnStatistics EMPTY = HiveColumnStatistics.builder().build(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePartitionName.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePartitionName.java new file mode 100644 index 0000000000000..8c1f86704c620 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePartitionName.java @@ -0,0 +1,112 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + +import javax.annotation.concurrent.Immutable; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static com.facebook.presto.hive.HiveUtil.toPartitionValues; +import static com.facebook.presto.hive.metastore.HiveTableName.hiveTableName; +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +@Immutable +public class HivePartitionName +{ + private final HiveTableName hiveTableName; + private final List partitionValues; + private final Optional partitionName; // does not participate in hashCode/equals + + @JsonCreator + public HivePartitionName( + @JsonProperty("hiveTableName") HiveTableName hiveTableName, + @JsonProperty("partitionValues") List partitionValues, + @JsonProperty("partitionName") Optional partitionName) + { + this.hiveTableName = requireNonNull(hiveTableName, "hiveTableName is null"); + this.partitionValues = ImmutableList.copyOf(requireNonNull(partitionValues, "partitionValues is null")); + this.partitionName = requireNonNull(partitionName, "partitionName is null"); + } + + public static HivePartitionName hivePartitionName(HiveTableName hiveTableName, String partitionName) + { + return new HivePartitionName(hiveTableName, toPartitionValues(partitionName), Optional.of(partitionName)); + } + + public static HivePartitionName hivePartitionName(String databaseName, String tableName, String partitionName) + { + return hivePartitionName(hiveTableName(databaseName, tableName), partitionName); + } + + public static HivePartitionName hivePartitionName(String databaseName, String tableName, List partitionValues) + { + return new HivePartitionName(hiveTableName(databaseName, tableName), partitionValues, Optional.empty()); + } + + @JsonProperty + public HiveTableName getHiveTableName() + { + return hiveTableName; + } + + @JsonProperty + public List getPartitionValues() + { + return partitionValues; + } + + @JsonProperty + public Optional getPartitionName() + { + return partitionName; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("hiveTableName", hiveTableName) + .add("partitionValues", partitionValues) + .add("partitionName", partitionName) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HivePartitionName other = (HivePartitionName) o; + return Objects.equals(hiveTableName, other.hiveTableName) && + Objects.equals(partitionValues, other.partitionValues); + } + + @Override + public int hashCode() + { + return Objects.hash(hiveTableName, partitionValues); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePrivilegeInfo.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePrivilegeInfo.java index d08430e7b0030..2ac43d2f221a5 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePrivilegeInfo.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HivePrivilegeInfo.java @@ -15,9 +15,13 @@ import com.facebook.presto.spi.security.Privilege; import com.facebook.presto.spi.security.PrivilegeInfo; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableSet; import org.apache.hadoop.hive.metastore.api.PrivilegeGrantInfo; +import javax.annotation.concurrent.Immutable; + import java.util.Arrays; import java.util.Objects; import java.util.Set; @@ -31,6 +35,7 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Locale.ENGLISH; +@Immutable public class HivePrivilegeInfo { public enum HivePrivilege @@ -41,17 +46,22 @@ public enum HivePrivilege private final HivePrivilege hivePrivilege; private final boolean grantOption; - public HivePrivilegeInfo(HivePrivilege hivePrivilege, boolean grantOption) + @JsonCreator + public HivePrivilegeInfo( + @JsonProperty("hivePrivilege") HivePrivilege hivePrivilege, + @JsonProperty("grantOption") boolean grantOption) { this.hivePrivilege = hivePrivilege; this.grantOption = grantOption; } + @JsonProperty public HivePrivilege getHivePrivilege() { return hivePrivilege; } + @JsonProperty public boolean isGrantOption() { return grantOption; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveTableName.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveTableName.java new file mode 100644 index 0000000000000..f80a945763f52 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/HiveTableName.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.concurrent.Immutable; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; + +@Immutable +public class HiveTableName +{ + private final String databaseName; + private final String tableName; + + @JsonCreator + public HiveTableName(@JsonProperty("databaseName") String databaseName, @JsonProperty("tableName") String tableName) + { + this.databaseName = databaseName; + this.tableName = tableName; + } + + public static HiveTableName hiveTableName(String databaseName, String tableName) + { + return new HiveTableName(databaseName, tableName); + } + + @JsonProperty + public String getDatabaseName() + { + return databaseName; + } + + @JsonProperty + public String getTableName() + { + return tableName; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("databaseName", databaseName) + .add("tableName", tableName) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + HiveTableName other = (HiveTableName) o; + return Objects.equals(databaseName, other.databaseName) && + Objects.equals(tableName, other.tableName); + } + + @Override + public int hashCode() + { + return Objects.hash(databaseName, tableName); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/IntegerStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/IntegerStatistics.java index 1c1a3ae71565b..75b24ad200cae 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/IntegerStatistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/IntegerStatistics.java @@ -16,12 +16,15 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + import java.util.Objects; import java.util.OptionalLong; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class IntegerStatistics { private final OptionalLong min; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Partition.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Partition.java index 66810b935a3e0..ad83528c1ec60 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Partition.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Partition.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Consumer; import static com.google.common.base.MoreObjects.toStringHelper; @@ -100,6 +101,31 @@ public String toString() .toString(); } + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Partition partition = (Partition) o; + return Objects.equals(databaseName, partition.databaseName) && + Objects.equals(tableName, partition.tableName) && + Objects.equals(values, partition.values) && + Objects.equals(storage, partition.storage) && + Objects.equals(columns, partition.columns) && + Objects.equals(parameters, partition.parameters); + } + + @Override + public int hashCode() + { + return Objects.hash(databaseName, tableName, values, storage, columns, parameters); + } + public static Builder builder() { return new Builder(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionFilter.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionFilter.java new file mode 100644 index 0000000000000..fa04bb608f7f3 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionFilter.java @@ -0,0 +1,88 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + +import javax.annotation.concurrent.Immutable; + +import java.util.List; +import java.util.Objects; + +import static com.facebook.presto.hive.metastore.HiveTableName.hiveTableName; +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +@Immutable +public class PartitionFilter +{ + private final HiveTableName hiveTableName; + private final List parts; + + @JsonCreator + public PartitionFilter(@JsonProperty("hiveTableName") HiveTableName hiveTableName, @JsonProperty("parts") List parts) + { + this.hiveTableName = requireNonNull(hiveTableName, "hiveTableName is null"); + this.parts = ImmutableList.copyOf(requireNonNull(parts, "parts is null")); + } + + public static PartitionFilter partitionFilter(String databaseName, String tableName, List parts) + { + return new PartitionFilter(hiveTableName(databaseName, tableName), parts); + } + + @JsonProperty + public HiveTableName getHiveTableName() + { + return hiveTableName; + } + + @JsonProperty + public List getParts() + { + return parts; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("hiveTableName", hiveTableName) + .add("parts", parts) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + PartitionFilter other = (PartitionFilter) o; + return Objects.equals(hiveTableName, other.hiveTableName) && + Objects.equals(parts, other.parts); + } + + @Override + public int hashCode() + { + return Objects.hash(hiveTableName, parts); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionWithStatistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionWithStatistics.java new file mode 100644 index 0000000000000..8f881775fe6db --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PartitionWithStatistics.java @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.facebook.presto.hive.PartitionStatistics; + +import static com.facebook.presto.hive.HiveUtil.toPartitionValues; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class PartitionWithStatistics +{ + private final Partition partition; + private final String partitionName; + private final PartitionStatistics statistics; + + public PartitionWithStatistics(Partition partition, String partitionName, PartitionStatistics statistics) + { + this.partition = requireNonNull(partition, "partition is null"); + this.partitionName = requireNonNull(partitionName, "partitionName is null"); + checkArgument(toPartitionValues(partitionName).equals(partition.getValues()), "unexpected partition name: %s != %s", partitionName, partition.getValues()); + this.statistics = requireNonNull(statistics, "statistics is null"); + } + + public Partition getPartition() + { + return partition; + } + + public String getPartitionName() + { + return partitionName; + } + + public PartitionStatistics getStatistics() + { + return statistics; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PrincipalPrivileges.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PrincipalPrivileges.java index dad9d86e69188..2f1be22de7c63 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PrincipalPrivileges.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/PrincipalPrivileges.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.hive.metastore; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; @@ -42,54 +41,4 @@ public SetMultimap getRolePrivileges() { return rolePrivileges; } - - public static Builder builder() - { - return new Builder(); - } - - public static Builder builder(PrincipalPrivileges table) - { - return new Builder(table); - } - - public static class Builder - { - private Multimap userPrivileges = ArrayListMultimap.create(); - private Multimap rolePrivileges = ArrayListMultimap.create(); - - public Builder() - { - } - - public Builder(PrincipalPrivileges principalPrivileges) - { - userPrivileges.putAll(principalPrivileges.getUserPrivileges()); - rolePrivileges.putAll(principalPrivileges.getRolePrivileges()); - } - - public Builder setUserPrivileges(Multimap userPrivileges) - { - this.userPrivileges = ArrayListMultimap.create(userPrivileges); - return this; - } - - public Builder addUserPriviledge(String userName, HivePrivilegeInfo privilege) - { - userPrivileges.put(userName, privilege); - return this; - } - - public Builder setRolePrivileges(Multimap rolePrivileges) - { - this.rolePrivileges = ArrayListMultimap.create(rolePrivileges); - return this; - } - - public Builder addRolePriviledge(String roleName, HivePrivilegeInfo privilege) - { - rolePrivileges.put(roleName, privilege); - return this; - } - } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/RecordingHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/RecordingHiveMetastore.java new file mode 100644 index 0000000000000..29b1318f738a2 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/RecordingHiveMetastore.java @@ -0,0 +1,618 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.facebook.presto.hive.ForRecordingHiveMetastore; +import com.facebook.presto.hive.HiveClientConfig; +import com.facebook.presto.hive.HiveType; +import com.facebook.presto.hive.PartitionStatistics; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.statistics.ColumnStatisticType; +import com.facebook.presto.spi.type.Type; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.airlift.json.ObjectMapperProvider; +import org.weakref.jmx.Managed; + +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +import static com.facebook.presto.hive.metastore.HivePartitionName.hivePartitionName; +import static com.facebook.presto.hive.metastore.HiveTableName.hiveTableName; +import static com.facebook.presto.hive.metastore.PartitionFilter.partitionFilter; +import static com.facebook.presto.spi.StandardErrorCode.NOT_FOUND; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public class RecordingHiveMetastore + implements ExtendedHiveMetastore +{ + private final ExtendedHiveMetastore delegate; + private final String recordingPath; + private final boolean replay; + + private volatile Optional> allDatabases = Optional.empty(); + + private final Cache> databaseCache; + private final Cache> tableCache; + private final Cache> supportedColumnStatisticsCache; + private final Cache tableStatisticsCache; + private final Cache, Map> partitionStatisticsCache; + private final Cache>> allTablesCache; + private final Cache>> allViewsCache; + private final Cache> partitionCache; + private final Cache>> partitionNamesCache; + private final Cache>> partitionNamesByPartsCache; + private final Cache, Map>> partitionsByNamesCache; + private final Cache> rolesCache; + private final Cache> databasePrivilegesCache; + private final Cache> tablePrivilegesCache; + + @Inject + public RecordingHiveMetastore(@ForRecordingHiveMetastore ExtendedHiveMetastore delegate, HiveClientConfig hiveClientConfig) + throws IOException + { + this.delegate = requireNonNull(delegate, "delegate is null"); + requireNonNull(hiveClientConfig, "hiveClientConfig is null"); + this.recordingPath = requireNonNull(hiveClientConfig.getRecordingPath(), "recordingPath is null"); + this.replay = hiveClientConfig.isReplay(); + + databaseCache = createCache(hiveClientConfig); + tableCache = createCache(hiveClientConfig); + supportedColumnStatisticsCache = createCache(hiveClientConfig); + tableStatisticsCache = createCache(hiveClientConfig); + partitionStatisticsCache = createCache(hiveClientConfig); + allTablesCache = createCache(hiveClientConfig); + allViewsCache = createCache(hiveClientConfig); + partitionCache = createCache(hiveClientConfig); + partitionNamesCache = createCache(hiveClientConfig); + partitionNamesByPartsCache = createCache(hiveClientConfig); + partitionsByNamesCache = createCache(hiveClientConfig); + rolesCache = createCache(hiveClientConfig); + databasePrivilegesCache = createCache(hiveClientConfig); + tablePrivilegesCache = createCache(hiveClientConfig); + + if (replay) { + loadRecording(); + } + } + + @VisibleForTesting + void loadRecording() + throws IOException + { + Recording recording = new ObjectMapperProvider().get().readValue(new File(recordingPath), Recording.class); + + allDatabases = recording.getAllDatabases(); + databaseCache.putAll(toMap(recording.getDatabases())); + tableCache.putAll(toMap(recording.getTables())); + supportedColumnStatisticsCache.putAll(toMap(recording.getSupportedColumnStatistics())); + tableStatisticsCache.putAll(toMap(recording.getTableStatistics())); + partitionStatisticsCache.putAll(toMap(recording.getPartitionStatistics())); + allTablesCache.putAll(toMap(recording.getAllTables())); + allViewsCache.putAll(toMap(recording.getAllViews())); + partitionCache.putAll(toMap(recording.getPartitions())); + partitionNamesCache.putAll(toMap(recording.getPartitionNames())); + partitionNamesByPartsCache.putAll(toMap(recording.getPartitionNamesByParts())); + partitionsByNamesCache.putAll(toMap(recording.getPartitionsByNames())); + rolesCache.putAll(toMap(recording.getRoles())); + databasePrivilegesCache.putAll(toMap(recording.getDatabasePrivileges())); + tablePrivilegesCache.putAll(toMap(recording.getTablePrivileges())); + } + + private static Cache createCache(HiveClientConfig hiveClientConfig) + { + if (hiveClientConfig.isReplay()) { + return CacheBuilder.newBuilder() + .build(); + } + + return CacheBuilder.newBuilder() + .expireAfterWrite(hiveClientConfig.getRecordingDuration().toMillis(), MILLISECONDS) + .build(); + } + + @Managed + public void writeRecording() + throws IOException + { + if (replay) { + throw new IllegalStateException("Cannot write recording in replay mode"); + } + + Recording recording = new Recording( + allDatabases, + toPairs(databaseCache), + toPairs(tableCache), + toPairs(supportedColumnStatisticsCache), + toPairs(tableStatisticsCache), + toPairs(partitionStatisticsCache), + toPairs(allTablesCache), + toPairs(allViewsCache), + toPairs(partitionCache), + toPairs(partitionNamesCache), + toPairs(partitionNamesByPartsCache), + toPairs(partitionsByNamesCache), + toPairs(rolesCache), + toPairs(databasePrivilegesCache), + toPairs(tablePrivilegesCache)); + new ObjectMapperProvider().get() + .writerWithDefaultPrettyPrinter() + .writeValue(new File(recordingPath), recording); + } + + private static Map toMap(List> pairs) + { + return pairs.stream() + .collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue)); + } + + private static List> toPairs(Cache cache) + { + return cache.asMap().entrySet().stream() + .map(entry -> new Pair<>(entry.getKey(), entry.getValue())) + .collect(toImmutableList()); + } + + @Override + public Optional getDatabase(String databaseName) + { + return loadValue(databaseCache, databaseName, () -> delegate.getDatabase(databaseName)); + } + + @Override + public List getAllDatabases() + { + if (replay) { + return allDatabases.orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry for all databases")); + } + + List result = delegate.getAllDatabases(); + allDatabases = Optional.of(result); + return result; + } + + @Override + public Optional
getTable(String databaseName, String tableName) + { + return loadValue(tableCache, hiveTableName(databaseName, tableName), () -> delegate.getTable(databaseName, tableName)); + } + + @Override + public Set getSupportedColumnStatistics(Type type) + { + return loadValue(supportedColumnStatisticsCache, type.getTypeSignature().toString(), () -> delegate.getSupportedColumnStatistics(type)); + } + + @Override + public PartitionStatistics getTableStatistics(String databaseName, String tableName) + { + return loadValue( + tableStatisticsCache, + hiveTableName(databaseName, tableName), + () -> delegate.getTableStatistics(databaseName, tableName)); + } + + @Override + public Map getPartitionStatistics(String databaseName, String tableName, Set partitionNames) + { + return loadValue( + partitionStatisticsCache, + getHivePartitionNames(databaseName, tableName, partitionNames), + () -> delegate.getPartitionStatistics(databaseName, tableName, partitionNames)); + } + + @Override + public void updateTableStatistics(String databaseName, String tableName, Function update) + { + verifyRecordingMode(); + delegate.updateTableStatistics(databaseName, tableName, update); + } + + @Override + public void updatePartitionStatistics(String databaseName, String tableName, String partitionName, Function update) + { + verifyRecordingMode(); + delegate.updatePartitionStatistics(databaseName, tableName, partitionName, update); + } + + @Override + public Optional> getAllTables(String databaseName) + { + return loadValue(allTablesCache, databaseName, () -> delegate.getAllTables(databaseName)); + } + + @Override + public Optional> getAllViews(String databaseName) + { + return loadValue(allViewsCache, databaseName, () -> delegate.getAllViews(databaseName)); + } + + @Override + public void createDatabase(Database database) + { + verifyRecordingMode(); + delegate.createDatabase(database); + } + + @Override + public void dropDatabase(String databaseName) + { + verifyRecordingMode(); + delegate.dropDatabase(databaseName); + } + + @Override + public void renameDatabase(String databaseName, String newDatabaseName) + { + verifyRecordingMode(); + delegate.renameDatabase(databaseName, newDatabaseName); + } + + @Override + public void createTable(Table table, PrincipalPrivileges principalPrivileges) + { + verifyRecordingMode(); + delegate.createTable(table, principalPrivileges); + } + + @Override + public void dropTable(String databaseName, String tableName, boolean deleteData) + { + verifyRecordingMode(); + delegate.dropTable(databaseName, tableName, deleteData); + } + + @Override + public void replaceTable(String databaseName, String tableName, Table newTable, PrincipalPrivileges principalPrivileges) + { + verifyRecordingMode(); + delegate.replaceTable(databaseName, tableName, newTable, principalPrivileges); + } + + @Override + public void renameTable(String databaseName, String tableName, String newDatabaseName, String newTableName) + { + verifyRecordingMode(); + delegate.renameTable(databaseName, tableName, newDatabaseName, newTableName); + } + + @Override + public void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) + { + verifyRecordingMode(); + delegate.addColumn(databaseName, tableName, columnName, columnType, columnComment); + } + + @Override + public void renameColumn(String databaseName, String tableName, String oldColumnName, String newColumnName) + { + verifyRecordingMode(); + delegate.renameColumn(databaseName, tableName, oldColumnName, newColumnName); + } + + @Override + public void dropColumn(String databaseName, String tableName, String columnName) + { + verifyRecordingMode(); + delegate.dropColumn(databaseName, tableName, columnName); + } + + @Override + public Optional getPartition(String databaseName, String tableName, List partitionValues) + { + return loadValue( + partitionCache, + hivePartitionName(databaseName, tableName, partitionValues), + () -> delegate.getPartition(databaseName, tableName, partitionValues)); + } + + @Override + public Optional> getPartitionNames(String databaseName, String tableName) + { + return loadValue( + partitionNamesCache, + hiveTableName(databaseName, tableName), + () -> delegate.getPartitionNames(databaseName, tableName)); + } + + @Override + public Optional> getPartitionNamesByParts(String databaseName, String tableName, List parts) + { + return loadValue( + partitionNamesByPartsCache, + partitionFilter(databaseName, tableName, parts), + () -> delegate.getPartitionNamesByParts(databaseName, tableName, parts)); + } + + @Override + public Map> getPartitionsByNames(String databaseName, String tableName, List partitionNames) + { + return loadValue( + partitionsByNamesCache, + getHivePartitionNames(databaseName, tableName, ImmutableSet.copyOf(partitionNames)), + () -> delegate.getPartitionsByNames(databaseName, tableName, partitionNames)); + } + + @Override + public void addPartitions(String databaseName, String tableName, List partitions) + { + verifyRecordingMode(); + delegate.addPartitions(databaseName, tableName, partitions); + } + + @Override + public void dropPartition(String databaseName, String tableName, List parts, boolean deleteData) + { + verifyRecordingMode(); + delegate.dropPartition(databaseName, tableName, parts, deleteData); + } + + @Override + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition) + { + verifyRecordingMode(); + delegate.alterPartition(databaseName, tableName, partition); + } + + @Override + public Set getRoles(String user) + { + return loadValue(rolesCache, user, () -> delegate.getRoles(user)); + } + + @Override + public Set getDatabasePrivileges(String user, String databaseName) + { + return loadValue( + databasePrivilegesCache, + new UserDatabaseKey(user, databaseName), + () -> delegate.getDatabasePrivileges(user, databaseName)); + } + + @Override + public Set getTablePrivileges(String user, String databaseName, String tableName) + { + return loadValue( + tablePrivilegesCache, + new UserTableKey(user, databaseName, tableName), + () -> delegate.getTablePrivileges(user, databaseName, tableName)); + } + + @Override + public void grantTablePrivileges(String databaseName, String tableName, String grantee, Set privileges) + { + verifyRecordingMode(); + delegate.grantTablePrivileges(databaseName, tableName, grantee, privileges); + } + + @Override + public void revokeTablePrivileges(String databaseName, String tableName, String grantee, Set privileges) + { + verifyRecordingMode(); + delegate.revokeTablePrivileges(databaseName, tableName, grantee, privileges); + } + + private Set getHivePartitionNames(String databaseName, String tableName, Set partitionNames) + { + return partitionNames.stream() + .map(partitionName -> HivePartitionName.hivePartitionName(databaseName, tableName, partitionName)) + .collect(ImmutableSet.toImmutableSet()); + } + + private V loadValue(Cache cache, K key, Supplier valueSupplier) + { + if (replay) { + return Optional.ofNullable(cache.getIfPresent(key)) + .orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key)); + } + + V value = valueSupplier.get(); + cache.put(key, value); + return value; + } + + private void verifyRecordingMode() + { + if (replay) { + throw new IllegalStateException("Cannot perform Metastore updates in replay mode"); + } + } + + @Immutable + public static class Recording + { + private final Optional> allDatabases; + private final List>> databases; + private final List>> tables; + private final List>> supportedColumnStatistics; + private final List> tableStatistics; + private final List, Map>> partitionStatistics; + private final List>>> allTables; + private final List>>> allViews; + private final List>> partitions; + private final List>>> partitionNames; + private final List>>> partitionNamesByParts; + private final List, Map>>> partitionsByNames; + private final List>> roles; + private final List>> databasePrivileges; + private final List>> tablePrivileges; + + @JsonCreator + public Recording( + @JsonProperty("allDatabases") Optional> allDatabases, + @JsonProperty("databases") List>> databases, + @JsonProperty("tables") List>> tables, + @JsonProperty("supportedColumnStatistics") List>> supportedColumnStatistics, + @JsonProperty("tableStatistics") List> tableStatistics, + @JsonProperty("partitionStatistics") List, Map>> partitionStatistics, + @JsonProperty("allTables") List>>> allTables, + @JsonProperty("allViews") List>>> allViews, + @JsonProperty("partitions") List>> partitions, + @JsonProperty("partitionNames") List>>> partitionNames, + @JsonProperty("partitionNamesByParts") List>>> partitionNamesByParts, + @JsonProperty("partitionsByNames") List, Map>>> partitionsByNames, + @JsonProperty("roles") List>> roles, + @JsonProperty("databasePrivileges") List>> databasePrivileges, + @JsonProperty("tablePrivileges") List>> tablePrivileges) + { + this.allDatabases = allDatabases; + this.databases = databases; + this.tables = tables; + this.supportedColumnStatistics = supportedColumnStatistics; + this.tableStatistics = tableStatistics; + this.partitionStatistics = partitionStatistics; + this.allTables = allTables; + this.allViews = allViews; + this.partitions = partitions; + this.partitionNames = partitionNames; + this.partitionNamesByParts = partitionNamesByParts; + this.partitionsByNames = partitionsByNames; + this.roles = roles; + this.databasePrivileges = databasePrivileges; + this.tablePrivileges = tablePrivileges; + } + + @JsonProperty + public Optional> getAllDatabases() + { + return allDatabases; + } + + @JsonProperty + public List>> getDatabases() + { + return databases; + } + + @JsonProperty + public List>> getTables() + { + return tables; + } + + @JsonProperty + public List>> getSupportedColumnStatistics() + { + return supportedColumnStatistics; + } + + @JsonProperty + public List> getTableStatistics() + { + return tableStatistics; + } + + @JsonProperty + public List, Map>> getPartitionStatistics() + { + return partitionStatistics; + } + + @JsonProperty + public List>>> getAllTables() + { + return allTables; + } + + @JsonProperty + public List>>> getAllViews() + { + return allViews; + } + + @JsonProperty + public List>> getPartitions() + { + return partitions; + } + + @JsonProperty + public List>>> getPartitionNames() + { + return partitionNames; + } + + @JsonProperty + public List>>> getPartitionNamesByParts() + { + return partitionNamesByParts; + } + + @JsonProperty + public List, Map>>> getPartitionsByNames() + { + return partitionsByNames; + } + + @JsonProperty + public List>> getRoles() + { + return roles; + } + + @JsonProperty + public List>> getDatabasePrivileges() + { + return databasePrivileges; + } + + @JsonProperty + public List>> getTablePrivileges() + { + return tablePrivileges; + } + } + + @Immutable + public static class Pair + { + private final K key; + private final V value; + + @JsonCreator + public Pair(@JsonProperty("key") K key, @JsonProperty("value") V value) + { + this.key = requireNonNull(key, "key is null"); + this.value = requireNonNull(value, "value is null"); + } + + @JsonProperty + public K getKey() + { + return key; + } + + @JsonProperty + public V getValue() + { + return value; + } + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SemiTransactionalHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SemiTransactionalHiveMetastore.java index aac36dc736f89..1bbe0baeae224 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SemiTransactionalHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SemiTransactionalHiveMetastore.java @@ -57,11 +57,13 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_CORRUPTED_COLUMN_STATISTICS; import static com.facebook.presto.hive.HiveErrorCode.HIVE_FILESYSTEM_ERROR; import static com.facebook.presto.hive.HiveErrorCode.HIVE_METASTORE_ERROR; import static com.facebook.presto.hive.HiveErrorCode.HIVE_PATH_ALREADY_EXISTS; import static com.facebook.presto.hive.HiveErrorCode.HIVE_TABLE_DROPPED_DURING_QUERY; import static com.facebook.presto.hive.HiveMetadata.PRESTO_QUERY_ID_NAME; +import static com.facebook.presto.hive.HiveUtil.isPrestoView; import static com.facebook.presto.hive.HiveUtil.toPartitionValues; import static com.facebook.presto.hive.HiveWriteUtils.createDirectory; import static com.facebook.presto.hive.HiveWriteUtils.pathExists; @@ -1038,11 +1040,13 @@ private void prepareAddTable(HdfsContext context, TableAndMore tableAndMore) } } addTableOperations.add(new CreateTableOperation(table, tableAndMore.getPrincipalPrivileges(), tableAndMore.isIgnoreExisting())); - updateStatisticsOperations.add(new UpdateStatisticsOperation( - new SchemaTableName(table.getDatabaseName(), table.getTableName()), - Optional.empty(), - tableAndMore.getStatisticsUpdate(), - false)); + if (!isPrestoView(table)) { + updateStatisticsOperations.add(new UpdateStatisticsOperation( + new SchemaTableName(table.getDatabaseName(), table.getTableName()), + Optional.empty(), + tableAndMore.getStatisticsUpdate(), + false)); + } } private void prepareInsertExistingTable(HdfsContext context, TableAndMore tableAndMore) @@ -1066,7 +1070,7 @@ private void prepareInsertExistingTable(HdfsContext context, TableAndMore tableA private void prepareDropPartition(SchemaTableName schemaTableName, List partitionValues) { metastoreDeleteOperations.add(new IrreversibleMetastoreOperation( - format("drop partition %s.%s %s", schemaTableName, schemaTableName.getTableName(), partitionValues), + format("drop partition %s.%s %s", schemaTableName.getSchemaName(), schemaTableName.getTableName(), partitionValues), () -> delegate.dropPartition(schemaTableName.getSchemaName(), schemaTableName.getTableName(), partitionValues, true))); } @@ -1082,6 +1086,8 @@ private void prepareAlterPartition(HdfsContext context, PartitionAndMore partiti TRANSACTION_CONFLICT, format("The partition that this transaction modified was deleted in another transaction. %s %s", partition.getTableName(), partition.getValues())); } + String partitionName = getPartitionName(partition.getDatabaseName(), partition.getTableName(), partition.getValues()); + PartitionStatistics oldPartitionStatistics = getExistingPartitionStatistics(partition, partitionName); String oldPartitionLocation = oldPartition.get().getStorage().getLocation(); Path oldPartitionPath = new Path(oldPartitionLocation); @@ -1122,12 +1128,35 @@ private void prepareAlterPartition(HdfsContext context, PartitionAndMore partiti } // Partition alter must happen regardless of whether original and current location is the same // because metadata might change: e.g. storage format, column types, etc - alterPartitionOperations.add(new AlterPartitionOperation(partition, oldPartition.get())); - updateStatisticsOperations.add(new UpdateStatisticsOperation( - new SchemaTableName(partition.getDatabaseName(), partition.getTableName()), - Optional.of(getPartitionName(partition.getDatabaseName(), partition.getTableName(), partition.getValues())), - partitionAndMore.getStatisticsUpdate(), - false)); + alterPartitionOperations.add(new AlterPartitionOperation( + new PartitionWithStatistics(partition, partitionName, partitionAndMore.getStatisticsUpdate()), + new PartitionWithStatistics(oldPartition.get(), partitionName, oldPartitionStatistics))); + } + + private PartitionStatistics getExistingPartitionStatistics(Partition partition, String partitionName) + { + try { + PartitionStatistics statistics = delegate.getPartitionStatistics(partition.getDatabaseName(), partition.getTableName(), ImmutableSet.of(partitionName)) + .get(partitionName); + if (statistics == null) { + throw new PrestoException( + TRANSACTION_CONFLICT, + format("The partition that this transaction modified was deleted in another transaction. %s %s", partition.getTableName(), partition.getValues())); + } + return statistics; + } + catch (PrestoException e) { + if (e.getErrorCode().equals(HIVE_CORRUPTED_COLUMN_STATISTICS.toErrorCode())) { + log.warn( + e, + "Corrupted statistics found when altering partition. Table: %s.%s. Partition: %s", + partition.getDatabaseName(), + partition.getTableName(), + partition.getValues()); + return PartitionStatistics.empty(); + } + throw e; + } } private void prepareAddPartition(HdfsContext context, PartitionAndMore partitionAndMore) @@ -1152,12 +1181,8 @@ private void prepareAddPartition(HdfsContext context, PartitionAndMore partition targetPath, () -> cleanUpTasksForAbort.add(new DirectoryCleanUpTask(context, targetPath, true))); } - partitionAdder.addPartition(partition); - updateStatisticsOperations.add(new UpdateStatisticsOperation( - new SchemaTableName(partition.getDatabaseName(), partition.getTableName()), - Optional.of(getPartitionName(partition.getDatabaseName(), partition.getTableName(), partition.getValues())), - partitionAndMore.getStatisticsUpdate(), - false)); + String partitionName = getPartitionName(partition.getDatabaseName(), partition.getTableName(), partition.getValues()); + partitionAdder.addPartition(new PartitionWithStatistics(partition, partitionName, partitionAndMore.getStatisticsUpdate())); } private void prepareInsertExistingPartition(HdfsContext context, PartitionAndMore partitionAndMore) @@ -2289,36 +2314,40 @@ public void undo(ExtendedHiveMetastore metastore) private static class AlterPartitionOperation { - private final Partition newPartition; - private final Partition oldPartition; - private boolean done; + private final PartitionWithStatistics newPartition; + private final PartitionWithStatistics oldPartition; + private boolean undo; - public AlterPartitionOperation(Partition newPartition, Partition oldPartition) + public AlterPartitionOperation(PartitionWithStatistics newPartition, PartitionWithStatistics oldPartition) { this.newPartition = requireNonNull(newPartition, "newPartition is null"); this.oldPartition = requireNonNull(oldPartition, "oldPartition is null"); - checkArgument(newPartition.getDatabaseName().equals(oldPartition.getDatabaseName())); - checkArgument(newPartition.getTableName().equals(oldPartition.getTableName())); - checkArgument(newPartition.getValues().equals(oldPartition.getValues())); + checkArgument(newPartition.getPartition().getDatabaseName().equals(oldPartition.getPartition().getDatabaseName())); + checkArgument(newPartition.getPartition().getTableName().equals(oldPartition.getPartition().getTableName())); + checkArgument(newPartition.getPartition().getValues().equals(oldPartition.getPartition().getValues())); } public String getDescription() { - return format("alter partition %s.%s %s", newPartition.getDatabaseName(), newPartition.getTableName(), newPartition.getValues()); + return format( + "alter partition %s.%s %s", + newPartition.getPartition().getDatabaseName(), + newPartition.getPartition().getTableName(), + newPartition.getPartition().getValues()); } public void run(ExtendedHiveMetastore metastore) { - metastore.alterPartition(newPartition.getDatabaseName(), newPartition.getTableName(), newPartition); - done = true; + undo = true; + metastore.alterPartition(newPartition.getPartition().getDatabaseName(), newPartition.getPartition().getTableName(), newPartition); } public void undo(ExtendedHiveMetastore metastore) { - if (!done) { + if (!undo) { return; } - metastore.alterPartition(oldPartition.getDatabaseName(), oldPartition.getTableName(), oldPartition); + metastore.alterPartition(oldPartition.getPartition().getDatabaseName(), oldPartition.getPartition().getTableName(), oldPartition); } } @@ -2388,7 +2417,7 @@ private static class PartitionAdder private final String tableName; private final ExtendedHiveMetastore metastore; private final int batchSize; - private final List partitions; + private final List partitions; private List> createdPartitionValues = new ArrayList<>(); public PartitionAdder(String schemaName, String tableName, ExtendedHiveMetastore metastore, int batchSize) @@ -2410,32 +2439,32 @@ public String getTableName() return tableName; } - public void addPartition(Partition partition) + public void addPartition(PartitionWithStatistics partition) { - checkArgument(getPrestoQueryId(partition).isPresent()); + checkArgument(getPrestoQueryId(partition.getPartition()).isPresent()); partitions.add(partition); } public void execute() { - List> batchedPartitions = Lists.partition(partitions, batchSize); - for (List batch : batchedPartitions) { + List> batchedPartitions = Lists.partition(partitions, batchSize); + for (List batch : batchedPartitions) { try { metastore.addPartitions(schemaName, tableName, batch); - for (Partition partition : batch) { - createdPartitionValues.add(partition.getValues()); + for (PartitionWithStatistics partition : batch) { + createdPartitionValues.add(partition.getPartition().getValues()); } } catch (Throwable t) { // Add partition to the created list conservatively. // Some metastore implementations are known to violate the "all or none" guarantee for add_partitions call. boolean batchCompletelyAdded = true; - for (Partition partition : batch) { + for (PartitionWithStatistics partition : batch) { try { - Optional remotePartition = metastore.getPartition(schemaName, tableName, partition.getValues()); + Optional remotePartition = metastore.getPartition(schemaName, tableName, partition.getPartition().getValues()); // getPrestoQueryId(partition) is guaranteed to be non-empty. It is asserted in PartitionAdder.addPartition. - if (remotePartition.isPresent() && getPrestoQueryId(remotePartition.get()).equals(getPrestoQueryId(partition))) { - createdPartitionValues.add(partition.getValues()); + if (remotePartition.isPresent() && getPrestoQueryId(remotePartition.get()).equals(getPrestoQueryId(partition.getPartition()))) { + createdPartitionValues.add(partition.getPartition().getValues()); } else { batchCompletelyAdded = false; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SortingColumn.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SortingColumn.java index 9323e703befbd..019ed67aded6d 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SortingColumn.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/SortingColumn.java @@ -18,11 +18,17 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.concurrent.Immutable; + +import java.util.Objects; + import static com.facebook.presto.hive.HiveErrorCode.HIVE_INVALID_METADATA; import static com.facebook.presto.spi.block.SortOrder.ASC_NULLS_FIRST; import static com.facebook.presto.spi.block.SortOrder.DESC_NULLS_LAST; +import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; +@Immutable public class SortingColumn { public enum Order @@ -88,4 +94,34 @@ public static SortingColumn fromMetastoreApiOrder(org.apache.hadoop.hive.metasto { return new SortingColumn(order.getCol(), Order.fromMetastoreApiOrder(order.getOrder(), tablePartitionName)); } + + @Override + public String toString() + { + return toStringHelper(this) + .add("columnName", columnName) + .add("order", order) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + SortingColumn that = (SortingColumn) o; + return Objects.equals(columnName, that.columnName) && + order == that.order; + } + + @Override + public int hashCode() + { + return Objects.hash(columnName, order); + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Storage.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Storage.java index c18e1d36fcdba..6e233b377c247 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Storage.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Storage.java @@ -14,14 +14,17 @@ package com.facebook.presto.hive.metastore; import com.facebook.presto.hive.HiveBucketProperty; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableMap; import javax.annotation.concurrent.Immutable; import java.util.Map; +import java.util.Objects; import java.util.Optional; +import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @Immutable @@ -33,6 +36,7 @@ public class Storage private final boolean skewed; private final Map serdeParameters; + @JsonCreator public Storage( @JsonProperty("storageFormat") StorageFormat storageFormat, @JsonProperty("location") String location, @@ -77,6 +81,42 @@ public Map getSerdeParameters() return serdeParameters; } + @Override + public String toString() + { + return toStringHelper(this) + .add("skewed", skewed) + .add("storageFormat", storageFormat) + .add("location", location) + .add("bucketProperty", bucketProperty) + .add("serdeParameters", serdeParameters) + .toString(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Storage storage = (Storage) o; + return skewed == storage.skewed && + Objects.equals(storageFormat, storage.storageFormat) && + Objects.equals(location, storage.location) && + Objects.equals(bucketProperty, storage.bucketProperty) && + Objects.equals(serdeParameters, storage.serdeParameters); + } + + @Override + public int hashCode() + { + return Objects.hash(skewed, storageFormat, location, bucketProperty, serdeParameters); + } + public static Builder builder() { return new Builder(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/StorageFormat.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/StorageFormat.java index 9d5baffbb5288..35ab1ff5ffc03 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/StorageFormat.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/StorageFormat.java @@ -21,6 +21,7 @@ import java.util.Objects; +import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @Immutable @@ -123,4 +124,14 @@ public int hashCode() { return Objects.hash(serDe, inputFormat, outputFormat); } + + @Override + public String toString() + { + return toStringHelper(this) + .add("serDe", serDe) + .add("inputFormat", inputFormat) + .add("outputFormat", outputFormat) + .toString(); + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Table.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Table.java index 9baae8a9aff01..19adf3a4ed8dd 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Table.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/Table.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; @@ -137,23 +138,70 @@ public Optional getViewExpandedText() return viewExpandedText; } + public static Builder builder() + { + return new Builder(); + } + + public static Builder builder(Table table) + { + return new Builder(table); + } + @Override public String toString() { return toStringHelper(this) .add("databaseName", databaseName) .add("tableName", tableName) + .add("owner", owner) + .add("tableType", tableType) + .add("dataColumns", dataColumns) + .add("partitionColumns", partitionColumns) + .add("storage", storage) + .add("parameters", parameters) + .add("viewOriginalText", viewOriginalText) + .add("viewExpandedText", viewExpandedText) .toString(); } - public static Builder builder() + @Override + public boolean equals(Object o) { - return new Builder(); + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Table table = (Table) o; + return Objects.equals(databaseName, table.databaseName) && + Objects.equals(tableName, table.tableName) && + Objects.equals(owner, table.owner) && + Objects.equals(tableType, table.tableType) && + Objects.equals(dataColumns, table.dataColumns) && + Objects.equals(partitionColumns, table.partitionColumns) && + Objects.equals(storage, table.storage) && + Objects.equals(parameters, table.parameters) && + Objects.equals(viewOriginalText, table.viewOriginalText) && + Objects.equals(viewExpandedText, table.viewExpandedText); } - public static Builder builder(Table table) + @Override + public int hashCode() { - return new Builder(table); + return Objects.hash( + databaseName, + tableName, + owner, + tableType, + dataColumns, + partitionColumns, + storage, + parameters, + viewOriginalText, + viewExpandedText); } public static class Builder diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserDatabaseKey.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserDatabaseKey.java new file mode 100644 index 0000000000000..c4835e81a1240 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserDatabaseKey.java @@ -0,0 +1,79 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.concurrent.Immutable; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +@Immutable +public class UserDatabaseKey +{ + private final String user; + private final String database; + + @JsonCreator + public UserDatabaseKey(@JsonProperty("user") String user, @JsonProperty("database") String database) + { + this.user = requireNonNull(user, "principalName is null"); + this.database = requireNonNull(database, "database is null"); + } + + @JsonProperty + public String getUser() + { + return user; + } + + @JsonProperty + public String getDatabase() + { + return database; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserDatabaseKey that = (UserDatabaseKey) o; + return Objects.equals(user, that.user) && + Objects.equals(database, that.database); + } + + @Override + public int hashCode() + { + return Objects.hash(user, database); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("principalName", user) + .add("database", database) + .toString(); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserTableKey.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserTableKey.java new file mode 100644 index 0000000000000..fea0808f7b0c5 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/UserTableKey.java @@ -0,0 +1,94 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.concurrent.Immutable; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +@Immutable +public class UserTableKey +{ + private final String user; + private final String database; + private final String table; + + @JsonCreator + public UserTableKey(@JsonProperty("user") String user, @JsonProperty("table") String table, @JsonProperty("database") String database) + { + this.user = requireNonNull(user, "user is null"); + this.table = requireNonNull(table, "table is null"); + this.database = requireNonNull(database, "database is null"); + } + + @JsonProperty + public String getUser() + { + return user; + } + + @JsonProperty + public String getDatabase() + { + return database; + } + + @JsonProperty + public String getTable() + { + return table; + } + + public boolean matches(String databaseName, String tableName) + { + return this.database.equals(databaseName) && this.table.equals(tableName); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UserTableKey that = (UserTableKey) o; + return Objects.equals(user, that.user) && + Objects.equals(table, that.table) && + Objects.equals(database, that.database); + } + + @Override + public int hashCode() + { + return Objects.hash(user, table, database); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("user", user) + .add("table", table) + .add("database", database) + .toString(); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/FileHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/FileHiveMetastore.java index 29ce3becc243c..c30e39e444747 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/FileHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/FileHiveMetastore.java @@ -32,6 +32,7 @@ import com.facebook.presto.hive.metastore.HiveColumnStatistics; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.PrincipalPrivileges; import com.facebook.presto.hive.metastore.PrincipalType; import com.facebook.presto.hive.metastore.Table; @@ -78,7 +79,7 @@ import static com.facebook.presto.hive.HiveErrorCode.HIVE_METASTORE_ERROR; import static com.facebook.presto.hive.HiveErrorCode.HIVE_PARTITION_DROPPED_DURING_QUERY; -import static com.facebook.presto.hive.HivePartitionManager.extractPartitionKeyValues; +import static com.facebook.presto.hive.HivePartitionManager.extractPartitionValues; import static com.facebook.presto.hive.HiveUtil.toPartitionValues; import static com.facebook.presto.hive.metastore.Database.DEFAULT_DATABASE_NAME; import static com.facebook.presto.hive.metastore.HivePrivilegeInfo.HivePrivilege.OWNERSHIP; @@ -301,7 +302,7 @@ public synchronized Map getPartitionStatistics(Stri Table table = getRequiredTable(databaseName, tableName); ImmutableMap.Builder statistics = ImmutableMap.builder(); for (String partitionName : partitionNames) { - List partitionValues = extractPartitionKeyValues(partitionName); + List partitionValues = extractPartitionValues(partitionName); Path partitionDirectory = getPartitionMetadataDirectory(table, ImmutableList.copyOf(partitionValues)); PartitionMetadata partitionMetadata = readSchemaFile("partition", partitionDirectory, partitionCodec) .orElseThrow(() -> new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partitionValues)); @@ -351,7 +352,7 @@ public synchronized void updatePartitionStatistics(String databaseName, String t PartitionStatistics updatedStatistics = update.apply(originalStatistics); Table table = getRequiredTable(databaseName, tableName); - List partitionValues = extractPartitionKeyValues(partitionName); + List partitionValues = extractPartitionValues(partitionName); Path partitionDirectory = getPartitionMetadataDirectory(table, partitionValues); PartitionMetadata partitionMetadata = readSchemaFile("partition", partitionDirectory, partitionCodec) .orElseThrow(() -> new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partitionValues)); @@ -469,24 +470,6 @@ public synchronized void renameTable(String databaseName, String tableName, Stri } } - @Override - public synchronized void updateTableParameters(String databaseName, String tableName, Function, Map> update) - { - requireNonNull(databaseName, "databaseName is null"); - requireNonNull(tableName, "tableName is null"); - requireNonNull(update, "update is null"); - - Path tableMetadataDirectory = getTableMetadataDirectory(databaseName, tableName); - TableMetadata table = readSchemaFile("table", tableMetadataDirectory, tableCodec) - .orElseThrow(() -> new TableNotFoundException(new SchemaTableName(databaseName, tableName))); - - Map parameters = table.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - writeSchemaFile("table", tableMetadataDirectory, tableCodec, table.withParameters(updatedParameters), true); - } - } - @Override public synchronized void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) { @@ -572,7 +555,7 @@ private void alterTable(String databaseName, String tableName, Function partitions) + public synchronized void addPartitions(String databaseName, String tableName, List partitions) { requireNonNull(databaseName, "databaseName is null"); requireNonNull(tableName, "tableName is null"); @@ -585,14 +568,15 @@ public synchronized void addPartitions(String databaseName, String tableName, Li try { Map schemaFiles = new LinkedHashMap<>(); - for (Partition partition : partitions) { + for (PartitionWithStatistics partitionWithStatistics : partitions) { + Partition partition = partitionWithStatistics.getPartition(); verifiedPartition(table, partition); Path partitionMetadataDirectory = getPartitionMetadataDirectory(table, partition.getValues()); Path schemaPath = new Path(partitionMetadataDirectory, PRESTO_SCHEMA_FILE_NAME); if (metadataFileSystem.exists(schemaPath)) { throw new PrestoException(HIVE_METASTORE_ERROR, "Partition already exists"); } - byte[] schemaJson = partitionCodec.toJsonBytes(new PartitionMetadata(table, partition)); + byte[] schemaJson = partitionCodec.toJsonBytes(new PartitionMetadata(table, partitionWithStatistics)); schemaFiles.put(schemaPath, schemaJson); } @@ -676,34 +660,15 @@ public synchronized void dropPartition(String databaseName, String tableName, Li } @Override - public synchronized void alterPartition(String databaseName, String tableName, Partition partition) + public synchronized void alterPartition(String databaseName, String tableName, PartitionWithStatistics partitionWithStatistics) { Table table = getRequiredTable(databaseName, tableName); + Partition partition = partitionWithStatistics.getPartition(); verifiedPartition(table, partition); Path partitionMetadataDirectory = getPartitionMetadataDirectory(table, partition.getValues()); - writeSchemaFile("partition", partitionMetadataDirectory, partitionCodec, new PartitionMetadata(table, partition), true); - } - - @Override - public synchronized void updatePartitionParameters(String databaseName, String tableName, List partitionValues, Function, Map> update) - { - requireNonNull(databaseName, "databaseName is null"); - requireNonNull(tableName, "tableName is null"); - requireNonNull(partitionValues, "partitionValues is null"); - requireNonNull(update, "update is null"); - - Table table = getRequiredTable(databaseName, tableName); - Path partitionDirectory = getPartitionMetadataDirectory(table, partitionValues); - PartitionMetadata partition = readSchemaFile("partition", partitionDirectory, partitionCodec) - .orElseThrow(() -> new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partitionValues)); - - Map parameters = partition.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - writeSchemaFile("partition", partitionDirectory, partitionCodec, partition.withParameters(updatedParameters), true); - } + writeSchemaFile("partition", partitionMetadataDirectory, partitionCodec, new PartitionMetadata(table, partitionWithStatistics), true); } @Override diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/PartitionMetadata.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/PartitionMetadata.java index 7037b5ed1fdff..f43ba2db730f1 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/PartitionMetadata.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/file/PartitionMetadata.java @@ -15,9 +15,11 @@ import com.facebook.presto.hive.HiveBucketProperty; import com.facebook.presto.hive.HiveStorageFormat; +import com.facebook.presto.hive.PartitionStatistics; import com.facebook.presto.hive.metastore.Column; import com.facebook.presto.hive.metastore.HiveColumnStatistics; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.Storage; import com.facebook.presto.hive.metastore.StorageFormat; import com.facebook.presto.hive.metastore.Table; @@ -33,6 +35,7 @@ import java.util.Optional; import static com.facebook.presto.hive.metastore.StorageFormat.VIEW_STORAGE_FORMAT; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.updateStatisticsParameters; import static java.util.Objects.requireNonNull; public class PartitionMetadata @@ -69,15 +72,13 @@ public PartitionMetadata( this.columnStatistics = ImmutableMap.copyOf(requireNonNull(columnStatistics, "columnStatistics is null")); } - public PartitionMetadata(Table table, Partition partition) + public PartitionMetadata(Table table, PartitionWithStatistics partitionWithStatistics) { - this(table, partition, ImmutableMap.of()); - } + Partition partition = partitionWithStatistics.getPartition(); + PartitionStatistics statistics = partitionWithStatistics.getStatistics(); - public PartitionMetadata(Table table, Partition partition, Map columnStatistics) - { this.columns = partition.getColumns(); - this.parameters = partition.getParameters(); + this.parameters = updateStatisticsParameters(partition.getParameters(), statistics.getBasicStatistics()); StorageFormat tableFormat = partition.getStorage().getStorageFormat(); storageFormat = Arrays.stream(HiveStorageFormat.values()) @@ -93,7 +94,7 @@ public PartitionMetadata(Table table, Partition partition, Map, Map> update) - { - Table table = getTableOrElseThrow(databaseName, tableName); - try { - Map parameters = table.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - TableInput tableInput = GlueInputConverter.convertTable(table); - tableInput.setParameters(updatedParameters); - glueClient.updateTable(new UpdateTableRequest() - .withDatabaseName(databaseName) - .withTableInput(tableInput)); - } - } - catch (EntityNotFoundException e) { - throw new TableNotFoundException(new SchemaTableName(databaseName, tableName)); - } - catch (AmazonServiceException e) { - throw new PrestoException(HIVE_METASTORE_ERROR, e); - } - } - @Override public void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) { @@ -609,6 +587,7 @@ public Optional> getPartitionNames(String databaseName, String tabl * ['1','2','3'] or * ['', '2', ''] * + * * @param parts Full or partial list of partition values to filter on. Keys without filter will be empty strings. * @return a list of partition names. */ @@ -658,6 +637,7 @@ private static List buildPartitionNames(List partitionColumns, L * Ex: Partition keys = ['a', 'b'] * Partition names = ['a=1/b=2', 'a=2/b=2'] * + * * @param partitionNames List of full partition names * @return Mapping of partition name to partition object */ @@ -717,13 +697,13 @@ private List batchGetPartition(String databaseName, String tableName, } @Override - public void addPartitions(String databaseName, String tableName, List partitions) + public void addPartitions(String databaseName, String tableName, List partitions) { try { - List> batchedPartitions = Lists.partition(partitions, BATCH_CREATE_PARTITION_MAX_PAGE_SIZE); + List> batchedPartitions = Lists.partition(partitions, BATCH_CREATE_PARTITION_MAX_PAGE_SIZE); List> futures = new ArrayList<>(); - for (List partitionBatch : batchedPartitions) { + for (List partitionBatch : batchedPartitions) { List partitionInputs = partitionBatch.stream().map(GlueInputConverter::convertPartition).collect(toList()); futures.add(glueClient.batchCreatePartitionAsync(new BatchCreatePartitionRequest() .withDatabaseName(databaseName) @@ -785,7 +765,7 @@ public void dropPartition(String databaseName, String tableName, List pa } @Override - public void alterPartition(String databaseName, String tableName, Partition partition) + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition) { try { PartitionInput newPartition = GlueInputConverter.convertPartition(partition); @@ -793,36 +773,10 @@ public void alterPartition(String databaseName, String tableName, Partition part .withDatabaseName(databaseName) .withTableName(tableName) .withPartitionInput(newPartition) - .withPartitionValueList(partition.getValues())); - } - catch (EntityNotFoundException e) { - throw new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partition.getValues()); - } - catch (AmazonServiceException e) { - throw new PrestoException(HIVE_METASTORE_ERROR, e); - } - } - - @Override - public synchronized void updatePartitionParameters(String databaseName, String tableName, List partitionValues, Function, Map> update) - { - Partition partition = getPartition(databaseName, tableName, partitionValues) - .orElseThrow(() -> new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partitionValues)); - try { - Map parameters = partition.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - PartitionInput partitionInput = GlueInputConverter.convertPartition(partition); - partitionInput.setParameters(updatedParameters); - glueClient.updatePartition(new UpdatePartitionRequest() - .withDatabaseName(databaseName) - .withTableName(tableName) - .withPartitionValueList(partition.getValues()) - .withPartitionInput(partitionInput)); - } + .withPartitionValueList(partition.getPartition().getValues())); } catch (EntityNotFoundException e) { - throw new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partition.getValues()); + throw new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partition.getPartition().getValues()); } catch (AmazonServiceException e) { throw new PrestoException(HIVE_METASTORE_ERROR, e); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/glue/converter/GlueInputConverter.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/glue/converter/GlueInputConverter.java index bfd78d6e3ab4d..6ed213572d505 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/glue/converter/GlueInputConverter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/glue/converter/GlueInputConverter.java @@ -18,15 +18,20 @@ import com.amazonaws.services.glue.model.SerDeInfo; import com.amazonaws.services.glue.model.StorageDescriptor; import com.amazonaws.services.glue.model.TableInput; +import com.facebook.presto.hive.PartitionStatistics; import com.facebook.presto.hive.metastore.Column; import com.facebook.presto.hive.metastore.Database; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.Storage; import com.facebook.presto.hive.metastore.Table; +import com.facebook.presto.spi.PrestoException; import com.google.common.collect.ImmutableMap; import java.util.List; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.updateStatisticsParameters; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static java.util.stream.Collectors.toList; public final class GlueInputConverter @@ -57,6 +62,17 @@ public static TableInput convertTable(Table table) return input; } + public static PartitionInput convertPartition(PartitionWithStatistics partitionWithStatistics) + { + PartitionInput input = convertPartition(partitionWithStatistics.getPartition()); + PartitionStatistics statistics = partitionWithStatistics.getStatistics(); + if (!statistics.getColumnStatistics().isEmpty()) { + throw new PrestoException(NOT_SUPPORTED, "Glue metastore does not support column level statistics"); + } + input.setParameters(updateStatisticsParameters(input.getParameters(), statistics.getBasicStatistics())); + return input; + } + public static PartitionInput convertPartition(Partition partition) { PartitionInput input = new PartitionInput(); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/BridgingHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/BridgingHiveMetastore.java index 693b8b246a0f3..eb7b14cf91b66 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/BridgingHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/BridgingHiveMetastore.java @@ -15,12 +15,12 @@ import com.facebook.presto.hive.HiveType; import com.facebook.presto.hive.HiveUtil; -import com.facebook.presto.hive.PartitionNotFoundException; import com.facebook.presto.hive.PartitionStatistics; import com.facebook.presto.hive.metastore.Database; import com.facebook.presto.hive.metastore.ExtendedHiveMetastore; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.PrincipalPrivileges; import com.facebook.presto.hive.metastore.Table; import com.facebook.presto.spi.PrestoException; @@ -43,8 +43,9 @@ import java.util.stream.Collectors; import static com.facebook.presto.hive.metastore.MetastoreUtil.verifyCanDropColumn; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.fromMetastoreApiTable; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.isAvroTableWithSchemaSet; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiDatabase; -import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiPartition; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiPrivilegeGrantInfo; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiTable; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; @@ -77,7 +78,12 @@ public List getAllDatabases() @Override public Optional
getTable(String databaseName, String tableName) { - return delegate.getTable(databaseName, tableName).map(ThriftMetastoreUtil::fromMetastoreApiTable); + return delegate.getTable(databaseName, tableName).map(table -> { + if (isAvroTableWithSchemaSet(table)) { + return fromMetastoreApiTable(table, delegate.getFields(databaseName, tableName).get()); + } + return fromMetastoreApiTable(table); + }); } @Override @@ -180,19 +186,6 @@ public void renameTable(String databaseName, String tableName, String newDatabas alterTable(databaseName, tableName, table); } - @Override - public synchronized void updateTableParameters(String databaseName, String tableName, Function, Map> update) - { - org.apache.hadoop.hive.metastore.api.Table table = delegate.getTable(databaseName, tableName) - .orElseThrow(() -> new TableNotFoundException(new SchemaTableName(databaseName, tableName))); - Map parameters = table.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - table.setParameters(updatedParameters); - alterTable(databaseName, tableName, table); - } - } - @Override public void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) { @@ -281,14 +274,9 @@ public Map> getPartitionsByNames(String databaseName } @Override - public void addPartitions(String databaseName, String tableName, List partitions) + public void addPartitions(String databaseName, String tableName, List partitions) { - delegate.addPartitions( - databaseName, - tableName, - partitions.stream() - .map(ThriftMetastoreUtil::toMetastoreApiPartition) - .collect(Collectors.toList())); + delegate.addPartitions(databaseName, tableName, partitions); } @Override @@ -298,22 +286,9 @@ public void dropPartition(String databaseName, String tableName, List pa } @Override - public void alterPartition(String databaseName, String tableName, Partition partition) + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition) { - delegate.alterPartition(databaseName, tableName, toMetastoreApiPartition(partition)); - } - - @Override - public synchronized void updatePartitionParameters(String databaseName, String tableName, List partitionValues, Function, Map> update) - { - org.apache.hadoop.hive.metastore.api.Partition partition = delegate.getPartition(databaseName, tableName, partitionValues) - .orElseThrow(() -> new PartitionNotFoundException(new SchemaTableName(databaseName, tableName), partitionValues)); - Map parameters = partition.getParameters(); - Map updatedParameters = requireNonNull(update.apply(parameters), "updatedParameters is null"); - if (!parameters.equals(updatedParameters)) { - partition.setParameters(updatedParameters); - delegate.alterPartition(databaseName, tableName, partition); - } + delegate.alterPartition(databaseName, tableName, partition); } @Override diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveCluster.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveCluster.java index dcd54cdd5f1d0..dfa6e04fa9565 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveCluster.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveCluster.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.hive.metastore.thrift; +import org.apache.thrift.TException; + /** * A Hive cluster is a single logical installation of Hive. It might * have multiple instances of the metastore service (for scalability @@ -27,5 +29,6 @@ public interface HiveCluster /** * Create a connected {@link HiveMetastoreClient} to this HiveCluster */ - HiveMetastoreClient createMetastoreClient(); + HiveMetastoreClient createMetastoreClient() + throws TException; } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastore.java index d9b6a34eda656..e670d5bc02e4b 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastore.java @@ -15,9 +15,14 @@ import com.facebook.presto.hive.PartitionStatistics; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.SchemaTableName; +import com.facebook.presto.spi.TableNotFoundException; import com.facebook.presto.spi.statistics.ColumnStatisticType; import com.facebook.presto.spi.type.Type; import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.Partition; import org.apache.hadoop.hive.metastore.api.PrivilegeGrantInfo; import org.apache.hadoop.hive.metastore.api.Table; @@ -28,6 +33,7 @@ import java.util.Set; import java.util.function.Function; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_INVALID_METADATA; import static com.facebook.presto.hive.metastore.Database.DEFAULT_DATABASE_NAME; import static org.apache.hadoop.hive.metastore.api.PrincipalType.ROLE; import static org.apache.hadoop.hive.metastore.api.PrincipalType.USER; @@ -54,16 +60,11 @@ public interface HiveMetastore Optional getDatabase(String databaseName); - /** - * Adds partitions to the table in a single atomic task. The implementation - * must either add all partitions and return normally, or add no partitions and - * throw an exception. - */ - void addPartitions(String databaseName, String tableName, List partitions); + void addPartitions(String databaseName, String tableName, List partitions); void dropPartition(String databaseName, String tableName, List parts, boolean deleteData); - void alterPartition(String databaseName, String tableName, Partition partition); + void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition); Optional> getPartitionNames(String databaseName, String tableName); @@ -125,4 +126,18 @@ default boolean isTableOwner(String user, String databaseName, String tableName) Optional
table = getTable(databaseName, tableName); return table.isPresent() && user.equals(table.get().getOwner()); } + + default Optional> getFields(String databaseName, String tableName) + { + Optional
table = getTable(databaseName, tableName); + if (!table.isPresent()) { + throw new TableNotFoundException(new SchemaTableName(databaseName, tableName)); + } + + if (table.get().getSd() == null) { + throw new PrestoException(HIVE_INVALID_METADATA, "Table is missing storage descriptor"); + } + + return Optional.of(table.get().getSd().getCols()); + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastoreClient.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastoreClient.java index 64b867299dea1..b5e650f0d7264 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastoreClient.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/HiveMetastoreClient.java @@ -15,6 +15,7 @@ import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj; import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.HiveObjectPrivilege; import org.apache.hadoop.hive.metastore.api.HiveObjectRef; import org.apache.hadoop.hive.metastore.api.Partition; @@ -68,6 +69,9 @@ void alterTable(String databaseName, String tableName, Table newTable) Table getTable(String databaseName, String tableName) throws TException; + List getFields(String databaseName, String tableName) + throws TException; + List getTableColumnStatistics(String databaseName, String tableName, List columnNames) throws TException; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/StaticHiveCluster.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/StaticHiveCluster.java index 9b5a392339dd3..b3b69cb1e2bbd 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/StaticHiveCluster.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/StaticHiveCluster.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.hive.metastore.thrift; -import com.facebook.presto.spi.PrestoException; import com.google.common.net.HostAndPort; import org.apache.thrift.TException; @@ -24,7 +23,6 @@ import java.util.Collections; import java.util.List; -import static com.facebook.presto.hive.HiveErrorCode.HIVE_METASTORE_ERROR; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.Objects.requireNonNull; @@ -65,6 +63,7 @@ public StaticHiveCluster(List metastoreUris, String metastoreUsername, Hive */ @Override public HiveMetastoreClient createMetastoreClient() + throws TException { List metastores = new ArrayList<>(addresses); Collections.shuffle(metastores.subList(1, metastores.size())); @@ -82,8 +81,7 @@ public HiveMetastoreClient createMetastoreClient() lastException = e; } } - - throw new PrestoException(HIVE_METASTORE_ERROR, "Failed connecting to Hive metastore: " + addresses, lastException); + throw new TException("Failed connecting to Hive metastore: " + addresses, lastException); } private static URI checkMetastoreUri(URI uri) diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastore.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastore.java index 24f136ed3db41..2525d0d710127 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastore.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastore.java @@ -14,15 +14,18 @@ package com.facebook.presto.hive.metastore.thrift; import com.facebook.presto.hive.HiveBasicStatistics; +import com.facebook.presto.hive.HiveType; import com.facebook.presto.hive.HiveViewNotSupportedException; import com.facebook.presto.hive.PartitionNotFoundException; import com.facebook.presto.hive.PartitionStatistics; import com.facebook.presto.hive.RetryDriver; import com.facebook.presto.hive.SchemaAlreadyExistsException; import com.facebook.presto.hive.TableAlreadyExistsException; +import com.facebook.presto.hive.metastore.Column; import com.facebook.presto.hive.metastore.HiveColumnStatistics; import com.facebook.presto.hive.metastore.HivePrincipal; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaNotFoundException; import com.facebook.presto.spi.SchemaTableName; @@ -52,6 +55,7 @@ import org.apache.hadoop.hive.metastore.api.Role; import org.apache.hadoop.hive.metastore.api.Table; import org.apache.hadoop.hive.metastore.api.UnknownDBException; +import org.apache.hadoop.hive.metastore.api.UnknownTableException; import org.apache.thrift.TException; import org.weakref.jmx.Flatten; import org.weakref.jmx.Managed; @@ -59,6 +63,7 @@ import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -79,6 +84,7 @@ import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.fromMetastoreApiTable; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.getHiveBasicStatistics; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toGrants; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiPartition; import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.updateStatisticsParameters; import static com.facebook.presto.spi.StandardErrorCode.ALREADY_EXISTS; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; @@ -321,12 +327,45 @@ public Map getPartitionStatistics(String databaseNa return result.build(); } + @Override + public Optional> getFields(String databaseName, String tableName) + { + try { + return retry() + .stopOn(MetaException.class, UnknownTableException.class, UnknownDBException.class) + .stopOnIllegalExceptions() + .run("getFields", stats.getGetFields().wrap(() -> { + try (HiveMetastoreClient client = clientProvider.createMetastoreClient()) { + return Optional.of(ImmutableList.copyOf(client.getFields(databaseName, tableName))); + } + })); + } + catch (NoSuchObjectException e) { + return Optional.empty(); + } + catch (TException e) { + throw new PrestoException(HIVE_METASTORE_ERROR, e); + } + catch (Exception e) { + throw propagate(e); + } + } + private Map> getPartitionColumnStatistics( String databaseName, String tableName, Set partitionNames, List columnNames, Map partitionRowCounts) + { + return getMetastorePartitionColumnStatistics(databaseName, tableName, partitionNames, columnNames).entrySet().stream() + .filter(entry -> !entry.getValue().isEmpty()) + .collect(toImmutableMap( + Map.Entry::getKey, + entry -> groupStatisticsByColumn(entry.getValue(), partitionRowCounts.getOrDefault(entry.getKey(), OptionalLong.empty())))); + } + + private Map> getMetastorePartitionColumnStatistics(String databaseName, String tableName, Set partitionNames, List columnNames) { try { return retry() @@ -334,13 +373,7 @@ private Map> getPartitionColumnStatist .stopOnIllegalExceptions() .run("getPartitionColumnStatistics", stats.getGetPartitionColumnStatistics().wrap(() -> { try (HiveMetastoreClient client = clientProvider.createMetastoreClient()) { - Map> partitionColumnStatistics = client.getPartitionColumnStatistics(databaseName, tableName, ImmutableList.copyOf(partitionNames), columnNames); - return partitionColumnStatistics.entrySet() - .stream() - .filter(entry -> !entry.getValue().isEmpty()) - .collect(toImmutableMap( - Map.Entry::getKey, - entry -> groupStatisticsByColumn(entry.getValue(), partitionRowCounts.getOrDefault(entry.getKey(), OptionalLong.empty())))); + return client.getPartitionColumnStatistics(databaseName, tableName, ImmutableList.copyOf(partitionNames), columnNames); } })); } @@ -450,21 +483,31 @@ public synchronized void updatePartitionStatistics(String databaseName, String t Partition modifiedPartition = originalPartition.deepCopy(); HiveBasicStatistics basicStatistics = updatedStatistics.getBasicStatistics(); modifiedPartition.setParameters(updateStatisticsParameters(modifiedPartition.getParameters(), basicStatistics)); - alterPartition(databaseName, tableName, modifiedPartition); + alterPartitionWithoutStatistics(databaseName, tableName, modifiedPartition); - com.facebook.presto.hive.metastore.Table table = getTable(databaseName, tableName) - .map(ThriftMetastoreUtil::fromMetastoreApiTable) - .orElseThrow(() -> new TableNotFoundException(new SchemaTableName(databaseName, tableName))); + Map columns = modifiedPartition.getSd().getCols().stream() + .collect(toImmutableMap(FieldSchema::getName, schema -> HiveType.valueOf(schema.getType()))); + setPartitionColumnStatistics(databaseName, tableName, partitionName, columns, updatedStatistics.getColumnStatistics(), basicStatistics.getRowCount()); - OptionalLong rowCount = basicStatistics.getRowCount(); - List metastoreColumnStatistics = updatedStatistics.getColumnStatistics().entrySet().stream() - .map(entry -> createMetastoreColumnStatistics(entry.getKey(), table.getColumn(entry.getKey()).get().getType(), entry.getValue(), rowCount)) + Set removedStatistics = difference(currentStatistics.getColumnStatistics().keySet(), updatedStatistics.getColumnStatistics().keySet()); + removedStatistics.forEach(column -> deletePartitionColumnStatistics(databaseName, tableName, partitionName, column)); + } + + private void setPartitionColumnStatistics( + String databaseName, + String tableName, + String partitionName, + Map columns, + Map columnStatistics, + OptionalLong rowCount) + { + List metastoreColumnStatistics = columnStatistics.entrySet().stream() + .filter(entry -> columns.containsKey(entry.getKey())) + .map(entry -> createMetastoreColumnStatistics(entry.getKey(), columns.get(entry.getKey()), entry.getValue(), rowCount)) .collect(toImmutableList()); if (!metastoreColumnStatistics.isEmpty()) { setPartitionColumnStatistics(databaseName, tableName, partitionName, metastoreColumnStatistics); } - Set removedStatistics = difference(currentStatistics.getColumnStatistics().keySet(), updatedStatistics.getColumnStatistics().keySet()); - removedStatistics.forEach(column -> deletePartitionColumnStatistics(databaseName, tableName, partitionName, column)); } private void setPartitionColumnStatistics(String databaseName, String tableName, String partitionName, List statistics) @@ -746,7 +789,18 @@ public Optional> getPartitionNamesByParts(String databaseName, Stri } @Override - public void addPartitions(String databaseName, String tableName, List partitions) + public void addPartitions(String databaseName, String tableName, List partitionsWithStatistics) + { + List partitions = partitionsWithStatistics.stream() + .map(ThriftMetastoreUtil::toMetastoreApiPartition) + .collect(toImmutableList()); + addPartitionsWithoutStatistics(databaseName, tableName, partitions); + for (PartitionWithStatistics partitionWithStatistics : partitionsWithStatistics) { + storePartitionColumnStatistics(databaseName, tableName, partitionWithStatistics.getPartitionName(), partitionWithStatistics); + } + } + + private void addPartitionsWithoutStatistics(String databaseName, String tableName, List partitions) { if (partitions.isEmpty()) { return; @@ -806,7 +860,14 @@ public void dropPartition(String databaseName, String tableName, List pa } @Override - public void alterPartition(String databaseName, String tableName, Partition partition) + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partitionWithStatistics) + { + alterPartitionWithoutStatistics(databaseName, tableName, toMetastoreApiPartition(partitionWithStatistics)); + storePartitionColumnStatistics(databaseName, tableName, partitionWithStatistics.getPartitionName(), partitionWithStatistics); + dropExtraColumnStatisticsAfterAlterPartition(databaseName, tableName, partitionWithStatistics); + } + + private void alterPartitionWithoutStatistics(String databaseName, String tableName, Partition partition) { try { retry() @@ -830,6 +891,59 @@ public void alterPartition(String databaseName, String tableName, Partition part } } + private void storePartitionColumnStatistics(String databaseName, String tableName, String partitionName, PartitionWithStatistics partitionWithStatistics) + { + PartitionStatistics statistics = partitionWithStatistics.getStatistics(); + Map columnStatistics = statistics.getColumnStatistics(); + if (columnStatistics.isEmpty()) { + return; + } + Map columnTypes = partitionWithStatistics.getPartition().getColumns().stream() + .collect(toImmutableMap(Column::getName, Column::getType)); + setPartitionColumnStatistics(databaseName, tableName, partitionName, columnTypes, columnStatistics, statistics.getBasicStatistics().getRowCount()); + } + + /* + * After altering a partition metastore preserves all column statistics for that partition. + * + * The old statistics are supposed to be replaced by storing the new partition statistics. + * + * In case when the new statistics are not present for some columns, or if the table schema has changed + * if is needed to explicitly remove the statistics from the metastore for that columns. + */ + private void dropExtraColumnStatisticsAfterAlterPartition( + String databaseName, + String tableName, + PartitionWithStatistics partitionWithStatistics) + { + List dataColumns = partitionWithStatistics.getPartition().getColumns().stream() + .map(Column::getName) + .collect(toImmutableList()); + + Set columnsWithMissingStatistics = new HashSet<>(dataColumns); + columnsWithMissingStatistics.removeAll(partitionWithStatistics.getStatistics().getColumnStatistics().keySet()); + + // In case new partition had the statistics computed for all the columns, the storePartitionColumnStatistics + // call in the alterPartition will just overwrite the old statistics. There is no need to explicitly remove anything. + if (columnsWithMissingStatistics.isEmpty()) { + return; + } + + // check if statistics for the columnsWithMissingStatistics are actually stored in the metastore + // when trying to remove any missing statistics the metastore throws NoSuchObjectException + String partitionName = partitionWithStatistics.getPartitionName(); + List statisticsToBeRemoved = getMetastorePartitionColumnStatistics( + databaseName, + tableName, + ImmutableSet.of(partitionName), + ImmutableList.copyOf(columnsWithMissingStatistics)) + .getOrDefault(partitionName, ImmutableList.of()); + + for (ColumnStatisticsObj statistics : statisticsToBeRemoved) { + deletePartitionColumnStatistics(databaseName, tableName, partitionName, statistics.getColName()); + } + } + @Override public Optional getPartition(String databaseName, String tableName, List partitionValues) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreClient.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreClient.java index 5cefaf2dfabba..968ed33ad00e0 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreClient.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreClient.java @@ -17,6 +17,7 @@ import org.apache.hadoop.hive.metastore.api.ColumnStatisticsDesc; import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj; import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.HiveObjectPrivilege; import org.apache.hadoop.hive.metastore.api.HiveObjectRef; import org.apache.hadoop.hive.metastore.api.Partition; @@ -140,6 +141,13 @@ public Table getTable(String databaseName, String tableName) return client.get_table(databaseName, tableName); } + @Override + public List getFields(String databaseName, String tableName) + throws TException + { + return client.get_fields(databaseName, tableName); + } + @Override public List getTableColumnStatistics(String databaseName, String tableName, List columnNames) throws TException diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreStats.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreStats.java index 94d18e837a1e0..b0a7565548d23 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreStats.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftHiveMetastoreStats.java @@ -23,6 +23,7 @@ public class ThriftHiveMetastoreStats private final HiveMetastoreApiStats getAllTables = new HiveMetastoreApiStats(); private final HiveMetastoreApiStats getAllViews = new HiveMetastoreApiStats(); private final HiveMetastoreApiStats getTable = new HiveMetastoreApiStats(); + private final HiveMetastoreApiStats getFields = new HiveMetastoreApiStats(); private final HiveMetastoreApiStats getTableColumnStatistics = new HiveMetastoreApiStats(); private final HiveMetastoreApiStats getPartitionColumnStatistics = new HiveMetastoreApiStats(); private final HiveMetastoreApiStats getPartitionNames = new HiveMetastoreApiStats(); @@ -79,6 +80,13 @@ public HiveMetastoreApiStats getGetTable() return getTable; } + @Managed + @Nested + public HiveMetastoreApiStats getGetFields() + { + return getFields; + } + @Managed @Nested public HiveMetastoreApiStats getGetTableColumnStatistics() diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreModule.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreModule.java index 62d94b3875ba0..83fd6037a6bbc 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreModule.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreModule.java @@ -14,11 +14,14 @@ package com.facebook.presto.hive.metastore.thrift; import com.facebook.presto.hive.ForCachingHiveMetastore; +import com.facebook.presto.hive.ForRecordingHiveMetastore; +import com.facebook.presto.hive.HiveClientConfig; import com.facebook.presto.hive.metastore.CachingHiveMetastore; import com.facebook.presto.hive.metastore.ExtendedHiveMetastore; +import com.facebook.presto.hive.metastore.RecordingHiveMetastore; import com.google.inject.Binder; -import com.google.inject.Module; import com.google.inject.Scopes; +import io.airlift.configuration.AbstractConfigurationAwareModule; import static io.airlift.configuration.ConfigBinder.configBinder; import static java.util.Objects.requireNonNull; @@ -26,7 +29,7 @@ import static org.weakref.jmx.guice.ExportBinder.newExporter; public class ThriftMetastoreModule - implements Module + extends AbstractConfigurationAwareModule { private final String connectorId; @@ -36,14 +39,34 @@ public ThriftMetastoreModule(String connectorId) } @Override - public void configure(Binder binder) + protected void setup(Binder binder) { binder.bind(HiveMetastoreClientFactory.class).in(Scopes.SINGLETON); binder.bind(HiveCluster.class).to(StaticHiveCluster.class).in(Scopes.SINGLETON); configBinder(binder).bindConfig(StaticMetastoreConfig.class); binder.bind(HiveMetastore.class).to(ThriftHiveMetastore.class).in(Scopes.SINGLETON); - binder.bind(ExtendedHiveMetastore.class).annotatedWith(ForCachingHiveMetastore.class).to(BridgingHiveMetastore.class).in(Scopes.SINGLETON); + + if (buildConfigObject(HiveClientConfig.class).getRecordingPath() != null) { + binder.bind(ExtendedHiveMetastore.class) + .annotatedWith(ForRecordingHiveMetastore.class) + .to(BridgingHiveMetastore.class) + .in(Scopes.SINGLETON); + binder.bind(ExtendedHiveMetastore.class) + .annotatedWith(ForCachingHiveMetastore.class) + .to(RecordingHiveMetastore.class) + .in(Scopes.SINGLETON); + binder.bind(RecordingHiveMetastore.class).in(Scopes.SINGLETON); + newExporter(binder).export(RecordingHiveMetastore.class) + .as(generatedNameOf(RecordingHiveMetastore.class, connectorId)); + } + else { + binder.bind(ExtendedHiveMetastore.class) + .annotatedWith(ForCachingHiveMetastore.class) + .to(BridgingHiveMetastore.class) + .in(Scopes.SINGLETON); + } + binder.bind(ExtendedHiveMetastore.class).to(CachingHiveMetastore.class).in(Scopes.SINGLETON); newExporter(binder).export(HiveMetastore.class) .as(generatedNameOf(ThriftHiveMetastore.class, connectorId)); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreUtil.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreUtil.java index d3156fb7eec1d..5f37700e801c7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreUtil.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/ThriftMetastoreUtil.java @@ -21,6 +21,7 @@ import com.facebook.presto.hive.metastore.HiveColumnStatistics; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.PrincipalPrivileges; import com.facebook.presto.hive.metastore.PrincipalType; import com.facebook.presto.hive.metastore.Storage; @@ -71,6 +72,8 @@ import java.util.Set; import static com.facebook.presto.hive.HiveErrorCode.HIVE_INVALID_METADATA; +import static com.facebook.presto.hive.HiveMetadata.AVRO_SCHEMA_URL_KEY; +import static com.facebook.presto.hive.HiveStorageFormat.AVRO; import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createBinaryColumnStatistics; import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createBooleanColumnStatistics; import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createDateColumnStatistics; @@ -191,6 +194,13 @@ public static org.apache.hadoop.hive.metastore.api.PrincipalType toMetastoreApiP } } + public static org.apache.hadoop.hive.metastore.api.Partition toMetastoreApiPartition(PartitionWithStatistics partitionWithStatistics) + { + org.apache.hadoop.hive.metastore.api.Partition partition = toMetastoreApiPartition(partitionWithStatistics.getPartition()); + partition.setParameters(updateStatisticsParameters(partition.getParameters(), partitionWithStatistics.getStatistics().getBasicStatistics())); + return partition; + } + public static org.apache.hadoop.hive.metastore.api.Partition toMetastoreApiPartition(Partition partition) { org.apache.hadoop.hive.metastore.api.Partition result = new org.apache.hadoop.hive.metastore.api.Partition(); @@ -240,6 +250,15 @@ public static Database fromMetastoreApiDatabase(org.apache.hadoop.hive.metastore } public static Table fromMetastoreApiTable(org.apache.hadoop.hive.metastore.api.Table table) + { + StorageDescriptor storageDescriptor = table.getSd(); + if (storageDescriptor == null) { + throw new PrestoException(HIVE_INVALID_METADATA, "Table is missing storage descriptor"); + } + return fromMetastoreApiTable(table, storageDescriptor.getCols()); + } + + public static Table fromMetastoreApiTable(org.apache.hadoop.hive.metastore.api.Table table, List schema) { StorageDescriptor storageDescriptor = table.getSd(); if (storageDescriptor == null) { @@ -251,7 +270,7 @@ public static Table fromMetastoreApiTable(org.apache.hadoop.hive.metastore.api.T .setTableName(table.getTableName()) .setOwner(nullToEmpty(table.getOwner())) .setTableType(table.getTableType()) - .setDataColumns(storageDescriptor.getCols().stream() + .setDataColumns(schema.stream() .map(ThriftMetastoreUtil::fromMetastoreApiFieldSchema) .collect(toList())) .setPartitionColumns(table.getPartitionKeys().stream() @@ -266,6 +285,25 @@ public static Table fromMetastoreApiTable(org.apache.hadoop.hive.metastore.api.T return tableBuilder.build(); } + public static boolean isAvroTableWithSchemaSet(org.apache.hadoop.hive.metastore.api.Table table) + { + if (table.getParameters() == null) { + return false; + } + StorageDescriptor storageDescriptor = table.getSd(); + if (storageDescriptor == null) { + throw new PrestoException(HIVE_INVALID_METADATA, "Table does not contain a storage descriptor: " + table); + } + SerDeInfo serdeInfo = storageDescriptor.getSerdeInfo(); + if (serdeInfo == null) { + throw new PrestoException(HIVE_INVALID_METADATA, "Table storage descriptor is missing SerDe info"); + } + + return serdeInfo.getSerializationLib() != null && + table.getParameters().get(AVRO_SCHEMA_URL_KEY) != null && + serdeInfo.getSerializationLib().equals(AVRO.getSerDe()); + } + public static Partition fromMetastoreApiPartition(org.apache.hadoop.hive.metastore.api.Partition partition) { StorageDescriptor storageDescriptor = partition.getSd(); @@ -293,58 +331,58 @@ public static HiveColumnStatistics fromMetastoreApiColumnStatistics(ColumnStatis LongColumnStatsData longStatsData = columnStatistics.getStatsData().getLongStats(); OptionalLong min = longStatsData.isSetLowValue() ? OptionalLong.of(longStatsData.getLowValue()) : OptionalLong.empty(); OptionalLong max = longStatsData.isSetHighValue() ? OptionalLong.of(longStatsData.getHighValue()) : OptionalLong.empty(); - OptionalLong nullsCount = longStatsData.isSetNumNulls() ? OptionalLong.of(longStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = longStatsData.isSetNumNulls() ? fromMetastoreNullsCount(longStatsData.getNumNulls()) : OptionalLong.empty(); OptionalLong distinctValuesCount = longStatsData.isSetNumDVs() ? OptionalLong.of(longStatsData.getNumDVs()) : OptionalLong.empty(); - return createIntegerColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount)); + return createIntegerColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount, rowCount)); } if (columnStatistics.getStatsData().isSetDoubleStats()) { DoubleColumnStatsData doubleStatsData = columnStatistics.getStatsData().getDoubleStats(); OptionalDouble min = doubleStatsData.isSetLowValue() ? OptionalDouble.of(doubleStatsData.getLowValue()) : OptionalDouble.empty(); OptionalDouble max = doubleStatsData.isSetHighValue() ? OptionalDouble.of(doubleStatsData.getHighValue()) : OptionalDouble.empty(); - OptionalLong nullsCount = doubleStatsData.isSetNumNulls() ? OptionalLong.of(doubleStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = doubleStatsData.isSetNumNulls() ? fromMetastoreNullsCount(doubleStatsData.getNumNulls()) : OptionalLong.empty(); OptionalLong distinctValuesCount = doubleStatsData.isSetNumDVs() ? OptionalLong.of(doubleStatsData.getNumDVs()) : OptionalLong.empty(); - return createDoubleColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount)); + return createDoubleColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount, rowCount)); } if (columnStatistics.getStatsData().isSetDecimalStats()) { DecimalColumnStatsData decimalStatsData = columnStatistics.getStatsData().getDecimalStats(); Optional min = decimalStatsData.isSetLowValue() ? fromMetastoreDecimal(decimalStatsData.getLowValue()) : Optional.empty(); Optional max = decimalStatsData.isSetHighValue() ? fromMetastoreDecimal(decimalStatsData.getHighValue()) : Optional.empty(); - OptionalLong nullsCount = decimalStatsData.isSetNumNulls() ? OptionalLong.of(decimalStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = decimalStatsData.isSetNumNulls() ? fromMetastoreNullsCount(decimalStatsData.getNumNulls()) : OptionalLong.empty(); OptionalLong distinctValuesCount = decimalStatsData.isSetNumDVs() ? OptionalLong.of(decimalStatsData.getNumDVs()) : OptionalLong.empty(); - return createDecimalColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount)); + return createDecimalColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount, rowCount)); } if (columnStatistics.getStatsData().isSetDateStats()) { DateColumnStatsData dateStatsData = columnStatistics.getStatsData().getDateStats(); Optional min = dateStatsData.isSetLowValue() ? fromMetastoreDate(dateStatsData.getLowValue()) : Optional.empty(); Optional max = dateStatsData.isSetHighValue() ? fromMetastoreDate(dateStatsData.getHighValue()) : Optional.empty(); - OptionalLong nullsCount = dateStatsData.isSetNumNulls() ? OptionalLong.of(dateStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = dateStatsData.isSetNumNulls() ? fromMetastoreNullsCount(dateStatsData.getNumNulls()) : OptionalLong.empty(); OptionalLong distinctValuesCount = dateStatsData.isSetNumDVs() ? OptionalLong.of(dateStatsData.getNumDVs()) : OptionalLong.empty(); - return createDateColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount)); + return createDateColumnStatistics(min, max, nullsCount, fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount, rowCount)); } if (columnStatistics.getStatsData().isSetBooleanStats()) { BooleanColumnStatsData booleanStatsData = columnStatistics.getStatsData().getBooleanStats(); return createBooleanColumnStatistics( booleanStatsData.isSetNumTrues() ? OptionalLong.of(booleanStatsData.getNumTrues()) : OptionalLong.empty(), booleanStatsData.isSetNumFalses() ? OptionalLong.of(booleanStatsData.getNumFalses()) : OptionalLong.empty(), - booleanStatsData.isSetNumNulls() ? OptionalLong.of(booleanStatsData.getNumNulls()) : OptionalLong.empty()); + booleanStatsData.isSetNumNulls() ? fromMetastoreNullsCount(booleanStatsData.getNumNulls()) : OptionalLong.empty()); } if (columnStatistics.getStatsData().isSetStringStats()) { StringColumnStatsData stringStatsData = columnStatistics.getStatsData().getStringStats(); OptionalLong maxColumnLength = stringStatsData.isSetMaxColLen() ? OptionalLong.of(stringStatsData.getMaxColLen()) : OptionalLong.empty(); OptionalDouble averageColumnLength = stringStatsData.isSetAvgColLen() ? OptionalDouble.of(stringStatsData.getAvgColLen()) : OptionalDouble.empty(); - OptionalLong nullsCount = stringStatsData.isSetNumNulls() ? OptionalLong.of(stringStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = stringStatsData.isSetNumNulls() ? fromMetastoreNullsCount(stringStatsData.getNumNulls()) : OptionalLong.empty(); OptionalLong distinctValuesCount = stringStatsData.isSetNumDVs() ? OptionalLong.of(stringStatsData.getNumDVs()) : OptionalLong.empty(); return createStringColumnStatistics( maxColumnLength, getTotalSizeInBytes(averageColumnLength, rowCount, nullsCount), nullsCount, - fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount)); + fromMetastoreDistinctValuesCount(distinctValuesCount, nullsCount, rowCount)); } if (columnStatistics.getStatsData().isSetBinaryStats()) { BinaryColumnStatsData binaryStatsData = columnStatistics.getStatsData().getBinaryStats(); OptionalLong maxColumnLength = binaryStatsData.isSetMaxColLen() ? OptionalLong.of(binaryStatsData.getMaxColLen()) : OptionalLong.empty(); OptionalDouble averageColumnLength = binaryStatsData.isSetAvgColLen() ? OptionalDouble.of(binaryStatsData.getAvgColLen()) : OptionalDouble.empty(); - OptionalLong nullsCount = binaryStatsData.isSetNumNulls() ? OptionalLong.of(binaryStatsData.getNumNulls()) : OptionalLong.empty(); + OptionalLong nullsCount = binaryStatsData.isSetNumNulls() ? fromMetastoreNullsCount(binaryStatsData.getNumNulls()) : OptionalLong.empty(); return createBinaryColumnStatistics( maxColumnLength, getTotalSizeInBytes(averageColumnLength, rowCount, nullsCount), @@ -363,6 +401,19 @@ public static Optional fromMetastoreDate(Date date) return Optional.of(LocalDate.ofEpochDay(date.getDaysSinceEpoch())); } + /** + * Impala `COMPUTE STATS` will write -1 as the null count. + * + * @see IMPALA-7497 + */ + public static OptionalLong fromMetastoreNullsCount(long nullsCount) + { + if (nullsCount == -1L) { + return OptionalLong.empty(); + } + return OptionalLong.of(nullsCount); + } + public static Optional fromMetastoreDecimal(@Nullable Decimal decimal) { if (decimal == null) { @@ -386,10 +437,29 @@ public static OptionalLong getTotalSizeInBytes(OptionalDouble averageColumnLengt /** * Hive calculates NDV considering null as a distinct value */ - private static OptionalLong fromMetastoreDistinctValuesCount(OptionalLong distinctValuesCount, OptionalLong nullsCount) + private static OptionalLong fromMetastoreDistinctValuesCount(OptionalLong distinctValuesCount, OptionalLong nullsCount, OptionalLong rowCount) + { + if (distinctValuesCount.isPresent() && nullsCount.isPresent() && rowCount.isPresent()) { + return OptionalLong.of(fromMetastoreDistinctValuesCount(distinctValuesCount.getAsLong(), nullsCount.getAsLong(), rowCount.getAsLong())); + } + return OptionalLong.empty(); + } + + private static long fromMetastoreDistinctValuesCount(long distinctValuesCount, long nullsCount, long rowCount) { - if (distinctValuesCount.isPresent() && nullsCount.isPresent() && distinctValuesCount.getAsLong() > 0 && nullsCount.getAsLong() > 0) { - return OptionalLong.of(distinctValuesCount.getAsLong() - 1); + long nonNullsCount = rowCount - nullsCount; + if (nullsCount > 0 && distinctValuesCount > 0) { + distinctValuesCount--; + } + + // normalize distinctValuesCount in case there is a non null element + if (nonNullsCount > 0 && distinctValuesCount == 0) { + distinctValuesCount = 1; + } + + // the metastore may store an estimate, so the value stored may be higher than the total number of rows + if (distinctValuesCount > nonNullsCount) { + return nonNullsCount; } return distinctValuesCount; } @@ -626,10 +696,10 @@ public static Decimal toMetastoreDecimal(BigDecimal decimal) private static OptionalLong toMetastoreDistinctValuesCount(OptionalLong distinctValuesCount, OptionalLong nullsCount) { // metastore counts null as a distinct value - if (distinctValuesCount.isPresent() && nullsCount.isPresent() && (nullsCount.getAsLong() > 0)) { - return OptionalLong.of(distinctValuesCount.getAsLong() + 1); + if (distinctValuesCount.isPresent() && nullsCount.isPresent()) { + return OptionalLong.of(distinctValuesCount.getAsLong() + (nullsCount.getAsLong() > 0 ? 1 : 0)); } - return distinctValuesCount; + return OptionalLong.empty(); } private static OptionalDouble getAverageColumnLength(OptionalLong totalSizeInBytes, OptionalLong rowCount, OptionalLong nullsCount) diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/Transport.java b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/Transport.java index 3ef8d436c9475..f23c245ba85b1 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/Transport.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/metastore/thrift/Transport.java @@ -88,7 +88,7 @@ private static TTransport createRaw(HostAndPort address, Optional ss private static TTransportException rewriteException(TTransportException e, HostAndPort address) { - return new TTransportException(e.getType(), String.format("%s: %s", address, e.getMessage()), e.getCause()); + return new TTransportException(e.getType(), String.format("%s: %s", address, e.getMessage()), e); } private static class TTransportWrapper diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/HdfsParquetDataSource.java b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/HdfsParquetDataSource.java index fdd53230bc9b1..a1bc6852f9cf8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/HdfsParquetDataSource.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/HdfsParquetDataSource.java @@ -14,6 +14,8 @@ package com.facebook.presto.hive.parquet; import com.facebook.presto.hive.FileFormatDataSourceStats; +import com.facebook.presto.parquet.ParquetDataSource; +import com.facebook.presto.parquet.ParquetDataSourceId; import com.facebook.presto.spi.PrestoException; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; @@ -26,30 +28,44 @@ import static com.facebook.presto.hive.HiveErrorCode.HIVE_FILESYSTEM_ERROR; import static com.google.common.base.Strings.nullToEmpty; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; public class HdfsParquetDataSource implements ParquetDataSource { - private final String name; + private final ParquetDataSourceId id; private final long size; private final FSDataInputStream inputStream; + private long readTimeNanos; private long readBytes; private final FileFormatDataSourceStats stats; - public HdfsParquetDataSource(Path path, long size, FSDataInputStream inputStream, FileFormatDataSourceStats stats) + public HdfsParquetDataSource(ParquetDataSourceId id, long size, FSDataInputStream inputStream, FileFormatDataSourceStats stats) { - this.name = path.toString(); + this.id = requireNonNull(id, "id is null"); this.size = size; this.inputStream = inputStream; this.stats = stats; } + @Override + public ParquetDataSourceId getId() + { + return id; + } + @Override public final long getReadBytes() { return readBytes; } + @Override + public long getReadTimeNanos() + { + return readTimeNanos; + } + @Override public final long getSize() { @@ -72,23 +88,27 @@ public final void readFully(long position, byte[] buffer) @Override public final void readFully(long position, byte[] buffer, int bufferOffset, int bufferLength) { - readInternal(position, buffer, bufferOffset, bufferLength); readBytes += bufferLength; + + long start = System.nanoTime(); + readInternal(position, buffer, bufferOffset, bufferLength); + long currentReadTimeNanos = System.nanoTime() - start; + + readTimeNanos += currentReadTimeNanos; + stats.readDataBytesPerSecond(bufferLength, currentReadTimeNanos); } private void readInternal(long position, byte[] buffer, int bufferOffset, int bufferLength) { try { - long readStart = System.nanoTime(); inputStream.readFully(position, buffer, bufferOffset, bufferLength); - stats.readDataBytesPerSecond(bufferLength, System.nanoTime() - readStart); } catch (PrestoException e) { // just in case there is a Presto wrapper or hook throw e; } catch (Exception e) { - throw new PrestoException(HIVE_FILESYSTEM_ERROR, format("Error reading from %s at position %s", name, position), e); + throw new PrestoException(HIVE_FILESYSTEM_ERROR, format("Error reading from %s at position %s", id, position), e); } } @@ -96,7 +116,7 @@ public static HdfsParquetDataSource buildHdfsParquetDataSource(FileSystem fileSy { try { FSDataInputStream inputStream = fileSystem.open(path); - return new HdfsParquetDataSource(path, fileSize, inputStream, stats); + return new HdfsParquetDataSource(new ParquetDataSourceId(path.toString()), fileSize, inputStream, stats); } catch (Exception e) { if (nullToEmpty(e.getMessage()).trim().equals("Filesystem closed") || @@ -109,6 +129,6 @@ public static HdfsParquetDataSource buildHdfsParquetDataSource(FileSystem fileSy public static HdfsParquetDataSource buildHdfsParquetDataSource(FSDataInputStream inputStream, Path path, long fileSize, FileFormatDataSourceStats stats) { - return new HdfsParquetDataSource(path, fileSize, inputStream, stats); + return new HdfsParquetDataSource(new ParquetDataSourceId(path.toString()), fileSize, inputStream, stats); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetHiveRecordCursor.java b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetHiveRecordCursor.java deleted file mode 100644 index ce9b3f2bad52c..0000000000000 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetHiveRecordCursor.java +++ /dev/null @@ -1,1375 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.hive.parquet; - -import com.facebook.presto.hive.FileFormatDataSourceStats; -import com.facebook.presto.hive.HdfsEnvironment; -import com.facebook.presto.hive.HiveColumnHandle; -import com.facebook.presto.hive.parquet.predicate.ParquetPredicate; -import com.facebook.presto.hive.util.DecimalUtils; -import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.RecordCursor; -import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.block.BlockBuilder; -import com.facebook.presto.spi.predicate.TupleDomain; -import com.facebook.presto.spi.type.DecimalType; -import com.facebook.presto.spi.type.Decimals; -import com.facebook.presto.spi.type.Type; -import com.facebook.presto.spi.type.TypeManager; -import com.google.common.collect.ImmutableList; -import io.airlift.slice.Slice; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.mapreduce.TaskAttemptContext; -import org.apache.hadoop.mapreduce.TaskAttemptID; -import parquet.column.ColumnDescriptor; -import parquet.column.Dictionary; -import parquet.hadoop.ParquetFileReader; -import parquet.hadoop.ParquetInputSplit; -import parquet.hadoop.ParquetRecordReader; -import parquet.hadoop.api.ReadSupport; -import parquet.hadoop.metadata.BlockMetaData; -import parquet.hadoop.metadata.FileMetaData; -import parquet.hadoop.metadata.ParquetMetadata; -import parquet.hadoop.util.ContextUtil; -import parquet.io.api.Binary; -import parquet.io.api.Converter; -import parquet.io.api.GroupConverter; -import parquet.io.api.PrimitiveConverter; -import parquet.io.api.RecordMaterializer; -import parquet.schema.DecimalMetadata; -import parquet.schema.GroupType; -import parquet.schema.MessageType; -import parquet.schema.PrimitiveType; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.math.BigInteger; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Properties; - -import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; -import static com.facebook.presto.hive.HiveErrorCode.HIVE_CANNOT_OPEN_SPLIT; -import static com.facebook.presto.hive.HiveErrorCode.HIVE_CURSOR_ERROR; -import static com.facebook.presto.hive.HiveErrorCode.HIVE_MISSING_DATA; -import static com.facebook.presto.hive.HiveUtil.closeWithSuppression; -import static com.facebook.presto.hive.HiveUtil.getDecimalType; -import static com.facebook.presto.hive.parquet.HdfsParquetDataSource.buildHdfsParquetDataSource; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getDescriptors; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getParquetType; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.buildParquetPredicate; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.getParquetTupleDomain; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.predicateMatches; -import static com.facebook.presto.spi.type.Chars.isCharType; -import static com.facebook.presto.spi.type.Chars.truncateToLengthAndTrimSpaces; -import static com.facebook.presto.spi.type.DecimalType.createDecimalType; -import static com.facebook.presto.spi.type.StandardTypes.ARRAY; -import static com.facebook.presto.spi.type.StandardTypes.MAP; -import static com.facebook.presto.spi.type.StandardTypes.ROW; -import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; -import static com.facebook.presto.spi.type.Varchars.isVarcharType; -import static com.facebook.presto.spi.type.Varchars.truncateToLength; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Throwables.throwIfInstanceOf; -import static io.airlift.slice.Slices.wrappedBuffer; -import static java.lang.Float.floatToRawIntBits; -import static java.lang.Math.max; -import static java.lang.Math.min; -import static java.lang.String.format; -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; -import static parquet.format.converter.ParquetMetadataConverter.NO_FILTER; -import static parquet.schema.OriginalType.DECIMAL; -import static parquet.schema.OriginalType.MAP_KEY_VALUE; - -public class ParquetHiveRecordCursor - implements RecordCursor -{ - private final ParquetRecordReader recordReader; - - private final Type[] types; - - private final boolean[] booleans; - private final long[] longs; - private final double[] doubles; - private final Slice[] slices; - private final Object[] objects; - private final boolean[] nulls; - - private final long totalBytes; - private long completedBytes; - private boolean closed; - - private final FileFormatDataSourceStats stats; - - public ParquetHiveRecordCursor( - HdfsEnvironment hdfsEnvironment, - String sessionUser, - Configuration configuration, - Path path, - long start, - long length, - long fileSize, - Properties splitSchema, - List columns, - boolean useParquetColumnNames, - TypeManager typeManager, - boolean predicatePushdownEnabled, - TupleDomain effectivePredicate, - FileFormatDataSourceStats stats) - { - requireNonNull(path, "path is null"); - checkArgument(length >= 0, "length is negative"); - requireNonNull(splitSchema, "splitSchema is null"); - requireNonNull(columns, "columns is null"); - this.stats = requireNonNull(stats, "stats is null"); - - this.totalBytes = length; - - int size = columns.size(); - - this.types = new Type[size]; - - this.booleans = new boolean[size]; - this.longs = new long[size]; - this.doubles = new double[size]; - this.slices = new Slice[size]; - this.objects = new Object[size]; - this.nulls = new boolean[size]; - - for (int columnIndex = 0; columnIndex < columns.size(); columnIndex++) { - HiveColumnHandle column = columns.get(columnIndex); - checkState(column.getColumnType() == REGULAR, "column type must be regular"); - - types[columnIndex] = typeManager.getType(column.getTypeSignature()); - } - - this.recordReader = createParquetRecordReader( - hdfsEnvironment, - sessionUser, - configuration, - path, - start, - length, - fileSize, - columns, - useParquetColumnNames, - predicatePushdownEnabled, - effectivePredicate); - } - - @Override - public long getCompletedBytes() - { - if (!closed) { - updateCompletedBytes(); - } - return completedBytes; - } - - @Override - public long getReadTimeNanos() - { - return 0; - } - - private void updateCompletedBytes() - { - try { - long newCompletedBytes = (long) (totalBytes * recordReader.getProgress()); - completedBytes = min(totalBytes, max(completedBytes, newCompletedBytes)); - } - catch (IOException ignored) { - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - @Override - public Type getType(int field) - { - return types[field]; - } - - @Override - public boolean advanceNextPosition() - { - try { - // reset null flags - Arrays.fill(nulls, true); - - if (closed || !recordReader.nextKeyValue()) { - close(); - return false; - } - - return true; - } - catch (IOException | RuntimeException | InterruptedException e) { - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - } - - closeWithSuppression(this, e); - throw new PrestoException(HIVE_CURSOR_ERROR, e); - } - } - - @Override - public boolean getBoolean(int fieldId) - { - checkState(!closed, "Cursor is closed"); - - validateType(fieldId, boolean.class); - return booleans[fieldId]; - } - - @Override - public long getLong(int fieldId) - { - checkState(!closed, "Cursor is closed"); - - validateType(fieldId, long.class); - return longs[fieldId]; - } - - @Override - public double getDouble(int fieldId) - { - checkState(!closed, "Cursor is closed"); - - validateType(fieldId, double.class); - return doubles[fieldId]; - } - - @Override - public Slice getSlice(int fieldId) - { - checkState(!closed, "Cursor is closed"); - - validateType(fieldId, Slice.class); - return slices[fieldId]; - } - - @Override - public Object getObject(int fieldId) - { - checkState(!closed, "Cursor is closed"); - - validateType(fieldId, Block.class); - return objects[fieldId]; - } - - @Override - public boolean isNull(int fieldId) - { - checkState(!closed, "Cursor is closed"); - return nulls[fieldId]; - } - - private void validateType(int fieldId, Class javaType) - { - if (types[fieldId].getJavaType() != javaType) { - // we don't use Preconditions.checkArgument because it requires boxing fieldId, which affects inner loop performance - throw new IllegalArgumentException(format("Expected field to be %s, actual %s (field %s)", javaType.getName(), types[fieldId].getJavaType().getName(), fieldId)); - } - } - - @Override - public void close() - { - // some hive input formats are broken and bad things can happen if you close them multiple times - if (closed) { - return; - } - closed = true; - - updateCompletedBytes(); - - try { - recordReader.close(); - } - catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private ParquetRecordReader createParquetRecordReader( - HdfsEnvironment hdfsEnvironment, - String sessionUser, - Configuration configuration, - Path path, - long start, - long length, - long fileSize, - List columns, - boolean useParquetColumnNames, - boolean predicatePushdownEnabled, - TupleDomain effectivePredicate) - { - ParquetDataSource dataSource = null; - try { - FileSystem fileSystem = hdfsEnvironment.getFileSystem(sessionUser, path, configuration); - dataSource = buildHdfsParquetDataSource(fileSystem, path, start, length, fileSize, stats); - ParquetMetadata parquetMetadata = hdfsEnvironment.doAs(sessionUser, () -> ParquetFileReader.readFooter(configuration, path, NO_FILTER)); - List blocks = parquetMetadata.getBlocks(); - FileMetaData fileMetaData = parquetMetadata.getFileMetaData(); - MessageType fileSchema = fileMetaData.getSchema(); - - PrestoReadSupport readSupport = new PrestoReadSupport(useParquetColumnNames, columns, fileSchema); - - List fields = columns.stream() - .filter(column -> column.getColumnType() == REGULAR) - .map(column -> getParquetType(column, fileSchema, useParquetColumnNames)) - .filter(Objects::nonNull) - .collect(toList()); - - MessageType requestedSchema = new MessageType(fileSchema.getName(), fields); - - LongArrayList offsets = new LongArrayList(blocks.size()); - for (BlockMetaData block : blocks) { - long firstDataPage = block.getColumns().get(0).getFirstDataPageOffset(); - if (firstDataPage >= start && firstDataPage < start + length) { - if (predicatePushdownEnabled) { - Map, RichColumnDescriptor> descriptorsByPath = getDescriptors(fileSchema, requestedSchema); - TupleDomain parquetTupleDomain = getParquetTupleDomain(descriptorsByPath, effectivePredicate); - ParquetPredicate parquetPredicate = buildParquetPredicate(requestedSchema, parquetTupleDomain, descriptorsByPath); - if (predicateMatches(parquetPredicate, block, dataSource, descriptorsByPath, parquetTupleDomain)) { - offsets.add(block.getStartingPos()); - } - } - else { - offsets.add(block.getStartingPos()); - } - } - } - - ParquetInputSplit split = new ParquetInputSplit(path, start, start + length, length, null, offsets.toLongArray()); - - TaskAttemptContext taskContext = ContextUtil.newTaskAttemptContext(configuration, new TaskAttemptID()); - - return hdfsEnvironment.doAs(sessionUser, () -> { - ParquetRecordReader realReader = new PrestoParquetRecordReader(readSupport); - realReader.initialize(split, taskContext); - return realReader; - }); - } - catch (Exception e) { - throwIfInstanceOf(e, PrestoException.class); - if (e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } - String message = format("Error opening Hive split %s (offset=%s, length=%s): %s", path, start, length, e.getMessage()); - if (e.getClass().getSimpleName().equals("BlockMissingException")) { - throw new PrestoException(HIVE_MISSING_DATA, message, e); - } - throw new PrestoException(HIVE_CANNOT_OPEN_SPLIT, message, e); - } - finally { - if (dataSource != null) { - try { - dataSource.close(); - } - catch (IOException ignored) { - } - } - } - } - - public class PrestoParquetRecordReader - extends ParquetRecordReader - { - public PrestoParquetRecordReader(PrestoReadSupport readSupport) - { - super(readSupport); - } - } - - public final class PrestoReadSupport - extends ReadSupport - { - private final boolean useParquetColumnNames; - private final List columns; - private final List converters; - - public PrestoReadSupport(boolean useParquetColumnNames, List columns, MessageType messageType) - { - this.columns = columns; - this.useParquetColumnNames = useParquetColumnNames; - - ImmutableList.Builder converters = ImmutableList.builder(); - for (int i = 0; i < columns.size(); i++) { - HiveColumnHandle column = columns.get(i); - if (column.getColumnType() == REGULAR) { - parquet.schema.Type parquetType = getParquetType(column, messageType, useParquetColumnNames); - if (parquetType == null) { - continue; - } - if (parquetType.isPrimitive()) { - Optional decimalType = getDecimalType(column.getHiveType()); - if (decimalType.isPresent()) { - converters.add(new ParquetDecimalColumnConverter(i, decimalType.get())); - } - else { - converters.add(new ParquetPrimitiveColumnConverter(i)); - } - } - else { - converters.add(new ParquetColumnConverter(createGroupConverter(types[i], parquetType.getName(), parquetType, i), i)); - } - } - } - this.converters = converters.build(); - } - - @Override - @SuppressWarnings("deprecation") - public ReadContext init( - Configuration configuration, - Map keyValueMetaData, - MessageType messageType) - { - List fields = columns.stream() - .filter(column -> column.getColumnType() == REGULAR) - .map(column -> getParquetType(column, messageType, useParquetColumnNames)) - .filter(Objects::nonNull) - .collect(toList()); - MessageType requestedProjection = new MessageType(messageType.getName(), fields); - return new ReadContext(requestedProjection); - } - - @Override - public RecordMaterializer prepareForRead( - Configuration configuration, - Map keyValueMetaData, - MessageType fileSchema, - ReadContext readContext) - { - return new ParquetRecordConverter(converters); - } - } - - private static class ParquetRecordConverter - extends RecordMaterializer - { - private final ParquetGroupConverter groupConverter; - - public ParquetRecordConverter(List converters) - { - groupConverter = new ParquetGroupConverter(converters); - } - - @Override - public FakeParquetRecord getCurrentRecord() - { - // Parquet skips the record if it is null, so we need non-null record - return FakeParquetRecord.MATERIALIZE_RECORD; - } - - @Override - public GroupConverter getRootConverter() - { - return groupConverter; - } - } - - private enum FakeParquetRecord - { - MATERIALIZE_RECORD - } - - public static class ParquetGroupConverter - extends GroupConverter - { - private final List converters; - - public ParquetGroupConverter(List converters) - { - this.converters = converters; - } - - @Override - public Converter getConverter(int fieldIndex) - { - return converters.get(fieldIndex); - } - - @Override - public void start() - { - } - - @Override - public void end() - { - } - } - - @SuppressWarnings("AccessingNonPublicFieldOfAnotherObject") - private class ParquetPrimitiveColumnConverter - extends PrimitiveConverter - { - private final int fieldIndex; - - private ParquetPrimitiveColumnConverter(int fieldIndex) - { - this.fieldIndex = fieldIndex; - } - - @Override - public boolean isPrimitive() - { - return true; - } - - @Override - public PrimitiveConverter asPrimitiveConverter() - { - return this; - } - - @Override - public boolean hasDictionarySupport() - { - return false; - } - - @Override - public void setDictionary(Dictionary dictionary) - { - } - - @Override - public void addValueFromDictionary(int dictionaryId) - { - } - - @Override - public void addBoolean(boolean value) - { - nulls[fieldIndex] = false; - booleans[fieldIndex] = value; - } - - @Override - public void addDouble(double value) - { - nulls[fieldIndex] = false; - doubles[fieldIndex] = value; - } - - @Override - public void addLong(long value) - { - nulls[fieldIndex] = false; - longs[fieldIndex] = value; - } - - @Override - public void addBinary(Binary value) - { - nulls[fieldIndex] = false; - Type type = types[fieldIndex]; - if (type == TIMESTAMP) { - longs[fieldIndex] = ParquetTimestampUtils.getTimestampMillis(value); - } - else if (isVarcharType(type)) { - slices[fieldIndex] = truncateToLength(wrappedBuffer(value.getBytes()), type); - } - else if (isCharType(type)) { - slices[fieldIndex] = truncateToLengthAndTrimSpaces(wrappedBuffer(value.getBytes()), type); - } - else { - slices[fieldIndex] = wrappedBuffer(value.getBytes()); - } - } - - @Override - public void addFloat(float value) - { - nulls[fieldIndex] = false; - longs[fieldIndex] = floatToRawIntBits(value); - } - - @Override - public void addInt(int value) - { - nulls[fieldIndex] = false; - longs[fieldIndex] = value; - } - } - - // todo: support for other types of decimal storage (see https://github.com/Parquet/parquet-format/blob/master/LogicalTypes.md) - private class ParquetDecimalColumnConverter - extends PrimitiveConverter - { - private final int fieldIndex; - private final DecimalType decimalType; - - private ParquetDecimalColumnConverter(int fieldIndex, DecimalType decimalType) - { - this.fieldIndex = fieldIndex; - this.decimalType = requireNonNull(decimalType, "decimalType is null"); - } - - public void addBinary(Binary value) - { - nulls[fieldIndex] = false; - if (decimalType.isShort()) { - longs[fieldIndex] = DecimalUtils.getShortDecimalValue(value.getBytes()); - } - else { - slices[fieldIndex] = Decimals.encodeUnscaledValue(new BigInteger(value.getBytes())); - } - } - } - - public class ParquetColumnConverter - extends GroupConverter - { - private final GroupedConverter groupedConverter; - private final int fieldIndex; - - public ParquetColumnConverter(GroupedConverter groupedConverter, int fieldIndex) - { - this.groupedConverter = groupedConverter; - this.fieldIndex = fieldIndex; - } - - @Override - public Converter getConverter(int fieldIndex) - { - return groupedConverter.getConverter(fieldIndex); - } - - @Override - public void start() - { - groupedConverter.beforeValue(null); - groupedConverter.start(); - } - - @Override - public void end() - { - groupedConverter.afterValue(); - groupedConverter.end(); - - nulls[fieldIndex] = false; - - objects[fieldIndex] = groupedConverter.getBlock(); - } - } - - private interface BlockConverter - { - void beforeValue(BlockBuilder builder); - - void afterValue(); - } - - private abstract static class GroupedConverter - extends GroupConverter - implements BlockConverter - { - public abstract Block getBlock(); - } - - private static BlockConverter createConverter(Type prestoType, String columnName, parquet.schema.Type parquetType, int fieldIndex) - { - if (parquetType.isPrimitive()) { - if (parquetType.getOriginalType() == DECIMAL) { - DecimalMetadata decimalMetadata = ((PrimitiveType) parquetType).getDecimalMetadata(); - return new ParquetDecimalConverter(createDecimalType(decimalMetadata.getPrecision(), decimalMetadata.getScale())); - } - else { - return new ParquetPrimitiveConverter(prestoType, fieldIndex); - } - } - - return createGroupConverter(prestoType, columnName, parquetType, fieldIndex); - } - - private static GroupedConverter createGroupConverter(Type prestoType, String columnName, parquet.schema.Type parquetType, int fieldIndex) - { - GroupType groupType = parquetType.asGroupType(); - switch (prestoType.getTypeSignature().getBase()) { - case ARRAY: - return new ParquetListConverter(prestoType, columnName, groupType, fieldIndex); - case MAP: - return new ParquetMapConverter(prestoType, columnName, groupType, fieldIndex); - case ROW: - return new ParquetStructConverter(prestoType, columnName, groupType, fieldIndex); - default: - throw new IllegalArgumentException("Column " + columnName + " type " + parquetType.getOriginalType() + " not supported"); - } - } - - private static class ParquetStructConverter - extends GroupedConverter - { - private static final int NULL_BUILDER_POSITIONS_THRESHOLD = 100; - private static final int NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD = 32768; - - private final Type rowType; - private final int fieldIndex; - - private final List converters; - private BlockBuilder builder; - private BlockBuilder nullBuilder; // used internally when builder is set to null - private BlockBuilder currentEntryBuilder; - - public ParquetStructConverter(Type prestoType, String columnName, GroupType entryType, int fieldIndex) - { - checkArgument(ROW.equals(prestoType.getTypeSignature().getBase())); - List prestoTypeParameters = prestoType.getTypeParameters(); - List fieldTypes = entryType.getFields(); - checkArgument( - prestoTypeParameters.size() == fieldTypes.size(), - "Schema mismatch, metastore schema for row column %s has %s fields but parquet schema has %s fields", - columnName, - prestoTypeParameters.size(), - fieldTypes.size()); - - this.rowType = prestoType; - this.fieldIndex = fieldIndex; - - ImmutableList.Builder converters = ImmutableList.builder(); - for (int i = 0; i < prestoTypeParameters.size(); i++) { - parquet.schema.Type fieldType = fieldTypes.get(i); - converters.add(createConverter(prestoTypeParameters.get(i), columnName + "." + fieldType.getName(), fieldType, i)); - } - this.converters = converters.build(); - } - - @Override - public Converter getConverter(int fieldIndex) - { - return (Converter) converters.get(fieldIndex); - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = builder; - } - - @Override - public void start() - { - if (builder == null) { - if (nullBuilder == null || (nullBuilder.getPositionCount() >= NULL_BUILDER_POSITIONS_THRESHOLD && nullBuilder.getSizeInBytes() >= NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD)) { - nullBuilder = rowType.createBlockBuilder(null, NULL_BUILDER_POSITIONS_THRESHOLD); - } - currentEntryBuilder = nullBuilder.beginBlockEntry(); - } - else { - while (builder.getPositionCount() < fieldIndex) { - builder.appendNull(); - } - currentEntryBuilder = builder.beginBlockEntry(); - } - for (BlockConverter converter : converters) { - converter.beforeValue(currentEntryBuilder); - } - } - - @Override - public void end() - { - for (BlockConverter converter : converters) { - converter.afterValue(); - } - while (currentEntryBuilder.getPositionCount() < converters.size()) { - currentEntryBuilder.appendNull(); - } - - if (builder == null) { - nullBuilder.closeEntry(); - } - else { - builder.closeEntry(); - } - } - - @Override - public void afterValue() - { - } - - @Override - public Block getBlock() - { - checkState(builder == null && nullBuilder != null); // check that user requested a result block (builder == null), and the program followed the request (nullBuilder != null) - return nullBuilder.getObject(nullBuilder.getPositionCount() - 1, Block.class); - } - } - - private static class ParquetListConverter - extends GroupedConverter - { - private static final int NULL_BUILDER_POSITIONS_THRESHOLD = 100; - private static final int NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD = 32768; - - private final Type arrayType; - private final int fieldIndex; - - private final BlockConverter elementConverter; - private BlockBuilder builder; - private BlockBuilder nullBuilder; // used internally when builder is set to null - private BlockBuilder currentEntryBuilder; - - public ParquetListConverter(Type prestoType, String columnName, GroupType listType, int fieldIndex) - { - checkArgument( - listType.getFieldCount() == 1, - "Expected LIST column '%s' to only have one field, but has %s fields", - columnName, - listType.getFieldCount()); - checkArgument(ARRAY.equals(prestoType.getTypeSignature().getBase())); - - this.arrayType = prestoType; - this.fieldIndex = fieldIndex; - - // The Parquet specification requires that the element value of a - // LIST type be wrapped in an inner repeated group, like so: - // - // optional group listField (LIST) { - // repeated group list { - // optional int element - // } - // } - // - // However, some parquet libraries don't follow this spec. The - // compatibility rules used here are specified in the Parquet - // documentation at http://git.io/vOpNz. - parquet.schema.Type elementType = listType.getType(0); - if (isElementType(elementType, listType.getName())) { - elementConverter = createConverter(prestoType.getTypeParameters().get(0), columnName + ".element", elementType, 0); - } - else { - elementConverter = new ParquetListEntryConverter(prestoType.getTypeParameters().get(0), columnName, elementType.asGroupType()); - } - } - - //copied over from Apache Hive - private boolean isElementType(parquet.schema.Type repeatedType, String parentName) - { - if (repeatedType.isPrimitive() || - (repeatedType.asGroupType().getFieldCount() > 1)) { - return true; - } - - if (repeatedType.getName().equals("array")) { - return true; // existing avro data - } - - if (repeatedType.getName().equals(parentName + "_tuple")) { - return true; // existing thrift data - } - // false for the following cases: - // * name is "list", which matches the spec - // * name is "bag", which indicates existing hive or pig data - // * ambiguous case, which should be assumed is 3-level according to spec - return false; - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = builder; - } - - @Override - public Converter getConverter(int fieldIndex) - { - if (fieldIndex == 0) { - return (Converter) elementConverter; - } - throw new IllegalArgumentException("LIST field must be 0 not " + fieldIndex); - } - - @Override - public void start() - { - if (builder == null) { - if (nullBuilder == null || (nullBuilder.getPositionCount() >= NULL_BUILDER_POSITIONS_THRESHOLD && nullBuilder.getSizeInBytes() >= NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD)) { - nullBuilder = arrayType.createBlockBuilder(null, NULL_BUILDER_POSITIONS_THRESHOLD); - } - currentEntryBuilder = nullBuilder.beginBlockEntry(); - } - else { - while (builder.getPositionCount() < fieldIndex) { - builder.appendNull(); - } - currentEntryBuilder = builder.beginBlockEntry(); - } - elementConverter.beforeValue(currentEntryBuilder); - } - - @Override - public void end() - { - elementConverter.afterValue(); - - if (builder == null) { - nullBuilder.closeEntry(); - } - else { - builder.closeEntry(); - } - } - - @Override - public void afterValue() - { - } - - @Override - public Block getBlock() - { - checkState(builder == null && nullBuilder != null); // check that user requested a result block (builder == null), and the program followed the request (nullBuilder != null) - return nullBuilder.getObject(nullBuilder.getPositionCount() - 1, Block.class); - } - } - - private static class ParquetListEntryConverter - extends GroupConverter - implements BlockConverter - { - private final BlockConverter elementConverter; - - private BlockBuilder builder; - private int startingPosition; - - public ParquetListEntryConverter(Type prestoType, String columnName, GroupType elementType) - { - checkArgument( - elementType.getOriginalType() == null, - "Expected LIST column '%s' field to be type STRUCT, but is %s", - columnName, - elementType); - - checkArgument( - elementType.getFieldCount() == 1, - "Expected LIST column '%s' element to have one field, but has %s fields", - columnName, - elementType.getFieldCount()); - - elementConverter = createConverter(prestoType, columnName + ".element", elementType.getType(0), 0); - } - - @Override - public Converter getConverter(int fieldIndex) - { - if (fieldIndex == 0) { - return (Converter) elementConverter; - } - throw new IllegalArgumentException("LIST entry field must be 0 not " + fieldIndex); - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = builder; - } - - @Override - public void start() - { - elementConverter.beforeValue(builder); - startingPosition = builder.getPositionCount(); - } - - @Override - public void end() - { - elementConverter.afterValue(); - // we have read nothing, this means there is a null element in this list - if (builder.getPositionCount() == startingPosition) { - builder.appendNull(); - } - } - - @Override - public void afterValue() - { - } - } - - private static class ParquetMapConverter - extends GroupedConverter - { - private static final int NULL_BUILDER_POSITIONS_THRESHOLD = 100; - private static final int NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD = 32768; - - private final Type mapType; - private final int fieldIndex; - - private final ParquetMapEntryConverter entryConverter; - private BlockBuilder builder; - private BlockBuilder nullBuilder; // used internally when builder is set to null - private BlockBuilder currentEntryBuilder; - - public ParquetMapConverter(Type type, String columnName, GroupType mapType, int fieldIndex) - { - checkArgument( - mapType.getFieldCount() == 1, - "Expected MAP column '%s' to only have one field, but has %s fields", - mapType.getName(), - mapType.getFieldCount()); - - this.mapType = type; - this.fieldIndex = fieldIndex; - - parquet.schema.Type entryType = mapType.getFields().get(0); - - entryConverter = new ParquetMapEntryConverter(type, columnName + ".entry", entryType.asGroupType()); - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = builder; - } - - @Override - public Converter getConverter(int fieldIndex) - { - if (fieldIndex == 0) { - return entryConverter; - } - throw new IllegalArgumentException("Map field must be 0 not " + fieldIndex); - } - - @Override - public void start() - { - if (builder == null) { - if (nullBuilder == null || (nullBuilder.getPositionCount() >= NULL_BUILDER_POSITIONS_THRESHOLD && nullBuilder.getSizeInBytes() >= NULL_BUILDER_SIZE_IN_BYTES_THRESHOLD)) { - nullBuilder = mapType.createBlockBuilder(null, NULL_BUILDER_POSITIONS_THRESHOLD); - } - currentEntryBuilder = nullBuilder.beginBlockEntry(); - } - else { - while (builder.getPositionCount() < fieldIndex) { - builder.appendNull(); - } - currentEntryBuilder = builder.beginBlockEntry(); - } - entryConverter.beforeValue(currentEntryBuilder); - } - - @Override - public void end() - { - entryConverter.afterValue(); - - if (builder == null) { - nullBuilder.closeEntry(); - } - else { - builder.closeEntry(); - } - } - - @Override - public void afterValue() - { - } - - @Override - public Block getBlock() - { - checkState(builder == null && nullBuilder != null); // check that user requested a result block (builder == null), and the program followed the request (nullBuilder != null) - return nullBuilder.getObject(nullBuilder.getPositionCount() - 1, Block.class); - } - } - - private static class ParquetMapEntryConverter - extends GroupConverter - implements BlockConverter - { - private final BlockConverter keyConverter; - private final BlockConverter valueConverter; - - private BlockBuilder builder; - - public ParquetMapEntryConverter(Type prestoType, String columnName, GroupType entryType) - { - checkArgument(MAP.equals(prestoType.getTypeSignature().getBase())); - // original version of parquet used null for entry due to a bug - if (entryType.getOriginalType() != null) { - checkArgument( - entryType.getOriginalType() == MAP_KEY_VALUE, - "Expected MAP column '%s' field to be type %s, but is %s", - columnName, - MAP_KEY_VALUE, - entryType); - } - - GroupType entryGroupType = entryType.asGroupType(); - checkArgument( - entryGroupType.getFieldCount() == 2, - "Expected MAP column '%s' entry to have two fields, but has %s fields", - columnName, - entryGroupType.getFieldCount()); - checkArgument( - entryGroupType.getFieldName(0).equals("key"), - "Expected MAP column '%s' entry field 0 to be named 'key', but is named %s", - columnName, - entryGroupType.getFieldName(0)); - checkArgument( - entryGroupType.getFieldName(1).equals("value"), - "Expected MAP column '%s' entry field 1 to be named 'value', but is named %s", - columnName, - entryGroupType.getFieldName(1)); - checkArgument( - entryGroupType.getType(0).isPrimitive(), - "Expected MAP column '%s' entry field 0 to be primitive, but is %s", - columnName, - entryGroupType.getType(0)); - - keyConverter = createConverter(prestoType.getTypeParameters().get(0), columnName + ".key", entryGroupType.getFields().get(0), 0); - valueConverter = createConverter(prestoType.getTypeParameters().get(1), columnName + ".value", entryGroupType.getFields().get(1), 1); - } - - @Override - public Converter getConverter(int fieldIndex) - { - if (fieldIndex == 0) { - return (Converter) keyConverter; - } - if (fieldIndex == 1) { - return (Converter) valueConverter; - } - throw new IllegalArgumentException("Map entry field must be 0 or 1 not " + fieldIndex); - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = builder; - } - - @Override - public void start() - { - keyConverter.beforeValue(builder); - valueConverter.beforeValue(builder); - } - - @Override - public void end() - { - keyConverter.afterValue(); - valueConverter.afterValue(); - // handle the case where we have a key, but the value is null - // null keys are not supported anyway, so we can ignore that case here - if (builder.getPositionCount() % 2 != 0) { - builder.appendNull(); - } - } - - @Override - public void afterValue() - { - } - } - - private static class ParquetPrimitiveConverter - extends PrimitiveConverter - implements BlockConverter - { - private final Type type; - private final int fieldIndex; - private BlockBuilder builder; - - public ParquetPrimitiveConverter(Type type, int fieldIndex) - { - this.type = type; - this.fieldIndex = fieldIndex; - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = requireNonNull(builder, "parent builder is null"); - } - - @Override - public void afterValue() - { - } - - private void addMissingValues() - { - while (builder.getPositionCount() < fieldIndex) { - builder.appendNull(); - } - } - - @Override - public boolean isPrimitive() - { - return true; - } - - @Override - public PrimitiveConverter asPrimitiveConverter() - { - return this; - } - - @Override - public boolean hasDictionarySupport() - { - return false; - } - - @Override - public void setDictionary(Dictionary dictionary) - { - } - - @Override - public void addValueFromDictionary(int dictionaryId) - { - } - - @Override - public void addBoolean(boolean value) - { - addMissingValues(); - type.writeBoolean(builder, value); - } - - @Override - public void addDouble(double value) - { - addMissingValues(); - type.writeDouble(builder, value); - } - - @Override - public void addLong(long value) - { - addMissingValues(); - type.writeLong(builder, value); - } - - @Override - public void addBinary(Binary value) - { - addMissingValues(); - if (type == TIMESTAMP) { - type.writeLong(builder, ParquetTimestampUtils.getTimestampMillis(value)); - } - else if (isVarcharType(type)) { - type.writeSlice(builder, truncateToLength(wrappedBuffer(value.getBytes()), type)); - } - else if (isCharType(type)) { - type.writeSlice(builder, truncateToLengthAndTrimSpaces(wrappedBuffer(value.getBytes()), type)); - } - else { - type.writeSlice(builder, wrappedBuffer(value.getBytes())); - } - } - - @Override - public void addFloat(float value) - { - addMissingValues(); - type.writeLong(builder, floatToRawIntBits(value)); - } - - @Override - public void addInt(int value) - { - addMissingValues(); - type.writeLong(builder, value); - } - } - - private static class ParquetDecimalConverter - extends PrimitiveConverter - implements BlockConverter - { - private final DecimalType decimalType; - private BlockBuilder builder; - private boolean wroteValue; - - public ParquetDecimalConverter(DecimalType decimalType) - { - this.decimalType = requireNonNull(decimalType, "decimalType is null"); - } - - @Override - public void beforeValue(BlockBuilder builder) - { - this.builder = requireNonNull(builder, "parent builder is null"); - wroteValue = false; - } - - @Override - public void afterValue() - { - if (wroteValue) { - return; - } - - builder.appendNull(); - } - - @Override - public boolean isPrimitive() - { - return true; - } - - @Override - public PrimitiveConverter asPrimitiveConverter() - { - return this; - } - - @Override - public boolean hasDictionarySupport() - { - return false; - } - - @Override - public void addBinary(Binary value) - { - if (decimalType.isShort()) { - decimalType.writeLong(builder, DecimalUtils.getShortDecimalValue(value.getBytes())); - } - else { - BigInteger unboundedDecimal = new BigInteger(value.getBytes()); - decimalType.writeSlice(builder, Decimals.encodeUnscaledValue(unboundedDecimal)); - } - wroteValue = true; - } - } -} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSource.java b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSource.java index 690b03dc26c2c..e83b5507607a8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSource.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSource.java @@ -14,7 +14,9 @@ package com.facebook.presto.hive.parquet; import com.facebook.presto.hive.HiveColumnHandle; -import com.facebook.presto.hive.parquet.reader.ParquetReader; +import com.facebook.presto.parquet.Field; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.reader.ParquetReader; import com.facebook.presto.spi.ConnectorPageSource; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; @@ -38,9 +40,9 @@ import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA; import static com.facebook.presto.hive.HiveErrorCode.HIVE_CURSOR_ERROR; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getFieldIndex; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getParquetType; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.lookupColumnByName; +import static com.facebook.presto.hive.parquet.ParquetPageSourceFactory.getParquetType; +import static com.facebook.presto.parquet.ParquetTypeUtils.getFieldIndex; +import static com.facebook.presto.parquet.ParquetTypeUtils.lookupColumnByName; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static parquet.io.ColumnIOConverter.constructField; @@ -62,7 +64,6 @@ public class ParquetPageSource private int batchId; private boolean closed; - private long readTimeNanos; private final boolean useParquetColumnNames; public ParquetPageSource( @@ -123,7 +124,7 @@ public long getCompletedBytes() @Override public long getReadTimeNanos() { - return readTimeNanos; + return parquetReader.getDataSource().getReadTimeNanos(); } @Override @@ -143,12 +144,8 @@ public Page getNextPage() { try { batchId++; - long start = System.nanoTime(); - int batchSize = parquetReader.nextBatch(); - readTimeNanos += System.nanoTime() - start; - if (closed || batchSize <= 0) { close(); return null; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSourceFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSourceFactory.java index 5dd5a33f081be..d84117f27c4c9 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSourceFactory.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPageSourceFactory.java @@ -17,15 +17,21 @@ import com.facebook.presto.hive.HdfsEnvironment; import com.facebook.presto.hive.HiveColumnHandle; import com.facebook.presto.hive.HivePageSourceFactory; -import com.facebook.presto.hive.parquet.predicate.ParquetPredicate; -import com.facebook.presto.hive.parquet.reader.ParquetMetadataReader; -import com.facebook.presto.hive.parquet.reader.ParquetReader; import com.facebook.presto.memory.context.AggregatedMemoryContext; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.ParquetDataSource; +import com.facebook.presto.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.predicate.Predicate; +import com.facebook.presto.parquet.reader.MetadataReader; +import com.facebook.presto.parquet.reader.ParquetReader; import com.facebook.presto.spi.ConnectorPageSource; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.TypeManager; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; @@ -43,33 +49,33 @@ import java.io.FileNotFoundException; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.Set; import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA; import static com.facebook.presto.hive.HiveErrorCode.HIVE_CANNOT_OPEN_SPLIT; import static com.facebook.presto.hive.HiveErrorCode.HIVE_MISSING_DATA; -import static com.facebook.presto.hive.HiveSessionProperties.isParquetOptimizedReaderEnabled; -import static com.facebook.presto.hive.HiveSessionProperties.isParquetPredicatePushdownEnabled; +import static com.facebook.presto.hive.HiveSessionProperties.isFailOnCorruptedParquetStatistics; import static com.facebook.presto.hive.HiveSessionProperties.isUseParquetColumnNames; import static com.facebook.presto.hive.HiveUtil.getDeserializerClassName; import static com.facebook.presto.hive.parquet.HdfsParquetDataSource.buildHdfsParquetDataSource; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getColumnIO; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getDescriptors; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getParquetType; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.buildParquetPredicate; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.getParquetTupleDomain; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.predicateMatches; import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.parquet.ParquetTypeUtils.getColumnIO; +import static com.facebook.presto.parquet.ParquetTypeUtils.getDescriptors; +import static com.facebook.presto.parquet.ParquetTypeUtils.getParquetTypeByName; +import static com.facebook.presto.parquet.predicate.PredicateUtils.buildPredicate; +import static com.facebook.presto.parquet.predicate.PredicateUtils.predicateMatches; import static com.google.common.base.Strings.nullToEmpty; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; +import static org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category.PRIMITIVE; public class ParquetPageSourceFactory implements HivePageSourceFactory @@ -104,10 +110,6 @@ public Optional createPageSource( TupleDomain effectivePredicate, DateTimeZone hiveStorageTimeZone) { - if (!isParquetOptimizedReaderEnabled(session)) { - return Optional.empty(); - } - if (!PARQUET_SERDE_CLASS_NAMES.contains(getDeserializerClassName(schema))) { return Optional.empty(); } @@ -123,8 +125,8 @@ public Optional createPageSource( schema, columns, isUseParquetColumnNames(session), + isFailOnCorruptedParquetStatistics(session), typeManager, - isParquetPredicatePushdownEnabled(session), effectivePredicate, stats)); } @@ -140,8 +142,8 @@ public static ParquetPageSource createParquetPageSource( Properties schema, List columns, boolean useParquetColumnNames, + boolean failOnCorruptedParquetStatistics, TypeManager typeManager, - boolean predicatePushdownEnabled, TupleDomain effectivePredicate, FileFormatDataSourceStats stats) { @@ -151,7 +153,7 @@ public static ParquetPageSource createParquetPageSource( try { FileSystem fileSystem = hdfsEnvironment.getFileSystem(user, path, configuration); FSDataInputStream inputStream = fileSystem.open(path); - ParquetMetadata parquetMetadata = ParquetMetadataReader.readFooter(inputStream, path, fileSize); + ParquetMetadata parquetMetadata = MetadataReader.readFooter(inputStream, path, fileSize); FileMetaData fileMetaData = parquetMetadata.getFileMetaData(); MessageType fileSchema = fileMetaData.getSchema(); dataSource = buildHdfsParquetDataSource(inputStream, path, fileSize, stats); @@ -164,27 +166,28 @@ public static ParquetPageSource createParquetPageSource( MessageType requestedSchema = new MessageType(fileSchema.getName(), fields); - List blocks = new ArrayList<>(); + ImmutableList.Builder footerBlocks = ImmutableList.builder(); for (BlockMetaData block : parquetMetadata.getBlocks()) { long firstDataPage = block.getColumns().get(0).getFirstDataPageOffset(); if (firstDataPage >= start && firstDataPage < start + length) { - blocks.add(block); + footerBlocks.add(block); } } - if (predicatePushdownEnabled) { - Map, RichColumnDescriptor> descriptorsByPath = getDescriptors(fileSchema, requestedSchema); - TupleDomain parquetTupleDomain = getParquetTupleDomain(descriptorsByPath, effectivePredicate); - ParquetPredicate parquetPredicate = buildParquetPredicate(requestedSchema, parquetTupleDomain, descriptorsByPath); - final ParquetDataSource finalDataSource = dataSource; - blocks = blocks.stream() - .filter(block -> predicateMatches(parquetPredicate, block, finalDataSource, descriptorsByPath, parquetTupleDomain)) - .collect(toList()); + Map, RichColumnDescriptor> descriptorsByPath = getDescriptors(fileSchema, requestedSchema); + TupleDomain parquetTupleDomain = getParquetTupleDomain(descriptorsByPath, effectivePredicate); + Predicate parquetPredicate = buildPredicate(requestedSchema, parquetTupleDomain, descriptorsByPath); + final ParquetDataSource finalDataSource = dataSource; + ImmutableList.Builder blocks = ImmutableList.builder(); + for (BlockMetaData block : footerBlocks.build()) { + if (predicateMatches(parquetPredicate, block, finalDataSource, descriptorsByPath, parquetTupleDomain, failOnCorruptedParquetStatistics)) { + blocks.add(block); + } } MessageColumnIO messageColumnIO = getColumnIO(fileSchema, requestedSchema); ParquetReader parquetReader = new ParquetReader( messageColumnIO, - blocks, + blocks.build(), dataSource, systemMemoryContext); @@ -209,6 +212,9 @@ public static ParquetPageSource createParquetPageSource( if (e instanceof PrestoException) { throw (PrestoException) e; } + if (e instanceof ParquetCorruptionException) { + throw new PrestoException(HIVE_BAD_DATA, e); + } if (nullToEmpty(e.getMessage()).trim().equals("Filesystem closed") || e instanceof FileNotFoundException) { throw new PrestoException(HIVE_CANNOT_OPEN_SPLIT, e); @@ -220,4 +226,38 @@ public static ParquetPageSource createParquetPageSource( throw new PrestoException(HIVE_CANNOT_OPEN_SPLIT, message, e); } } + + public static TupleDomain getParquetTupleDomain(Map, RichColumnDescriptor> descriptorsByPath, TupleDomain effectivePredicate) + { + if (effectivePredicate.isNone()) { + return TupleDomain.none(); + } + + ImmutableMap.Builder predicate = ImmutableMap.builder(); + for (Entry entry : effectivePredicate.getDomains().get().entrySet()) { + HiveColumnHandle columnHandle = entry.getKey(); + // skip looking up predicates for complex types as Parquet only stores stats for primitives + if (!columnHandle.getHiveType().getCategory().equals(PRIMITIVE)) { + continue; + } + + RichColumnDescriptor descriptor = descriptorsByPath.get(ImmutableList.of(columnHandle.getName())); + if (descriptor != null) { + predicate.put(descriptor, entry.getValue()); + } + } + return TupleDomain.withColumnDomains(predicate.build()); + } + + public static parquet.schema.Type getParquetType(HiveColumnHandle column, MessageType messageType, boolean useParquetColumnNames) + { + if (useParquetColumnNames) { + return getParquetTypeByName(column.getName(), messageType); + } + + if (column.getHiveColumnIndex() < messageType.getFieldCount()) { + return messageType.getType(column.getHiveColumnIndex()); + } + return null; + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetRecordCursorProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetRecordCursorProvider.java deleted file mode 100644 index d39f7f16779e0..0000000000000 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetRecordCursorProvider.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.hive.parquet; - -import com.facebook.presto.hive.FileFormatDataSourceStats; -import com.facebook.presto.hive.HdfsEnvironment; -import com.facebook.presto.hive.HiveColumnHandle; -import com.facebook.presto.hive.HiveRecordCursorProvider; -import com.facebook.presto.spi.ConnectorSession; -import com.facebook.presto.spi.RecordCursor; -import com.facebook.presto.spi.predicate.TupleDomain; -import com.facebook.presto.spi.type.TypeManager; -import com.google.common.collect.ImmutableSet; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.Path; -import org.joda.time.DateTimeZone; - -import javax.inject.Inject; - -import java.util.List; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; - -import static com.facebook.presto.hive.HiveSessionProperties.isParquetPredicatePushdownEnabled; -import static com.facebook.presto.hive.HiveSessionProperties.isUseParquetColumnNames; -import static com.facebook.presto.hive.HiveUtil.getDeserializerClassName; -import static java.util.Objects.requireNonNull; - -public class ParquetRecordCursorProvider - implements HiveRecordCursorProvider -{ - private static final Set PARQUET_SERDE_CLASS_NAMES = ImmutableSet.builder() - .add("org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe") - .add("parquet.hive.serde.ParquetHiveSerDe") - .build(); - - private final HdfsEnvironment hdfsEnvironment; - private final FileFormatDataSourceStats stats; - - @Inject - public ParquetRecordCursorProvider(HdfsEnvironment hdfsEnvironment, FileFormatDataSourceStats stats) - { - this.hdfsEnvironment = requireNonNull(hdfsEnvironment, "hdfsEnvironment is null"); - this.stats = requireNonNull(stats, "stats is null"); - } - - @Override - public Optional createRecordCursor( - Configuration configuration, - ConnectorSession session, - Path path, - long start, - long length, - long fileSize, - Properties schema, - List columns, - TupleDomain effectivePredicate, - DateTimeZone hiveStorageTimeZone, - TypeManager typeManager) - { - if (!PARQUET_SERDE_CLASS_NAMES.contains(getDeserializerClassName(schema))) { - return Optional.empty(); - } - - return Optional.of(new ParquetHiveRecordCursor( - hdfsEnvironment, - session.getUser(), - configuration, - path, - start, - length, - fileSize, - schema, - columns, - isUseParquetColumnNames(session), - typeManager, - isParquetPredicatePushdownEnabled(session), - effectivePredicate, - stats)); - } -} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/HiveS3Config.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/HiveS3Config.java index 256845a66e783..a6bc08d727d35 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/s3/HiveS3Config.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/HiveS3Config.java @@ -56,6 +56,7 @@ public class HiveS3Config private DataSize s3MultipartMinPartSize = new DataSize(5, MEGABYTE); private boolean pinS3ClientToCurrentRegion; private String s3UserAgentPrefix = ""; + private PrestoS3AclType s3AclType = PrestoS3AclType.PRIVATE; public String getS3AwsAccessKey() { @@ -374,4 +375,18 @@ public HiveS3Config setS3UserAgentPrefix(String s3UserAgentPrefix) this.s3UserAgentPrefix = s3UserAgentPrefix; return this; } + + @NotNull + public PrestoS3AclType getS3AclType() + { + return s3AclType; + } + + @Config("hive.s3.upload-acl-type") + @ConfigDescription("Canned ACL type for S3 uploads") + public HiveS3Config setS3AclType(PrestoS3AclType s3AclType) + { + this.s3AclType = s3AclType; + return this; + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3AclType.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3AclType.java new file mode 100644 index 0000000000000..05890ca200314 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3AclType.java @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.s3; + +import com.amazonaws.services.s3.model.CannedAccessControlList; + +import static java.util.Objects.requireNonNull; + +public enum PrestoS3AclType +{ + AUTHENTICATED_READ(CannedAccessControlList.AuthenticatedRead), + AWS_EXEC_READ(CannedAccessControlList.AwsExecRead), + BUCKET_OWNER_FULL_CONTROL(CannedAccessControlList.BucketOwnerFullControl), + BUCKET_OWNER_READ(CannedAccessControlList.BucketOwnerRead), + LOG_DELIVERY_WRITE(CannedAccessControlList.LogDeliveryWrite), + PRIVATE(CannedAccessControlList.Private), + PUBLIC_READ(CannedAccessControlList.PublicRead), + PUBLIC_READ_WRITE(CannedAccessControlList.PublicReadWrite); + + private final CannedAccessControlList cannedACL; + + PrestoS3AclType(CannedAccessControlList cannedACL) + { + this.cannedACL = requireNonNull(cannedACL, "cannedACL is null"); + } + + CannedAccessControlList getCannedACL() + { + return cannedACL; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ClientFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ClientFactory.java new file mode 100644 index 0000000000000..e493ac666c702 --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ClientFactory.java @@ -0,0 +1,180 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.s3; + +import com.amazonaws.ClientConfiguration; +import com.amazonaws.Protocol; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProvider; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.auth.InstanceProfileCredentialsProvider; +import com.amazonaws.metrics.RequestMetricCollector; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Builder; +import com.amazonaws.services.s3.AmazonS3Client; +import com.facebook.presto.hive.HiveClientConfig; +import io.airlift.units.Duration; +import org.apache.hadoop.conf.Configuration; + +import javax.annotation.concurrent.GuardedBy; + +import java.net.URI; +import java.util.Optional; + +import static com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; +import static com.amazonaws.regions.Regions.US_EAST_1; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ENDPOINT; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_PIN_CLIENT_TO_CURRENT_REGION; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.base.Verify.verify; +import static java.lang.Math.toIntExact; +import static java.lang.String.format; + +/** + * This factory provides AmazonS3 client required for executing S3SelectPushdown requests. + * Normal S3 GET requests use AmazonS3 clients initialized in PrestoS3FileSystem or EMRFS. + * The ideal state will be to merge this logic with the two file systems and get rid of this + * factory class. + * Please do not use the client provided by this factory for any other use cases. + */ +public class PrestoS3ClientFactory +{ + private static final String S3_ACCESS_KEY = "presto.s3.access-key"; + private static final String S3_SECRET_KEY = "presto.s3.secret-key"; + private static final String S3_CREDENTIALS_PROVIDER = "presto.s3.credentials-provider"; + private static final String S3_USE_INSTANCE_CREDENTIALS = "presto.s3.use-instance-credentials"; + private static final String S3_CONNECT_TIMEOUT = "presto.s3.connect-timeout"; + private static final String S3_SOCKET_TIMEOUT = "presto.s3.socket-timeout"; + private static final String S3_SSL_ENABLED = "presto.s3.ssl.enabled"; + private static final String S3_MAX_ERROR_RETRIES = "presto.s3.max-error-retries"; + private static final String S3_USER_AGENT_PREFIX = "presto.s3.user-agent-prefix"; + private static final String S3_SELECT_PUSHDOWN_MAX_CONNECTIONS = "hive.s3select-pushdown.max-connections"; + private static String s3UserAgentSuffix = "presto"; + + @GuardedBy("this") + private AmazonS3 s3Client; + + synchronized AmazonS3 getS3Client(Configuration config, HiveClientConfig clientConfig) + { + if (s3Client != null) { + return s3Client; + } + + HiveS3Config defaults = new HiveS3Config(); + String userAgentPrefix = config.get(S3_USER_AGENT_PREFIX, defaults.getS3UserAgentPrefix()); + int maxErrorRetries = config.getInt(S3_MAX_ERROR_RETRIES, defaults.getS3MaxErrorRetries()); + boolean sslEnabled = config.getBoolean(S3_SSL_ENABLED, defaults.isS3SslEnabled()); + Duration connectTimeout = Duration.valueOf(config.get(S3_CONNECT_TIMEOUT, defaults.getS3ConnectTimeout().toString())); + Duration socketTimeout = Duration.valueOf(config.get(S3_SOCKET_TIMEOUT, defaults.getS3SocketTimeout().toString())); + int maxConnections = config.getInt(S3_SELECT_PUSHDOWN_MAX_CONNECTIONS, clientConfig.getS3SelectPushdownMaxConnections()); + + if (clientConfig.isS3SelectPushdownEnabled()) { + s3UserAgentSuffix = "presto-select"; + } + + ClientConfiguration clientConfiguration = new ClientConfiguration() + .withMaxErrorRetry(maxErrorRetries) + .withProtocol(sslEnabled ? Protocol.HTTPS : Protocol.HTTP) + .withConnectionTimeout(toIntExact(connectTimeout.toMillis())) + .withSocketTimeout(toIntExact(socketTimeout.toMillis())) + .withMaxConnections(maxConnections) + .withUserAgentPrefix(userAgentPrefix) + .withUserAgentSuffix(s3UserAgentSuffix); + + PrestoS3FileSystemStats stats = new PrestoS3FileSystemStats(); + RequestMetricCollector metricCollector = new PrestoS3FileSystemMetricCollector(stats); + AWSCredentialsProvider awsCredentialsProvider = getAwsCredentialsProvider(config, defaults); + AmazonS3Builder clientBuilder = AmazonS3Client.builder() + .withCredentials(awsCredentialsProvider) + .withClientConfiguration(clientConfiguration) + .withMetricsCollector(metricCollector) + .enablePathStyleAccess(); + + boolean regionOrEndpointSet = false; + + String endpoint = config.get(S3_ENDPOINT); + boolean pinS3ClientToCurrentRegion = config.getBoolean(S3_PIN_CLIENT_TO_CURRENT_REGION, defaults.isPinS3ClientToCurrentRegion()); + verify(!pinS3ClientToCurrentRegion || endpoint == null, + "Invalid configuration: either endpoint can be set or S3 client can be pinned to the current region"); + + // use local region when running inside of EC2 + if (pinS3ClientToCurrentRegion) { + Region region = Regions.getCurrentRegion(); + if (region != null) { + clientBuilder.withRegion(region.getName()); + regionOrEndpointSet = true; + } + } + + if (!isNullOrEmpty(endpoint)) { + clientBuilder.withEndpointConfiguration(new EndpointConfiguration(endpoint, null)); + regionOrEndpointSet = true; + } + + if (!regionOrEndpointSet) { + clientBuilder.withRegion(US_EAST_1); + clientBuilder.setForceGlobalBucketAccessEnabled(true); + } + + s3Client = clientBuilder.build(); + return s3Client; + } + + private AWSCredentialsProvider getAwsCredentialsProvider(Configuration conf, HiveS3Config defaults) + { + Optional credentials = getAwsCredentials(conf); + if (credentials.isPresent()) { + return new AWSStaticCredentialsProvider(credentials.get()); + } + + boolean useInstanceCredentials = conf.getBoolean(S3_USE_INSTANCE_CREDENTIALS, defaults.isS3UseInstanceCredentials()); + if (useInstanceCredentials) { + return InstanceProfileCredentialsProvider.getInstance(); + } + + String providerClass = conf.get(S3_CREDENTIALS_PROVIDER); + if (!isNullOrEmpty(providerClass)) { + return getCustomAWSCredentialsProvider(conf, providerClass); + } + + throw new RuntimeException("S3 credentials not configured"); + } + + private static AWSCredentialsProvider getCustomAWSCredentialsProvider(Configuration conf, String providerClass) + { + try { + return conf.getClassByName(providerClass) + .asSubclass(AWSCredentialsProvider.class) + .getConstructor(URI.class, Configuration.class) + .newInstance(null, conf); + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(format("Error creating an instance of %s", providerClass), e); + } + } + + private static Optional getAwsCredentials(Configuration conf) + { + String accessKey = conf.get(S3_ACCESS_KEY); + String secretKey = conf.get(S3_SECRET_KEY); + + if (isNullOrEmpty(accessKey) || isNullOrEmpty(secretKey)) { + return Optional.empty(); + } + return Optional.of(new BasicAWSCredentials(accessKey, secretKey)); + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ConfigurationUpdater.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ConfigurationUpdater.java index eae5de99304a0..64c8747364d33 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ConfigurationUpdater.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3ConfigurationUpdater.java @@ -48,6 +48,7 @@ public class PrestoS3ConfigurationUpdater private final File stagingDirectory; private final boolean pinClientToCurrentRegion; private final String userAgentPrefix; + private final PrestoS3AclType aclType; @Inject public PrestoS3ConfigurationUpdater(HiveS3Config config) @@ -76,6 +77,7 @@ public PrestoS3ConfigurationUpdater(HiveS3Config config) this.stagingDirectory = config.getS3StagingDirectory(); this.pinClientToCurrentRegion = config.isPinS3ClientToCurrentRegion(); this.userAgentPrefix = config.getS3UserAgentPrefix(); + this.aclType = config.getS3AclType(); } @Override @@ -124,5 +126,6 @@ public void updateConfiguration(Configuration config) config.setLong(S3_MULTIPART_MIN_PART_SIZE, multipartMinPartSize.toBytes()); config.setBoolean(S3_PIN_CLIENT_TO_CURRENT_REGION, pinClientToCurrentRegion); config.set(S3_USER_AGENT_PREFIX, userAgentPrefix); + config.set(S3_ACL_TYPE, aclType.name()); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java index 5b43e6514b8f9..e8cbe6911e55e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3FileSystem.java @@ -33,6 +33,7 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.AmazonS3EncryptionClient; import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.KMSEncryptionMaterialsProvider; @@ -93,6 +94,7 @@ import static com.amazonaws.services.s3.Headers.UNENCRYPTED_CONTENT_LENGTH; import static com.facebook.presto.hive.RetryDriver.retry; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ACCESS_KEY; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ACL_TYPE; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_CONNECT_TIMEOUT; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_CREDENTIALS_PROVIDER; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ENCRYPTION_MATERIALS_PROVIDER; @@ -167,6 +169,7 @@ public class PrestoS3FileSystem private boolean isPathStyleAccess; private long multiPartUploadMinFileSize; private long multiPartUploadMinPartSize; + private PrestoS3AclType s3AclType; @Override public void initialize(URI uri, Configuration conf) @@ -200,6 +203,7 @@ public void initialize(URI uri, Configuration conf) this.sseEnabled = conf.getBoolean(S3_SSE_ENABLED, defaults.isS3SseEnabled()); this.sseType = PrestoS3SseType.valueOf(conf.get(S3_SSE_TYPE, defaults.getS3SseType().name())); this.sseKmsKeyId = conf.get(S3_SSE_KMS_KEY_ID, defaults.getS3SseKmsKeyId()); + this.s3AclType = PrestoS3AclType.valueOf(conf.get(S3_ACL_TYPE, defaults.getS3AclType().name())); String userAgentPrefix = conf.get(S3_USER_AGENT_PREFIX, defaults.getS3UserAgentPrefix()); ClientConfiguration configuration = new ClientConfiguration() @@ -363,7 +367,7 @@ public FSDataOutputStream create(Path path, FsPermission permission, boolean ove String key = keyFromPath(qualifiedPath(path)); return new FSDataOutputStream( - new PrestoS3OutputStream(s3, getBucketName(uri), key, tempFile, sseEnabled, sseType, sseKmsKeyId, multiPartUploadMinFileSize, multiPartUploadMinPartSize), + new PrestoS3OutputStream(s3, getBucketName(uri), key, tempFile, sseEnabled, sseType, sseKmsKeyId, multiPartUploadMinFileSize, multiPartUploadMinPartSize, s3AclType), statistics); } @@ -611,7 +615,7 @@ private static boolean keysEqual(Path p1, Path p2) return keyFromPath(p1).equals(keyFromPath(p2)); } - private static String keyFromPath(Path path) + public static String keyFromPath(Path path) { checkArgument(path.isAbsolute(), "Path is not absolute: %s", path); String key = nullToEmpty(path.toUri().getPath()); @@ -976,6 +980,7 @@ private static class PrestoS3OutputStream private final boolean sseEnabled; private final PrestoS3SseType sseType; private final String sseKmsKeyId; + private final CannedAccessControlList aclType; private boolean closed; @@ -988,7 +993,8 @@ public PrestoS3OutputStream( PrestoS3SseType sseType, String sseKmsKeyId, long multiPartUploadMinFileSize, - long multiPartUploadMinPartSize) + long multiPartUploadMinPartSize, + PrestoS3AclType aclType) throws IOException { super(new BufferedOutputStream(new FileOutputStream(requireNonNull(tempFile, "tempFile is null")))); @@ -998,6 +1004,8 @@ public PrestoS3OutputStream( .withMinimumUploadPartSize(multiPartUploadMinPartSize) .withMultipartUploadThreshold(multiPartUploadMinFileSize).build(); + requireNonNull(aclType, "aclType is null"); + this.aclType = aclType.getCannedACL(); this.host = requireNonNull(host, "host is null"); this.key = requireNonNull(key, "key is null"); this.tempFile = tempFile; @@ -1056,6 +1064,8 @@ private void uploadObject() } } + request.withCannedAcl(aclType); + Upload upload = transferManager.upload(request); if (log.isDebugEnabled()) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3SelectClient.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3SelectClient.java new file mode 100644 index 0000000000000..e77e03331defd --- /dev/null +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/PrestoS3SelectClient.java @@ -0,0 +1,87 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.s3; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.SelectObjectContentEventVisitor; +import com.amazonaws.services.s3.model.SelectObjectContentRequest; +import com.amazonaws.services.s3.model.SelectObjectContentResult; +import com.facebook.presto.hive.HiveClientConfig; +import org.apache.hadoop.conf.Configuration; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +import static com.amazonaws.services.s3.model.SelectObjectContentEvent.EndEvent; +import static java.util.Objects.requireNonNull; + +public class PrestoS3SelectClient + implements Closeable +{ + private final AmazonS3 s3Client; + private boolean requestComplete; + private SelectObjectContentRequest selectObjectRequest; + private SelectObjectContentResult selectObjectContentResult; + + public PrestoS3SelectClient(Configuration config, HiveClientConfig clientConfig, PrestoS3ClientFactory s3ClientFactory) + { + requireNonNull(config, "config is null"); + requireNonNull(clientConfig, "clientConfig is null"); + requireNonNull(s3ClientFactory, "s3ClientFactory is null"); + this.s3Client = s3ClientFactory.getS3Client(config, clientConfig); + } + + public InputStream getRecordsContent(SelectObjectContentRequest selectObjectRequest) + { + this.selectObjectRequest = requireNonNull(selectObjectRequest, "selectObjectRequest is null"); + this.selectObjectContentResult = s3Client.selectObjectContent(selectObjectRequest); + return selectObjectContentResult.getPayload() + .getRecordsInputStream( + new SelectObjectContentEventVisitor() + { + @Override + public void visit(EndEvent endEvent) + { + requestComplete = true; + } + }); + } + + @Override + public void close() + throws IOException + { + selectObjectContentResult.close(); + } + + public String getKeyName() + { + return selectObjectRequest.getKey(); + } + + public String getBucketName() + { + return selectObjectRequest.getBucketName(); + } + + /** + * The End Event indicates all matching records have been transmitted. + * If the End Event is not received, the results may be incomplete. + */ + public boolean isRequestComplete() + { + return requestComplete; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/s3/S3ConfigurationUpdater.java b/presto-hive/src/main/java/com/facebook/presto/hive/s3/S3ConfigurationUpdater.java index 0cd54675322e4..a5d03975aee50 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/s3/S3ConfigurationUpdater.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/s3/S3ConfigurationUpdater.java @@ -43,6 +43,7 @@ public interface S3ConfigurationUpdater String S3_ENDPOINT = "presto.s3.endpoint"; String S3_SECRET_KEY = "presto.s3.secret-key"; String S3_ACCESS_KEY = "presto.s3.access-key"; + String S3_ACL_TYPE = "presto.s3.upload-acl-type"; void updateConfiguration(Configuration config); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/security/LegacyAccessControl.java b/presto-hive/src/main/java/com/facebook/presto/hive/security/LegacyAccessControl.java index 865c1f984f6d8..1fd187f238130 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/security/LegacyAccessControl.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/security/LegacyAccessControl.java @@ -183,7 +183,7 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/security/PartitionsAwareAccessControl.java b/presto-hive/src/main/java/com/facebook/presto/hive/security/PartitionsAwareAccessControl.java index a91a6fb370fbe..06546302f33dc 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/security/PartitionsAwareAccessControl.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/security/PartitionsAwareAccessControl.java @@ -14,12 +14,12 @@ package com.facebook.presto.hive.security; +import com.facebook.presto.plugin.base.security.ForwardingConnectorAccessControl; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.connector.ConnectorAccessControl; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.security.AccessDeniedException; import com.facebook.presto.spi.security.Identity; -import com.facebook.presto.spi.security.Privilege; import java.util.Set; @@ -29,7 +29,7 @@ import static java.util.Objects.requireNonNull; public class PartitionsAwareAccessControl - implements ConnectorAccessControl + extends ForwardingConnectorAccessControl { private final ConnectorAccessControl delegate; @@ -39,81 +39,9 @@ public PartitionsAwareAccessControl(ConnectorAccessControl delegate) } @Override - public void checkCanCreateSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + protected ConnectorAccessControl delegate() { - delegate.checkCanCreateSchema(transactionHandle, identity, schemaName); - } - - @Override - public void checkCanDropSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) - { - delegate.checkCanDropSchema(transactionHandle, identity, schemaName); - } - - @Override - public void checkCanRenameSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName, String newSchemaName) - { - delegate.checkCanRenameSchema(transactionHandle, identity, schemaName, newSchemaName); - } - - @Override - public void checkCanShowSchemas(ConnectorTransactionHandle transactionHandle, Identity identity) - { - delegate.checkCanShowSchemas(transactionHandle, identity); - } - - @Override - public Set filterSchemas(ConnectorTransactionHandle transactionHandle, Identity identity, Set schemaNames) - { - return delegate.filterSchemas(transactionHandle, identity, schemaNames); - } - - @Override - public void checkCanCreateTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanCreateTable(transactionHandle, identity, tableName); - } - - @Override - public void checkCanDropTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanDropTable(transactionHandle, identity, tableName); - } - - @Override - public void checkCanRenameTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName, SchemaTableName newTableName) - { - delegate.checkCanRenameTable(transactionHandle, identity, tableName, newTableName); - } - - @Override - public void checkCanShowTablesMetadata(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) - { - delegate.checkCanShowTablesMetadata(transactionHandle, identity, schemaName); - } - - @Override - public Set filterTables(ConnectorTransactionHandle transactionHandle, Identity identity, Set tableNames) - { - return delegate.filterTables(transactionHandle, identity, tableNames); - } - - @Override - public void checkCanAddColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanAddColumn(transactionHandle, identity, tableName); - } - - @Override - public void checkCanDropColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanDropColumn(transactionHandle, identity, tableName); - } - - @Override - public void checkCanRenameColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanRenameColumn(transactionHandle, identity, tableName); + return delegate; } @Override @@ -131,52 +59,4 @@ public void checkCanSelectFromColumns(ConnectorTransactionHandle transactionHand delegate.checkCanSelectFromColumns(transactionHandle, identity, tableName, columnNames); } - - @Override - public void checkCanInsertIntoTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanInsertIntoTable(transactionHandle, identity, tableName); - } - - @Override - public void checkCanDeleteFromTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) - { - delegate.checkCanDeleteFromTable(transactionHandle, identity, tableName); - } - - @Override - public void checkCanCreateView(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName viewName) - { - delegate.checkCanCreateView(transactionHandle, identity, viewName); - } - - @Override - public void checkCanDropView(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName viewName) - { - delegate.checkCanDropView(transactionHandle, identity, viewName); - } - - @Override - public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName, Set columnNames) - { - delegate.checkCanCreateViewWithSelectFromColumns(transactionHandle, identity, tableName, columnNames); - } - - @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) - { - delegate.checkCanSetCatalogSessionProperty(identity, propertyName); - } - - @Override - public void checkCanGrantTablePrivilege(ConnectorTransactionHandle transactionHandle, Identity identity, Privilege privilege, SchemaTableName tableName, String grantee, boolean withGrantOption) - { - delegate.checkCanGrantTablePrivilege(transactionHandle, identity, privilege, tableName, grantee, withGrantOption); - } - - @Override - public void checkCanRevokeTablePrivilege(ConnectorTransactionHandle transactionHandle, Identity identity, Privilege privilege, SchemaTableName tableName, String revokee, boolean grantOptionFor) - { - delegate.checkCanRevokeTablePrivilege(transactionHandle, identity, privilege, tableName, revokee, grantOptionFor); - } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/security/SqlStandardAccessControl.java b/presto-hive/src/main/java/com/facebook/presto/hive/security/SqlStandardAccessControl.java index d063a93276b67..0209ff0206e6c 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/security/SqlStandardAccessControl.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/security/SqlStandardAccessControl.java @@ -15,7 +15,6 @@ import com.facebook.presto.hive.HiveConnectorId; import com.facebook.presto.hive.HiveTransactionHandle; -import com.facebook.presto.hive.metastore.ExtendedHiveMetastore; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; import com.facebook.presto.hive.metastore.SemiTransactionalHiveMetastore; import com.facebook.presto.spi.SchemaTableName; @@ -64,21 +63,15 @@ public class SqlStandardAccessControl private static final String INFORMATION_SCHEMA_NAME = "information_schema"; private final String connectorId; - // Two metastores (one transaction-aware, the other not) are available in this class - // so that an appropriate one can be chosen based on whether transaction handle is available. - // Transaction handle is not available for checkCanSetCatalogSessionProperty. private final Function metastoreProvider; - private final ExtendedHiveMetastore metastore; @Inject public SqlStandardAccessControl( HiveConnectorId connectorId, - Function metastoreProvider, - ExtendedHiveMetastore metastore) + Function metastoreProvider) { this.connectorId = requireNonNull(connectorId, "connectorId is null").toString(); this.metastoreProvider = requireNonNull(metastoreProvider, "metastoreProvider is null"); - this.metastore = requireNonNull(metastore, "metastore is null"); } @Override @@ -229,10 +222,9 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transaction, Identity identity, String propertyName) { - // TODO: when this is updated to have a transaction, use isAdmin() - if (!metastore.getRoles(identity.getUser()).contains(ADMIN_ROLE_NAME)) { + if (!isAdmin(transaction, identity)) { denySetCatalogSessionProperty(connectorId, propertyName); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/statistics/HiveStatisticsProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/statistics/HiveStatisticsProvider.java index 7511d6a6e8041..539250431e021 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/statistics/HiveStatisticsProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/statistics/HiveStatisticsProvider.java @@ -17,20 +17,22 @@ import com.facebook.presto.hive.HivePartition; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorSession; -import com.facebook.presto.spi.ConnectorTableHandle; +import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.statistics.TableStatistics; +import com.facebook.presto.spi.type.Type; import java.util.List; import java.util.Map; -/** - * @param tableColumns must be Hive columns, not hidden (Presto-internal) columns - */ public interface HiveStatisticsProvider { + /** + * @param columns must be Hive columns, not hidden (Presto-internal) columns + */ TableStatistics getTableStatistics( ConnectorSession session, - ConnectorTableHandle tableHandle, - List hivePartitions, - Map tableColumns); + SchemaTableName table, + Map columns, + Map columnTypes, + List partitions); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/statistics/MetastoreHiveStatisticsProvider.java b/presto-hive/src/main/java/com/facebook/presto/hive/statistics/MetastoreHiveStatisticsProvider.java index fe7565634a9c5..6923fb10c1f99 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/statistics/MetastoreHiveStatisticsProvider.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/statistics/MetastoreHiveStatisticsProvider.java @@ -17,63 +17,75 @@ import com.facebook.presto.hive.HiveBasicStatistics; import com.facebook.presto.hive.HiveColumnHandle; import com.facebook.presto.hive.HivePartition; -import com.facebook.presto.hive.HiveTableHandle; import com.facebook.presto.hive.PartitionStatistics; +import com.facebook.presto.hive.metastore.DateStatistics; +import com.facebook.presto.hive.metastore.DecimalStatistics; +import com.facebook.presto.hive.metastore.DoubleStatistics; import com.facebook.presto.hive.metastore.HiveColumnStatistics; +import com.facebook.presto.hive.metastore.IntegerStatistics; import com.facebook.presto.hive.metastore.SemiTransactionalHiveMetastore; -import com.facebook.presto.hive.util.Statistics.Range; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorSession; -import com.facebook.presto.spi.ConnectorTableHandle; -import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.predicate.NullableValue; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; -import com.facebook.presto.spi.statistics.RangeColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.Type; -import com.facebook.presto.spi.type.TypeManager; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableList; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableMap; import com.google.common.hash.HashFunction; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Shorts; +import com.google.common.primitives.SignedBytes; import io.airlift.log.Logger; import io.airlift.slice.Slice; -import org.joda.time.DateTimeZone; +import java.math.BigDecimal; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalLong; -import java.util.PrimitiveIterator; -import java.util.function.Function; -import java.util.stream.DoubleStream; +import java.util.Set; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_CORRUPTED_COLUMN_STATISTICS; +import static com.facebook.presto.hive.HivePartition.UNPARTITIONED_ID; import static com.facebook.presto.hive.HiveSessionProperties.getPartitionStatisticsSampleSize; +import static com.facebook.presto.hive.HiveSessionProperties.isIgnoreCorruptedStatistics; import static com.facebook.presto.hive.HiveSessionProperties.isStatisticsEnabled; -import static com.facebook.presto.hive.util.Statistics.getMinMaxAsPrestoNativeValues; -import static com.facebook.presto.spi.predicate.Utils.nativeValueToBlock; import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.Chars.isCharType; import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.Decimals.isLongDecimal; +import static com.facebook.presto.spi.type.Decimals.isShortDecimal; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.StandardTypes.CHAR; -import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; -import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TinyintType.TINYINT; +import static com.facebook.presto.spi.type.Varchars.isVarcharType; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Maps.immutableEntry; -import static com.google.common.hash.Hashing.goodFastHash; +import static com.google.common.hash.Hashing.murmur3_128; +import static java.lang.Double.isFinite; +import static java.lang.Double.isNaN; +import static java.lang.Double.parseDouble; +import static java.lang.Float.intBitsToFloat; +import static java.lang.String.format; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; @@ -82,411 +94,758 @@ public class MetastoreHiveStatisticsProvider { private static final Logger log = Logger.get(MetastoreHiveStatisticsProvider.class); - private final TypeManager typeManager; - private final SemiTransactionalHiveMetastore metastore; - private final DateTimeZone timeZone; + private final PartitionsStatisticsProvider statisticsProvider; - public MetastoreHiveStatisticsProvider(TypeManager typeManager, SemiTransactionalHiveMetastore metastore, DateTimeZone timeZone) + public MetastoreHiveStatisticsProvider(SemiTransactionalHiveMetastore metastore) { - this.typeManager = requireNonNull(typeManager, "typeManager is null"); - this.metastore = requireNonNull(metastore, "metastore is null"); - this.timeZone = requireNonNull(timeZone, "timeZone is null"); + requireNonNull(metastore, "metastore is null"); + this.statisticsProvider = (table, hivePartitions) -> getPartitionsStatistics(metastore, table, hivePartitions); + } + + @VisibleForTesting + MetastoreHiveStatisticsProvider(PartitionsStatisticsProvider statisticsProvider) + { + this.statisticsProvider = requireNonNull(statisticsProvider, "statisticsProvider is null"); + } + + private static Map getPartitionsStatistics(SemiTransactionalHiveMetastore metastore, SchemaTableName table, List hivePartitions) + { + if (hivePartitions.isEmpty()) { + return ImmutableMap.of(); + } + boolean unpartitioned = hivePartitions.stream().anyMatch(partition -> partition.getPartitionId().equals(UNPARTITIONED_ID)); + if (unpartitioned) { + checkArgument(hivePartitions.size() == 1, "expected only one hive partition"); + return ImmutableMap.of(UNPARTITIONED_ID, metastore.getTableStatistics(table.getSchemaName(), table.getTableName())); + } + Set partitionNames = hivePartitions.stream() + .map(HivePartition::getPartitionId) + .collect(toImmutableSet()); + return metastore.getPartitionStatistics(table.getSchemaName(), table.getTableName(), partitionNames); } @Override - public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTableHandle tableHandle, List queriedPartitions, Map tableColumns) + public TableStatistics getTableStatistics( + ConnectorSession session, + SchemaTableName table, + Map columns, + Map columnTypes, + List partitions) { if (!isStatisticsEnabled(session)) { - return TableStatistics.EMPTY_STATISTICS; + return TableStatistics.empty(); + } + if (partitions.isEmpty()) { + return createZeroStatistics(columns, columnTypes); } - - int queriedPartitionsCount = queriedPartitions.size(); int sampleSize = getPartitionStatisticsSampleSize(session); - List samplePartitions = getPartitionsSample(queriedPartitions, sampleSize); - - Map statisticsSample = getPartitionsStatistics((HiveTableHandle) tableHandle, samplePartitions); - - TableStatistics.Builder tableStatistics = TableStatistics.builder(); - OptionalDouble rowsPerPartition = calculateRowsPerPartition(statisticsSample); - Estimate rowCount = calculateRowsCount(rowsPerPartition, queriedPartitionsCount); - tableStatistics.setRowCount(rowCount); - for (Map.Entry columnEntry : tableColumns.entrySet()) { - String columnName = columnEntry.getKey(); - HiveColumnHandle hiveColumnHandle = (HiveColumnHandle) columnEntry.getValue(); - RangeColumnStatistics.Builder rangeStatistics = RangeColumnStatistics.builder(); - - List lowValueCandidates = ImmutableList.of(); - List highValueCandidates = ImmutableList.of(); - - Type prestoType = typeManager.getType(hiveColumnHandle.getTypeSignature()); - Estimate nullsFraction; - Estimate dataSize; - if (hiveColumnHandle.isPartitionKey()) { - rangeStatistics.setDistinctValuesCount(countDistinctPartitionKeys(hiveColumnHandle, queriedPartitions)); - nullsFraction = calculateNullsFractionForPartitioningKey(hiveColumnHandle, queriedPartitions, statisticsSample, rowCount, rowsPerPartition); - if (isLowHighSupportedForType(prestoType)) { - lowValueCandidates = queriedPartitions.stream() - .map(HivePartition::getKeys) - .map(keys -> keys.get(hiveColumnHandle)) - .filter(value -> !value.isNull()) - .map(NullableValue::getValue) - .collect(toImmutableList()); - highValueCandidates = lowValueCandidates; - } - dataSize = calculateDataSizeForPartitioningKey(hiveColumnHandle, queriedPartitions, statisticsSample, rowCount, rowsPerPartition); + List partitionsSample = getPartitionsSample(partitions, sampleSize); + try { + Map statisticsSample = statisticsProvider.getPartitionsStatistics(table, partitionsSample); + validatePartitionStatistics(table, statisticsSample); + return getTableStatistics(columns, columnTypes, partitions, statisticsSample); + } + catch (PrestoException e) { + if (e.getErrorCode().equals(HIVE_CORRUPTED_COLUMN_STATISTICS.toErrorCode()) && isIgnoreCorruptedStatistics(session)) { + log.error(e); + return TableStatistics.empty(); } - else { - rangeStatistics.setDistinctValuesCount(calculateDistinctValuesCount(statisticsSample, columnName)); - nullsFraction = calculateNullsFraction(statisticsSample, queriedPartitionsCount, columnName, rowCount); - - if (isLowHighSupportedForType(prestoType)) { - List ranges = statisticsSample.values().stream() - .map(PartitionStatistics::getColumnStatistics) - .filter(stats -> stats.containsKey(columnName)) - .map(stats -> stats.get(columnName)) - .map(stats -> getMinMaxAsPrestoNativeValues(stats, prestoType, timeZone)) - .collect(toImmutableList()); - - // TODO[lo] Maybe we do not want to expose high/low value if it is based on too small fraction of - // partitions. And return unknown if most of the partitions we are working with do not have - // statistics computed. - lowValueCandidates = ranges.stream() - .filter(range -> range.getMin().isPresent()) - .map(range -> range.getMin().get()) - .collect(toImmutableList()); - highValueCandidates = ranges.stream() - .filter(range -> range.getMax().isPresent()) - .map(range -> range.getMax().get()) - .collect(toImmutableList()); - } - dataSize = calculateDataSize(statisticsSample, columnName, rowCount); + throw e; + } + } + + private TableStatistics createZeroStatistics(Map columns, Map columnTypes) + { + TableStatistics.Builder result = TableStatistics.builder(); + result.setRowCount(Estimate.of(0)); + columns.forEach((columnName, columnHandle) -> { + Type columnType = columnTypes.get(columnName); + verify(columnType != null, "columnType is missing for column: %s", columnName); + ColumnStatistics.Builder columnStatistics = ColumnStatistics.builder(); + columnStatistics.setNullsFraction(Estimate.of(0)); + columnStatistics.setDistinctValuesCount(Estimate.of(0)); + if (hasDataSize(columnType)) { + columnStatistics.setDataSize(Estimate.of(0)); } - rangeStatistics.setFraction(nullsFraction.map(value -> 1.0 - value)); + result.setColumnStatistics(columnHandle, columnStatistics.build()); + }); + return result.build(); + } - Comparator comparator = (leftValue, rightValue) -> { - Block leftBlock = nativeValueToBlock(prestoType, leftValue); - Block rightBlock = nativeValueToBlock(prestoType, rightValue); - return prestoType.compareTo(leftBlock, 0, rightBlock, 0); - }; - rangeStatistics.setLowValue(lowValueCandidates.stream().min(comparator)); - rangeStatistics.setHighValue(highValueCandidates.stream().max(comparator)); - rangeStatistics.setDataSize(dataSize); + @VisibleForTesting + static List getPartitionsSample(List partitions, int sampleSize) + { + checkArgument(sampleSize > 0, "sampleSize is expected to be greater than zero"); - ColumnStatistics.Builder columnStatistics = ColumnStatistics.builder(); - columnStatistics.setNullsFraction(nullsFraction); - columnStatistics.addRange(rangeStatistics.build()); - tableStatistics.setColumnStatistics(hiveColumnHandle, columnStatistics.build()); + if (partitions.size() <= sampleSize) { + return partitions; + } + + List result = new ArrayList<>(); + + int samplesLeft = sampleSize; + + HivePartition min = partitions.get(0); + HivePartition max = partitions.get(0); + for (HivePartition partition : partitions) { + if (partition.getPartitionId().compareTo(min.getPartitionId()) < 0) { + min = partition; + } + else if (partition.getPartitionId().compareTo(max.getPartitionId()) > 0) { + max = partition; + } + } + + result.add(min); + samplesLeft--; + if (samplesLeft > 0) { + result.add(max); + samplesLeft--; + } + + if (samplesLeft > 0) { + HashFunction hashFunction = murmur3_128(); + Comparator> hashComparator = Comparator + ., Long>comparing(Map.Entry::getValue) + .thenComparing(entry -> entry.getKey().getPartitionId()); + partitions.stream() + .filter(partition -> !result.contains(partition)) + .map(partition -> immutableEntry(partition, hashFunction.hashUnencodedChars(partition.getPartitionId()).asLong())) + .sorted(hashComparator) + .limit(samplesLeft) + .forEachOrdered(entry -> result.add(entry.getKey())); } - return tableStatistics.build(); + + return unmodifiableList(result); } - private boolean isLowHighSupportedForType(Type type) + @VisibleForTesting + static void validatePartitionStatistics(SchemaTableName table, Map partitionStatistics) { - if (type instanceof DecimalType) { - return true; + partitionStatistics.forEach((partition, statistics) -> { + HiveBasicStatistics basicStatistics = statistics.getBasicStatistics(); + OptionalLong rowCount = basicStatistics.getRowCount(); + rowCount.ifPresent(count -> checkStatistics(count >= 0, table, partition, "rowCount must be greater than or equal to zero: %s", count)); + basicStatistics.getFileCount().ifPresent(count -> checkStatistics(count >= 0, table, partition, "fileCount must be greater than or equal to zero: %s", count)); + basicStatistics.getInMemoryDataSizeInBytes().ifPresent(size -> checkStatistics(size >= 0, table, partition, "inMemoryDataSizeInBytes must be greater than or equal to zero: %s", size)); + basicStatistics.getOnDiskDataSizeInBytes().ifPresent(size -> checkStatistics(size >= 0, table, partition, "onDiskDataSizeInBytes must be greater than or equal to zero: %s", size)); + statistics.getColumnStatistics().forEach((column, columnStatistics) -> validateColumnStatistics(table, partition, column, rowCount, columnStatistics)); + }); + } + + private static void validateColumnStatistics(SchemaTableName table, String partition, String column, OptionalLong rowCount, HiveColumnStatistics columnStatistics) + { + columnStatistics.getMaxValueSizeInBytes().ifPresent(maxValueSizeInBytes -> + checkStatistics(maxValueSizeInBytes >= 0, table, partition, column, "maxValueSizeInBytes must be greater than or equal to zero: %s", maxValueSizeInBytes)); + columnStatistics.getTotalSizeInBytes().ifPresent(totalSizeInBytes -> + checkStatistics(totalSizeInBytes >= 0, table, partition, column, "totalSizeInBytes must be greater than or equal to zero: %s", totalSizeInBytes)); + columnStatistics.getNullsCount().ifPresent(nullsCount -> { + checkStatistics(nullsCount >= 0, table, partition, column, "nullsCount must be greater than or equal to zero: %s", nullsCount); + if (rowCount.isPresent()) { + checkStatistics( + nullsCount <= rowCount.getAsLong(), + table, + partition, + column, + "nullsCount must be less than or equal to rowCount. nullsCount: %s. rowCount: %s.", + nullsCount, + rowCount.getAsLong()); + } + }); + columnStatistics.getDistinctValuesCount().ifPresent(distinctValuesCount -> { + checkStatistics(distinctValuesCount >= 0, table, partition, column, "distinctValuesCount must be greater than or equal to zero: %s", distinctValuesCount); + if (rowCount.isPresent()) { + checkStatistics( + distinctValuesCount <= rowCount.getAsLong(), + table, + partition, + column, + "distinctValuesCount must be less than or equal to rowCount. distinctValuesCount: %s. rowCount: %s.", + distinctValuesCount, + rowCount.getAsLong()); + } + if (rowCount.isPresent() && columnStatistics.getNullsCount().isPresent()) { + long nonNullsCount = rowCount.getAsLong() - columnStatistics.getNullsCount().getAsLong(); + checkStatistics( + distinctValuesCount <= nonNullsCount, + table, + partition, + column, + "distinctValuesCount must be less than or equal to nonNullsCount. distinctValuesCount: %s. nonNullsCount: %s.", + distinctValuesCount, + nonNullsCount); + } + }); + + columnStatistics.getIntegerStatistics().ifPresent(integerStatistics -> { + OptionalLong min = integerStatistics.getMin(); + OptionalLong max = integerStatistics.getMax(); + if (min.isPresent() && max.isPresent()) { + checkStatistics( + min.getAsLong() <= max.getAsLong(), + table, + partition, + column, + "integerStatistics.min must be less than or equal to integerStatistics.max. integerStatistics.min: %s. integerStatistics.max: %s.", + min.getAsLong(), + max.getAsLong()); + } + }); + columnStatistics.getDoubleStatistics().ifPresent(doubleStatistics -> { + OptionalDouble min = doubleStatistics.getMin(); + OptionalDouble max = doubleStatistics.getMax(); + if (min.isPresent() && max.isPresent() && !isNaN(min.getAsDouble()) && !isNaN(max.getAsDouble())) { + checkStatistics( + min.getAsDouble() <= max.getAsDouble(), + table, + partition, + column, + "doubleStatistics.min must be less than or equal to doubleStatistics.max. doubleStatistics.min: %s. doubleStatistics.max: %s.", + min.getAsDouble(), + max.getAsDouble()); + } + }); + columnStatistics.getDecimalStatistics().ifPresent(decimalStatistics -> { + Optional min = decimalStatistics.getMin(); + Optional max = decimalStatistics.getMax(); + if (min.isPresent() && max.isPresent()) { + checkStatistics( + min.get().compareTo(max.get()) <= 0, + table, + partition, + column, + "decimalStatistics.min must be less than or equal to decimalStatistics.max. decimalStatistics.min: %s. decimalStatistics.max: %s.", + min.get(), + max.get()); + } + }); + columnStatistics.getDateStatistics().ifPresent(dateStatistics -> { + Optional min = dateStatistics.getMin(); + Optional max = dateStatistics.getMax(); + if (min.isPresent() && max.isPresent()) { + checkStatistics( + min.get().compareTo(max.get()) <= 0, + table, + partition, + column, + "dateStatistics.min must be less than or equal to dateStatistics.max. dateStatistics.min: %s. dateStatistics.max: %s.", + min.get(), + max.get()); + } + }); + columnStatistics.getBooleanStatistics().ifPresent(booleanStatistics -> { + OptionalLong falseCount = booleanStatistics.getFalseCount(); + OptionalLong trueCount = booleanStatistics.getTrueCount(); + falseCount.ifPresent(count -> + checkStatistics(count >= 0, table, partition, column, "falseCount must be greater than or equal to zero: %s", count)); + trueCount.ifPresent(count -> + checkStatistics(count >= 0, table, partition, column, "trueCount must be greater than or equal to zero: %s", count)); + if (rowCount.isPresent() && falseCount.isPresent()) { + checkStatistics( + falseCount.getAsLong() <= rowCount.getAsLong(), + table, + partition, + column, + "booleanStatistics.falseCount must be less than or equal to rowCount. booleanStatistics.falseCount: %s. rowCount: %s.", + falseCount.getAsLong(), + rowCount.getAsLong()); + } + if (rowCount.isPresent() && trueCount.isPresent()) { + checkStatistics( + trueCount.getAsLong() <= rowCount.getAsLong(), + table, + partition, + column, + "booleanStatistics.trueCount must be less than or equal to rowCount. booleanStatistics.trueCount: %s. rowCount: %s.", + trueCount.getAsLong(), + rowCount.getAsLong()); + } + }); + } + + private static void checkStatistics(boolean expression, SchemaTableName table, String partition, String column, String message, Object... args) + { + if (!expression) { + throw new PrestoException( + HIVE_CORRUPTED_COLUMN_STATISTICS, + format("Corrupted partition statistics (Table: %s Partition: [%s] Column: %s): %s", table, partition, column, format(message, args))); } - if (type.equals(TINYINT) - || type.equals(SMALLINT) - || type.equals(INTEGER) - || type.equals(BIGINT) - || type.equals(REAL) - || type.equals(DOUBLE) - || type.equals(DATE) - || type.equals(TIMESTAMP)) { - return true; + } + + private static void checkStatistics(boolean expression, SchemaTableName table, String partition, String message, Object... args) + { + if (!expression) { + throw new PrestoException( + HIVE_CORRUPTED_COLUMN_STATISTICS, + format("Corrupted partition statistics (Table: %s Partition: [%s]): %s", table, partition, format(message, args))); } - return false; } - private OptionalDouble calculateRowsPerPartition(Map statisticsSample) + private static TableStatistics getTableStatistics( + Map columns, + Map columnTypes, + List partitions, + Map statistics) { - return statisticsSample.values().stream() + if (statistics.isEmpty()) { + return TableStatistics.empty(); + } + + checkArgument(!partitions.isEmpty(), "partitions is empty"); + + OptionalDouble optionalAverageRowsPerPartition = calculateAverageRowsPerPartition(statistics.values()); + if (!optionalAverageRowsPerPartition.isPresent()) { + return TableStatistics.empty(); + } + double averageRowsPerPartition = optionalAverageRowsPerPartition.getAsDouble(); + verify(averageRowsPerPartition >= 0, "averageRowsPerPartition must be greater than or equal to zero"); + int queriedPartitionsCount = partitions.size(); + double rowCount = averageRowsPerPartition * queriedPartitionsCount; + + TableStatistics.Builder result = TableStatistics.builder(); + result.setRowCount(Estimate.of(rowCount)); + for (Map.Entry column : columns.entrySet()) { + String columnName = column.getKey(); + HiveColumnHandle columnHandle = (HiveColumnHandle) column.getValue(); + Type columnType = columnTypes.get(columnName); + ColumnStatistics columnStatistics; + if (columnHandle.isPartitionKey()) { + columnStatistics = createPartitionColumnStatistics(columnHandle, columnType, partitions, statistics, averageRowsPerPartition, rowCount); + } + else { + columnStatistics = createDataColumnStatistics(columnName, columnType, rowCount, statistics.values()); + } + result.setColumnStatistics(columnHandle, columnStatistics); + } + return result.build(); + } + + @VisibleForTesting + static OptionalDouble calculateAverageRowsPerPartition(Collection statistics) + { + return statistics.stream() .map(PartitionStatistics::getBasicStatistics) .map(HiveBasicStatistics::getRowCount) .filter(OptionalLong::isPresent) .mapToLong(OptionalLong::getAsLong) + .peek(count -> verify(count >= 0, "count must be greater than or equal to zero")) .average(); } - private Estimate calculateRowsCount(OptionalDouble rowsPerPartition, int queriedPartitionsCount) + private static ColumnStatistics createPartitionColumnStatistics( + HiveColumnHandle column, + Type type, + List partitions, + Map statistics, + double averageRowsPerPartition, + double rowCount) { - if (!rowsPerPartition.isPresent()) { - return Estimate.unknownValue(); + return ColumnStatistics.builder() + .setDistinctValuesCount(Estimate.of(calculateDistinctPartitionKeys(column, partitions, statistics, averageRowsPerPartition))) + .setNullsFraction(Estimate.of(calculateNullsFractionForPartitioningKey(column, partitions, statistics, averageRowsPerPartition, rowCount))) + .setRange(calculateRangeForPartitioningKey(column, type, partitions)) + .setDataSize(calculateDataSizeForPartitioningKey(column, type, partitions, statistics, averageRowsPerPartition)) + .build(); + } + + @VisibleForTesting + static long calculateDistinctPartitionKeys( + HiveColumnHandle column, + List partitions, + Map statistics, + double averageRowsPerPartition) + { + return partitions.stream() + // consider only non empty partitions + .filter(partition -> getPartitionRowCount(partition.getPartitionId(), statistics).orElse(averageRowsPerPartition) > 0) + .map(partition -> partition.getKeys().get(column)) + .filter(value -> !value.isNull()) + .distinct() + .count(); + } + + @VisibleForTesting + static double calculateNullsFractionForPartitioningKey( + HiveColumnHandle column, + List partitions, + Map statistics, + double averageRowsPerPartition, + double rowCount) + { + if (rowCount == 0) { + return 0; } - return new Estimate(rowsPerPartition.getAsDouble() * queriedPartitionsCount); + double estimatedNullsCount = partitions.stream() + .filter(partition -> partition.getKeys().get(column).isNull()) + .map(HivePartition::getPartitionId) + .mapToDouble(partitionName -> getPartitionRowCount(partitionName, statistics).orElse(averageRowsPerPartition)) + .sum(); + return normalizeFraction(estimatedNullsCount / rowCount); } - private Estimate calculateDistinctValuesCount(Map statisticsSample, String column) + private static double normalizeFraction(double fraction) { - return summarizePartitionStatistics( - statisticsSample.values(), - column, - columnStatistics -> { - if (columnStatistics.getDistinctValuesCount().isPresent()) { - return OptionalDouble.of(columnStatistics.getDistinctValuesCount().getAsLong()); - } - if (columnStatistics.getBooleanStatistics().isPresent() && - columnStatistics.getBooleanStatistics().get().getFalseCount().isPresent() && - columnStatistics.getBooleanStatistics().get().getTrueCount().isPresent()) { - long falseCount = columnStatistics.getBooleanStatistics().get().getFalseCount().getAsLong(); - long trueCount = columnStatistics.getBooleanStatistics().get().getTrueCount().getAsLong(); - return OptionalDouble.of((falseCount > 0 ? 1 : 0) + (trueCount > 0 ? 1 : 0)); - } - return OptionalDouble.empty(); - }, - DoubleStream::max); + checkArgument(!isNaN(fraction), "fraction is NaN"); + checkArgument(isFinite(fraction), "fraction must be finite"); + if (fraction < 0) { + return 0; + } + if (fraction > 1) { + return 1; + } + return fraction; } - private Estimate calculateNullsFraction(Map statisticsSample, int totalPartitionsCount, String column, Estimate rowCount) + @VisibleForTesting + static Estimate calculateDataSizeForPartitioningKey( + HiveColumnHandle column, + Type type, + List partitions, + Map statistics, + double averageRowsPerPartition) { - if (rowCount.isValueUnknown()) { - return Estimate.unknownValue(); + if (!hasDataSize(type)) { + return Estimate.unknown(); } - if (rowCount.getValue() == 0.0) { - return Estimate.zeroValue(); + double dataSize = 0; + for (HivePartition partition : partitions) { + int length = getSize(partition.getKeys().get(column)); + double rowCount = getPartitionRowCount(partition.getPartitionId(), statistics).orElse(averageRowsPerPartition); + dataSize += length * rowCount; } + return Estimate.of(dataSize); + } - Estimate totalNullsCount = summarizePartitionStatistics( - statisticsSample.values(), - column, - columnStatistics -> { - if (!columnStatistics.getNullsCount().isPresent()) { - return OptionalDouble.empty(); - } - return OptionalDouble.of(columnStatistics.getNullsCount().getAsLong()); - }, - nullsCountStream -> { - double nullsCount = 0; - long partitionsWithStatisticsCount = 0; - for (PrimitiveIterator.OfDouble nullsCountIterator = nullsCountStream.iterator(); nullsCountIterator.hasNext(); ) { - nullsCount += nullsCountIterator.nextDouble(); - partitionsWithStatisticsCount++; - } + private static boolean hasDataSize(Type type) + { + return isVarcharType(type) || isCharType(type); + } - if (partitionsWithStatisticsCount == 0) { - return OptionalDouble.empty(); - } - return OptionalDouble.of(totalPartitionsCount / partitionsWithStatisticsCount * nullsCount); - }); + private static int getSize(NullableValue nullableValue) + { + if (nullableValue.isNull()) { + return 0; + } + Object value = nullableValue.getValue(); + checkArgument(value instanceof Slice, "value is expected to be of Slice type"); + return ((Slice) value).length(); + } - if (totalNullsCount.isValueUnknown()) { - return Estimate.unknownValue(); + private static OptionalDouble getPartitionRowCount(String partitionName, Map statistics) + { + PartitionStatistics partitionStatistics = statistics.get(partitionName); + if (partitionStatistics == null) { + return OptionalDouble.empty(); } - return new Estimate(totalNullsCount.getValue() / rowCount.getValue()); + OptionalLong rowCount = partitionStatistics.getBasicStatistics().getRowCount(); + if (rowCount.isPresent()) { + verify(rowCount.getAsLong() >= 0, "rowCount must be greater than or equal to zero"); + return OptionalDouble.of(rowCount.getAsLong()); + } + return OptionalDouble.empty(); } - private Estimate calculateDataSize(Map statisticsSample, String columnName, Estimate rowCount) + @VisibleForTesting + static Optional calculateRangeForPartitioningKey(HiveColumnHandle column, Type type, List partitions) { - if (rowCount.isValueUnknown()) { - return Estimate.unknownValue(); + if (!isRangeSupported(type)) { + return Optional.empty(); } - int knownPartitionCount = 0; - double knownRowCount = 0; - double knownDataSize = 0; + List values = partitions.stream() + .map(HivePartition::getKeys) + .map(keys -> keys.get(column)) + .filter(value -> !value.isNull()) + .map(NullableValue::getValue) + .map(value -> convertPartitionValueToDouble(type, value)) + .collect(toImmutableList()); + + if (values.isEmpty()) { + return Optional.empty(); + } - for (PartitionStatistics statistics : statisticsSample.values()) { - if (!statistics.getBasicStatistics().getRowCount().isPresent()) { - continue; - } - double partitionRowCount = statistics.getBasicStatistics().getRowCount().getAsLong(); + double min = values.get(0); + double max = values.get(0); - HiveColumnStatistics partitionColumnStatistics = statistics.getColumnStatistics().get(columnName); - if (partitionColumnStatistics == null || !partitionColumnStatistics.getTotalSizeInBytes().isPresent()) { - continue; + for (Double value : values) { + if (value > max) { + max = value; + } + if (value < min) { + min = value; } - - knownPartitionCount++; - knownRowCount += partitionRowCount; - // Note: average column length from Hive might not translate directly into internal data size - knownDataSize += partitionColumnStatistics.getTotalSizeInBytes().getAsLong(); } - if (knownPartitionCount == 0) { - return Estimate.unknownValue(); + return Optional.of(new DoubleRange(min, max)); + } + + @VisibleForTesting + static double convertPartitionValueToDouble(Type type, Object value) + { + if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(SMALLINT) || type.equals(TINYINT)) { + return (Long) value; } + if (type.equals(DOUBLE)) { + return (Double) value; + } + if (type.equals(REAL)) { + return intBitsToFloat(((Long) value).intValue()); + } + if (type instanceof DecimalType) { + DecimalType decimalType = (DecimalType) type; + if (isShortDecimal(decimalType)) { + return parseDouble(Decimals.toString((Long) value, decimalType.getScale())); + } + if (isLongDecimal(decimalType)) { + return parseDouble(Decimals.toString((Slice) value, decimalType.getScale())); + } + throw new IllegalArgumentException("Unexpected decimal type: " + decimalType); + } + if (type.equals(DATE)) { + return (Long) value; + } + throw new IllegalArgumentException("Unexpected type: " + type); + } + + @VisibleForTesting + static ColumnStatistics createDataColumnStatistics(String column, Type type, double rowsCount, Collection partitionStatistics) + { + List columnStatistics = partitionStatistics.stream() + .map(PartitionStatistics::getColumnStatistics) + .map(statistics -> statistics.get(column)) + .filter(Objects::nonNull) + .collect(toImmutableList()); - if (knownDataSize == 0) { - return Estimate.zeroValue(); + if (columnStatistics.isEmpty()) { + return ColumnStatistics.empty(); } - verify(knownRowCount > 0); - return new Estimate(knownDataSize / knownRowCount * rowCount.getValue()); + return ColumnStatistics.builder() + .setDistinctValuesCount(calculateDistinctValuesCount(columnStatistics)) + .setNullsFraction(calculateNullsFraction(column, partitionStatistics)) + .setDataSize(calculateDataSize(column, partitionStatistics, rowsCount)) + .setRange(calculateRange(type, columnStatistics)) + .build(); } - private Estimate countDistinctPartitionKeys(HiveColumnHandle partitionColumn, List partitions) + @VisibleForTesting + static Estimate calculateDistinctValuesCount(List columnStatistics) { - return new Estimate(partitions.stream() - .map(HivePartition::getKeys) - .map(keys -> keys.get(partitionColumn)) - .distinct() - .count()); + return columnStatistics.stream() + .map(MetastoreHiveStatisticsProvider::getDistinctValuesCount) + .filter(OptionalLong::isPresent) + .map(OptionalLong::getAsLong) + .peek(distinctValuesCount -> verify(distinctValuesCount >= 0, "distinctValuesCount must be greater than or equal to zero")) + .max(Long::compare) + .map(Estimate::of) + .orElse(Estimate.unknown()); } - private Estimate calculateNullsFractionForPartitioningKey( - HiveColumnHandle partitionColumn, - List queriedPartitions, - Map statisticsSample, - Estimate rowCount, - OptionalDouble rowsPerPartition) + private static OptionalLong getDistinctValuesCount(HiveColumnStatistics statistics) { - if (rowCount.isValueUnknown()) { - return Estimate.unknownValue(); - } - if (rowCount.getValue() == 0.0) { - return Estimate.zeroValue(); + if (statistics.getBooleanStatistics().isPresent() && + statistics.getBooleanStatistics().get().getFalseCount().isPresent() && + statistics.getBooleanStatistics().get().getTrueCount().isPresent()) { + long falseCount = statistics.getBooleanStatistics().get().getFalseCount().getAsLong(); + long trueCount = statistics.getBooleanStatistics().get().getTrueCount().getAsLong(); + return OptionalLong.of((falseCount > 0 ? 1 : 0) + (trueCount > 0 ? 1 : 0)); } - if (!rowsPerPartition.isPresent()) { - return Estimate.unknownValue(); + if (statistics.getDistinctValuesCount().isPresent()) { + return statistics.getDistinctValuesCount(); } - - double estimatedNullsCount = queriedPartitions.stream() - .filter(partition -> partition.getKeys().get(partitionColumn).isNull()) - .map(HivePartition::getPartitionId) - .mapToDouble(partitionId -> orElse(statisticsSample.get(partitionId).getBasicStatistics().getRowCount(), rowsPerPartition.getAsDouble())) - .sum(); - return new Estimate(estimatedNullsCount / rowCount.getValue()); + return OptionalLong.empty(); } - private Estimate calculateDataSizeForPartitioningKey( - HiveColumnHandle partitionColumn, - List queriedPartitions, - Map statisticsSample, - Estimate rowCount, - OptionalDouble rowsPerPartition) + @VisibleForTesting + static Estimate calculateNullsFraction(String column, Collection partitionStatistics) { - if (rowCount.isValueUnknown() || !rowsPerPartition.isPresent()) { - return Estimate.unknownValue(); + List statisticsWithKnownRowCountAndNullsCount = partitionStatistics.stream() + .filter(statistics -> { + if (!statistics.getBasicStatistics().getRowCount().isPresent()) { + return false; + } + HiveColumnStatistics columnStatistics = statistics.getColumnStatistics().get(column); + if (columnStatistics == null) { + return false; + } + return columnStatistics.getNullsCount().isPresent(); + }) + .collect(toImmutableList()); + + if (statisticsWithKnownRowCountAndNullsCount.isEmpty()) { + return Estimate.unknown(); + } + + long totalNullsCount = 0; + long totalRowCount = 0; + for (PartitionStatistics statistics : statisticsWithKnownRowCountAndNullsCount) { + long rowCount = statistics.getBasicStatistics().getRowCount().orElseThrow(() -> new VerifyException("rowCount is not present")); + verify(rowCount >= 0, "rowCount must be greater than or equal to zero"); + HiveColumnStatistics columnStatistics = statistics.getColumnStatistics().get(column); + verify(columnStatistics != null, "columnStatistics is null"); + long nullsCount = columnStatistics.getNullsCount().orElseThrow(() -> new VerifyException("nullsCount is not present")); + verify(nullsCount >= 0, "nullsCount must be greater than or equal to zero"); + verify(nullsCount <= rowCount, "nullsCount must be less than or equal to rowCount. nullsCount: %s. rowCount: %s.", nullsCount, rowCount); + totalNullsCount += nullsCount; + totalRowCount += rowCount; } - String baseType = partitionColumn.getTypeSignature().getBase(); - if (!VARCHAR.equals(baseType) && !CHAR.equalsIgnoreCase(baseType)) { - // TODO support VARBINARY - return Estimate.unknownValue(); + if (totalRowCount == 0) { + return Estimate.zero(); } - double knownRowCount = 0; - double knownDataSize = 0; + verify( + totalNullsCount <= totalRowCount, + "totalNullsCount must be less than or equal to totalRowCount. totalNullsCount: %s. totalRowCount: %s.", + totalNullsCount, + totalRowCount); + return Estimate.of(((double) totalNullsCount) / totalRowCount); + } + + @VisibleForTesting + static Estimate calculateDataSize(String column, Collection partitionStatistics, double totalRowCount) + { + List statisticsWithKnownRowCountAndDataSize = partitionStatistics.stream() + .filter(statistics -> { + if (!statistics.getBasicStatistics().getRowCount().isPresent()) { + return false; + } + HiveColumnStatistics columnStatistics = statistics.getColumnStatistics().get(column); + if (columnStatistics == null) { + return false; + } + return columnStatistics.getTotalSizeInBytes().isPresent(); + }) + .collect(toImmutableList()); - for (HivePartition partition : queriedPartitions) { - NullableValue value = partition.getKeys().get(partitionColumn); - int length = value.isNull() ? 0 : ((Slice) value.getValue()).length(); + if (statisticsWithKnownRowCountAndDataSize.isEmpty()) { + return Estimate.unknown(); + } - double partitionRowCount = orElse( - Optional.ofNullable(statisticsSample.get(partition.getPartitionId())) - .orElseGet(PartitionStatistics::empty) - .getBasicStatistics() - .getRowCount(), - rowsPerPartition.getAsDouble()); - knownRowCount += partitionRowCount; - knownDataSize += length * partitionRowCount; + long knownRowCount = 0; + long knownDataSize = 0; + for (PartitionStatistics statistics : statisticsWithKnownRowCountAndDataSize) { + long rowCount = statistics.getBasicStatistics().getRowCount().orElseThrow(() -> new VerifyException("rowCount is not present")); + verify(rowCount >= 0, "rowCount must be greater than or equal to zero"); + HiveColumnStatistics columnStatistics = statistics.getColumnStatistics().get(column); + verify(columnStatistics != null, "columnStatistics is null"); + long dataSize = columnStatistics.getTotalSizeInBytes().orElseThrow(() -> new VerifyException("totalSizeInBytes is not present")); + verify(dataSize >= 0, "dataSize must be greater than or equal to zero"); + knownRowCount += rowCount; + knownDataSize += dataSize; + } + + if (totalRowCount == 0) { + return Estimate.zero(); } if (knownRowCount == 0) { - return Estimate.unknownValue(); + return Estimate.unknown(); } - return new Estimate(knownDataSize / knownRowCount * rowCount.getValue()); + double averageValueDataSizeInBytes = ((double) knownDataSize) / knownRowCount; + return Estimate.of(averageValueDataSizeInBytes * totalRowCount); } - private Estimate summarizePartitionStatistics( - Collection partitionStatistics, - String column, - Function valueExtractFunction, - Function valueAggregateFunction) + @VisibleForTesting + static Optional calculateRange(Type type, List columnStatistics) { - DoubleStream intermediateStream = partitionStatistics.stream() - .map(PartitionStatistics::getColumnStatistics) - .filter(stats -> stats.containsKey(column)) - .map(stats -> stats.get(column)) - .map(valueExtractFunction) - .filter(OptionalDouble::isPresent) - .mapToDouble(OptionalDouble::getAsDouble); - - OptionalDouble statisticsValue = valueAggregateFunction.apply(intermediateStream); - - if (!statisticsValue.isPresent()) { - return Estimate.unknownValue(); + if (!isRangeSupported(type)) { + return Optional.empty(); } - return new Estimate(statisticsValue.getAsDouble()); + return columnStatistics.stream() + .map(statistics -> createRange(type, statistics)) + .filter(Optional::isPresent) + .map(Optional::get) + .reduce(DoubleRange::union); } - private Map getPartitionsStatistics(HiveTableHandle tableHandle, List hivePartitions) + private static boolean isRangeSupported(Type type) { - if (hivePartitions.isEmpty()) { - return ImmutableMap.of(); + return type.equals(TINYINT) + || type.equals(SMALLINT) + || type.equals(INTEGER) + || type.equals(BIGINT) + || type.equals(REAL) + || type.equals(DOUBLE) + || type.equals(DATE) + || type instanceof DecimalType; + } + + private static Optional createRange(Type type, HiveColumnStatistics statistics) + { + if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(SMALLINT) || type.equals(TINYINT)) { + return statistics.getIntegerStatistics().flatMap(integerStatistics -> createIntegerRange(type, integerStatistics)); } - boolean unpartitioned = hivePartitions.stream().anyMatch(partition -> partition.getPartitionId().equals(HivePartition.UNPARTITIONED_ID)); - if (unpartitioned) { - checkArgument(hivePartitions.size() == 1, "expected only one hive partition"); + if (type.equals(DOUBLE) || type.equals(REAL)) { + return statistics.getDoubleStatistics().flatMap(MetastoreHiveStatisticsProvider::createDoubleRange); } - - if (unpartitioned) { - return ImmutableMap.of(HivePartition.UNPARTITIONED_ID, metastore.getTableStatistics(tableHandle.getSchemaName(), tableHandle.getTableName())); + if (type.equals(DATE)) { + return statistics.getDateStatistics().flatMap(MetastoreHiveStatisticsProvider::createDateRange); } - else { - return metastore.getPartitionStatistics( - tableHandle.getSchemaName(), - tableHandle.getTableName(), - hivePartitions.stream() - .map(HivePartition::getPartitionId) - .collect(toImmutableSet())); + if (type instanceof DecimalType) { + return statistics.getDecimalStatistics().flatMap(MetastoreHiveStatisticsProvider::createDecimalRange); } + throw new IllegalArgumentException("Unexpected type: " + type); } - @VisibleForTesting - static List getPartitionsSample(List partitions, int sampleSize) + private static Optional createIntegerRange(Type type, IntegerStatistics statistics) { - checkArgument(sampleSize > 0, "sampleSize is expected to be greater than zero"); - - if (partitions.size() <= sampleSize) { - return partitions; + if (statistics.getMin().isPresent() && statistics.getMax().isPresent()) { + return Optional.of(createIntegerRange(type, statistics.getMin().getAsLong(), statistics.getMax().getAsLong())); } + return Optional.empty(); + } - List result = new ArrayList<>(); - - int samplesLeft = sampleSize; + private static DoubleRange createIntegerRange(Type type, long min, long max) + { + return new DoubleRange(normalizeIntegerValue(type, min), normalizeIntegerValue(type, max)); + } - HivePartition min = partitions.get(0); - HivePartition max = partitions.get(0); - for (HivePartition partition : partitions) { - if (partition.getPartitionId().compareTo(min.getPartitionId()) < 0) { - min = partition; - } - else if (partition.getPartitionId().compareTo(max.getPartitionId()) > 0) { - max = partition; - } + private static long normalizeIntegerValue(Type type, long value) + { + if (type.equals(BIGINT)) { + return value; } - - verify(samplesLeft > 0); - result.add(min); - samplesLeft--; - if (samplesLeft > 0) { - result.add(max); - samplesLeft--; + if (type.equals(INTEGER)) { + return Ints.saturatedCast(value); } + if (type.equals(SMALLINT)) { + return Shorts.saturatedCast(value); + } + if (type.equals(TINYINT)) { + return SignedBytes.saturatedCast(value); + } + throw new IllegalArgumentException("Unexpected type: " + type); + } - if (samplesLeft > 0) { - HashFunction hashFunction = goodFastHash(32); - Comparator> hashComparator = Comparator - ., Integer>comparing(Map.Entry::getValue) - .thenComparing(entry -> entry.getKey().getPartitionId()); - partitions.stream() - .filter(partition -> !result.contains(partition)) - .map(partition -> immutableEntry(partition, hashFunction.hashUnencodedChars(partition.getPartitionId()).asInt())) - .sorted(hashComparator) - .limit(sampleSize) - .forEach(entry -> result.add(entry.getKey())); + private static Optional createDoubleRange(DoubleStatistics statistics) + { + if (statistics.getMin().isPresent() && statistics.getMax().isPresent() && !isNaN(statistics.getMin().getAsDouble()) && !isNaN(statistics.getMax().getAsDouble())) { + return Optional.of(new DoubleRange(statistics.getMin().getAsDouble(), statistics.getMax().getAsDouble())); } + return Optional.empty(); + } - return unmodifiableList(result); + private static Optional createDateRange(DateStatistics statistics) + { + if (statistics.getMin().isPresent() && statistics.getMax().isPresent()) { + return Optional.of(new DoubleRange(statistics.getMin().get().toEpochDay(), statistics.getMax().get().toEpochDay())); + } + return Optional.empty(); } - private static double orElse(OptionalLong value, double other) + private static Optional createDecimalRange(DecimalStatistics statistics) { - if (value.isPresent()) { - return value.getAsLong(); + if (statistics.getMin().isPresent() && statistics.getMax().isPresent()) { + return Optional.of(new DoubleRange(statistics.getMin().get().doubleValue(), statistics.getMax().get().doubleValue())); } - return other; + return Optional.empty(); + } + + @VisibleForTesting + interface PartitionsStatisticsProvider + { + Map getPartitionsStatistics(SchemaTableName table, List hivePartitions); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/util/ConfigurationUtils.java b/presto-hive/src/main/java/com/facebook/presto/hive/util/ConfigurationUtils.java index b647ab89791cd..103b55da971ac 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/util/ConfigurationUtils.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/util/ConfigurationUtils.java @@ -20,8 +20,33 @@ public final class ConfigurationUtils { + private static final Configuration INITIAL_CONFIGURATION; + + static { + Configuration.addDefaultResource("hdfs-default.xml"); + Configuration.addDefaultResource("hdfs-site.xml"); + + // must not be transitively reloaded during the future loading of various Hadoop modules + // all the required default resources must be declared above + INITIAL_CONFIGURATION = new Configuration(false); + Configuration defaultConfiguration = new Configuration(); + copy(defaultConfiguration, INITIAL_CONFIGURATION); + } + private ConfigurationUtils() {} + public static Configuration getInitialConfiguration() + { + return copy(INITIAL_CONFIGURATION); + } + + public static Configuration copy(Configuration configuration) + { + Configuration copy = new Configuration(false); + copy(configuration, copy); + return copy; + } + public static void copy(Configuration from, Configuration to) { for (Map.Entry entry : from) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/util/HiveFileIterator.java b/presto-hive/src/main/java/com/facebook/presto/hive/util/HiveFileIterator.java index bea9f9ddabbcc..4595b720c77ad 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/util/HiveFileIterator.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/util/HiveFileIterator.java @@ -160,7 +160,7 @@ private PrestoException processException(IOException exception) { namenodeStats.getRemoteIteratorNext().recordException(exception); if (exception instanceof FileNotFoundException) { - throw new PrestoException(HIVE_FILE_NOT_FOUND, "Partition location does not exist: " + path); + return new PrestoException(HIVE_FILE_NOT_FOUND, "Partition location does not exist: " + path); } return new PrestoException(HIVE_FILESYSTEM_ERROR, "Failed to list directory: " + path, exception); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/util/InternalHiveSplitFactory.java b/presto-hive/src/main/java/com/facebook/presto/hive/util/InternalHiveSplitFactory.java index dd19146b5800f..4d455531b20d7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/util/InternalHiveSplitFactory.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/util/InternalHiveSplitFactory.java @@ -19,6 +19,7 @@ import com.facebook.presto.hive.HiveTypeName; import com.facebook.presto.hive.InternalHiveSplit; import com.facebook.presto.hive.InternalHiveSplit.InternalHiveBlock; +import com.facebook.presto.hive.S3SelectPushdown; import com.facebook.presto.spi.HostAddress; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; @@ -58,6 +59,7 @@ public class InternalHiveSplitFactory private final Map columnCoercions; private final Optional bucketConversion; private final boolean forceLocalScheduling; + private final boolean s3SelectPushdownEnabled; public InternalHiveSplitFactory( FileSystem fileSystem, @@ -68,7 +70,8 @@ public InternalHiveSplitFactory( TupleDomain effectivePredicate, Map columnCoercions, Optional bucketConversion, - boolean forceLocalScheduling) + boolean forceLocalScheduling, + boolean s3SelectPushdownEnabled) { this.fileSystem = requireNonNull(fileSystem, "fileSystem is null"); this.partitionName = requireNonNull(partitionName, "partitionName is null"); @@ -79,6 +82,7 @@ public InternalHiveSplitFactory( this.columnCoercions = requireNonNull(columnCoercions, "columnCoercions is null"); this.bucketConversion = requireNonNull(bucketConversion, "bucketConversion is null"); this.forceLocalScheduling = forceLocalScheduling; + this.s3SelectPushdownEnabled = s3SelectPushdownEnabled; } public String getPartitionName() @@ -184,7 +188,8 @@ private Optional createInternalHiveSplit( splittable, forceLocalScheduling && allBlocksHaveRealAddress(blocks), columnCoercions, - bucketConversion)); + bucketConversion, + s3SelectPushdownEnabled && S3SelectPushdown.isCompressionCodecSupported(inputFormat, path))); } private static void checkBlocks(List blocks, long start, long length) diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/util/Statistics.java b/presto-hive/src/main/java/com/facebook/presto/hive/util/Statistics.java index 68b1445253842..1ae5c3b555f84 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/util/Statistics.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/util/Statistics.java @@ -28,7 +28,6 @@ import com.facebook.presto.spi.statistics.ColumnStatisticType; import com.facebook.presto.spi.statistics.ComputedStatistics; import com.facebook.presto.spi.type.DecimalType; -import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.SqlDate; import com.facebook.presto.spi.type.SqlDecimal; import com.facebook.presto.spi.type.Type; @@ -36,7 +35,6 @@ import org.joda.time.DateTimeZone; import java.math.BigDecimal; -import java.math.BigInteger; import java.time.LocalDate; import java.util.Collection; import java.util.HashMap; @@ -72,8 +70,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.Sets.intersection; -import static java.lang.Float.floatToRawIntBits; -import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; public final class Statistics @@ -175,23 +171,6 @@ private static Optional mergeBooleanStatistics(Optional> T min(T first, T second) return first.compareTo(second) <= 0 ? first : second; } - public static Range getMinMaxAsPrestoNativeValues(HiveColumnStatistics statistics, Type type, DateTimeZone timeZone) - { - if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(SMALLINT) || type.equals(TINYINT)) { - return statistics.getIntegerStatistics().map(integerStatistics -> Range.create( - integerStatistics.getMin(), - integerStatistics.getMax())) - .orElse(Range.empty()); - } - if (type.equals(DOUBLE)) { - return statistics.getDoubleStatistics().map(doubleStatistics -> Range.create( - doubleStatistics.getMin(), - doubleStatistics.getMax())) - .orElse(Range.empty()); - } - if (type.equals(REAL)) { - return statistics.getDoubleStatistics().map(doubleStatistics -> Range.create( - boxed(doubleStatistics.getMin()).map(Statistics::floatAsDoubleToLongBits), - boxed(doubleStatistics.getMax()).map(Statistics::floatAsDoubleToLongBits))) - .orElse(Range.empty()); - } - if (type.equals(DATE)) { - return statistics.getDateStatistics().map(dateStatistics -> Range.create( - dateStatistics.getMin().map(LocalDate::toEpochDay), - dateStatistics.getMax().map(LocalDate::toEpochDay))) - .orElse(Range.empty()); - } - if (type.equals(TIMESTAMP)) { - return statistics.getIntegerStatistics().map(integerStatistics -> Range.create( - boxed(integerStatistics.getMin()).map(value -> convertLocalToUtc(timeZone, value)), - boxed(integerStatistics.getMax()).map(value -> convertLocalToUtc(timeZone, value)))) - .orElse(Range.empty()); - } - if (type instanceof DecimalType) { - return statistics.getDecimalStatistics().map(decimalStatistics -> Range.create( - decimalStatistics.getMin().map(value -> encodeDecimal(type, value)), - decimalStatistics.getMax().map(value -> encodeDecimal(type, value)))) - .orElse(Range.empty()); - } - return Range.empty(); - } - - private static long floatAsDoubleToLongBits(double value) - { - return floatToRawIntBits((float) value); - } - - private static long convertLocalToUtc(DateTimeZone timeZone, long value) - { - return timeZone.convertLocalToUTC(value * 1000, false); - } - - private static Comparable encodeDecimal(Type type, BigDecimal value) - { - BigInteger unscaled = Decimals.rescale(value, (DecimalType) type).unscaledValue(); - if (Decimals.isShortDecimal(type)) { - return unscaled.longValueExact(); - } - return Decimals.encodeUnscaledValue(unscaled); - } - public static Map, ComputedStatistics> createComputedStatisticsToPartitionMap( Collection computedStatistics, List partitionColumns, @@ -395,16 +314,24 @@ private static HiveColumnStatistics createHiveColumnStatistics( result.setTotalSizeInBytes(getIntegerValue(session, BIGINT, computedStatistics.get(TOTAL_SIZE_IN_BYTES))); } - // NDV - if (computedStatistics.containsKey(NUMBER_OF_DISTINCT_VALUES)) { - result.setDistinctValuesCount(BIGINT.getLong(computedStatistics.get(NUMBER_OF_DISTINCT_VALUES), 0)); - } - // NUMBER OF NULLS if (computedStatistics.containsKey(NUMBER_OF_NON_NULL_VALUES)) { result.setNullsCount(rowCount - BIGINT.getLong(computedStatistics.get(NUMBER_OF_NON_NULL_VALUES), 0)); } + // NDV + if (computedStatistics.containsKey(NUMBER_OF_DISTINCT_VALUES) && computedStatistics.containsKey(NUMBER_OF_NON_NULL_VALUES)) { + // number of distinct value is estimated using HLL, and can be higher than the number of non null values + long numberOfNonNullValues = BIGINT.getLong(computedStatistics.get(NUMBER_OF_NON_NULL_VALUES), 0); + long numberOfDistinctValues = BIGINT.getLong(computedStatistics.get(NUMBER_OF_DISTINCT_VALUES), 0); + if (numberOfDistinctValues > numberOfNonNullValues) { + result.setDistinctValuesCount(numberOfNonNullValues); + } + else { + result.setDistinctValuesCount(numberOfDistinctValues); + } + } + // NUMBER OF FALSE, NUMBER OF TRUE if (computedStatistics.containsKey(NUMBER_OF_TRUE_VALUES) && computedStatistics.containsKey(NUMBER_OF_NON_NULL_VALUES)) { long numberOfTrue = BIGINT.getLong(computedStatistics.get(NUMBER_OF_TRUE_VALUES), 0); @@ -463,16 +390,6 @@ private static Optional getDecimalValue(ConnectorSession session, Ty return block.isNull(0) ? Optional.empty() : Optional.of(((SqlDecimal) type.getObjectValue(session, block, 0)).toBigDecimal()); } - private static Optional boxed(OptionalLong input) - { - return input.isPresent() ? Optional.of(input.getAsLong()) : Optional.empty(); - } - - private static Optional boxed(OptionalDouble input) - { - return input.isPresent() ? Optional.of(input.getAsDouble()) : Optional.empty(); - } - public enum ReduceOperator { ADD, @@ -480,48 +397,4 @@ public enum ReduceOperator MIN, MAX, } - - public static class Range - { - private static final Range EMPTY = new Range(Optional.empty(), Optional.empty()); - - private final Optional> min; - private final Optional> max; - - public static Range empty() - { - return EMPTY; - } - - public static Range create(Optional> min, Optional> max) - { - return new Range(min, max); - } - - public static Range create(OptionalLong min, OptionalLong max) - { - return new Range(boxed(min), boxed(max)); - } - - public static Range create(OptionalDouble min, OptionalDouble max) - { - return new Range(boxed(min), boxed(max)); - } - - public Range(Optional> min, Optional> max) - { - this.min = requireNonNull(min, "min is null"); - this.max = requireNonNull(max, "max is null"); - } - - public Optional> getMin() - { - return min; - } - - public Optional> getMax() - { - return max; - } - } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/util/TempFileWriter.java b/presto-hive/src/main/java/com/facebook/presto/hive/util/TempFileWriter.java index dc42e89df1b35..8ab4841550c17 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/util/TempFileWriter.java +++ b/presto-hive/src/main/java/com/facebook/presto/hive/util/TempFileWriter.java @@ -13,11 +13,11 @@ */ package com.facebook.presto.hive.util; +import com.facebook.presto.orc.OrcDataSink; import com.facebook.presto.orc.OrcWriteValidation.OrcWriteValidationMode; import com.facebook.presto.orc.OrcWriter; import com.facebook.presto.orc.OrcWriterOptions; import com.facebook.presto.orc.OrcWriterStats; -import com.facebook.presto.orc.OutputStreamOrcDataSink; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableMap; @@ -25,7 +25,6 @@ import java.io.Closeable; import java.io.IOException; -import java.io.OutputStream; import java.io.UncheckedIOException; import java.util.List; import java.util.stream.IntStream; @@ -42,9 +41,9 @@ public class TempFileWriter { private final OrcWriter orcWriter; - public TempFileWriter(List types, OutputStream output) + public TempFileWriter(List types, OrcDataSink sink) { - this.orcWriter = createOrcFileWriter(output, types); + this.orcWriter = createOrcFileWriter(sink, types); } public void writePage(Page page) @@ -69,22 +68,21 @@ public long getWrittenBytes() return orcWriter.getWrittenBytes(); } - private static OrcWriter createOrcFileWriter(OutputStream output, List types) + private static OrcWriter createOrcFileWriter(OrcDataSink sink, List types) { List columnNames = IntStream.range(0, types.size()) .mapToObj(String::valueOf) .collect(toImmutableList()); return new OrcWriter( - new OutputStreamOrcDataSink(output), + sink, columnNames, types, ORC, LZ4, new OrcWriterOptions() .withMaxStringStatisticsLimit(new DataSize(0, BYTE)) - .withStripeMinSize(new DataSize(4, MEGABYTE)) - .withStripeMaxSize(new DataSize(4, MEGABYTE)) + .withStripeMinSize(new DataSize(64, MEGABYTE)) .withDictionaryMaxMemory(new DataSize(1, MEGABYTE)), ImmutableMap.of(), UTC, diff --git a/presto-hive/src/main/java/parquet/io/ColumnIOConverter.java b/presto-hive/src/main/java/parquet/io/ColumnIOConverter.java index ca05ba94e8b1c..d1bb50f57de79 100644 --- a/presto-hive/src/main/java/parquet/io/ColumnIOConverter.java +++ b/presto-hive/src/main/java/parquet/io/ColumnIOConverter.java @@ -13,10 +13,10 @@ */ package parquet.io; -import com.facebook.presto.hive.parquet.Field; -import com.facebook.presto.hive.parquet.GroupField; -import com.facebook.presto.hive.parquet.PrimitiveField; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.Field; +import com.facebook.presto.parquet.GroupField; +import com.facebook.presto.parquet.PrimitiveField; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.NamedTypeSignature; import com.facebook.presto.spi.type.Type; @@ -27,9 +27,9 @@ import java.util.Locale; import java.util.Optional; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getArrayElementColumn; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getMapKeyValueColumn; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.lookupColumnByName; +import static com.facebook.presto.parquet.ParquetTypeUtils.getArrayElementColumn; +import static com.facebook.presto.parquet.ParquetTypeUtils.getMapKeyValueColumn; +import static com.facebook.presto.parquet.ParquetTypeUtils.lookupColumnByName; import static com.facebook.presto.spi.type.StandardTypes.ARRAY; import static com.facebook.presto.spi.type.StandardTypes.MAP; import static com.facebook.presto.spi.type.StandardTypes.ROW; diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClient.java b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClient.java index 7d214b0e2f941..53e1510c11f04 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClient.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClient.java @@ -24,6 +24,7 @@ import com.facebook.presto.hive.metastore.HivePrivilegeInfo; import com.facebook.presto.hive.metastore.HivePrivilegeInfo.HivePrivilege; import com.facebook.presto.hive.metastore.Partition; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.hive.metastore.PrincipalPrivileges; import com.facebook.presto.hive.metastore.SemiTransactionalHiveMetastore; import com.facebook.presto.hive.metastore.SortingColumn; @@ -34,7 +35,6 @@ import com.facebook.presto.hive.metastore.thrift.TestingHiveCluster; import com.facebook.presto.hive.metastore.thrift.ThriftHiveMetastore; import com.facebook.presto.hive.orc.OrcPageSource; -import com.facebook.presto.hive.parquet.ParquetHiveRecordCursor; import com.facebook.presto.hive.parquet.ParquetPageSource; import com.facebook.presto.hive.rcfile.RcFilePageSource; import com.facebook.presto.metadata.MetadataManager; @@ -75,7 +75,6 @@ import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.predicate.ValueSet; import com.facebook.presto.spi.statistics.ColumnStatistics; -import com.facebook.presto.spi.statistics.RangeColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.MapType; @@ -174,6 +173,7 @@ import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveDataStreamFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveFileWriterFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveRecordCursorProvider; +import static com.facebook.presto.hive.HiveTestUtils.getDefaultOrcFileWriterFactory; import static com.facebook.presto.hive.HiveTestUtils.getTypes; import static com.facebook.presto.hive.HiveTestUtils.mapType; import static com.facebook.presto.hive.HiveTestUtils.rowType; @@ -182,6 +182,7 @@ import static com.facebook.presto.hive.HiveType.HIVE_STRING; import static com.facebook.presto.hive.HiveType.toHiveType; import static com.facebook.presto.hive.HiveUtil.columnExtraInfo; +import static com.facebook.presto.hive.HiveUtil.toPartitionValues; import static com.facebook.presto.hive.HiveWriteUtils.createDirectory; import static com.facebook.presto.hive.LocationHandle.WriteMode.STAGE_AND_MOVE_TO_TARGET_DIRECTORY; import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createBinaryColumnStatistics; @@ -254,7 +255,6 @@ import static java.util.stream.Collectors.toList; import static org.apache.hadoop.hive.common.FileUtils.makePartName; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.entry; import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -450,7 +450,7 @@ private static RowType toRowType(List columns) .add(new ColumnMetadata("t_long_decimal", createDecimalType(20, 3))) .build(); - private static final List STATISTICS_PARTITIONED_TABLE_COLUMNS = ImmutableList.builder() + protected static final List STATISTICS_PARTITIONED_TABLE_COLUMNS = ImmutableList.builder() .addAll(STATISTICS_TABLE_COLUMNS) .add(new ColumnMetadata("ds", VARCHAR)) .build(); @@ -482,7 +482,7 @@ private static RowType toRowType(List columns) private static final PartitionStatistics STATISTICS_1_1 = new PartitionStatistics( - new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.of(10), OptionalLong.empty(), OptionalLong.of(0)), + new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.of(15), OptionalLong.empty(), OptionalLong.of(0)), STATISTICS_1.getColumnStatistics().entrySet() .stream() .filter(entry -> entry.getKey().hashCode() % 2 == 0) @@ -490,7 +490,7 @@ private static RowType toRowType(List columns) private static final PartitionStatistics STATISTICS_1_2 = new PartitionStatistics( - new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.of(10), OptionalLong.of(3), OptionalLong.of(0)), + new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.of(15), OptionalLong.of(3), OptionalLong.of(0)), STATISTICS_1.getColumnStatistics().entrySet() .stream() .filter(entry -> entry.getKey().hashCode() % 2 == 1) @@ -519,7 +519,7 @@ private static RowType toRowType(List columns) private static final PartitionStatistics STATISTICS_EMPTY_OPTIONAL_FIELDS = new PartitionStatistics( - new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.empty(), OptionalLong.empty(), OptionalLong.of(0)), + new HiveBasicStatistics(OptionalLong.of(0), OptionalLong.of(20), OptionalLong.empty(), OptionalLong.of(0)), ImmutableMap.builder() .put("t_boolean", createBooleanColumnStatistics(OptionalLong.of(4), OptionalLong.of(3), OptionalLong.of(2))) .put("t_bigint", createIntegerColumnStatistics(OptionalLong.empty(), OptionalLong.empty(), OptionalLong.of(4), OptionalLong.of(7))) @@ -528,10 +528,10 @@ private static RowType toRowType(List columns) .put("t_tinyint", createIntegerColumnStatistics(OptionalLong.empty(), OptionalLong.empty(), OptionalLong.of(2), OptionalLong.of(3))) .put("t_double", createDoubleColumnStatistics(OptionalDouble.empty(), OptionalDouble.empty(), OptionalLong.of(6), OptionalLong.of(3))) .put("t_float", createDoubleColumnStatistics(OptionalDouble.empty(), OptionalDouble.empty(), OptionalLong.of(7), OptionalLong.of(11))) - .put("t_string", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.empty(), OptionalLong.of(2), OptionalLong.of(6))) - .put("t_varchar", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.empty(), OptionalLong.of(7), OptionalLong.of(1))) - .put("t_char", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.empty(), OptionalLong.of(0), OptionalLong.of(3))) - .put("t_varbinary", createBinaryColumnStatistics(OptionalLong.of(0), OptionalLong.empty(), OptionalLong.of(2))) + .put("t_string", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.of(0), OptionalLong.of(2), OptionalLong.of(6))) + .put("t_varchar", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.of(0), OptionalLong.of(7), OptionalLong.of(1))) + .put("t_char", createStringColumnStatistics(OptionalLong.of(0), OptionalLong.of(0), OptionalLong.of(0), OptionalLong.of(3))) + .put("t_varbinary", createBinaryColumnStatistics(OptionalLong.of(0), OptionalLong.of(0), OptionalLong.of(2))) // https://issues.apache.org/jira/browse/HIVE-20098 // .put("t_date", createDateColumnStatistics(Optional.empty(), Optional.empty(), OptionalLong.of(8), OptionalLong.of(7))) .put("t_timestamp", createIntegerColumnStatistics(OptionalLong.empty(), OptionalLong.empty(), OptionalLong.of(9), OptionalLong.of(1))) @@ -776,8 +776,9 @@ protected final void setup(String databaseName, HiveClientConfig hiveClientConfi partitionUpdateCodec, new TestingNodeManager("fake-environment"), new HiveEventClient(), - new HiveSessionProperties(hiveClientConfig, new OrcFileWriterConfig()), - new HiveWriterStats()); + new HiveSessionProperties(hiveClientConfig, new OrcFileWriterConfig(), new ParquetFileWriterConfig()), + new HiveWriterStats(), + getDefaultOrcFileWriterFactory(hiveClientConfig)); pageSourceProvider = new HivePageSourceProvider(hiveClientConfig, hdfsEnvironment, getDefaultHiveRecordCursorProvider(hiveClientConfig), getDefaultHiveDataStreamFactories(hiveClientConfig), TYPE_MANAGER); } @@ -793,12 +794,12 @@ protected HiveClientConfig getHiveClientConfig() protected ConnectorSession newSession() { - return new TestingConnectorSession(new HiveSessionProperties(getHiveClientConfig(), new OrcFileWriterConfig()).getSessionProperties()); + return new TestingConnectorSession(new HiveSessionProperties(getHiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); } protected Transaction newTransaction() { - return new HiveTransaction(transactionManager, metadataFactory.create()); + return new HiveTransaction(transactionManager, metadataFactory.get()); } interface Transaction @@ -1309,7 +1310,7 @@ private void assertTableStatsComputed( ConnectorTableHandle tableHandle = getTableHandle(metadata, tableName); TableStatistics tableStatistics = metadata.getTableStatistics(session, tableHandle, Constraint.alwaysTrue()); - assertFalse(tableStatistics.getRowCount().isValueUnknown(), "row count is unknown"); + assertFalse(tableStatistics.getRowCount().isUnknown(), "row count is unknown"); Map columnsStatistics = tableStatistics .getColumnStatistics() @@ -1328,26 +1329,22 @@ private void assertTableStatsComputed( Type columnType = metadata.getColumnMetadata(session, tableHandle, columnHandle).getType(); assertFalse( - columnStatistics.getNullsFraction().isValueUnknown(), + columnStatistics.getNullsFraction().isUnknown(), "unknown nulls fraction for " + columnName); - RangeColumnStatistics rangeColumnStatistics = columnStatistics.getOnlyRangeColumnStatistics(); assertFalse( - rangeColumnStatistics.getDistinctValuesCount().isValueUnknown(), - "unknown range distinct values count for " + columnName); - assertFalse( - rangeColumnStatistics.getFraction().isValueUnknown(), - "unknown range non-null fraction for " + columnName); + columnStatistics.getDistinctValuesCount().isUnknown(), + "unknown distinct values count for " + columnName); if (isVarcharType(columnType)) { assertFalse( - rangeColumnStatistics.getDataSize().isValueUnknown(), - "unknown range data size for " + columnName); + columnStatistics.getDataSize().isUnknown(), + "unknown data size for " + columnName); } else { assertTrue( - rangeColumnStatistics.getDataSize().isValueUnknown(), - "known range data size for" + columnName); + columnStatistics.getDataSize().isUnknown(), + "unknown data size for" + columnName); } }); } @@ -2187,7 +2184,7 @@ public void testTableCreationIgnoreExisting() Path targetPath; try { try (Transaction transaction = newTransaction()) { - LocationService locationService = getLocationService(schemaName); + LocationService locationService = getLocationService(); LocationHandle locationHandle = locationService.forNewTable(transaction.getMetastore(schemaName), session, schemaName, tableName); targetPath = locationService.getQueryWriteInfo(locationHandle).getTargetPath(); Table table = createSimpleTable(schemaTableName, columns, session, targetPath, "q1"); @@ -2275,6 +2272,7 @@ private void doTestBucketSortedTables(SchemaTableName table) throws IOException { int bucketCount = 3; + int expectedRowCount = 0; try (Transaction transaction = newTransaction()) { ConnectorSession session = newSession(); @@ -2316,6 +2314,7 @@ private void doTestBucketSortedTables(SchemaTableName table) "test" + random.nextInt(100), random.nextLong(100_000), "2018-04-01"); + expectedRowCount++; } sink.appendPage(builder.build().toPage()); } @@ -2332,7 +2331,7 @@ private void doTestBucketSortedTables(SchemaTableName table) // verify there are no temporary files for (String file : listAllDataFiles(context, stagingPathRoot)) { - assertThat(file).doesNotStartWith(".tmp-sort."); + assertThat(file).doesNotContain(".tmp-sort."); } // finish creating table @@ -2352,6 +2351,7 @@ private void doTestBucketSortedTables(SchemaTableName table) List splits = getAllSplits(tableHandle, TupleDomain.all()); assertThat(splits).hasSize(bucketCount); + int actualRowCount = 0; for (ConnectorSplit split : splits) { try (ConnectorPageSource pageSource = pageSourceProvider.createPageSource(transaction.getTransactionHandle(), session, split, columnHandles)) { String lastValueAsc = null; @@ -2383,10 +2383,12 @@ private void doTestBucketSortedTables(SchemaTableName table) } } lastValueAsc = valueAsc; + actualRowCount++; } } } } + assertThat(actualRowCount).isEqualTo(expectedRowCount); } } @@ -2530,56 +2532,6 @@ public void testCreateTableUnsupportedType() } } - @Test - public void testUpdateTableParameters() - throws Exception - { - SchemaTableName tableName = temporaryTable("compare_and_set_table_parameters"); - try { - doCreateEmptyTable(tableName, ORC, CREATE_TABLE_COLUMNS); - - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); - metastoreClient.updateTableParameters(tableName.getSchemaName(), tableName.getTableName(), parameters -> { - Map updated = new HashMap<>(parameters); - updated.put("test_key", "test_value"); - return updated; - }); - - Table table = metastoreClient.getTable(tableName.getSchemaName(), tableName.getTableName()) - .orElseThrow(() -> new TableNotFoundException(tableName)); - assertThat(table.getParameters()).contains(entry("test_key", "test_value")); - } - finally { - dropTable(tableName); - } - } - - @Test - public void testUpdatePartitionParameters() - throws Exception - { - SchemaTableName tableName = temporaryTable("compare_and_set_partition_parameters"); - try { - doCreateEmptyTable(tableName, ORC, CREATE_TABLE_COLUMNS_PARTITIONED); - insertData(tableName, CREATE_TABLE_PARTITIONED_DATA); - ImmutableList partitionValues = ImmutableList.of("2015-07-01"); - - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); - metastoreClient.updatePartitionParameters(tableName.getSchemaName(), tableName.getTableName(), partitionValues, parameters -> { - Map updated = new HashMap<>(parameters); - updated.put("test_key", "test_value"); - return updated; - }); - - Partition partition = metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), partitionValues) - .orElseThrow(() -> new PartitionNotFoundException(tableName, partitionValues)); - assertThat(partition.getParameters()).contains(entry("test_key", "test_value")); - } - finally { - dropTable(tableName); - } - } - @Test public void testUpdateBasicTableStatistics() throws Exception @@ -2624,7 +2576,7 @@ public void testUpdateTableColumnStatisticsEmptyOptionalFields() protected void testUpdateTableStatistics(SchemaTableName tableName, PartitionStatistics initialStatistics, PartitionStatistics... statistics) { - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); assertThat(metastoreClient.getTableStatistics(tableName.getSchemaName(), tableName.getTableName())) .isEqualTo(initialStatistics); @@ -2725,7 +2677,7 @@ protected void createDummyPartitionedTable(SchemaTableName tableName, List new TableNotFoundException(tableName)); @@ -2735,17 +2687,9 @@ protected void createDummyPartitionedTable(SchemaTableName tableName, List partitions = ImmutableList.of(firstPartitionValues, secondPartitionValues) + List partitions = ImmutableList.of(firstPartitionName, secondPartitionName) .stream() - .map(values -> Partition.builder() - .setDatabaseName(tableName.getSchemaName()) - .setTableName(tableName.getTableName()) - .setColumns(table.getDataColumns()) - .setValues(values) - .withStorage(storage -> storage - .setStorageFormat(fromHiveStorageFormat(HiveStorageFormat.ORC)) - .setLocation(table.getStorage().getLocation() + "/" + makePartName(ImmutableList.of("ds"), values))) - .build()) + .map(partitionName -> new PartitionWithStatistics(createDummyPartition(table, partitionName), partitionName, PartitionStatistics.empty())) .collect(toImmutableList()); metastoreClient.addPartitions(tableName.getSchemaName(), tableName.getTableName(), partitions); metastoreClient.updatePartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), firstPartitionName, currentStatistics -> EMPTY_TABLE_STATISTICS); @@ -2763,7 +2707,7 @@ protected void testUpdatePartitionStatistics( String firstPartitionName = "ds=2016-01-01"; String secondPartitionName = "ds=2016-01-02"; - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); assertThat(metastoreClient.getPartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), ImmutableSet.of(firstPartitionName, secondPartitionName))) .isEqualTo(ImmutableMap.of(firstPartitionName, initialStatistics, secondPartitionName, initialStatistics)); @@ -2801,6 +2745,108 @@ protected void testUpdatePartitionStatistics( .isEqualTo(ImmutableMap.of(firstPartitionName, initialStatistics, secondPartitionName, initialStatistics)); } + @Test + public void testStorePartitionWithStatistics() + throws Exception + { + testStorePartitionWithStatistics(STATISTICS_PARTITIONED_TABLE_COLUMNS, STATISTICS_1, STATISTICS_2, STATISTICS_1_1, EMPTY_TABLE_STATISTICS); + } + + protected void testStorePartitionWithStatistics( + List columns, + PartitionStatistics statsForAllColumns1, + PartitionStatistics statsForAllColumns2, + PartitionStatistics statsForSubsetOfColumns, + PartitionStatistics emptyStatistics) + throws Exception + { + SchemaTableName tableName = temporaryTable("store_partition_with_statistics"); + try { + doCreateEmptyTable(tableName, ORC, columns); + + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); + Table table = metastoreClient.getTable(tableName.getSchemaName(), tableName.getTableName()) + .orElseThrow(() -> new TableNotFoundException(tableName)); + + List partitionValues = ImmutableList.of("2016-01-01"); + String partitionName = makePartName(ImmutableList.of("ds"), partitionValues); + + Partition partition = createDummyPartition(table, partitionName); + + // create partition with stats for all columns + metastoreClient.addPartitions(tableName.getSchemaName(), tableName.getTableName(), ImmutableList.of(new PartitionWithStatistics(partition, partitionName, statsForAllColumns1))); + assertEquals( + metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), partitionValues).get().getStorage().getStorageFormat(), + fromHiveStorageFormat(ORC)); + assertThat(metastoreClient.getPartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), ImmutableSet.of(partitionName))) + .isEqualTo(ImmutableMap.of(partitionName, statsForAllColumns1)); + + // alter the partition into one with other stats + Partition modifiedPartition = Partition.builder(partition) + .withStorage(storage -> storage + .setStorageFormat(fromHiveStorageFormat(DWRF)) + .setLocation(partitionTargetPath(tableName, partitionName))) + .build(); + metastoreClient.alterPartition(tableName.getSchemaName(), tableName.getTableName(), new PartitionWithStatistics(modifiedPartition, partitionName, statsForAllColumns2)); + assertEquals( + metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), partitionValues).get().getStorage().getStorageFormat(), + fromHiveStorageFormat(DWRF)); + assertThat(metastoreClient.getPartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), ImmutableSet.of(partitionName))) + .isEqualTo(ImmutableMap.of(partitionName, statsForAllColumns2)); + + // alter the partition into one with stats for only subset of columns + modifiedPartition = Partition.builder(partition) + .withStorage(storage -> storage + .setStorageFormat(fromHiveStorageFormat(TEXTFILE)) + .setLocation(partitionTargetPath(tableName, partitionName))) + .build(); + metastoreClient.alterPartition(tableName.getSchemaName(), tableName.getTableName(), new PartitionWithStatistics(modifiedPartition, partitionName, statsForSubsetOfColumns)); + assertThat(metastoreClient.getPartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), ImmutableSet.of(partitionName))) + .isEqualTo(ImmutableMap.of(partitionName, statsForSubsetOfColumns)); + + // alter the partition into one without stats + modifiedPartition = Partition.builder(partition) + .withStorage(storage -> storage + .setStorageFormat(fromHiveStorageFormat(TEXTFILE)) + .setLocation(partitionTargetPath(tableName, partitionName))) + .build(); + metastoreClient.alterPartition(tableName.getSchemaName(), tableName.getTableName(), new PartitionWithStatistics(modifiedPartition, partitionName, emptyStatistics)); + assertThat(metastoreClient.getPartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), ImmutableSet.of(partitionName))) + .isEqualTo(ImmutableMap.of(partitionName, emptyStatistics)); + } + finally { + dropTable(tableName); + } + } + + protected Partition createDummyPartition(Table table, String partitionName) + { + return Partition.builder() + .setDatabaseName(table.getDatabaseName()) + .setTableName(table.getTableName()) + .setColumns(table.getDataColumns()) + .setValues(toPartitionValues(partitionName)) + .withStorage(storage -> storage + .setStorageFormat(fromHiveStorageFormat(HiveStorageFormat.ORC)) + .setLocation(partitionTargetPath(new SchemaTableName(table.getDatabaseName(), table.getTableName()), partitionName))) + .setParameters(ImmutableMap.of( + PRESTO_VERSION_NAME, "testversion", + PRESTO_QUERY_ID_NAME, "20180101_123456_00001_x1y2z")) + .build(); + } + + protected String partitionTargetPath(SchemaTableName schemaTableName, String partitionName) + { + try (Transaction transaction = newTransaction()) { + ConnectorSession session = newSession(); + SemiTransactionalHiveMetastore metastore = transaction.getMetastore(schemaTableName.getSchemaName()); + LocationService locationService = getLocationService(); + Table table = metastore.getTable(schemaTableName.getSchemaName(), schemaTableName.getTableName()).get(); + LocationHandle handle = locationService.forExistingTable(metastore, session, table); + return locationService.getPartitionWriteInfo(handle, Optional.empty(), partitionName).getTargetPath().toString(); + } + } + /** * This test creates 2 identical partitions and verifies that the statistics projected based on * a single partition sample are equal to the statistics computed in a fair way @@ -2819,7 +2865,7 @@ protected void testPartitionStatisticsSampling(List columns, Par try { createDummyPartitionedTable(tableName, columns); - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); metastoreClient.updatePartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), "ds=2016-01-01", actualStatistics -> statistics); metastoreClient.updatePartitionStatistics(tableName.getSchemaName(), tableName.getTableName(), "ds=2016-01-02", actualStatistics -> statistics); @@ -2842,7 +2888,7 @@ private ConnectorSession sampleSize(int sampleSize) { HiveSessionProperties properties = new HiveSessionProperties( getHiveClientConfig().setPartitionStatisticsSampleSize(sampleSize), - new OrcFileWriterConfig()); + new OrcFileWriterConfig(), new ParquetFileWriterConfig()); return new TestingConnectorSession(properties.getSessionProperties()); } @@ -2955,7 +3001,7 @@ protected void doCreateTable(SchemaTableName tableName, HiveStorageFormat storag assertEqualsIgnoreOrder(result.getMaterializedRows(), CREATE_TABLE_DATA.getMaterializedRows()); // verify the node version and query ID in table - Table table = getMetastoreClient(tableName.getSchemaName()).getTable(tableName.getSchemaName(), tableName.getTableName()).get(); + Table table = getMetastoreClient().getTable(tableName.getSchemaName(), tableName.getTableName()).get(); assertEquals(table.getParameters().get(PRESTO_VERSION_NAME), TEST_SERVER_VERSION); assertEquals(table.getParameters().get(PRESTO_QUERY_ID_NAME), queryId); @@ -3154,7 +3200,7 @@ protected String getFilePrefix(ConnectorInsertTableHandle insertTableHandle) protected Path getStagingPathRoot(ConnectorInsertTableHandle insertTableHandle) { HiveInsertTableHandle handle = (HiveInsertTableHandle) insertTableHandle; - WriteInfo writeInfo = getLocationService(handle.getSchemaName()).getQueryWriteInfo(handle.getLocationHandle()); + WriteInfo writeInfo = getLocationService().getQueryWriteInfo(handle.getLocationHandle()); if (writeInfo.getWriteMode() != STAGE_AND_MOVE_TO_TARGET_DIRECTORY) { throw new AssertionError("writeMode is not STAGE_AND_MOVE_TO_TARGET_DIRECTORY"); } @@ -3164,7 +3210,7 @@ protected Path getStagingPathRoot(ConnectorInsertTableHandle insertTableHandle) protected Path getStagingPathRoot(ConnectorOutputTableHandle outputTableHandle) { HiveOutputTableHandle handle = (HiveOutputTableHandle) outputTableHandle; - return getLocationService(handle.getSchemaName()) + return getLocationService() .getQueryWriteInfo(handle.getLocationHandle()) .getWritePath(); } @@ -3173,7 +3219,7 @@ protected Path getTargetPathRoot(ConnectorInsertTableHandle insertTableHandle) { HiveInsertTableHandle hiveInsertTableHandle = (HiveInsertTableHandle) insertTableHandle; - return getLocationService(hiveInsertTableHandle.getSchemaName()) + return getLocationService() .getQueryWriteInfo(hiveInsertTableHandle.getLocationHandle()) .getTargetPath(); } @@ -3252,7 +3298,7 @@ private void doInsertIntoNewPartition(HiveStorageFormat storageFormat, SchemaTab .collect(toList())); // verify the node versions in partitions - Map> partitions = getMetastoreClient(tableName.getSchemaName()).getPartitionsByNames(tableName.getSchemaName(), tableName.getTableName(), partitionNames); + Map> partitions = getMetastoreClient().getPartitionsByNames(tableName.getSchemaName(), tableName.getTableName(), partitionNames); assertEquals(partitions.size(), partitionNames.size()); for (String partitionName : partitionNames) { Partition partition = partitions.get(partitionName).get(); @@ -3502,7 +3548,7 @@ private static HiveBasicStatistics getBasicStatisticsForPartition(Transaction tr private void eraseStatistics(SchemaTableName schemaTableName) { - ExtendedHiveMetastore metastoreClient = getMetastoreClient(schemaTableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); metastoreClient.updateTableStatistics(schemaTableName.getSchemaName(), schemaTableName.getTableName(), statistics -> new PartitionStatistics(createEmptyStatistics(), ImmutableMap.of())); Table table = metastoreClient.getTable(schemaTableName.getSchemaName(), schemaTableName.getTableName()) .orElseThrow(() -> new TableNotFoundException(schemaTableName)); @@ -3991,12 +4037,12 @@ private MaterializedResult readTable( return new MaterializedResult(allRows.build(), getTypes(columnHandles)); } - public ExtendedHiveMetastore getMetastoreClient(String namespace) + public ExtendedHiveMetastore getMetastoreClient() { return metastoreClient; } - public LocationService getLocationService(String namespace) + public LocationService getLocationService() { return locationService; } @@ -4058,10 +4104,6 @@ protected static void assertPageSourceType(ConnectorPageSource pageSource, HiveS private static Class recordCursorType(HiveStorageFormat hiveStorageFormat) { - switch (hiveStorageFormat) { - case PARQUET: - return ParquetHiveRecordCursor.class; - } return GenericHiveRecordCursor.class; } @@ -4226,7 +4268,7 @@ private void createEmptyTable(SchemaTableName schemaTableName, HiveStorageFormat String schemaName = schemaTableName.getSchemaName(); String tableName = schemaTableName.getTableName(); - LocationService locationService = getLocationService(schemaName); + LocationService locationService = getLocationService(); LocationHandle locationHandle = locationService.forNewTable(transaction.getMetastore(schemaName), session, schemaName, tableName); targetPath = locationService.getQueryWriteInfo(locationHandle).getTargetPath(); @@ -4589,7 +4631,7 @@ protected class AddPartitionFailure implements ConflictTrigger { private final ImmutableList copyPartitionFrom = ImmutableList.of("a", "insert1"); - private final ImmutableList partitionValueToConflict = ImmutableList.of("b", "add2"); + private final String partitionNameToConflict = "pk1=b/pk2=add2"; private Partition conflictPartition; @Override @@ -4597,12 +4639,15 @@ public void triggerConflict(ConnectorSession session, SchemaTableName tableName, { // This method bypasses transaction interface because this method is inherently hacky and doesn't work well with the transaction abstraction. // Additionally, this method is not part of a test. Its purpose is to set up an environment for another test. - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); Optional partition = metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), copyPartitionFrom); conflictPartition = Partition.builder(partition.get()) - .setValues(partitionValueToConflict) + .setValues(toPartitionValues(partitionNameToConflict)) .build(); - metastoreClient.addPartitions(tableName.getSchemaName(), tableName.getTableName(), ImmutableList.of(conflictPartition)); + metastoreClient.addPartitions( + tableName.getSchemaName(), + tableName.getTableName(), + ImmutableList.of(new PartitionWithStatistics(conflictPartition, partitionNameToConflict, PartitionStatistics.empty()))); } @Override @@ -4610,8 +4655,8 @@ public void verifyAndCleanup(SchemaTableName tableName) { // This method bypasses transaction interface because this method is inherently hacky and doesn't work well with the transaction abstraction. // Additionally, this method is not part of a test. Its purpose is to set up an environment for another test. - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); - Optional actualPartition = metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), partitionValueToConflict); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); + Optional actualPartition = metastoreClient.getPartition(tableName.getSchemaName(), tableName.getTableName(), toPartitionValues(partitionNameToConflict)); // Make sure the partition inserted to trigger conflict was not overwritten // Checking storage location is sufficient because implement never uses .../pk1=a/pk2=a2 as the directory for partition [b, b2]. assertEquals(actualPartition.get().getStorage().getLocation(), conflictPartition.getStorage().getLocation()); @@ -4629,7 +4674,7 @@ public void triggerConflict(ConnectorSession session, SchemaTableName tableName, { // This method bypasses transaction interface because this method is inherently hacky and doesn't work well with the transaction abstraction. // Additionally, this method is not part of a test. Its purpose is to set up an environment for another test. - ExtendedHiveMetastore metastoreClient = getMetastoreClient(tableName.getSchemaName()); + ExtendedHiveMetastore metastoreClient = getMetastoreClient(); metastoreClient.dropPartition(tableName.getSchemaName(), tableName.getTableName(), partitionValueToConflict, false); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClientLocal.java b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClientLocal.java index 0f71d03e47795..3e5c9838a63ec 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClientLocal.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveClientLocal.java @@ -29,12 +29,25 @@ import static com.google.common.io.MoreFiles.deleteRecursively; import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE; +import static java.util.Objects.requireNonNull; public abstract class AbstractTestHiveClientLocal extends AbstractTestHiveClient { - private static final String TEST_DB_NAME = "test"; + private static final String DEFAULT_TEST_DB_NAME = "test"; + private File tempDir; + private String testDbName; + + protected AbstractTestHiveClientLocal() + { + this(DEFAULT_TEST_DB_NAME); + } + + protected AbstractTestHiveClientLocal(String testDbName) + { + this.testDbName = requireNonNull(testDbName, "testDbName is null"); + } protected abstract ExtendedHiveMetastore createMetastore(File tempDir); @@ -46,7 +59,7 @@ public void initialize() ExtendedHiveMetastore metastore = createMetastore(tempDir); metastore.createDatabase(Database.builder() - .setDatabaseName(TEST_DB_NAME) + .setDatabaseName(testDbName) .setOwnerName("public") .setOwnerType(PrincipalType.ROLE) .build()); @@ -54,7 +67,7 @@ public void initialize() HiveClientConfig hiveConfig = new HiveClientConfig() .setTimeZone("America/Los_Angeles"); - setup(TEST_DB_NAME, hiveConfig, metastore); + setup(testDbName, hiveConfig, metastore); } @AfterClass(alwaysRun = true) @@ -62,7 +75,7 @@ public void cleanup() throws IOException { try { - getMetastoreClient(TEST_DB_NAME).dropDatabase(TEST_DB_NAME); + getMetastoreClient().dropDatabase(testDbName); } finally { deleteRecursively(tempDir.toPath(), ALLOW_INSECURE); diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystem.java b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystem.java index 888ba90072896..a15de7c13eba4 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystem.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/AbstractTestHiveFileSystem.java @@ -86,6 +86,7 @@ import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveDataStreamFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveFileWriterFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveRecordCursorProvider; +import static com.facebook.presto.hive.HiveTestUtils.getDefaultOrcFileWriterFactory; import static com.facebook.presto.hive.HiveTestUtils.getTypes; import static com.facebook.presto.spi.connector.ConnectorSplitManager.SplitSchedulingStrategy.UNGROUPED_SCHEDULING; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -120,6 +121,7 @@ public abstract class AbstractTestHiveFileSystem protected ConnectorPageSourceProvider pageSourceProvider; private ExecutorService executor; + private HiveClientConfig config; @BeforeClass public void setUp() @@ -138,7 +140,7 @@ public void tearDown() protected abstract Path getBasePath(); - protected void setup(String host, int port, String databaseName, Function hdfsConfigurationProvider) + protected void setup(String host, int port, String databaseName, Function hdfsConfigurationProvider, boolean s3SelectPushdownEnabled) { database = databaseName; table = new SchemaTableName(database, "presto_test_external_fs"); @@ -146,7 +148,8 @@ protected void setup(String host, int port, String databaseName, Function hiveCatalogConfig = ImmutableMap.builder() .put("hive.max-split-size", "10GB") diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/HiveQueryRunner.java b/presto-hive/src/test/java/com/facebook/presto/hive/HiveQueryRunner.java index 0c9e45d4bb5b2..7f29d2ba20cfb 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/HiveQueryRunner.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/HiveQueryRunner.java @@ -32,6 +32,7 @@ import java.io.File; import java.util.Map; +import java.util.Optional; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.tests.QueryAssertions.copyTpchTables; @@ -54,7 +55,7 @@ private HiveQueryRunner() public static final String HIVE_BUCKETED_CATALOG = "hive_bucketed"; public static final String TPCH_SCHEMA = "tpch"; private static final String TPCH_BUCKETED_SCHEMA = "tpch_bucketed"; - private static final DateTimeZone TIME_ZONE = DateTimeZone.forID("Asia/Kathmandu"); + private static final DateTimeZone TIME_ZONE = DateTimeZone.forID("America/Bahia_Banderas"); public static DistributedQueryRunner createQueryRunner(TpchTable... tables) throws Exception @@ -77,7 +78,7 @@ public static DistributedQueryRunner createQueryRunner(Iterable> ta public static DistributedQueryRunner createQueryRunner(Iterable> tables, Map extraProperties, String security, Map extraHiveProperties) throws Exception { - assertEquals(DateTimeZone.getDefault(), TIME_ZONE, "Timezone not configured correctly. Add -Duser.timezone=Asia/Katmandu to your JVM arguments"); + assertEquals(DateTimeZone.getDefault(), TIME_ZONE, "Timezone not configured correctly. Add -Duser.timezone=America/Bahia_Banderas to your JVM arguments"); DistributedQueryRunner queryRunner = new DistributedQueryRunner(createSession(), 4, extraProperties); @@ -94,7 +95,7 @@ public static DistributedQueryRunner createQueryRunner(Iterable> ta FileHiveMetastore metastore = new FileHiveMetastore(hdfsEnvironment, baseDir.toURI().toString(), "test"); metastore.createDatabase(createDatabaseMetastoreObject(TPCH_SCHEMA)); metastore.createDatabase(createDatabaseMetastoreObject(TPCH_BUCKETED_SCHEMA)); - queryRunner.installPlugin(new HivePlugin(HIVE_CATALOG, metastore)); + queryRunner.installPlugin(new HivePlugin(HIVE_CATALOG, Optional.of(metastore))); Map hiveProperties = ImmutableMap.builder() .putAll(extraHiveProperties) diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/HiveTestUtils.java b/presto-hive/src/test/java/com/facebook/presto/hive/HiveTestUtils.java index 481f42af89ae4..1dea997b4f21c 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/HiveTestUtils.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/HiveTestUtils.java @@ -19,7 +19,6 @@ import com.facebook.presto.hive.orc.DwrfPageSourceFactory; import com.facebook.presto.hive.orc.OrcPageSourceFactory; import com.facebook.presto.hive.parquet.ParquetPageSourceFactory; -import com.facebook.presto.hive.parquet.ParquetRecordCursorProvider; import com.facebook.presto.hive.rcfile.RcFilePageSourceFactory; import com.facebook.presto.hive.s3.HiveS3Config; import com.facebook.presto.hive.s3.PrestoS3ConfigurationUpdater; @@ -40,10 +39,13 @@ import com.facebook.presto.type.TypeRegistry; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import io.airlift.slice.Slice; +import java.math.BigDecimal; import java.util.List; import java.util.Set; +import static com.facebook.presto.spi.type.Decimals.encodeScaledValue; import static java.util.stream.Collectors.toList; public final class HiveTestUtils @@ -53,7 +55,7 @@ private HiveTestUtils() } public static final ConnectorSession SESSION = new TestingConnectorSession( - new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig()).getSessionProperties()); + new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); public static final TypeRegistry TYPE_MANAGER = new TypeRegistry(); @@ -82,7 +84,6 @@ public static Set getDefaultHiveRecordCursorProvider(H { HdfsEnvironment testHdfsEnvironment = createTestHdfsEnvironment(hiveClientConfig); return ImmutableSet.builder() - .add(new ParquetRecordCursorProvider(testHdfsEnvironment, new FileFormatDataSourceStats())) .add(new GenericHiveRecordCursorProvider(testHdfsEnvironment)) .build(); } @@ -92,16 +93,22 @@ public static Set getDefaultHiveFileWriterFactories(HiveC HdfsEnvironment testHdfsEnvironment = createTestHdfsEnvironment(hiveClientConfig); return ImmutableSet.builder() .add(new RcFileFileWriterFactory(testHdfsEnvironment, TYPE_MANAGER, new NodeVersion("test_version"), hiveClientConfig, new FileFormatDataSourceStats())) - .add(new OrcFileWriterFactory( - testHdfsEnvironment, - TYPE_MANAGER, - new NodeVersion("test_version"), - hiveClientConfig, - new FileFormatDataSourceStats(), - new OrcFileWriterConfig())) + .add(getDefaultOrcFileWriterFactory(hiveClientConfig)) .build(); } + public static OrcFileWriterFactory getDefaultOrcFileWriterFactory(HiveClientConfig hiveClientConfig) + { + HdfsEnvironment testHdfsEnvironment = createTestHdfsEnvironment(hiveClientConfig); + return new OrcFileWriterFactory( + testHdfsEnvironment, + TYPE_MANAGER, + new NodeVersion("test_version"), + hiveClientConfig, + new FileFormatDataSourceStats(), + new OrcFileWriterConfig()); + } + public static List getTypes(List columnHandles) { ImmutableList.Builder types = ImmutableList.builder(); @@ -139,4 +146,14 @@ public static RowType rowType(List elementTypeSignatures) .map(TypeSignatureParameter::of) .collect(toList()))); } + + public static Long shortDecimal(String value) + { + return new BigDecimal(value).unscaledValue().longValueExact(); + } + + public static Slice longDecimal(String value) + { + return encodeScaledValue(new BigDecimal(value)); + } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestBackgroundHiveSplitLoader.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestBackgroundHiveSplitLoader.java index a267068dfc965..add56250f110d 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestBackgroundHiveSplitLoader.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestBackgroundHiveSplitLoader.java @@ -140,7 +140,7 @@ public void testPathFilterOneBucketMatchPartitionedTable() RETURNED_PATH_DOMAIN, Optional.of(new HiveBucketFilter(ImmutableSet.of(0, 1))), PARTITIONED_TABLE, - Optional.of(new HiveBucketHandle(BUCKET_COLUMN_HANDLES, BUCKET_COUNT))); + Optional.of(new HiveBucketHandle(BUCKET_COLUMN_HANDLES, BUCKET_COUNT, BUCKET_COUNT))); HiveSplitSource hiveSplitSource = hiveSplitSource(backgroundHiveSplitLoader, RETURNED_PATH_DOMAIN); backgroundHiveSplitLoader.start(hiveSplitSource); @@ -161,6 +161,7 @@ public void testPathFilterBucketedPartitionedTable() Optional.of( new HiveBucketHandle( getRegularColumnHandles(PARTITIONED_TABLE), + BUCKET_COUNT, BUCKET_COUNT))); HiveSplitSource hiveSplitSource = hiveSplitSource(backgroundHiveSplitLoader, RETURNED_PATH_DOMAIN); @@ -247,7 +248,7 @@ private static BackgroundHiveSplitLoader backgroundHiveSplitLoader( ImmutableMap.of())); ConnectorSession connectorSession = new TestingConnectorSession( - new HiveSessionProperties(new HiveClientConfig().setMaxSplitSize(new DataSize(1.0, GIGABYTE)), new OrcFileWriterConfig()).getSessionProperties()); + new HiveSessionProperties(new HiveClientConfig().setMaxSplitSize(new DataSize(1.0, GIGABYTE)), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); return new BackgroundHiveSplitLoader( table, @@ -266,7 +267,7 @@ private static BackgroundHiveSplitLoader backgroundHiveSplitLoader( private static BackgroundHiveSplitLoader backgroundHiveSplitLoaderOfflinePartitions() { ConnectorSession connectorSession = new TestingConnectorSession( - new HiveSessionProperties(new HiveClientConfig().setMaxSplitSize(new DataSize(1.0, GIGABYTE)), new OrcFileWriterConfig()).getSessionProperties()); + new HiveSessionProperties(new HiveClientConfig().setMaxSplitSize(new DataSize(1.0, GIGABYTE)), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); return new BackgroundHiveSplitLoader( SIMPLE_TABLE, diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestFileSystemCache.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestFileSystemCache.java new file mode 100644 index 0000000000000..177d72503a24e --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestFileSystemCache.java @@ -0,0 +1,56 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.hive.authentication.ImpersonatingHdfsAuthentication; +import com.facebook.presto.hive.authentication.SimpleHadoopAuthentication; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.Assert.assertNotSame; +import static org.testng.Assert.assertSame; + +public class TestFileSystemCache +{ + @Test + public void testFileSystemCache() + throws IOException + { + ImpersonatingHdfsAuthentication auth = new ImpersonatingHdfsAuthentication(new SimpleHadoopAuthentication()); + HdfsEnvironment environment = + new HdfsEnvironment( + new HiveHdfsConfiguration(new HdfsConfigurationUpdater(new HiveClientConfig())), + new HiveClientConfig(), + auth); + FileSystem fs1 = getFileSystem(environment, "user"); + FileSystem fs2 = getFileSystem(environment, "user"); + assertSame(fs1, fs2); + + FileSystem fs3 = getFileSystem(environment, "other_user"); + assertNotSame(fs1, fs3); + + FileSystem fs4 = getFileSystem(environment, "other_user"); + assertSame(fs3, fs4); + } + + private FileSystem getFileSystem(HdfsEnvironment environment, String user) + throws IOException + { + return environment.getFileSystem(user, new Path("/"), new Configuration(false)); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveClientConfig.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveClientConfig.java index 591b7269cef2c..9716be6208dfd 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveClientConfig.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveClientConfig.java @@ -69,8 +69,8 @@ public void testDefaults() .setVerifyChecksum(true) .setDomainSocketPath(null) .setS3FileSystemType(S3FileSystemType.PRESTO) - .setResourceConfigFiles((String) null) - .setHiveStorageFormat(HiveStorageFormat.RCBINARY) + .setResourceConfigFiles("") + .setHiveStorageFormat(HiveStorageFormat.ORC) .setHiveCompressionCodec(HiveCompressionCodec.GZIP) .setRespectTableFormat(true) .setImmutablePartitions(false) @@ -78,10 +78,10 @@ public void testDefaults() .setMaxPartitionsPerWriter(100) .setMaxOpenSortFiles(50) .setWriteValidationThreads(16) + .setTextMaxLineLength(new DataSize(100, Unit.MEGABYTE)) .setUseParquetColumnNames(false) + .setFailOnCorruptedParquetStatistics(true) .setUseOrcColumnNames(false) - .setParquetPredicatePushdownEnabled(true) - .setParquetOptimizedReaderEnabled(true) .setAssumeCanonicalPartitionKeys(false) .setOrcBloomFiltersEnabled(false) .setOrcDefaultBloomFilterFpp(0.05) @@ -93,8 +93,8 @@ public void testDefaults() .setOrcLazyReadSmallRanges(true) .setRcfileOptimizedWriterEnabled(true) .setRcfileWriterValidate(false) - .setOrcOptimizedWriterEnabled(false) - .setOrcWriterValidationPercentage(100.0) + .setOrcOptimizedWriterEnabled(true) + .setOrcWriterValidationPercentage(0.0) .setOrcWriterValidationMode(OrcWriteValidationMode.BOTH) .setHiveMetastoreAuthenticationType(HiveMetastoreAuthenticationType.NONE) .setHdfsAuthenticationType(HdfsAuthenticationType.NONE) @@ -103,11 +103,19 @@ public void testDefaults() .setBucketExecutionEnabled(true) .setFileSystemMaxCacheSize(1000) .setTableStatisticsEnabled(true) + .setOptimizeMismatchedBucketCount(false) .setWritesToNonManagedTablesEnabled(false) .setCreatesOfNonManagedTablesEnabled(true) .setHdfsWireEncryptionEnabled(false) .setPartitionStatisticsSampleSize(100) - .setCollectColumnStatisticsOnWrite(false)); + .setIgnoreCorruptedStatistics(false) + .setRecordingPath(null) + .setRecordingDuration(new Duration(0, TimeUnit.MINUTES)) + .setReplay(false) + .setCollectColumnStatisticsOnWrite(false) + .setCollectColumnStatisticsOnWrite(false) + .setS3SelectPushdownEnabled(false) + .setS3SelectPushdownMaxConnections(500)); } @Test @@ -154,10 +162,10 @@ public void testExplicitPropertyMappings() .put("hive.force-local-scheduling", "true") .put("hive.max-concurrent-file-renames", "100") .put("hive.assume-canonical-partition-keys", "true") + .put("hive.text.max-line-length", "13MB") .put("hive.parquet.use-column-names", "true") + .put("hive.parquet.fail-on-corrupted-statistics", "false") .put("hive.orc.use-column-names", "true") - .put("hive.parquet-predicate-pushdown.enabled", "false") - .put("hive.parquet-optimized-reader.enabled", "false") .put("hive.orc.bloom-filters.enabled", "true") .put("hive.orc.default-bloom-filter-fpp", "0.96") .put("hive.orc.max-merge-distance", "22kB") @@ -168,7 +176,7 @@ public void testExplicitPropertyMappings() .put("hive.orc.lazy-read-small-ranges", "false") .put("hive.rcfile-optimized-writer.enabled", "false") .put("hive.rcfile.writer.validate", "true") - .put("hive.orc.optimized-writer.enabled", "true") + .put("hive.orc.optimized-writer.enabled", "false") .put("hive.orc.writer.validation-percentage", "0.16") .put("hive.orc.writer.validation-mode", "DETAILED") .put("hive.metastore.authentication.type", "KERBEROS") @@ -179,11 +187,18 @@ public void testExplicitPropertyMappings() .put("hive.sorted-writing", "false") .put("hive.fs.cache.max-size", "1010") .put("hive.table-statistics-enabled", "false") + .put("hive.optimize-mismatched-bucket-count", "true") .put("hive.non-managed-table-writes-enabled", "true") .put("hive.non-managed-table-creates-enabled", "false") .put("hive.hdfs.wire-encryption.enabled", "true") .put("hive.partition-statistics-sample-size", "1234") + .put("hive.ignore-corrupted-statistics", "true") + .put("hive.metastore-recording-path", "/foo/bar") + .put("hive.metastore-recoding-duration", "42s") + .put("hive.replay-metastore-recording", "true") .put("hive.collect-column-statistics-on-write", "true") + .put("hive.s3select-pushdown.enabled", "true") + .put("hive.s3select-pushdown.max-connections", "1234") .build(); HiveClientConfig expected = new HiveClientConfig() @@ -226,10 +241,10 @@ public void testExplicitPropertyMappings() .setWriteValidationThreads(11) .setDomainSocketPath("/foo") .setS3FileSystemType(S3FileSystemType.EMRFS) + .setTextMaxLineLength(new DataSize(13, Unit.MEGABYTE)) .setUseParquetColumnNames(true) + .setFailOnCorruptedParquetStatistics(false) .setUseOrcColumnNames(true) - .setParquetPredicatePushdownEnabled(false) - .setParquetOptimizedReaderEnabled(false) .setAssumeCanonicalPartitionKeys(true) .setOrcBloomFiltersEnabled(true) .setOrcDefaultBloomFilterFpp(0.96) @@ -241,7 +256,7 @@ public void testExplicitPropertyMappings() .setOrcLazyReadSmallRanges(false) .setRcfileOptimizedWriterEnabled(false) .setRcfileWriterValidate(true) - .setOrcOptimizedWriterEnabled(true) + .setOrcOptimizedWriterEnabled(false) .setOrcWriterValidationPercentage(0.16) .setOrcWriterValidationMode(OrcWriteValidationMode.DETAILED) .setHiveMetastoreAuthenticationType(HiveMetastoreAuthenticationType.KERBEROS) @@ -252,11 +267,19 @@ public void testExplicitPropertyMappings() .setSortedWritingEnabled(false) .setFileSystemMaxCacheSize(1010) .setTableStatisticsEnabled(false) + .setOptimizeMismatchedBucketCount(true) .setWritesToNonManagedTablesEnabled(true) .setCreatesOfNonManagedTablesEnabled(false) .setHdfsWireEncryptionEnabled(true) .setPartitionStatisticsSampleSize(1234) - .setCollectColumnStatisticsOnWrite(true); + .setIgnoreCorruptedStatistics(true) + .setRecordingPath("/foo/bar") + .setRecordingDuration(new Duration(42, TimeUnit.SECONDS)) + .setReplay(true) + .setCollectColumnStatisticsOnWrite(true) + .setCollectColumnStatisticsOnWrite(true) + .setS3SelectPushdownEnabled(true) + .setS3SelectPushdownMaxConnections(1234); ConfigAssertions.assertFullMapping(properties, expected); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveConnectorFactory.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveConnectorFactory.java index 15a69d0bb80df..d300e25bf85f5 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveConnectorFactory.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveConnectorFactory.java @@ -23,6 +23,7 @@ import org.testng.annotations.Test; import java.util.Map; +import java.util.Optional; import static com.facebook.presto.spi.transaction.IsolationLevel.READ_UNCOMMITTED; import static io.airlift.testing.Assertions.assertContains; @@ -50,7 +51,7 @@ private static void assertCreateConnector(String metastoreUri) HiveConnectorFactory connectorFactory = new HiveConnectorFactory( "hive-test", HiveConnector.class.getClassLoader(), - null); + Optional.empty()); Map config = ImmutableMap.builder() .put("hive.metastore.uri", metastoreUri) diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveDistributedQueries.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveDistributedQueries.java index 73457826b18af..e098838c5a464 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveDistributedQueries.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveDistributedQueries.java @@ -13,10 +13,15 @@ */ package com.facebook.presto.hive; +import com.facebook.presto.testing.MaterializedResult; import com.facebook.presto.tests.AbstractTestDistributedQueries; +import org.testng.annotations.Test; import static com.facebook.presto.hive.HiveQueryRunner.createQueryRunner; +import static com.facebook.presto.sql.tree.ExplainType.Type.LOGICAL; +import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.tpch.TpchTable.getTables; +import static org.testng.Assert.assertEquals; public class TestHiveDistributedQueries extends AbstractTestDistributedQueries @@ -32,5 +37,13 @@ public void testDelete() // Hive connector currently does not support row-by-row delete } + @Test + public void testExplainOfCreateTableAs() + { + String query = "CREATE TABLE copy_orders AS SELECT * FROM orders"; + MaterializedResult result = computeActual("EXPLAIN " + query); + assertEquals(getOnlyElement(result.getOnlyColumnAsSet()), getExplainPlan(query, LOGICAL)); + } + // Hive specific tests should normally go in TestHiveIntegrationSmokeTest } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveFileFormats.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveFileFormats.java index 14bb386c75c86..37b47ad6fd6b5 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveFileFormats.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveFileFormats.java @@ -16,7 +16,6 @@ import com.facebook.presto.hive.orc.DwrfPageSourceFactory; import com.facebook.presto.hive.orc.OrcPageSourceFactory; import com.facebook.presto.hive.parquet.ParquetPageSourceFactory; -import com.facebook.presto.hive.parquet.ParquetRecordCursorProvider; import com.facebook.presto.hive.rcfile.RcFilePageSourceFactory; import com.facebook.presto.orc.OrcWriterOptions; import com.facebook.presto.spi.ConnectorPageSource; @@ -25,8 +24,6 @@ import com.facebook.presto.spi.RecordCursor; import com.facebook.presto.spi.RecordPageSource; import com.facebook.presto.spi.predicate.TupleDomain; -import com.facebook.presto.spi.type.ArrayType; -import com.facebook.presto.spi.type.RowType; import com.facebook.presto.testing.TestingConnectorSession; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; @@ -36,7 +33,6 @@ import io.airlift.compress.lzo.LzoCodec; import io.airlift.compress.lzo.LzopCodec; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.Path; import org.apache.hadoop.hive.common.type.HiveVarchar; import org.apache.hadoop.hive.serde2.objectinspector.ListObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.MapObjectInspector; @@ -74,10 +70,6 @@ import static com.facebook.presto.hive.HiveTestUtils.SESSION; import static com.facebook.presto.hive.HiveTestUtils.TYPE_MANAGER; import static com.facebook.presto.hive.HiveTestUtils.getTypes; -import static com.facebook.presto.spi.type.IntegerType.INTEGER; -import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; -import static com.facebook.presto.tests.StructuralTestUtil.arrayBlockOf; -import static com.facebook.presto.tests.StructuralTestUtil.rowBlockOf; import static com.google.common.base.Predicates.not; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.filter; @@ -87,11 +79,7 @@ import static java.util.stream.Collectors.toList; import static org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.FILE_INPUT_FORMAT; import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_LIB; -import static org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory.getStandardListObjectInspector; -import static org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory.getStandardStructObjectInspector; import static org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory.getPrimitiveJavaObjectInspector; -import static org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory.javaIntObjectInspector; -import static org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory.javaStringObjectInspector; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; @@ -101,15 +89,10 @@ public class TestHiveFileFormats extends AbstractTestHiveFileFormats { private static final FileFormatDataSourceStats STATS = new FileFormatDataSourceStats(); - private static TestingConnectorSession parquetCursorSession = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(false, false, false), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetCursorSessionUseName = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(false, false, true), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetCursorPushdownSession = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(false, true, false), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetCursorPushdownSessionUseName = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(false, true, true), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetPageSourceSession = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(true, false, false), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetPageSourceSessionUseName = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(true, false, true), new OrcFileWriterConfig()).getSessionProperties()); - private static TestingConnectorSession parquetPageSourcePushdown = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(true, true, false), new OrcFileWriterConfig()).getSessionProperties()); + private static TestingConnectorSession parquetPageSourceSession = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(false), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); + private static TestingConnectorSession parquetPageSourceSessionUseName = new TestingConnectorSession(new HiveSessionProperties(createParquetHiveClientConfig(true), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); - private static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("Asia/Katmandu"); + private static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("America/Bahia_Banderas"); @DataProvider(name = "rowCount") public static Object[][] rowCountProvider() @@ -122,8 +105,8 @@ public void setUp() { // ensure the expected timezone is configured for this VM assertEquals(TimeZone.getDefault().getID(), - "Asia/Katmandu", - "Timezone not configured correctly. Add -Duser.timezone=Asia/Katmandu to your JVM arguments"); + "America/Bahia_Banderas", + "Timezone not configured correctly. Add -Duser.timezone=America/Bahia_Banderas to your JVM arguments"); } @Test(dataProvider = "rowCount") @@ -206,7 +189,7 @@ public void testRcTextOptimizedWriter(int rowCount) .collect(toImmutableList()); TestingConnectorSession session = new TestingConnectorSession( - new HiveSessionProperties(new HiveClientConfig().setRcfileOptimizedWriterEnabled(true), new OrcFileWriterConfig()).getSessionProperties()); + new HiveSessionProperties(new HiveClientConfig().setRcfileOptimizedWriterEnabled(true), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); assertThatFileFormat(RCTEXT) .withColumns(testColumns) @@ -260,7 +243,7 @@ public void testRcBinaryOptimizedWriter(int rowCount) .collect(toList()); TestingConnectorSession session = new TestingConnectorSession( - new HiveSessionProperties(new HiveClientConfig().setRcfileOptimizedWriterEnabled(true), new OrcFileWriterConfig()).getSessionProperties()); + new HiveSessionProperties(new HiveClientConfig().setRcfileOptimizedWriterEnabled(true), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); assertThatFileFormat(RCBINARY) .withColumns(testColumns) @@ -290,7 +273,8 @@ public void testOrcOptimizedWriter(int rowCount) new HiveClientConfig() .setOrcOptimizedWriterEnabled(true) .setOrcWriterValidationPercentage(100.0), - new OrcFileWriterConfig()).getSessionProperties()); + new OrcFileWriterConfig(), + new ParquetFileWriterConfig()).getSessionProperties()); // A Presto page can not contain a map with null keys, so a page based writer can not write null keys List testColumns = TEST_COLUMNS.stream() @@ -310,7 +294,7 @@ public void testOrcOptimizedWriter(int rowCount) public void testOrcUseColumnNames(int rowCount) throws Exception { - TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig()).getSessionProperties()); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); assertThatFileFormat(ORC) .withWriteColumns(TEST_COLUMNS) @@ -340,43 +324,6 @@ private static List getTestColumnsSupportedByAvro() .collect(toList()); } - @Test(dataProvider = "rowCount") - public void testParquet(int rowCount) - throws Exception - { - List testColumns = getTestColumnsSupportedByParquet(); - assertThatFileFormat(PARQUET) - .withColumns(testColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorSession) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withColumns(testColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorPushdownSession) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - } - - @Test(dataProvider = "rowCount") - public void testParquetCaseInsensitiveColumnLookup(int rowCount) - throws Exception - { - List writeColumns = ImmutableList.of(new TestColumn("column_name", javaStringObjectInspector, "test", utf8Slice("test"), false)); - List readColumns = ImmutableList.of(new TestColumn("Column_Name", javaStringObjectInspector, "test", utf8Slice("test"), false)); - assertThatFileFormat(PARQUET) - .withWriteColumns(writeColumns) - .withReadColumns(readColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorSessionUseName) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withWriteColumns(writeColumns) - .withReadColumns(readColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorPushdownSessionUseName) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - } - @Test(dataProvider = "rowCount") public void testParquetPageSource(int rowCount) throws Exception @@ -387,11 +334,6 @@ public void testParquetPageSource(int rowCount) .withSession(parquetPageSourceSession) .withRowsCount(rowCount) .isReadableByPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withColumns(testColumns) - .withSession(parquetPageSourcePushdown) - .withRowsCount(rowCount) - .isReadableByPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS)); } @Test(dataProvider = "rowCount") @@ -425,26 +367,6 @@ public void testParquetPageSourceSchemaEvolution(int rowCount) .isReadableByPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS)); } - @Test(dataProvider = "rowCount") - public void testParquetUseColumnNames(int rowCount) - throws Exception - { - List writeColumns = getTestColumnsSupportedByParquet(); - List readColumns = Lists.reverse(writeColumns); - assertThatFileFormat(PARQUET) - .withWriteColumns(writeColumns) - .withReadColumns(readColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorSessionUseName) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withWriteColumns(writeColumns) - .withReadColumns(readColumns) - .withRowsCount(rowCount) - .withSession(parquetCursorPushdownSessionUseName) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - } - private static List getTestColumnsSupportedByParquet() { // Write of complex hive data to Parquet is broken @@ -460,43 +382,6 @@ private static List getTestColumnsSupportedByParquet() .collect(toList()); } - @Test(dataProvider = "rowCount") - public void testParquetThrift(int rowCount) - { - RowType nameType = RowType.anonymous(ImmutableList.of(createUnboundedVarcharType(), createUnboundedVarcharType())); - RowType phoneType = RowType.anonymous(ImmutableList.of(createUnboundedVarcharType(), createUnboundedVarcharType())); - RowType personType = RowType.anonymous(ImmutableList.of(nameType, INTEGER, createUnboundedVarcharType(), new ArrayType(phoneType))); - - List testColumns = ImmutableList.of( - new TestColumn( - "persons", - getStandardListObjectInspector( - getStandardStructObjectInspector( - ImmutableList.of("name", "id", "email", "phones"), - ImmutableList.of( - getStandardStructObjectInspector( - ImmutableList.of("first_name", "last_name"), - ImmutableList.of(javaStringObjectInspector, javaStringObjectInspector)), - javaIntObjectInspector, - javaStringObjectInspector, - getStandardListObjectInspector( - getStandardStructObjectInspector( - ImmutableList.of("number", "type"), - ImmutableList.of(javaStringObjectInspector, javaStringObjectInspector)))))), - null, - arrayBlockOf(personType, - rowBlockOf(ImmutableList.of(nameType, INTEGER, createUnboundedVarcharType(), new ArrayType(phoneType)), - rowBlockOf(ImmutableList.of(createUnboundedVarcharType(), createUnboundedVarcharType()), "Bob", "Roberts"), - 0, - "bob.roberts@example.com", - arrayBlockOf(phoneType, rowBlockOf(ImmutableList.of(createUnboundedVarcharType(), createUnboundedVarcharType()), "1234567890", null)))))); - - File file = new File(this.getClass().getClassLoader().getResource("addressbook.parquet").getPath()); - FileSplit split = new FileSplit(new Path(file.getAbsolutePath()), 0, file.length(), new String[0]); - HiveRecordCursorProvider cursorProvider = new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS); - testCursorProvider(cursorProvider, split, PARQUET, testColumns, SESSION, 1); - } - @Test(dataProvider = "rowCount") public void testDwrf(int rowCount) throws Exception @@ -519,7 +404,8 @@ public void testDwrfOptimizedWriter(int rowCount) new HiveClientConfig() .setOrcOptimizedWriterEnabled(true) .setOrcWriterValidationPercentage(100.0), - new OrcFileWriterConfig()).getSessionProperties()); + new OrcFileWriterConfig(), + new ParquetFileWriterConfig()).getSessionProperties()); // DWRF does not support modern Hive types // A Presto page can not contain a map with null keys, so a page based writer can not write null keys @@ -561,27 +447,11 @@ public void testTruncateVarcharColumn() .withReadColumns(ImmutableList.of(readColumn)) .isReadableByPageSource(new OrcPageSourceFactory(TYPE_MANAGER, false, HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withWriteColumns(ImmutableList.of(writeColumn)) - .withReadColumns(ImmutableList.of(readColumn)) - .withSession(parquetCursorSession) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withWriteColumns(ImmutableList.of(writeColumn)) - .withReadColumns(ImmutableList.of(readColumn)) - .withSession(parquetCursorPushdownSession) - .isReadableByRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) .withWriteColumns(ImmutableList.of(writeColumn)) .withReadColumns(ImmutableList.of(readColumn)) .withSession(parquetPageSourceSession) .isReadableByPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS)); - assertThatFileFormat(PARQUET) - .withWriteColumns(ImmutableList.of(writeColumn)) - .withReadColumns(ImmutableList.of(readColumn)) - .withSession(parquetPageSourcePushdown) - .isReadableByPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS)); assertThatFileFormat(AVRO) .withWriteColumns(ImmutableList.of(writeColumn)) @@ -625,23 +495,10 @@ public void testFailForLongVarcharPartitionColumn() .withColumns(columns) .isFailingForPageSource(new OrcPageSourceFactory(TYPE_MANAGER, false, HDFS_ENVIRONMENT, STATS), expectedErrorCode, expectedMessage); - assertThatFileFormat(PARQUET) - .withColumns(columns) - .withSession(parquetCursorSession) - .isFailingForRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS), expectedErrorCode, expectedMessage); - assertThatFileFormat(PARQUET) - .withColumns(columns) - .withSession(parquetCursorPushdownSession) - .isFailingForRecordCursor(new ParquetRecordCursorProvider(HDFS_ENVIRONMENT, STATS), expectedErrorCode, expectedMessage); - assertThatFileFormat(PARQUET) .withColumns(columns) .withSession(parquetPageSourceSession) .isFailingForPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS), expectedErrorCode, expectedMessage); - assertThatFileFormat(PARQUET) - .withColumns(columns) - .withSession(parquetPageSourcePushdown) - .isFailingForPageSource(new ParquetPageSourceFactory(TYPE_MANAGER, HDFS_ENVIRONMENT, STATS), expectedErrorCode, expectedMessage); assertThatFileFormat(SEQUENCEFILE) .withColumns(columns) @@ -689,7 +546,8 @@ private void testCursorProvider(HiveRecordCursorProvider cursorProvider, DateTimeZone.getDefault(), TYPE_MANAGER, ImmutableMap.of(), - Optional.empty()); + Optional.empty(), + false); RecordCursor cursor = ((RecordPageSource) pageSource.get()).getCursor(); @@ -734,7 +592,8 @@ private void testPageSourceFactory(HivePageSourceFactory sourceFactory, DateTimeZone.getDefault(), TYPE_MANAGER, ImmutableMap.of(), - Optional.empty()); + Optional.empty(), + false); assertTrue(pageSource.isPresent()); @@ -787,12 +646,10 @@ private FileFormatAssertion assertThatFileFormat(HiveStorageFormat hiveStorageFo .withStorageFormat(hiveStorageFormat); } - private static HiveClientConfig createParquetHiveClientConfig(boolean enableOptimizedReader, boolean enablePredicatePushDown, boolean useParquetColumnNames) + private static HiveClientConfig createParquetHiveClientConfig(boolean useParquetColumnNames) { HiveClientConfig config = new HiveClientConfig(); - config.setParquetOptimizedReaderEnabled(enableOptimizedReader) - .setParquetPredicatePushdownEnabled(enablePredicatePushDown) - .setUseParquetColumnNames(useParquetColumnNames); + config.setUseParquetColumnNames(useParquetColumnNames); return config; } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveIntegrationSmokeTest.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveIntegrationSmokeTest.java index aab1b94b654af..8d7c9fb8243ae 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveIntegrationSmokeTest.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveIntegrationSmokeTest.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.hive.HiveSessionProperties.InsertExistingPartitionsBehavior; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; @@ -29,6 +30,8 @@ import com.facebook.presto.spi.security.Identity; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.sql.planner.Plan; +import com.facebook.presto.sql.planner.plan.ExchangeNode; import com.facebook.presto.sql.planner.planPrinter.IOPlanPrinter.ColumnConstraint; import com.facebook.presto.sql.planner.planPrinter.IOPlanPrinter.FormattedDomain; import com.facebook.presto.sql.planner.planPrinter.IOPlanPrinter.FormattedMarker; @@ -54,11 +57,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.LongStream; import static com.facebook.presto.SystemSessionProperties.COLOCATED_JOIN; import static com.facebook.presto.SystemSessionProperties.CONCURRENT_LIFESPANS_PER_NODE; +import static com.facebook.presto.SystemSessionProperties.DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION; import static com.facebook.presto.SystemSessionProperties.GROUPED_EXECUTION_FOR_AGGREGATION; import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; import static com.facebook.presto.hive.HiveColumnHandle.BUCKET_COLUMN_NAME; @@ -75,8 +81,6 @@ import static com.facebook.presto.hive.HiveTableProperties.STORAGE_FORMAT_PROPERTY; import static com.facebook.presto.hive.HiveTestUtils.TYPE_MANAGER; import static com.facebook.presto.hive.HiveUtil.columnExtraInfo; -import static com.facebook.presto.spi.predicate.Marker.Bound.ABOVE; -import static com.facebook.presto.spi.predicate.Marker.Bound.BELOW; import static com.facebook.presto.spi.predicate.Marker.Bound.EXACTLY; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.CharType.createCharType; @@ -88,13 +92,17 @@ import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; import static com.facebook.presto.spi.type.VarcharType.createVarcharType; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.BROADCAST; +import static com.facebook.presto.sql.planner.optimizations.PlanNodeSearcher.searchFrom; +import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textLogicalPlan; import static com.facebook.presto.testing.MaterializedResult.resultBuilder; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.testing.assertions.Assert.assertEquals; import static com.facebook.presto.tests.QueryAssertions.assertEqualsIgnoreOrder; import static com.facebook.presto.transaction.TransactionBuilder.transaction; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.io.Files.asCharSink; import static com.google.common.io.Files.createTempDir; import static com.google.common.io.MoreFiles.deleteRecursively; import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE; @@ -157,34 +165,51 @@ public void testSchemaOperations() @Test public void testIOExplain() { - computeActual("CREATE TABLE test_orders WITH (partitioned_by = ARRAY['orderkey']) AS select custkey, orderkey FROM orders where orderkey < 10"); - - MaterializedResult result = computeActual("EXPLAIN (TYPE IO, FORMAT JSON) INSERT INTO test_orders SELECT custkey, orderkey FROM orders where orderkey > 5 AND custkey <= 10"); - TableColumnInfo input = new TableColumnInfo( - new CatalogSchemaTableName(catalog, "tpch", "orders"), - ImmutableSet.of( - new ColumnConstraint( - "custkey", - BIGINT.getTypeSignature(), - new FormattedDomain( - false, - ImmutableSet.of( - new FormattedRange( - new FormattedMarker(Optional.empty(), ABOVE), - new FormattedMarker(Optional.of("10"), EXACTLY))))), - new ColumnConstraint( - "orderkey", - BIGINT.getTypeSignature(), - new FormattedDomain( - false, - ImmutableSet.of( - new FormattedRange( - new FormattedMarker(Optional.of("5"), ABOVE), - new FormattedMarker(Optional.empty(), BELOW))))))); + // Test IO explain with small number of discrete components. + computeActual("CREATE TABLE test_orders WITH (partitioned_by = ARRAY['orderkey']) AS select custkey, orderkey FROM orders where orderkey < 3"); + + MaterializedResult result = computeActual("EXPLAIN (TYPE IO, FORMAT JSON) INSERT INTO test_orders SELECT custkey, orderkey FROM test_orders where custkey <= 10"); assertEquals( jsonCodec(IOPlan.class).fromJson((String) getOnlyElement(result.getOnlyColumnAsSet())), new IOPlan( - ImmutableSet.of(input), + ImmutableSet.of(new TableColumnInfo( + new CatalogSchemaTableName(catalog, "tpch", "test_orders"), + ImmutableSet.of( + new ColumnConstraint( + "orderkey", + BIGINT.getTypeSignature(), + new FormattedDomain( + false, + ImmutableSet.of( + new FormattedRange( + new FormattedMarker(Optional.of("1"), EXACTLY), + new FormattedMarker(Optional.of("1"), EXACTLY)), + new FormattedRange( + new FormattedMarker(Optional.of("2"), EXACTLY), + new FormattedMarker(Optional.of("2"), EXACTLY)))))))), + Optional.of(new CatalogSchemaTableName(catalog, "tpch", "test_orders")))); + + assertUpdate("DROP TABLE test_orders"); + + // Test IO explain with large number of discrete components where Domain::simpify comes into play. + computeActual("CREATE TABLE test_orders WITH (partitioned_by = ARRAY['orderkey']) AS select custkey, orderkey FROM orders where orderkey < 200"); + + result = computeActual("EXPLAIN (TYPE IO, FORMAT JSON) INSERT INTO test_orders SELECT custkey, orderkey + 10 FROM test_orders where custkey <= 10"); + assertEquals( + jsonCodec(IOPlan.class).fromJson((String) getOnlyElement(result.getOnlyColumnAsSet())), + new IOPlan( + ImmutableSet.of(new TableColumnInfo( + new CatalogSchemaTableName(catalog, "tpch", "test_orders"), + ImmutableSet.of( + new ColumnConstraint( + "orderkey", + BIGINT.getTypeSignature(), + new FormattedDomain( + false, + ImmutableSet.of( + new FormattedRange( + new FormattedMarker(Optional.of("1"), EXACTLY), + new FormattedMarker(Optional.of("199"), EXACTLY)))))))), Optional.of(new CatalogSchemaTableName(catalog, "tpch", "test_orders")))); assertUpdate("DROP TABLE test_orders"); @@ -230,17 +255,17 @@ public void createTableWithEveryType() } @Test - public void createPartitionedTable() + public void testCreatePartitionedTable() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - if (insertOperationsSupported(storageFormat.getFormat())) { - createPartitionedTable(storageFormat.getSession(), storageFormat.getFormat()); - } - } + testWithAllStorageFormats(this::testCreatePartitionedTable); } - private void createPartitionedTable(Session session, HiveStorageFormat storageFormat) + private void testCreatePartitionedTable(Session session, HiveStorageFormat storageFormat) { + if (!insertOperationsSupported(storageFormat)) { + return; + } + @Language("SQL") String createTable = "" + "CREATE TABLE test_partitioned_table (" + " _string VARCHAR" + @@ -478,17 +503,17 @@ private void createTableLike(String likeSuffix, boolean hasPartition) } @Test - public void createTableAs() + public void testCreateTableAs() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - if (insertOperationsSupported(storageFormat.getFormat())) { - createTableAs(storageFormat.getSession(), storageFormat.getFormat()); - } - } + testWithAllStorageFormats(this::testCreateTableAs); } - private void createTableAs(Session session, HiveStorageFormat storageFormat) + private void testCreateTableAs(Session session, HiveStorageFormat storageFormat) { + if (!insertOperationsSupported(storageFormat)) { + return; + } + @Language("SQL") String select = "SELECT" + " 'foo' _varchar" + ", CAST('bar' AS CHAR(10)) _char" + @@ -528,14 +553,12 @@ private void createTableAs(Session session, HiveStorageFormat storageFormat) } @Test - public void createPartitionedTableAs() + public void testCreatePartitionedTableAs() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - createPartitionedTableAs(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testCreatePartitionedTableAs); } - private void createPartitionedTableAs(Session session, HiveStorageFormat storageFormat) + private void testCreatePartitionedTableAs(Session session, HiveStorageFormat storageFormat) { @Language("SQL") String createTable = "" + "CREATE TABLE test_create_partitioned_table_as " + @@ -630,9 +653,7 @@ public void testCreateTableNonSupportedVarcharColumn() public void testCreatePartitionedBucketedTableAsFewRows() { // go through all storage formats to make sure the empty buckets are correctly created - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - testCreatePartitionedBucketedTableAsFewRows(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testCreatePartitionedBucketedTableAsFewRows); } private void testCreatePartitionedBucketedTableAsFewRows(Session session, HiveStorageFormat storageFormat) @@ -833,9 +854,7 @@ public void testCreatePartitionedUnionAll() public void testInsertPartitionedBucketedTableFewRows() { // go through all storage formats to make sure the empty buckets are correctly created - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - testInsertPartitionedBucketedTableFewRows(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testInsertPartitionedBucketedTableFewRows); } private void testInsertPartitionedBucketedTableFewRows(Session session, HiveStorageFormat storageFormat) @@ -918,6 +937,34 @@ public void testCastNullToColumnTypes() assertUpdate("DROP TABLE " + tableName); } + @Test + public void testCreateEmptyBucketedPartition() + { + for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { + testCreateEmptyBucketedPartition(storageFormat.getFormat()); + } + } + + public void testCreateEmptyBucketedPartition(HiveStorageFormat storageFormat) + { + String tableName = "test_insert_empty_partitioned_bucketed_table"; + createPartitionedBucketedTable(tableName, storageFormat); + + List orderStatusList = ImmutableList.of("F", "O", "P"); + for (int i = 0; i < orderStatusList.size(); i++) { + String sql = format("CALL system.create_empty_partition('%s', '%s', ARRAY['orderstatus'], ARRAY['%s'])", TPCH_SCHEMA, tableName, orderStatusList.get(i)); + assertUpdate(sql); + assertQuery( + format("SELECT count(*) FROM \"%s$partitions\"", tableName), + "SELECT " + (i + 1)); + + assertQueryFails(sql, "Partition already exists.*"); + } + + assertUpdate("DROP TABLE " + tableName); + assertFalse(getQueryRunner().tableExists(getSession(), tableName)); + } + @Test public void testInsertPartitionedBucketedTable() { @@ -927,20 +974,9 @@ public void testInsertPartitionedBucketedTable() private void testInsertPartitionedBucketedTable(HiveStorageFormat storageFormat) { String tableName = "test_insert_partitioned_bucketed_table"; + createPartitionedBucketedTable(tableName, storageFormat); - assertUpdate("" + - "CREATE TABLE " + tableName + " (" + - " custkey bigint," + - " custkey2 bigint," + - " comment varchar," + - " orderstatus varchar)" + - "WITH (" + - "format = '" + storageFormat + "', " + - "partitioned_by = ARRAY[ 'orderstatus' ], " + - "bucketed_by = ARRAY[ 'custkey', 'custkey2' ], " + - "bucket_count = 11)"); - - ImmutableList orderStatusList = ImmutableList.of("F", "O", "P"); + List orderStatusList = ImmutableList.of("F", "O", "P"); for (int i = 0; i < orderStatusList.size(); i++) { String orderStatus = orderStatusList.get(i); assertUpdate( @@ -961,6 +997,21 @@ private void testInsertPartitionedBucketedTable(HiveStorageFormat storageFormat) assertFalse(getQueryRunner().tableExists(getSession(), tableName)); } + private void createPartitionedBucketedTable(String tableName, HiveStorageFormat storageFormat) + { + assertUpdate("" + + "CREATE TABLE " + tableName + " (" + + " custkey bigint," + + " custkey2 bigint," + + " comment varchar," + + " orderstatus varchar)" + + "WITH (" + + "format = '" + storageFormat + "', " + + "partitioned_by = ARRAY[ 'orderstatus' ], " + + "bucketed_by = ARRAY[ 'custkey', 'custkey2' ], " + + "bucket_count = 11)"); + } + @Test public void testInsertPartitionedBucketedTableWithUnionAll() { @@ -983,7 +1034,7 @@ private void testInsertPartitionedBucketedTableWithUnionAll(HiveStorageFormat st "bucketed_by = ARRAY[ 'custkey', 'custkey2' ], " + "bucket_count = 11)"); - ImmutableList orderStatusList = ImmutableList.of("F", "O", "P"); + List orderStatusList = ImmutableList.of("F", "O", "P"); for (int i = 0; i < orderStatusList.size(); i++) { String orderStatus = orderStatusList.get(i); assertUpdate( @@ -1009,17 +1060,16 @@ private void testInsertPartitionedBucketedTableWithUnionAll(HiveStorageFormat st } @Test - public void insertTable() + public void testInsert() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - if (insertOperationsSupported(storageFormat.getFormat())) { - insertTable(storageFormat.getSession(), storageFormat.getFormat()); - } - } + testWithAllStorageFormats(this::testInsert); } - private void insertTable(Session session, HiveStorageFormat storageFormat) + private void testInsert(Session session, HiveStorageFormat storageFormat) { + if (!insertOperationsSupported(storageFormat)) { + return; + } @Language("SQL") String createTable = "" + "CREATE TABLE test_insert_format_table " + "(" + @@ -1095,14 +1145,12 @@ private void insertTable(Session session, HiveStorageFormat storageFormat) } @Test - public void insertPartitionedTable() + public void testInsertPartitionedTable() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - insertPartitionedTable(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testInsertPartitionedTable); } - private void insertPartitionedTable(Session session, HiveStorageFormat storageFormat) + private void testInsertPartitionedTable(Session session, HiveStorageFormat storageFormat) { @Language("SQL") String createTable = "" + "CREATE TABLE test_insert_partitioned_table " + @@ -1170,9 +1218,7 @@ private void insertPartitionedTable(Session session, HiveStorageFormat storageFo @Test public void testInsertPartitionedTableExistingPartition() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - testInsertPartitionedTableExistingPartition(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testInsertPartitionedTableExistingPartition); } private void testInsertPartitionedTableExistingPartition(Session session, HiveStorageFormat storageFormat) @@ -1304,7 +1350,7 @@ public void testNullPartitionValues() public void testPartitionPerScanLimit() { TestingHiveStorageFormat storageFormat = new TestingHiveStorageFormat(getSession(), HiveStorageFormat.DWRF); - testPartitionPerScanLimit(storageFormat.getSession(), storageFormat.getFormat()); + testWithStorageFormat(storageFormat, this::testPartitionPerScanLimit); } private void testPartitionPerScanLimit(Session session, HiveStorageFormat storageFormat) @@ -1458,9 +1504,7 @@ public void testPartitionsTableInvalidAccess() @Test public void testInsertUnpartitionedTable() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - testInsertUnpartitionedTable(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testInsertUnpartitionedTable); } private void testInsertUnpartitionedTable(Session session, HiveStorageFormat storageFormat) @@ -1605,7 +1649,7 @@ private List getPartitions(String tableName) private int getBucketCount(String tableName) { - return (int) getHiveTableProperty(tableName, (HiveTableLayoutHandle table) -> table.getBucketHandle().get().getBucketCount()); + return (int) getHiveTableProperty(tableName, (HiveTableLayoutHandle table) -> table.getBucketHandle().get().getTableBucketCount()); } @Test @@ -1894,12 +1938,10 @@ public void testCreateExternalTable() @Test public void testPathHiddenColumn() { - for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { - doTestPathHiddenColumn(storageFormat.getSession(), storageFormat.getFormat()); - } + testWithAllStorageFormats(this::testPathHiddenColumn); } - private void doTestPathHiddenColumn(Session session, HiveStorageFormat storageFormat) + private void testPathHiddenColumn(Session session, HiveStorageFormat storageFormat) { @Language("SQL") String createTable = "CREATE TABLE test_path " + "WITH (" + @@ -2227,6 +2269,78 @@ public void testPredicatePushDownToTableScan() } } + @Test + public void testMismatchedBucketing() + { + try { + assertUpdate( + "CREATE TABLE test_mismatch_bucketing16\n" + + "WITH (bucket_count = 16, bucketed_by = ARRAY['key16']) AS\n" + + "SELECT orderkey key16, comment value16 FROM orders", + 15000); + assertUpdate( + "CREATE TABLE test_mismatch_bucketing32\n" + + "WITH (bucket_count = 32, bucketed_by = ARRAY['key32']) AS\n" + + "SELECT orderkey key32, comment value32 FROM orders", + 15000); + assertUpdate( + "CREATE TABLE test_mismatch_bucketingN AS\n" + + "SELECT orderkey keyN, comment valueN FROM orders", + 15000); + + Session withMismatchOptimization = Session.builder(getSession()) + .setSystemProperty(COLOCATED_JOIN, "true") + .setCatalogSessionProperty(catalog, "optimize_mismatched_bucket_count", "true") + .build(); + Session withoutMismatchOptimization = Session.builder(getSession()) + .setSystemProperty(COLOCATED_JOIN, "true") + .setCatalogSessionProperty(catalog, "optimize_mismatched_bucket_count", "false") + .build(); + + @Language("SQL") String writeToTableWithMoreBuckets = "CREATE TABLE test_mismatch_bucketing_out32\n" + + "WITH (bucket_count = 32, bucketed_by = ARRAY['key16'])\n" + + "AS\n" + + "SELECT key16, value16, key32, value32, keyN, valueN\n" + + "FROM\n" + + " test_mismatch_bucketing16\n" + + "JOIN\n" + + " test_mismatch_bucketing32\n" + + "ON key16=key32\n" + + "JOIN\n" + + " test_mismatch_bucketingN\n" + + "ON key16=keyN"; + @Language("SQL") String writeToTableWithFewerBuckets = "CREATE TABLE test_mismatch_bucketing_out8\n" + + "WITH (bucket_count = 8, bucketed_by = ARRAY['key16'])\n" + + "AS\n" + + "SELECT key16, value16, key32, value32, keyN, valueN\n" + + "FROM\n" + + " test_mismatch_bucketing16\n" + + "JOIN\n" + + " test_mismatch_bucketing32\n" + + "ON key16=key32\n" + + "JOIN\n" + + " test_mismatch_bucketingN\n" + + "ON key16=keyN"; + + assertUpdate(withoutMismatchOptimization, writeToTableWithMoreBuckets, 15000, assertRemoteExchangesCount(4)); + assertQuery("SELECT * FROM test_mismatch_bucketing_out32", "SELECT orderkey, comment, orderkey, comment, orderkey, comment from orders"); + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketing_out32"); + + assertUpdate(withMismatchOptimization, writeToTableWithMoreBuckets, 15000, assertRemoteExchangesCount(2)); + assertQuery("SELECT * FROM test_mismatch_bucketing_out32", "SELECT orderkey, comment, orderkey, comment, orderkey, comment from orders"); + + assertUpdate(withMismatchOptimization, writeToTableWithFewerBuckets, 15000, assertRemoteExchangesCount(2)); + assertQuery("SELECT * FROM test_mismatch_bucketing_out8", "SELECT orderkey, comment, orderkey, comment, orderkey, comment from orders"); + } + finally { + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketing16"); + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketing32"); + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketingN"); + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketing_out32"); + assertUpdate("DROP TABLE IF EXISTS test_mismatch_bucketing_out8"); + } + } + @Test public void testGroupedExecution() { @@ -2261,17 +2375,33 @@ public void testGroupedExecution() .setSystemProperty(COLOCATED_JOIN, "false") .setSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, "false") .build(); - // Co-located JOIN with all groups at once + // Co-located JOIN with all groups at once, fixed schedule Session colocatedAllGroupsAtOnce = Session.builder(getSession()) .setSystemProperty(COLOCATED_JOIN, "true") .setSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, "true") .setSystemProperty(CONCURRENT_LIFESPANS_PER_NODE, "0") + .setSystemProperty(DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, "false") .build(); - // Co-located JOIN, 1 group per worker at a time + // Co-located JOIN, 1 group per worker at a time, fixed schedule Session colocatedOneGroupAtATime = Session.builder(getSession()) .setSystemProperty(COLOCATED_JOIN, "true") .setSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, "true") .setSystemProperty(CONCURRENT_LIFESPANS_PER_NODE, "1") + .setSystemProperty(DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, "false") + .build(); + // Co-located JOIN with all groups at once, dynamic schedule + Session colocatedAllGroupsAtOnceDynamic = Session.builder(getSession()) + .setSystemProperty(COLOCATED_JOIN, "true") + .setSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, "true") + .setSystemProperty(CONCURRENT_LIFESPANS_PER_NODE, "0") + .setSystemProperty(DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, "true") + .build(); + // Co-located JOIN, 1 group per worker at a time, dynamic schedule + Session colocatedOneGroupAtATimeDynamic = Session.builder(getSession()) + .setSystemProperty(COLOCATED_JOIN, "true") + .setSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, "true") + .setSystemProperty(CONCURRENT_LIFESPANS_PER_NODE, "1") + .setSystemProperty(DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, "true") .build(); // Broadcast JOIN, 1 group per worker at a time Session broadcastOneGroupAtATime = Session.builder(getSession()) @@ -2316,15 +2446,23 @@ public void testGroupedExecution() assertQuery(notColocated, leftJoinBucketedTable, expectedOuterJoinQuery); assertQuery(notColocated, rightJoinBucketedTable, expectedOuterJoinQuery); - assertQuery(colocatedAllGroupsAtOnce, joinThreeBucketedTable, expectedJoinQuery); - assertQuery(colocatedAllGroupsAtOnce, joinThreeMixedTable, expectedJoinQuery); - assertQuery(colocatedOneGroupAtATime, joinThreeBucketedTable, expectedJoinQuery); - assertQuery(colocatedOneGroupAtATime, joinThreeMixedTable, expectedJoinQuery); - - assertQuery(colocatedAllGroupsAtOnce, leftJoinBucketedTable, expectedOuterJoinQuery); - assertQuery(colocatedAllGroupsAtOnce, rightJoinBucketedTable, expectedOuterJoinQuery); - assertQuery(colocatedOneGroupAtATime, leftJoinBucketedTable, expectedOuterJoinQuery); - assertQuery(colocatedOneGroupAtATime, rightJoinBucketedTable, expectedOuterJoinQuery); + assertQuery(colocatedAllGroupsAtOnce, joinThreeBucketedTable, expectedJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnce, joinThreeMixedTable, expectedJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, joinThreeBucketedTable, expectedJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, joinThreeMixedTable, expectedJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedAllGroupsAtOnceDynamic, joinThreeBucketedTable, expectedJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnceDynamic, joinThreeMixedTable, expectedJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, joinThreeBucketedTable, expectedJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, joinThreeMixedTable, expectedJoinQuery, assertRemoteExchangesCount(2)); + + assertQuery(colocatedAllGroupsAtOnce, leftJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnce, rightJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, leftJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, rightJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnceDynamic, leftJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnceDynamic, rightJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, leftJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, rightJoinBucketedTable, expectedOuterJoinQuery, assertRemoteExchangesCount(1)); // // CROSS JOIN and HASH JOIN mixed @@ -2343,8 +2481,8 @@ public void testGroupedExecution() "CROSS JOIN\n" + " (SELECT orderkey key3, comment value3 FROM orders where orderkey <= 3)"; assertQuery(notColocated, crossJoin, expectedCrossJoinQuery); - assertQuery(colocatedAllGroupsAtOnce, crossJoin, expectedCrossJoinQuery); - assertQuery(colocatedOneGroupAtATime, crossJoin, expectedCrossJoinQuery); + assertQuery(colocatedAllGroupsAtOnce, crossJoin, expectedCrossJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, crossJoin, expectedCrossJoinQuery, assertRemoteExchangesCount(2)); // // Bucketed and unbucketed HASH JOIN mixed @@ -2364,8 +2502,9 @@ public void testGroupedExecution() "ON key1 = key3"; @Language("SQL") String expectedBucketedAndUnbucketedJoinQuery = "SELECT orderkey, comment, orderkey, comment, orderkey, comment, orderkey, comment from orders"; assertQuery(notColocated, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery); - assertQuery(colocatedAllGroupsAtOnce, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery); - assertQuery(colocatedOneGroupAtATime, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery); + assertQuery(colocatedAllGroupsAtOnce, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, bucketedAndUnbucketedJoin, expectedBucketedAndUnbucketedJoinQuery, assertRemoteExchangesCount(2)); // // UNION ALL / GROUP BY @@ -2445,14 +2584,16 @@ public void testGroupedExecution() @Language("SQL") String expectedGroupByOfUnionOfGroupBy = "SELECT orderkey, 3 from orders"; // Eligible GROUP BYs run in the same fragment regardless of colocated_join flag - assertQuery(colocatedAllGroupsAtOnce, groupBySingleBucketed, expectedSingleGroupByQuery); - assertQuery(colocatedOneGroupAtATime, groupBySingleBucketed, expectedSingleGroupByQuery); - assertQuery(colocatedAllGroupsAtOnce, groupByOfUnionBucketed, expectedGroupByOfUnion); - assertQuery(colocatedOneGroupAtATime, groupByOfUnionBucketed, expectedGroupByOfUnion); + assertQuery(colocatedAllGroupsAtOnce, groupBySingleBucketed, expectedSingleGroupByQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, groupBySingleBucketed, expectedSingleGroupByQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, groupBySingleBucketed, expectedSingleGroupByQuery, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnce, groupByOfUnionBucketed, expectedGroupByOfUnion, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, groupByOfUnionBucketed, expectedGroupByOfUnion, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, groupByOfUnionBucketed, expectedGroupByOfUnion, assertRemoteExchangesCount(1)); // cannot be executed in a grouped manner but should still produce correct result - assertQuery(colocatedOneGroupAtATime, groupByOfUnionMixed, expectedGroupByOfUnion); - assertQuery(colocatedOneGroupAtATime, groupByOfUnionOfGroupByMixed, expectedGroupByOfUnionOfGroupBy); + assertQuery(colocatedOneGroupAtATime, groupByOfUnionMixed, expectedGroupByOfUnion, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, groupByOfUnionOfGroupByMixed, expectedGroupByOfUnionOfGroupBy, assertRemoteExchangesCount(2)); // // GROUP BY and JOIN mixed @@ -2505,16 +2646,20 @@ public void testGroupedExecution() @Language("SQL") String expectedGroupOnJoinResult = "SELECT orderkey, 2, 2 from orders"; // Eligible GROUP BYs run in the same fragment regardless of colocated_join flag - assertQuery(colocatedAllGroupsAtOnce, joinGroupedWithGrouped, expectedJoinGroupedWithGrouped); - assertQuery(colocatedOneGroupAtATime, joinGroupedWithGrouped, expectedJoinGroupedWithGrouped); - assertQuery(colocatedAllGroupsAtOnce, joinGroupedWithUngrouped, expectedJoinGroupedWithUngrouped); - assertQuery(colocatedOneGroupAtATime, joinGroupedWithUngrouped, expectedJoinGroupedWithUngrouped); - assertQuery(colocatedAllGroupsAtOnce, groupOnJoinResult, expectedGroupOnJoinResult); - assertQuery(colocatedOneGroupAtATime, groupOnJoinResult, expectedGroupOnJoinResult); - assertQuery(broadcastOneGroupAtATime, groupOnJoinResult, expectedGroupOnJoinResult); + assertQuery(colocatedAllGroupsAtOnce, joinGroupedWithGrouped, expectedJoinGroupedWithGrouped, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, joinGroupedWithGrouped, expectedJoinGroupedWithGrouped, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, joinGroupedWithGrouped, expectedJoinGroupedWithGrouped, assertRemoteExchangesCount(1)); + assertQuery(colocatedAllGroupsAtOnce, joinGroupedWithUngrouped, expectedJoinGroupedWithUngrouped, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, joinGroupedWithUngrouped, expectedJoinGroupedWithUngrouped, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, joinGroupedWithUngrouped, expectedJoinGroupedWithUngrouped, assertRemoteExchangesCount(2)); + assertQuery(colocatedAllGroupsAtOnce, groupOnJoinResult, expectedGroupOnJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, groupOnJoinResult, expectedGroupOnJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, groupOnJoinResult, expectedGroupOnJoinResult, assertRemoteExchangesCount(2)); + + assertQuery(broadcastOneGroupAtATime, groupOnJoinResult, expectedGroupOnJoinResult, assertRemoteExchangesCount(2)); // cannot be executed in a grouped manner but should still produce correct result - assertQuery(colocatedOneGroupAtATime, joinUngroupedWithGrouped, expectedJoinUngroupedWithGrouped); + assertQuery(colocatedOneGroupAtATime, joinUngroupedWithGrouped, expectedJoinUngroupedWithGrouped, assertRemoteExchangesCount(2)); // // Outer JOIN (that involves LookupOuterOperator) @@ -2531,6 +2676,25 @@ public void testGroupedExecution() "FULL JOIN\n" + " (SELECT * FROM test_grouped_join3 where mod(key3, 5) = 0)\n" + "ON key2 = key3"; + // Probe is grouped execution, but build is not + @Language("SQL") String sharedBuildOuterJoin = + "SELECT key1, value1, keyN, valueN\n" + + "FROM\n" + + " (SELECT key1, arbitrary(value1) value1 FROM test_grouped_join1 where mod(key1, 2) = 0 group by key1)\n" + + "RIGHT JOIN\n" + + " (SELECT * FROM test_grouped_joinN where mod(keyN, 3) = 0)\n" + + "ON key1 = keyN"; + // The preceding test case, which then feeds into another join + @Language("SQL") String chainedSharedBuildOuterJoin = + "SELECT key1, value1, keyN, valueN, key3, value3\n" + + "FROM\n" + + " (SELECT key1, arbitrary(value1) value1 FROM test_grouped_join1 where mod(key1, 2) = 0 group by key1)\n" + + "RIGHT JOIN\n" + + " (SELECT * FROM test_grouped_joinN where mod(keyN, 3) = 0)\n" + + "ON key1 = keyN\n" + + "FULL JOIN\n" + + " (SELECT * FROM test_grouped_join3 where mod(key3, 5) = 0)\n" + + "ON keyN = key3"; @Language("SQL") String expectedChainedOuterJoinResult = "SELECT\n" + " CASE WHEN mod(orderkey, 2 * 3) = 0 THEN orderkey END,\n" + " CASE WHEN mod(orderkey, 2 * 3) = 0 THEN comment END,\n" + @@ -2540,24 +2704,61 @@ public void testGroupedExecution() " CASE WHEN mod(orderkey, 5) = 0 THEN comment END\n" + "FROM ORDERS\n" + "WHERE mod(orderkey, 3) = 0 OR mod(orderkey, 5) = 0"; + @Language("SQL") String expectedSharedBuildOuterJoinResult = "SELECT\n" + + " CASE WHEN mod(orderkey, 2) = 0 THEN orderkey END,\n" + + " CASE WHEN mod(orderkey, 2) = 0 THEN comment END,\n" + + " orderkey,\n" + + " comment\n" + + "FROM ORDERS\n" + + "WHERE mod(orderkey, 3) = 0"; assertQuery(notColocated, chainedOuterJoin, expectedChainedOuterJoinResult); - assertQuery(colocatedAllGroupsAtOnce, chainedOuterJoin, expectedChainedOuterJoinResult); - assertQuery(colocatedOneGroupAtATime, chainedOuterJoin, expectedChainedOuterJoinResult); + assertQuery(colocatedAllGroupsAtOnce, chainedOuterJoin, expectedChainedOuterJoinResult, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, chainedOuterJoin, expectedChainedOuterJoinResult, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATimeDynamic, chainedOuterJoin, expectedChainedOuterJoinResult, assertRemoteExchangesCount(1)); + assertQuery(notColocated, sharedBuildOuterJoin, expectedSharedBuildOuterJoinResult); + assertQuery(colocatedAllGroupsAtOnce, sharedBuildOuterJoin, expectedSharedBuildOuterJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, sharedBuildOuterJoin, expectedSharedBuildOuterJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, sharedBuildOuterJoin, expectedSharedBuildOuterJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATime, chainedSharedBuildOuterJoin, expectedChainedOuterJoinResult, assertRemoteExchangesCount(2)); + assertQuery(colocatedOneGroupAtATimeDynamic, chainedSharedBuildOuterJoin, expectedChainedOuterJoinResult, assertRemoteExchangesCount(2)); // - // Filter out all splits - // ===================== + // Filter out all or majority of splits + // ==================================== @Language("SQL") String noSplits = "SELECT key1, arbitrary(value1)\n" + "FROM test_grouped_join1\n" + "WHERE \"$bucket\" < 0\n" + "GROUP BY key1"; + @Language("SQL") String joinMismatchedBuckets = + "SELECT key1, value1, key2, value2\n" + + "FROM (\n" + + " SELECT *\n" + + " FROM test_grouped_join1\n" + + " WHERE \"$bucket\"=1\n" + + ")\n" + + "FULL OUTER JOIN (\n" + + " SELECT *\n" + + " FROM test_grouped_join2\n" + + " WHERE \"$bucket\"=11\n" + + ")\n" + + "ON key1=key2"; @Language("SQL") String expectedNoSplits = "SELECT 1, 'a' WHERE FALSE"; + @Language("SQL") String expectedJoinMismatchedBuckets = "SELECT\n" + + " CASE WHEN mod(orderkey, 13) = 1 THEN orderkey END,\n" + + " CASE WHEN mod(orderkey, 13) = 1 THEN comment END,\n" + + " CASE WHEN mod(orderkey, 13) = 11 THEN orderkey END,\n" + + " CASE WHEN mod(orderkey, 13) = 11 THEN comment END\n" + + "FROM ORDERS\n" + + "WHERE mod(orderkey, 13) IN (1, 11)"; assertQuery(notColocated, noSplits, expectedNoSplits); - assertQuery(colocatedAllGroupsAtOnce, noSplits, expectedNoSplits); - assertQuery(colocatedOneGroupAtATime, noSplits, expectedNoSplits); + assertQuery(colocatedAllGroupsAtOnce, noSplits, expectedNoSplits, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, noSplits, expectedNoSplits, assertRemoteExchangesCount(1)); + assertQuery(notColocated, joinMismatchedBuckets, expectedJoinMismatchedBuckets); + assertQuery(colocatedAllGroupsAtOnce, joinMismatchedBuckets, expectedJoinMismatchedBuckets, assertRemoteExchangesCount(1)); + assertQuery(colocatedOneGroupAtATime, joinMismatchedBuckets, expectedJoinMismatchedBuckets, assertRemoteExchangesCount(1)); } finally { assertUpdate("DROP TABLE IF EXISTS test_grouped_join1"); @@ -2568,6 +2769,27 @@ public void testGroupedExecution() } } + private Consumer assertRemoteExchangesCount(int expectedRemoteExchangesCount) + { + return plan -> + { + int actualRemoteExchangesCount = searchFrom(plan.getRoot()) + .where(node -> node instanceof ExchangeNode && ((ExchangeNode) node).getScope() == ExchangeNode.Scope.REMOTE) + .findAll() + .size(); + if (actualRemoteExchangesCount != expectedRemoteExchangesCount) { + Session session = getSession(); + Metadata metadata = ((DistributedQueryRunner) getQueryRunner()).getCoordinator().getMetadata(); + String formattedPlan = textLogicalPlan(plan.getRoot(), plan.getTypes(), metadata.getFunctionRegistry(), StatsAndCosts.empty(), session, 0); + throw new AssertionError(format( + "Expected [\n%s\n] remote exchanges but found [\n%s\n] remote exchanges. Actual plan is [\n\n%s\n]", + expectedRemoteExchangesCount, + actualRemoteExchangesCount, + formattedPlan)); + } + }; + } + @Test public void testRcTextCharDecoding() { @@ -2668,7 +2890,7 @@ public void testCollectColumnStatisticsOnCreateTable() "('c_boolean', null, 2.0E0, 0.5E0, null, null, null), " + "('c_bigint', null, 2.0E0, 0.5E0, null, '0', '1'), " + "('c_double', null, 2.0E0, 0.5E0, null, '1.2', '2.2'), " + - "('c_timestamp', null, 2.0E0, 0.5E0, null, '2012-08-08 00:00:00.000', '2012-08-08 01:00:00.000'), " + + "('c_timestamp', null, 2.0E0, 0.5E0, null, null, null), " + "('c_varchar', 8.0E0, 2.0E0, 0.5E0, null, null, null), " + "('c_varbinary', 8.0E0, null, 0.5E0, null, null, null), " + "('p_varchar', 8.0E0, 1.0E0, 0.0E0, null, null, null), " + @@ -2678,12 +2900,24 @@ public void testCollectColumnStatisticsOnCreateTable() "('c_boolean', null, 2.0E0, 0.5E0, null, null, null), " + "('c_bigint', null, 2.0E0, 0.5E0, null, '1', '2'), " + "('c_double', null, 2.0E0, 0.5E0, null, '2.3', '3.3'), " + - "('c_timestamp', null, 2.0E0, 0.5E0, null, '2012-09-09 00:00:00.000', '2012-09-09 01:00:00.000'), " + + "('c_timestamp', null, 2.0E0, 0.5E0, null, null, null), " + "('c_varchar', 8.0E0, 2.0E0, 0.5E0, null, null, null), " + "('c_varbinary', 8.0E0, null, 0.5E0, null, null, null), " + "('p_varchar', 8.0E0, 1.0E0, 0.0E0, null, null, null), " + "(null, null, null, null, 4.0E0, null, null)"); + // non existing partition + assertQuery(format("SHOW STATS FOR (SELECT * FROM %s WHERE p_varchar = 'p3')", tableName), + "SELECT * FROM VALUES " + + "('c_boolean', null, 0E0, 0E0, null, null, null), " + + "('c_bigint', null, 0E0, 0E0, null, null, null), " + + "('c_double', null, 0E0, 0E0, null, null, null), " + + "('c_timestamp', null, 0E0, 0E0, null, null, null), " + + "('c_varchar', 0E0, 0E0, 0E0, null, null, null), " + + "('c_varbinary', null, 0E0, 0E0, null, null, null), " + + "('p_varchar', 0E0, 0E0, 0E0, null, null, null), " + + "(null, null, null, null, 0E0, null, null)"); + assertUpdate(format("DROP TABLE %s", tableName)); } @@ -2725,7 +2959,7 @@ public void testCollectColumnStatisticsOnInsert() "('c_boolean', null, 2.0E0, 0.5E0, null, null, null), " + "('c_bigint', null, 2.0E0, 0.5E0, null, '0', '1'), " + "('c_double', null, 2.0E0, 0.5E0, null, '1.2', '2.2'), " + - "('c_timestamp', null, 2.0E0, 0.5E0, null, '2012-08-08 00:00:00.000', '2012-08-08 01:00:00.000'), " + + "('c_timestamp', null, 2.0E0, 0.5E0, null, null, null), " + "('c_varchar', 8.0E0, 2.0E0, 0.5E0, null, null, null), " + "('c_varbinary', 8.0E0, null, 0.5E0, null, null, null), " + "('p_varchar', 8.0E0, 1.0E0, 0.0E0, null, null, null), " + @@ -2735,12 +2969,24 @@ public void testCollectColumnStatisticsOnInsert() "('c_boolean', null, 2.0E0, 0.5E0, null, null, null), " + "('c_bigint', null, 2.0E0, 0.5E0, null, '1', '2'), " + "('c_double', null, 2.0E0, 0.5E0, null, '2.3', '3.3'), " + - "('c_timestamp', null, 2.0E0, 0.5E0, null, '2012-09-09 00:00:00.000', '2012-09-09 01:00:00.000'), " + + "('c_timestamp', null, 2.0E0, 0.5E0, null, null, null), " + "('c_varchar', 8.0E0, 2.0E0, 0.5E0, null, null, null), " + "('c_varbinary', 8.0E0, null, 0.5E0, null, null, null), " + "('p_varchar', 8.0E0, 1.0E0, 0.0E0, null, null, null), " + "(null, null, null, null, 4.0E0, null, null)"); + // non existing partition + assertQuery(format("SHOW STATS FOR (SELECT * FROM %s WHERE p_varchar = 'p3')", tableName), + "SELECT * FROM VALUES " + + "('c_boolean', null, 0E0, 0E0, null, null, null), " + + "('c_bigint', null, 0E0, 0E0, null, null, null), " + + "('c_double', null, 0E0, 0E0, null, null, null), " + + "('c_timestamp', null, 0E0, 0E0, null, null, null), " + + "('c_varchar', 0E0, 0E0, 0E0, null, null, null), " + + "('c_varbinary', null, 0E0, 0E0, null, null, null), " + + "('p_varchar', 0E0, 0E0, 0E0, null, null, null), " + + "(null, null, null, null, 0E0, null, null)"); + assertUpdate(format("DROP TABLE %s", tableName)); } @@ -2765,8 +3011,8 @@ public void testInsertMultipleColumnsFromSameChannel() assertQuery(format("SHOW STATS FOR (SELECT * FROM %s WHERE p_varchar_1 = '2' AND p_varchar_2 = '2')", tableName), "SELECT * FROM VALUES " + - "('c_bigint_1', null, 1.0E0, 0.0E0, null, 1, 1), " + - "('c_bigint_2', null, 1.0E0, 0.0E0, null, 1, 1), " + + "('c_bigint_1', null, 1.0E0, 0.0E0, null, '1', '1'), " + + "('c_bigint_2', null, 1.0E0, 0.0E0, null, '1', '1'), " + "('p_varchar_1', 1.0E0, 1.0E0, 0.0E0, null, null, null), " + "('p_varchar_2', 1.0E0, 1.0E0, 0.0E0, null, null, null), " + "(null, null, null, null, 1.0E0, null, null)"); @@ -2779,8 +3025,8 @@ public void testInsertMultipleColumnsFromSameChannel() assertQuery(format("SHOW STATS FOR (SELECT * FROM %s WHERE p_varchar_1 = 'O' AND p_varchar_2 = 'O')", tableName), "SELECT * FROM VALUES " + - "('c_bigint_1', null, 1.0E0, 0.0E0, null, 15008, 15008), " + - "('c_bigint_2', null, 1.0E0, 0.0E0, null, 15008, 15008), " + + "('c_bigint_1', null, 1.0E0, 0.0E0, null, '15008', '15008'), " + + "('c_bigint_2', null, 1.0E0, 0.0E0, null, '15008', '15008'), " + "('p_varchar_1', 1.0E0, 1.0E0, 0.0E0, null, null, null), " + "('p_varchar_2', 1.0E0, 1.0E0, 0.0E0, null, null, null), " + "(null, null, null, null, 1.0E0, null, null)"); @@ -2788,6 +3034,162 @@ public void testInsertMultipleColumnsFromSameChannel() assertUpdate(format("DROP TABLE %s", tableName)); } + @Test + public void testCreateAvroTableWithSchemaUrl() + throws Exception + { + String tableName = "test_create_avro_table_with_schema_url"; + File schemaFile = createAvroSchemaFile(); + + String createTableSql = getAvroCreateTableSql(tableName, schemaFile.getAbsolutePath()); + String expectedShowCreateTable = getAvroCreateTableSql(tableName, schemaFile.toURI().toString()); + + assertUpdate(createTableSql); + + try { + MaterializedResult actual = computeActual(format("SHOW CREATE TABLE %s", tableName)); + assertEquals(actual.getOnlyValue(), expectedShowCreateTable); + } + finally { + assertUpdate(format("DROP TABLE %s", tableName)); + verify(schemaFile.delete(), "cannot delete temporary file: %s", schemaFile); + } + } + + @Test + public void testAlterAvroTableWithSchemaUrl() + throws Exception + { + testAlterAvroTableWithSchemaUrl(true, true, true); + } + + protected void testAlterAvroTableWithSchemaUrl(boolean renameColumn, boolean addColumn, boolean dropColumn) + throws Exception + { + String tableName = "test_alter_avro_table_with_schema_url"; + File schemaFile = createAvroSchemaFile(); + + assertUpdate(getAvroCreateTableSql(tableName, schemaFile.getAbsolutePath())); + + try { + if (renameColumn) { + assertQueryFails(format("ALTER TABLE %s RENAME COLUMN dummy_col TO new_dummy_col", tableName), "ALTER TABLE not supported when Avro schema url is set"); + } + if (addColumn) { + assertQueryFails(format("ALTER TABLE %s ADD COLUMN new_dummy_col VARCHAR", tableName), "ALTER TABLE not supported when Avro schema url is set"); + } + if (dropColumn) { + assertQueryFails(format("ALTER TABLE %s DROP COLUMN dummy_col", tableName), "ALTER TABLE not supported when Avro schema url is set"); + } + } + finally { + assertUpdate(format("DROP TABLE %s", tableName)); + verify(schemaFile.delete(), "cannot delete temporary file: %s", schemaFile); + } + } + + private String getAvroCreateTableSql(String tableName, String schemaFile) + { + return format("CREATE TABLE %s.%s.%s (\n" + + " dummy_col varchar,\n" + + " another_dummy_col varchar\n" + + ")\n" + + "WITH (\n" + + " avro_schema_url = '%s',\n" + + " format = 'AVRO'\n" + + ")", + getSession().getCatalog().get(), + getSession().getSchema().get(), + tableName, + schemaFile); + } + + private static File createAvroSchemaFile() + throws Exception + { + File schemaFile = File.createTempFile("avro_single_column-", ".avsc"); + String schema = "{\n" + + " \"namespace\": \"com.facebook.test\",\n" + + " \"name\": \"single_column\",\n" + + " \"type\": \"record\",\n" + + " \"fields\": [\n" + + " { \"name\":\"string_col\", \"type\":\"string\" }\n" + + "]}"; + asCharSink(schemaFile, UTF_8).write(schema); + return schemaFile; + } + + @Test + public void testCreateOrcTableWithSchemaUrl() + throws Exception + { + @Language("SQL") String createTableSql = format("" + + "CREATE TABLE %s.%s.test_orc (\n" + + " dummy_col varchar\n" + + ")\n" + + "WITH (\n" + + " avro_schema_url = 'dummy.avsc',\n" + + " format = 'ORC'\n" + + ")", + getSession().getCatalog().get(), + getSession().getSchema().get()); + + assertQueryFails(createTableSql, "Cannot specify avro_schema_url table property for storage format: ORC"); + } + + @Test + public void testCtasFailsWithAvroSchemaUrl() + throws Exception + { + @Language("SQL") String ctasSqlWithoutData = "CREATE TABLE create_avro\n" + + "WITH (avro_schema_url = 'dummy_schema')\n" + + "AS SELECT 'dummy_value' as dummy_col WITH NO DATA"; + + assertQueryFails(ctasSqlWithoutData, "CREATE TABLE AS not supported when Avro schema url is set"); + + @Language("SQL") String ctasSql = "CREATE TABLE create_avro\n" + + "WITH (avro_schema_url = 'dummy_schema')\n" + + "AS SELECT * FROM (VALUES('a')) t (a)"; + + assertQueryFails(ctasSql, "CREATE TABLE AS not supported when Avro schema url is set"); + } + + @Test + public void testBucketedTablesFailWithAvroSchemaUrl() + throws Exception + { + @Language("SQL") String createSql = "CREATE TABLE create_avro (dummy VARCHAR)\n" + + "WITH (avro_schema_url = 'dummy_schema',\n" + + " bucket_count = 2, bucketed_by=ARRAY['dummy'])"; + + assertQueryFails(createSql, "Bucketing/Partitioning columns not supported when Avro schema url is set"); + } + + @Test + public void testPartitionedTablesFailWithAvroSchemaUrl() + throws Exception + { + @Language("SQL") String createSql = "CREATE TABLE create_avro (dummy VARCHAR)\n" + + "WITH (avro_schema_url = 'dummy_schema',\n" + + " partitioned_by=ARRAY['dummy'])"; + + assertQueryFails(createSql, "Bucketing/Partitioning columns not supported when Avro schema url is set"); + } + + @Test + public void testPrunePartitionFailure() + { + assertUpdate("CREATE TABLE test_prune_failure\n" + + "WITH (partitioned_by = ARRAY['p']) AS\n" + + "SELECT 123 x, 'abc' p", 1); + + assertQueryReturnsEmptyResult("" + + "SELECT * FROM test_prune_failure\n" + + "WHERE x < 0 AND cast(p AS int) > 0"); + + assertUpdate("DROP TABLE test_prune_failure"); + } + private Session getParallelWriteSession() { return Session.builder(getSession()) @@ -2866,6 +3268,26 @@ private static ConnectorSession getConnectorSession(Session session) return session.toConnectorSession(new ConnectorId(session.getCatalog().get())); } + private void testWithAllStorageFormats(BiConsumer test) + { + for (TestingHiveStorageFormat storageFormat : getAllTestingHiveStorageFormat()) { + testWithStorageFormat(storageFormat, test); + } + } + + private static void testWithStorageFormat(TestingHiveStorageFormat storageFormat, BiConsumer test) + { + requireNonNull(storageFormat, "storageFormat is null"); + requireNonNull(test, "test is null"); + Session session = storageFormat.getSession(); + try { + test.accept(session, storageFormat.getFormat()); + } + catch (Exception | AssertionError e) { + fail(format("Failure for format %s with properties %s", storageFormat.getFormat(), session.getConnectorProperties()), e); + } + } + private List getAllTestingHiveStorageFormat() { Session session = getSession(); @@ -2879,9 +3301,6 @@ private List getAllTestingHiveStorageFormat() formats.add(new TestingHiveStorageFormat( Session.builder(session).setCatalogSessionProperty(session.getCatalog().get(), "orc_optimized_writer_enabled", "true").build(), HiveStorageFormat.DWRF)); - formats.add(new TestingHiveStorageFormat( - Session.builder(session).setCatalogSessionProperty(session.getCatalog().get(), "parquet_optimized_reader_enabled", "true").build(), - HiveStorageFormat.PARQUET)); return formats.build(); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHivePageSink.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHivePageSink.java index 002bbdf39a6f5..7a45796fda6f6 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHivePageSink.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHivePageSink.java @@ -61,6 +61,7 @@ import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveDataStreamFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveFileWriterFactories; import static com.facebook.presto.hive.HiveTestUtils.getDefaultHiveRecordCursorProvider; +import static com.facebook.presto.hive.HiveTestUtils.getDefaultOrcFileWriterFactory; import static com.facebook.presto.hive.HiveType.HIVE_DATE; import static com.facebook.presto.hive.HiveType.HIVE_DOUBLE; import static com.facebook.presto.hive.HiveType.HIVE_INT; @@ -227,7 +228,8 @@ private static ConnectorPageSource createPageSource(HiveTransactionHandle transa false, TupleDomain.all(), ImmutableMap.of(), - Optional.empty()); + Optional.empty(), + false); HivePageSourceProvider provider = new HivePageSourceProvider(config, createTestHdfsEnvironment(config), getDefaultHiveRecordCursorProvider(config), getDefaultHiveDataStreamFactories(config), TYPE_MANAGER); return provider.createPageSource(transaction, getSession(config), split, ImmutableList.copyOf(getColumnHandles())); } @@ -262,14 +264,15 @@ private static ConnectorPageSink createPageSink(HiveTransactionHandle transactio partitionUpdateCodec, new TestingNodeManager("fake-environment"), new HiveEventClient(), - new HiveSessionProperties(config, new OrcFileWriterConfig()), - stats); + new HiveSessionProperties(config, new OrcFileWriterConfig(), new ParquetFileWriterConfig()), + stats, + getDefaultOrcFileWriterFactory(config)); return provider.createPageSink(transaction, getSession(config), handle); } private static TestingConnectorSession getSession(HiveClientConfig config) { - return new TestingConnectorSession(new HiveSessionProperties(config, new OrcFileWriterConfig()).getSessionProperties()); + return new TestingConnectorSession(new HiveSessionProperties(config, new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); } private static List getColumnHandles() diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplit.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplit.java index 5971aad5b1d54..26d0441f2e6ee 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplit.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplit.java @@ -61,7 +61,8 @@ public void testJsonRoundTrip() Optional.of(new HiveSplit.BucketConversion( 32, 16, - ImmutableList.of(new HiveColumnHandle("col", HIVE_LONG, BIGINT.getTypeSignature(), 5, ColumnType.REGULAR, Optional.of("comment")))))); + ImmutableList.of(new HiveColumnHandle("col", HIVE_LONG, BIGINT.getTypeSignature(), 5, ColumnType.REGULAR, Optional.of("comment"))))), + false); String json = codec.toJson(expected); HiveSplit actual = codec.fromJson(json); @@ -79,5 +80,6 @@ public void testJsonRoundTrip() assertEquals(actual.getColumnCoercions(), expected.getColumnCoercions()); assertEquals(actual.getBucketConversion(), expected.getBucketConversion()); assertEquals(actual.isForceLocalScheduling(), expected.isForceLocalScheduling()); + assertEquals(actual.isS3SelectPushdownEnabled(), expected.isS3SelectPushdownEnabled()); } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplitSource.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplitSource.java index 84cc6f65daa3f..abfab4ffd3726 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplitSource.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveSplitSource.java @@ -306,7 +306,8 @@ private TestSplit(int id, OptionalInt bucketNumber) true, false, ImmutableMap.of(), - Optional.empty()); + Optional.empty(), + false); } private static Properties properties(String key, String value) diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveUtil.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveUtil.java index 62968fe36aae7..0747ce32edd2d 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveUtil.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveUtil.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.hive; +import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hive.metastore.Warehouse; import org.apache.hadoop.hive.metastore.api.MetaException; import org.apache.hadoop.hive.serde2.thrift.ThriftDeserializer; @@ -58,7 +59,7 @@ public void testGetThriftDeserializer() schema.setProperty(SERIALIZATION_CLASS, IntString.class.getName()); schema.setProperty(SERIALIZATION_FORMAT, TBinaryProtocol.class.getName()); - assertInstanceOf(getDeserializer(schema), ThriftDeserializer.class); + assertInstanceOf(getDeserializer(new Configuration(false), schema), ThriftDeserializer.class); } @Test diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestIonSqlQueryBuilder.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestIonSqlQueryBuilder.java new file mode 100644 index 0000000000000..b620e8c2c4cc1 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestIonSqlQueryBuilder.java @@ -0,0 +1,128 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.predicate.Domain; +import com.facebook.presto.spi.predicate.Range; +import com.facebook.presto.spi.predicate.SortedRangeSet; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.type.TypeRegistry; +import com.facebook.presto.util.DateTimeUtils; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; +import static com.facebook.presto.hive.HiveTestUtils.longDecimal; +import static com.facebook.presto.hive.HiveTestUtils.shortDecimal; +import static com.facebook.presto.hive.HiveType.HIVE_DATE; +import static com.facebook.presto.hive.HiveType.HIVE_DOUBLE; +import static com.facebook.presto.hive.HiveType.HIVE_INT; +import static com.facebook.presto.hive.HiveType.HIVE_STRING; +import static com.facebook.presto.hive.HiveType.HIVE_TIMESTAMP; +import static com.facebook.presto.spi.predicate.TupleDomain.withColumnDomains; +import static com.facebook.presto.spi.predicate.ValueSet.ofRanges; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.StandardTypes.DECIMAL; +import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.spi.type.StandardTypes.TIMESTAMP; +import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static org.testng.Assert.assertEquals; + +public class TestIonSqlQueryBuilder +{ + @Test + public void testBuildSQL() + { + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(new TypeRegistry()); + List columns = ImmutableList.of( + new HiveColumnHandle("n_nationkey", HIVE_INT, parseTypeSignature(INTEGER), 0, REGULAR, Optional.empty()), + new HiveColumnHandle("n_name", HIVE_STRING, parseTypeSignature(VARCHAR), 1, REGULAR, Optional.empty()), + new HiveColumnHandle("n_regionkey", HIVE_INT, parseTypeSignature(INTEGER), 2, REGULAR, Optional.empty())); + + assertEquals("SELECT s._1, s._2, s._3 FROM S3Object s", + queryBuilder.buildSql(columns, TupleDomain.all())); + TupleDomain tupleDomain = withColumnDomains(ImmutableMap.of( + columns.get(2), Domain.create(SortedRangeSet.copyOf(BIGINT, ImmutableList.of(Range.equal(BIGINT, 3L))), false))); + assertEquals("SELECT s._1, s._2, s._3 FROM S3Object s WHERE (case s._3 when '' then null else CAST(s._3 AS INT) end = 3)", + queryBuilder.buildSql(columns, tupleDomain)); + } + + @Test + public void testEmptyColumns() + { + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(new TypeRegistry()); + assertEquals("SELECT ' ' FROM S3Object s", queryBuilder.buildSql(ImmutableList.of(), TupleDomain.all())); + } + + @Test + public void testDecimalColumns() + { + TypeManager typeManager = new TypeRegistry(); + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(typeManager); + List columns = ImmutableList.of( + new HiveColumnHandle("quantity", HiveType.valueOf("decimal(20,0)"), parseTypeSignature(DECIMAL), 0, REGULAR, Optional.empty()), + new HiveColumnHandle("extendedprice", HiveType.valueOf("decimal(20,2)"), parseTypeSignature(DECIMAL), 1, REGULAR, Optional.empty()), + new HiveColumnHandle("discount", HiveType.valueOf("decimal(10,2)"), parseTypeSignature(DECIMAL), 2, REGULAR, Optional.empty())); + DecimalType decimalType = DecimalType.createDecimalType(10, 2); + TupleDomain tupleDomain = withColumnDomains( + ImmutableMap.of( + columns.get(0), Domain.create(ofRanges(Range.lessThan(DecimalType.createDecimalType(20, 0), longDecimal("50"))), false), + columns.get(1), Domain.create(ofRanges(Range.equal(HiveType.valueOf("decimal(20,2)").getType(typeManager), longDecimal("0.05"))), false), + columns.get(2), Domain.create(ofRanges(Range.range(decimalType, shortDecimal("0.0"), true, shortDecimal("0.02"), true)), false))); + assertEquals("SELECT s._1, s._2, s._3 FROM S3Object s WHERE ((case s._1 when '' then null else CAST(s._1 AS DECIMAL(20,0)) end < 50)) AND " + + "(case s._2 when '' then null else CAST(s._2 AS DECIMAL(20,2)) end = 0.05) AND ((case s._3 when '' then null else CAST(s._3 AS DECIMAL(10,2)) " + + "end >= 0.00 AND case s._3 when '' then null else CAST(s._3 AS DECIMAL(10,2)) end <= 0.02))", + queryBuilder.buildSql(columns, tupleDomain)); + } + + @Test + public void testDateColumn() + { + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(new TypeRegistry()); + List columns = ImmutableList.of( + new HiveColumnHandle("t1", HIVE_TIMESTAMP, parseTypeSignature(TIMESTAMP), 0, REGULAR, Optional.empty()), + new HiveColumnHandle("t2", HIVE_DATE, parseTypeSignature(StandardTypes.DATE), 1, REGULAR, Optional.empty())); + TupleDomain tupleDomain = withColumnDomains(ImmutableMap.of( + columns.get(1), Domain.create(SortedRangeSet.copyOf(DATE, ImmutableList.of(Range.equal(DATE, (long) DateTimeUtils.parseDate("2001-08-22")))), false))); + + assertEquals("SELECT s._1, s._2 FROM S3Object s WHERE (case s._2 when '' then null else CAST(s._2 AS TIMESTAMP) end = `2001-08-22`)", queryBuilder.buildSql(columns, tupleDomain)); + } + + @Test + public void testNotPushDoublePredicates() + { + IonSqlQueryBuilder queryBuilder = new IonSqlQueryBuilder(new TypeRegistry()); + List columns = ImmutableList.of( + new HiveColumnHandle("quantity", HIVE_INT, parseTypeSignature(INTEGER), 0, REGULAR, Optional.empty()), + new HiveColumnHandle("extendedprice", HIVE_DOUBLE, parseTypeSignature(StandardTypes.DOUBLE), 1, REGULAR, Optional.empty()), + new HiveColumnHandle("discount", HIVE_DOUBLE, parseTypeSignature(StandardTypes.DOUBLE), 2, REGULAR, Optional.empty())); + TupleDomain tupleDomain = withColumnDomains( + ImmutableMap.of( + columns.get(0), Domain.create(ofRanges(Range.lessThan(BIGINT, 50L)), false), + columns.get(1), Domain.create(ofRanges(Range.equal(DOUBLE, 0.05)), false), + columns.get(2), Domain.create(ofRanges(Range.range(DOUBLE, 0.0, true, 0.02, true)), false))); + assertEquals("SELECT s._1, s._2, s._3 FROM S3Object s WHERE ((case s._1 when '' then null else CAST(s._1 AS INT) end < 50))", + queryBuilder.buildSql(columns, tupleDomain)); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestOrcPageSourceMemoryTracking.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestOrcPageSourceMemoryTracking.java index aef80b1f1a286..d712b2a097ab2 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestOrcPageSourceMemoryTracking.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestOrcPageSourceMemoryTracking.java @@ -254,7 +254,7 @@ public void testMaxReadBytes(int rowCount) int maxReadBytes = 1_000; HiveClientConfig config = new HiveClientConfig(); config.setOrcMaxReadBlockSize(new DataSize(maxReadBytes, BYTE)); - ConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(config, new OrcFileWriterConfig()).getSessionProperties()); + ConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(config, new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); FileFormatDataSourceStats stats = new FileFormatDataSourceStats(); // Build a table where every row gets larger, so we can test that the "batchSize" reduces @@ -484,7 +484,8 @@ public ConnectorPageSource newPageSource(FileFormatDataSourceStats stats, Connec DateTimeZone.UTC, TYPE_MANAGER, ImmutableMap.of(), - Optional.empty()) + Optional.empty(), + false) .get(); } @@ -529,7 +530,7 @@ public SourceOperator newScanFilterAndProjectOperator(DriverContext driverContex private DriverContext newDriverContext() { return createTaskContext(executor, scheduledExecutor, testSessionBuilder().build()) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestParquetFileWriterConfig.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestParquetFileWriterConfig.java new file mode 100644 index 0000000000000..2653ad1b9b2c7 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestParquetFileWriterConfig.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.google.common.collect.ImmutableMap; +import io.airlift.units.DataSize; +import org.testng.annotations.Test; +import parquet.hadoop.ParquetWriter; + +import java.util.Map; + +import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; +import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; +import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; +import static io.airlift.units.DataSize.Unit.BYTE; +import static io.airlift.units.DataSize.Unit.MEGABYTE; + +public class TestParquetFileWriterConfig +{ + @Test + public void testDefaults() + { + assertRecordedDefaults(recordDefaults(ParquetFileWriterConfig.class) + .setBlockSize(new DataSize(ParquetWriter.DEFAULT_BLOCK_SIZE, BYTE)) + .setPageSize(new DataSize(ParquetWriter.DEFAULT_PAGE_SIZE, BYTE))); + } + + @Test + public void testExplicitPropertyMappings() + { + Map properties = new ImmutableMap.Builder() + .put("hive.parquet.writer.block-size", "234MB") + .put("hive.parquet.writer.page-size", "11MB") + .build(); + + ParquetFileWriterConfig expected = new ParquetFileWriterConfig() + .setBlockSize(new DataSize(234, MEGABYTE)) + .setPageSize(new DataSize(11, MEGABYTE)); + + assertFullMapping(properties, expected); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestPartitionUpdate.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestPartitionUpdate.java new file mode 100644 index 0000000000000..0e2f5b7409b8b --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestPartitionUpdate.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.hive.PartitionUpdate.UpdateMode; +import com.google.common.collect.ImmutableList; +import io.airlift.json.JsonCodec; +import org.apache.hadoop.fs.Path; +import org.testng.annotations.Test; + +import static io.airlift.json.JsonCodec.jsonCodec; +import static org.testng.Assert.assertEquals; + +public class TestPartitionUpdate +{ + private static final JsonCodec CODEC = jsonCodec(PartitionUpdate.class); + + @Test + public void testRoundTrip() + { + PartitionUpdate expected = new PartitionUpdate( + "test", + UpdateMode.APPEND, + "/writePath", + "/targetPath", + ImmutableList.of("file1", "file3"), + 123, + 456, + 789); + + PartitionUpdate actual = CODEC.fromJson(CODEC.toJson(expected)); + + assertEquals(actual.getName(), "test"); + assertEquals(actual.getUpdateMode(), UpdateMode.APPEND); + assertEquals(actual.getWritePath(), new Path("/writePath")); + assertEquals(actual.getTargetPath(), new Path("/targetPath")); + assertEquals(actual.getFileNames(), ImmutableList.of("file1", "file3")); + assertEquals(actual.getRowCount(), 123); + assertEquals(actual.getInMemoryDataSizeInBytes(), 456); + assertEquals(actual.getOnDiskDataSizeInBytes(), 789); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectLineRecordReader.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectLineRecordReader.java new file mode 100644 index 0000000000000..80cbef85a67a4 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectLineRecordReader.java @@ -0,0 +1,29 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import org.testng.annotations.Test; + +import java.io.IOException; + +import static com.facebook.presto.hive.S3SelectLineRecordReader.UnrecoverableS3OperationException; + +public class TestS3SelectLineRecordReader +{ + @Test(expectedExceptions = UnrecoverableS3OperationException.class, expectedExceptionsMessageRegExp = "java.io.IOException: test io exception \\(Bucket: test-bucket, Key: test-key\\)") + public void testUnrecoverableS3ExceptionMessage() + { + throw new UnrecoverableS3OperationException("test-bucket", "test-key", new IOException("test io exception")); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectPushdown.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectPushdown.java new file mode 100644 index 0000000000000..545ee30809e11 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectPushdown.java @@ -0,0 +1,45 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.mapred.JobConf; +import org.apache.hadoop.mapred.TextInputFormat; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class TestS3SelectPushdown +{ + private TextInputFormat inputFormat; + + @BeforeClass + public void setUp() + { + inputFormat = new TextInputFormat(); + inputFormat.configure(new JobConf()); + } + + @Test + public void testIsCompressionCodecSupported() + { + assertTrue(S3SelectPushdown.isCompressionCodecSupported(inputFormat, new Path("s3://fakeBucket/fakeObject.gz"))); + assertTrue(S3SelectPushdown.isCompressionCodecSupported(inputFormat, new Path("s3://fakeBucket/fakeObject"))); + assertFalse(S3SelectPushdown.isCompressionCodecSupported(inputFormat, new Path("s3://fakeBucket/fakeObject.lz4"))); + assertFalse(S3SelectPushdown.isCompressionCodecSupported(inputFormat, new Path("s3://fakeBucket/fakeObject.snappy"))); + assertTrue(S3SelectPushdown.isCompressionCodecSupported(inputFormat, new Path("s3://fakeBucket/fakeObject.bz2"))); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectRecordCursor.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectRecordCursor.java new file mode 100644 index 0000000000000..2f76e68ccdccd --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestS3SelectRecordCursor.java @@ -0,0 +1,279 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.TestingTypeManager; +import com.facebook.presto.spi.type.TypeManager; +import com.google.common.collect.ImmutableList; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe; +import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo; +import org.apache.hadoop.mapred.RecordReader; +import org.joda.time.DateTimeZone; +import org.testng.annotations.Test; + +import java.util.Optional; +import java.util.Properties; +import java.util.stream.Stream; + +import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.PARTITION_KEY; +import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; +import static com.facebook.presto.hive.HiveType.HIVE_INT; +import static com.facebook.presto.hive.HiveType.HIVE_STRING; +import static com.facebook.presto.hive.S3SelectRecordCursor.updateSplitSchema; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.joining; +import static org.apache.hadoop.hive.serde.serdeConstants.LIST_COLUMNS; +import static org.apache.hadoop.hive.serde.serdeConstants.LIST_COLUMN_TYPES; +import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_DDL; +import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_LIB; +import static org.testng.Assert.assertEquals; + +public class TestS3SelectRecordCursor +{ + private static final String LAZY_SERDE_CLASS_NAME = LazySimpleSerDe.class.getName(); + + private static final HiveColumnHandle ARTICLE_COLUMN = new HiveColumnHandle("article", HIVE_STRING, parseTypeSignature(StandardTypes.VARCHAR), 1, REGULAR, Optional.empty()); + private static final HiveColumnHandle AUTHOR_COLUMN = new HiveColumnHandle("author", HIVE_STRING, parseTypeSignature(StandardTypes.VARCHAR), 1, REGULAR, Optional.empty()); + private static final HiveColumnHandle DATE_ARTICLE_COLUMN = new HiveColumnHandle("date_pub", HIVE_INT, parseTypeSignature(StandardTypes.DATE), 1, REGULAR, Optional.empty()); + private static final HiveColumnHandle QUANTITY_COLUMN = new HiveColumnHandle("quantity", HIVE_INT, parseTypeSignature(StandardTypes.INTEGER), 1, REGULAR, Optional.empty()); + private static final HiveColumnHandle[] DEFAULT_TEST_COLUMNS = {ARTICLE_COLUMN, AUTHOR_COLUMN, DATE_ARTICLE_COLUMN, QUANTITY_COLUMN}; + private static final HiveColumnHandle MOCK_HIVE_COLUMN_HANDLE = new HiveColumnHandle("mockName", HiveType.HIVE_FLOAT, parseTypeSignature(StandardTypes.DOUBLE), 88, PARTITION_KEY, Optional.empty()); + private static final TypeManager MOCK_TYPE_MANAGER = new TestingTypeManager(); + private static final Path MOCK_PATH = new Path("mockPath"); + + @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "splitSchema is null") + public void shouldFailOnNullSplitSchema() + { + new S3SelectRecordCursor( + new Configuration(), + MOCK_PATH, + MOCK_RECORD_READER, + 100L, + null, + singletonList(MOCK_HIVE_COLUMN_HANDLE), + DateTimeZone.UTC, + MOCK_TYPE_MANAGER); + } + + @Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "columns is null") + public void shouldFailOnNullColumns() + { + new S3SelectRecordCursor( + new Configuration(), + MOCK_PATH, + MOCK_RECORD_READER, + 100L, + new Properties(), + null, + DateTimeZone.UTC, + MOCK_TYPE_MANAGER); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Invalid Thrift DDL struct article \\{ \\}") + public void shouldThrowIllegalArgumentExceptionWhenSerialDDLHasNoColumns() + { + String ddlSerializationValue = "struct article { }"; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Thrift DDL should start with struct") + public void shouldThrowIllegalArgumentExceptionWhenSerialDDLNotStartingWithStruct() + { + String ddlSerializationValue = "foo article { varchar article varchar }"; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Invalid Thrift DDL struct article \\{varchar article\\}") + public void shouldThrowIllegalArgumentExceptionWhenSerialDDLNotStartingWithStruct2() + { + String ddlSerializationValue = "struct article {varchar article}"; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Invalid Thrift DDL struct article varchar article varchar \\}") + public void shouldThrowIllegalArgumentExceptionWhenMissingOpenStartStruct() + { + String ddlSerializationValue = "struct article varchar article varchar }"; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Invalid Thrift DDL struct article\\{varchar article varchar author date date_pub int quantity") + public void shouldThrowIllegalArgumentExceptionWhenDDlFormatNotCorrect() + { + String ddlSerializationValue = "struct article{varchar article varchar author date date_pub int quantity"; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Invalid Thrift DDL struct article \\{ varchar article varchar author date date_pub int quantity ") + public void shouldThrowIllegalArgumentExceptionWhenEndOfStructNotFound() + { + String ddlSerializationValue = "struct article { varchar article varchar author date date_pub int quantity "; + buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS); + } + + @Test + public void shouldFilterColumnsWhichDoesNotMatchInTheHiveTable() + { + String ddlSerializationValue = "struct article { varchar address varchar company date date_pub int quantity}"; + String expectedDDLSerialization = "struct article { date date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test + public void shouldReturnOnlyQuantityColumnInTheDDl() + { + String ddlSerializationValue = "struct article { varchar address varchar company date date_pub int quantity}"; + String expectedDDLSerialization = "struct article { int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, ARTICLE_COLUMN, QUANTITY_COLUMN), + buildExpectedProperties(expectedDDLSerialization, ARTICLE_COLUMN, QUANTITY_COLUMN)); + } + + @Test + public void shouldReturnProperties() + { + String ddlSerializationValue = "struct article { varchar article varchar author date date_pub int quantity}"; + String expectedDDLSerialization = "struct article { varchar article, varchar author, date date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test + public void shouldReturnPropertiesWithoutDoubleCommaInColumnsNameLastColumnNameWithEndStruct() + { + String ddlSerializationValue = "struct article { varchar article, varchar author, date date_pub, int quantity}"; + String expectedDDLSerialization = "struct article { varchar article, varchar author, date date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test + public void shouldReturnPropertiesWithoutDoubleCommaInColumnsNameLastColumnNameWithoutEndStruct() + { + String ddlSerializationValue = "struct article { varchar article, varchar author, date date_pub, int quantity }"; + String expectedDDLSerialization = "struct article { varchar article, varchar author, date date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test + public void shouldOnlyGetColumnTypeFromHiveObjectAndNotFromDDLSerialLastColumnNameWithEndStruct() + { + String ddlSerializationValue = "struct article { int article, double author, xxxx date_pub, int quantity}"; + String expectedDDLSerialization = "struct article { int article, double author, xxxx date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test + public void shouldOnlyGetColumnTypeFromHiveObjectAndNotFromDDLSerialLastColumnNameWithoutEndStruct() + { + String ddlSerializationValue = "struct article { int article, double author, xxxx date_pub, int quantity }"; + String expectedDDLSerialization = "struct article { int article, double author, xxxx date_pub, int quantity}"; + assertEquals(buildSplitSchema(ddlSerializationValue, DEFAULT_TEST_COLUMNS), + buildExpectedProperties(expectedDDLSerialization, DEFAULT_TEST_COLUMNS)); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNullPointerExceptionWhenColumnsIsNull() + { + updateSplitSchema(new Properties(), null); + } + + @Test(expectedExceptions = NullPointerException.class) + public void shouldThrowNullPointerExceptionWhenSchemaIsNull() + { + updateSplitSchema(null, ImmutableList.of()); + } + + private Properties buildSplitSchema(String ddlSerializationValue, HiveColumnHandle... columns) + { + Properties properties = new Properties(); + properties.put(SERIALIZATION_LIB, LAZY_SERDE_CLASS_NAME); + properties.put(SERIALIZATION_DDL, ddlSerializationValue); + return updateSplitSchema(properties, asList(columns)); + } + + private Properties buildExpectedProperties(String expectedDDLSerialization, HiveColumnHandle... expectedColumns) + { + String expectedColumnsType = getTypes(expectedColumns); + String expectedColumnsName = getName(expectedColumns); + Properties propExpected = new Properties(); + propExpected.put(LIST_COLUMNS, expectedColumnsName); + propExpected.put(SERIALIZATION_LIB, LAZY_SERDE_CLASS_NAME); + propExpected.put(SERIALIZATION_DDL, expectedDDLSerialization); + propExpected.put(LIST_COLUMN_TYPES, expectedColumnsType); + return propExpected; + } + + private String getName(HiveColumnHandle[] expectedColumns) + { + return Stream.of(expectedColumns) + .map(HiveColumnHandle::getName) + .collect(joining(",")); + } + + private String getTypes(HiveColumnHandle[] expectedColumns) + { + return Stream.of(expectedColumns) + .map(HiveColumnHandle::getHiveType) + .map(HiveType::getTypeInfo) + .map(TypeInfo::getTypeName) + .collect(joining(",")); + } + + private static final RecordReader MOCK_RECORD_READER = new RecordReader() + { + @Override + public boolean next(Object key, Object value) + { + throw new UnsupportedOperationException(); + } + + @Override + public Object createKey() + { + throw new UnsupportedOperationException(); + } + + @Override + public Object createValue() + { + throw new UnsupportedOperationException(); + } + + @Override + public long getPos() + { + throw new UnsupportedOperationException(); + } + + @Override + public void close() + { + throw new UnsupportedOperationException(); + } + + @Override + public float getProgress() + { + throw new UnsupportedOperationException(); + } + }; +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestShowStats.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestShowStats.java new file mode 100644 index 0000000000000..aa7714b38aa31 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestShowStats.java @@ -0,0 +1,186 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive; + +import com.facebook.presto.tests.AbstractTestQueryFramework; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static com.facebook.presto.hive.HiveQueryRunner.createQueryRunner; + +public class TestShowStats + extends AbstractTestQueryFramework +{ + public TestShowStats() + { + super(() -> createQueryRunner(ImmutableList.of())); + } + + @BeforeClass + public void setUp() + { + assertUpdate("CREATE TABLE nation_partitioned(nationkey BIGINT, name VARCHAR, comment VARCHAR, regionkey BIGINT) WITH (partitioned_by = ARRAY['regionkey'])"); + assertUpdate("INSERT INTO nation_partitioned SELECT nationkey, name, comment, regionkey from tpch.tiny.nation", 25); + assertUpdate("CREATE TABLE region(regionkey BIGINT, name VARCHAR, comment VARCHAR)"); + assertUpdate("CREATE TABLE orders(orderkey BIGINT, custkey BIGINT, totalprice DOUBLE, orderdate DATE, " + + "orderpriority VARCHAR, clerk VARCHAR, shippriority VARCHAR, comment VARCHAR, orderstatus VARCHAR)"); + } + + @Test + public void testShowStats() + { + assertQuery("SHOW STATS FOR nation_partitioned", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 5.0, 0.0, null, 0, 4), " + + " ('nationkey', null, 5.0, 0.0, null, 0, 24), " + + " ('name', 177.0, 5.0, 0.0, null, null, null), " + + " ('comment', 1857.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 25.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 5.0, 0.0, null, 0, 4), " + + " ('nationkey', null, 5.0, 0.0, null, 0, 24), " + + " ('name', 177.0, 5.0, 0.0, null, null, null), " + + " ('comment', 1857.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 25.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey IS NOT NULL)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 5.0, 0.0, null, 0, 4), " + + " ('nationkey', null, 5.0, 0.0, null, 0, 24), " + + " ('name', 177.0, 5.0, 0.0, null, null, null), " + + " ('comment', 1857.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 25.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey IS NULL)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 0.0, 0.0, null, null, null), " + + " ('nationkey', null, 0.0, 0.0, null, null, null), " + + " ('name', 0.0, 0.0, 0.0, null, null, null), " + + " ('comment', 0.0, 0.0, 0.0, null, null, null), " + + " (null, null, null, null, 0.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey = 1)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 1.0, 0.0, null, 1, 1), " + + " ('nationkey', null, 5.0, 0.0, null, 1, 24), " + + " ('name', 38.0, 5.0, 0.0, null, null, null), " + + " ('comment', 500.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 5.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey IN (1, 3))", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 2.0, 0.0, null, 1, 3), " + + " ('nationkey', null, 5.0, 0.0, null, 1, 24), " + + " ('name', 78.0, 5.0, 0.0, null, null, null), " + + " ('comment', 847.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 10.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey BETWEEN 1 AND 1 + 2)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 3.0, 0.0, null, 1, 3), " + + " ('nationkey', null, 5.0, 0.0, null, 1, 24), " + + " ('name', 109.0, 5.0, 0.0, null, null, null), " + + " ('comment', 1199.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 15.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey > 3)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 1.0, 0.0, null, 4, 4), " + + " ('nationkey', null, 5.0, 0.0, null, 4, 20), " + + " ('name', 31.0, 5.0, 0.0, null, null, null), " + + " ('comment', 348.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 5.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey < 1)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 1.0, 0.0, null, 0, 0), " + + " ('nationkey', null, 5.0, 0.0, null, 0, 16), " + + " ('name', 37.0, 5.0, 0.0, null, null, null), " + + " ('comment', 310.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 5.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey > 0 and regionkey < 4)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 3.0, 0.0, null, 1, 3), " + + " ('nationkey', null, 5.0, 0.0, null, 1, 24), " + + " ('name', 109.0, 5.0, 0.0, null, null, null), " + + " ('comment', 1199.0, 5.0, 0.0, null, null, null), " + + " (null, null, null, null, 15.0, null, null))"); + + assertQuery("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey > 10 or regionkey < 0)", + "SELECT * FROM (VALUES " + + " ('regionkey', null, 0.0, 0.0, null, null, null), " + + " ('nationkey', null, 0.0, 0.0, null, null, null), " + + " ('name', 0.0, 0.0, 0.0, null, null, null), " + + " ('comment', 0.0, 0.0, 0.0, null, null, null), " + + " (null, null, null, null, 0.0, null, null))"); + } + + @Test + public void testShowStatsWithoutFromFails() + { + assertQueryFails("SHOW STATS FOR (SELECT 1)", ".*There must be exactly one table in query passed to SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithMultipleFromFails() + { + assertQueryFails("SHOW STATS FOR (SELECT * FROM nation_partitioned, region)", ".*There must be exactly one table in query passed to SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithGroupByFails() + { + assertQueryFails("SHOW STATS FOR (SELECT avg(totalprice) FROM orders GROUP BY orderkey)", ".*GROUP BY is not supported in SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithHavingFails() + { + assertQueryFails("SHOW STATS FOR (SELECT count(nationkey) FROM nation_partitioned GROUP BY regionkey HAVING regionkey > 0)", ".*HAVING is not supported in SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsSelectNonStarFails() + { + assertQueryFails("SHOW STATS FOR (SELECT orderkey FROM orders)", ".*Only SELECT \\* is supported in SHOW STATS SELECT clause"); + assertQueryFails("SHOW STATS FOR (SELECT orderkey, custkey FROM orders)", ".*Only SELECT \\* is supported in SHOW STATS SELECT clause"); + assertQueryFails("SHOW STATS FOR (SELECT *, * FROM orders)", ".*Only SELECT \\* is supported in SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithSelectDistinctFails() + { + assertQueryFails("SHOW STATS FOR (SELECT DISTINCT * FROM orders)", ".*DISTINCT is not supported by SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithSelectFunctionCallFails() + { + assertQueryFails("SHOW STATS FOR (SELECT sin(orderkey) FROM orders)", ".*Only SELECT \\* is supported in SHOW STATS SELECT clause"); + assertQueryFails("SHOW STATS FOR (SELECT count(*) FROM orders)", ".*Only SELECT \\* is supported in SHOW STATS SELECT clause"); + } + + @Test + public void testShowStatsWithNonPushDownFilterFails() + { + assertQueryFails("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey + 100 < 200)", ".*Only predicates that can be pushed down are supported in the SHOW STATS WHERE clause"); + assertQueryFails("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE regionkey > 0 and nationkey > 0)", ".*Only predicates that can be pushed down are supported in the SHOW STATS WHERE clause"); + assertQueryFails("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE nationkey = 1 and name is not null)", ".*Only predicates that can be pushed down are supported in the SHOW STATS WHERE clause"); + assertQueryFails("SHOW STATS FOR (SELECT * FROM nation_partitioned WHERE sin(regionkey) > 0)", ".*Only predicates that can be pushed down are supported in the SHOW STATS WHERE clause"); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/FileFormat.java b/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/FileFormat.java index 88cf0e591d78e..47dfe5aeeb53c 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/FileFormat.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/FileFormat.java @@ -30,7 +30,6 @@ import com.facebook.presto.hive.orc.DwrfPageSourceFactory; import com.facebook.presto.hive.orc.OrcPageSourceFactory; import com.facebook.presto.hive.parquet.ParquetPageSourceFactory; -import com.facebook.presto.hive.parquet.ParquetRecordCursorProvider; import com.facebook.presto.hive.rcfile.RcFilePageSourceFactory; import com.facebook.presto.orc.OrcWriter; import com.facebook.presto.orc.OrcWriterOptions; @@ -204,7 +203,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.PARQUET); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.PARQUET, session); } }, @@ -224,7 +223,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.RCBINARY); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.RCBINARY, session); } }, @@ -244,7 +243,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.RCTEXT); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.RCTEXT, session); } }, @@ -264,7 +263,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.ORC); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.ORC, session); } }, @@ -284,7 +283,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.DWRF); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.DWRF, session); } @Override @@ -298,8 +297,8 @@ public boolean supportsDate() @Override public ConnectorPageSource createFileFormatReader(ConnectorSession session, HdfsEnvironment hdfsEnvironment, File targetFile, List columnNames, List columnTypes) { - HiveRecordCursorProvider cursorProvider = new ParquetRecordCursorProvider(hdfsEnvironment, new FileFormatDataSourceStats()); - return createPageSource(cursorProvider, session, targetFile, columnNames, columnTypes, HiveStorageFormat.PARQUET); + HivePageSourceFactory pageSourceFactory = new ParquetPageSourceFactory(TYPE_MANAGER, hdfsEnvironment, new FileFormatDataSourceStats()); + return createPageSource(pageSourceFactory, session, targetFile, columnNames, columnTypes, HiveStorageFormat.PARQUET); } @Override @@ -310,7 +309,7 @@ public FormatWriter createFileFormatWriter( List columnTypes, HiveCompressionCodec compressionCodec) { - return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.PARQUET); + return new RecordFormatWriter(targetFile, columnNames, columnTypes, compressionCodec, HiveStorageFormat.PARQUET, session); } }; @@ -370,7 +369,8 @@ private static ConnectorPageSource createPageSource( columnHandles, TupleDomain.all(), DateTimeZone.forID(session.getTimeZoneKey().getId()), - TYPE_MANAGER) + TYPE_MANAGER, + false) .get(); return new RecordPageSource(columnTypes, recordCursor); } @@ -415,7 +415,8 @@ public RecordFormatWriter(File targetFile, List columnNames, List columnTypes, HiveCompressionCodec compressionCodec, - HiveStorageFormat format) + HiveStorageFormat format, + ConnectorSession session) { JobConf config = new JobConf(conf); configureCompression(config, compressionCodec); @@ -427,7 +428,8 @@ public RecordFormatWriter(File targetFile, createSchema(format, columnNames, columnTypes), format.getEstimatedWriterSystemMemoryUsage(), config, - TYPE_MANAGER); + TYPE_MANAGER, + session); } @Override diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/HiveFileFormatBenchmark.java b/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/HiveFileFormatBenchmark.java index 00ed6da275f53..50a7cc516b114 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/HiveFileFormatBenchmark.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/benchmark/HiveFileFormatBenchmark.java @@ -19,6 +19,7 @@ import com.facebook.presto.hive.HiveCompressionCodec; import com.facebook.presto.hive.HiveSessionProperties; import com.facebook.presto.hive.OrcFileWriterConfig; +import com.facebook.presto.hive.ParquetFileWriterConfig; import com.facebook.presto.spi.ConnectorPageSource; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; @@ -93,10 +94,9 @@ public class HiveFileFormatBenchmark } @SuppressWarnings("deprecation") - private static final HiveClientConfig CONFIG = new HiveClientConfig() - .setParquetOptimizedReaderEnabled(true); + private static final HiveClientConfig CONFIG = new HiveClientConfig(); - private static final ConnectorSession SESSION = new TestingConnectorSession(new HiveSessionProperties(CONFIG, new OrcFileWriterConfig()) + private static final ConnectorSession SESSION = new TestingConnectorSession(new HiveSessionProperties(CONFIG, new OrcFileWriterConfig(), new ParquetFileWriterConfig()) .getSessionProperties()); private static final HdfsEnvironment HDFS_ENVIRONMENT = createTestHdfsEnvironment(CONFIG); diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestMetastoreUtil.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestMetastoreUtil.java index 356b822971ee2..981f6ebc11707 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestMetastoreUtil.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestMetastoreUtil.java @@ -26,17 +26,19 @@ import org.apache.hadoop.hive.metastore.api.StorageDescriptor; import org.testng.annotations.Test; +import java.util.List; import java.util.Properties; import static org.testng.Assert.assertEquals; public class TestMetastoreUtil { + private static final List TEST_SCHEMA = ImmutableList.of( + new FieldSchema("col1", "bigint", "comment1"), + new FieldSchema("col2", "binary", null), + new FieldSchema("col3", "string", null)); private static final StorageDescriptor TEST_STORAGE_DESCRIPTOR = new StorageDescriptor( - ImmutableList.of( - new FieldSchema("col1", "bigint", "comment1"), - new FieldSchema("col2", "binary", null), - new FieldSchema("col3", "string", null)), + TEST_SCHEMA, "hdfs://VOL1:9000/db_name/table_name", "com.facebook.hive.orc.OrcInputFormat", "com.facebook.hive.orc.OrcOutputFormat", @@ -75,10 +77,7 @@ public class TestMetastoreUtil TEST_STORAGE_DESCRIPTOR, ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3")); private static final StorageDescriptor TEST_STORAGE_DESCRIPTOR_WITH_UNSUPPORTED_FIELDS = new StorageDescriptor( - ImmutableList.of( - new FieldSchema("col1", "bigint", "comment1"), - new FieldSchema("col2", "binary", null), - new FieldSchema("col3", "string", null)), + TEST_SCHEMA, "hdfs://VOL1:9000/db_name/table_name", "com.facebook.hive.orc.OrcInputFormat", "com.facebook.hive.orc.OrcOutputFormat", @@ -122,7 +121,7 @@ public class TestMetastoreUtil @Test public void testTableRoundTrip() { - Table table = ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE); + Table table = ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE, TEST_SCHEMA); PrincipalPrivileges privileges = new PrincipalPrivileges(ImmutableMultimap.of(), ImmutableMultimap.of()); org.apache.hadoop.hive.metastore.api.Table metastoreApiTable = ThriftMetastoreUtil.toMetastoreApiTable(table, privileges); assertEquals(metastoreApiTable, TEST_TABLE); @@ -140,7 +139,7 @@ public void testPartitionRoundTrip() public void testHiveSchemaTable() { Properties expected = MetaStoreUtils.getTableMetadata(TEST_TABLE_WITH_UNSUPPORTED_FIELDS); - Properties actual = MetastoreUtil.getHiveSchema(ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS)); + Properties actual = MetastoreUtil.getHiveSchema(ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS, TEST_SCHEMA)); assertEquals(actual, expected); } @@ -148,14 +147,14 @@ public void testHiveSchemaTable() public void testHiveSchemaPartition() { Properties expected = MetaStoreUtils.getPartitionMetadata(TEST_PARTITION_WITH_UNSUPPORTED_FIELDS, TEST_TABLE_WITH_UNSUPPORTED_FIELDS); - Properties actual = MetastoreUtil.getHiveSchema(ThriftMetastoreUtil.fromMetastoreApiPartition(TEST_PARTITION_WITH_UNSUPPORTED_FIELDS), ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS)); + Properties actual = MetastoreUtil.getHiveSchema(ThriftMetastoreUtil.fromMetastoreApiPartition(TEST_PARTITION_WITH_UNSUPPORTED_FIELDS), ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS, TEST_SCHEMA)); assertEquals(actual, expected); } @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Writing to skewed table/partition is not supported") public void testTableRoundTripUnsupported() { - Table table = ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS); + Table table = ThriftMetastoreUtil.fromMetastoreApiTable(TEST_TABLE_WITH_UNSUPPORTED_FIELDS, TEST_SCHEMA); ThriftMetastoreUtil.toMetastoreApiTable(table, null); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestRecordingHiveMetastore.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestRecordingHiveMetastore.java new file mode 100644 index 0000000000000..434287f46568b --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestRecordingHiveMetastore.java @@ -0,0 +1,286 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.facebook.presto.hive.HiveBasicStatistics; +import com.facebook.presto.hive.HiveBucketProperty; +import com.facebook.presto.hive.HiveClientConfig; +import com.facebook.presto.hive.HiveType; +import com.facebook.presto.hive.PartitionStatistics; +import com.facebook.presto.hive.metastore.HivePrivilegeInfo.HivePrivilege; +import com.facebook.presto.hive.metastore.SortingColumn.Order; +import com.facebook.presto.spi.statistics.ColumnStatisticType; +import com.facebook.presto.spi.type.Type; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.airlift.units.Duration; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalLong; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.hive.HiveBasicStatistics.createEmptyStatistics; +import static com.facebook.presto.spi.statistics.ColumnStatisticType.MAX_VALUE; +import static com.facebook.presto.spi.statistics.ColumnStatisticType.MIN_VALUE; +import static com.facebook.presto.spi.type.VarcharType.createVarcharType; +import static org.testng.Assert.assertEquals; + +public class TestRecordingHiveMetastore +{ + private static final Database DATABASE = new Database( + "database", + Optional.of("location"), + "owner", + PrincipalType.USER, + Optional.of("comment"), + ImmutableMap.of("param", "value")); + private static final Column TABLE_COLUMN = new Column( + "column", + HiveType.HIVE_INT, + Optional.of("comment")); + private static final Storage TABLE_STORAGE = new Storage( + StorageFormat.create("serde", "input", "output"), + "location", + Optional.of(new HiveBucketProperty(ImmutableList.of("column"), 10, ImmutableList.of(new SortingColumn("column", Order.ASCENDING)))), + true, + ImmutableMap.of("param", "value2")); + private static final Table TABLE = new Table( + "database", + "table", + "owner", + "table_type", + TABLE_STORAGE, + ImmutableList.of(TABLE_COLUMN), + ImmutableList.of(TABLE_COLUMN), + ImmutableMap.of("param", "value3"), + Optional.of("original_text"), + Optional.of("expanded_text")); + private static final Partition PARTITION = new Partition( + "database", + "table", + ImmutableList.of("value"), + TABLE_STORAGE, + ImmutableList.of(TABLE_COLUMN), + ImmutableMap.of("param", "value4")); + private static final PartitionStatistics PARTITION_STATISTICS = new PartitionStatistics( + new HiveBasicStatistics(10, 11, 10000, 10001), + ImmutableMap.of("column", new HiveColumnStatistics( + Optional.of(new IntegerStatistics( + OptionalLong.of(-100), + OptionalLong.of(102))), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + OptionalLong.of(1234), + OptionalLong.of(1235), + OptionalLong.of(1), + OptionalLong.of(8)))); + private static final HivePrivilegeInfo PRIVILEGE_INFO = new HivePrivilegeInfo(HivePrivilege.SELECT, true); + + @Test + public void testRecordingHiveMetastore() + throws IOException + { + HiveClientConfig recordingHiveClientConfig = new HiveClientConfig() + .setRecordingPath(File.createTempFile("recording_test", "json").getAbsolutePath()) + .setRecordingDuration(new Duration(10, TimeUnit.MINUTES)); + + RecordingHiveMetastore recordingHiveMetastore = new RecordingHiveMetastore(new TestingHiveMetastore(), recordingHiveClientConfig); + validateMetadata(recordingHiveMetastore); + recordingHiveMetastore.dropDatabase("other_database"); + recordingHiveMetastore.writeRecording(); + + HiveClientConfig replayingHiveClientConfig = recordingHiveClientConfig + .setReplay(true); + + recordingHiveMetastore = new RecordingHiveMetastore(new UnimplementedHiveMetastore(), replayingHiveClientConfig); + recordingHiveMetastore.loadRecording(); + validateMetadata(recordingHiveMetastore); + } + + private void validateMetadata(ExtendedHiveMetastore hiveMetastore) + { + assertEquals(hiveMetastore.getDatabase("database"), Optional.of(DATABASE)); + assertEquals(hiveMetastore.getAllDatabases(), ImmutableList.of("database")); + assertEquals(hiveMetastore.getTable("database", "table"), Optional.of(TABLE)); + assertEquals(hiveMetastore.getSupportedColumnStatistics(createVarcharType(123)), ImmutableSet.of(MIN_VALUE, MAX_VALUE)); + assertEquals(hiveMetastore.getTableStatistics("database", "table"), PARTITION_STATISTICS); + assertEquals(hiveMetastore.getPartitionStatistics("database", "table", ImmutableSet.of("value")), ImmutableMap.of("value", PARTITION_STATISTICS)); + assertEquals(hiveMetastore.getAllTables("database"), Optional.of(ImmutableList.of("table"))); + assertEquals(hiveMetastore.getAllViews("database"), Optional.empty()); + assertEquals(hiveMetastore.getPartition("database", "table", ImmutableList.of("value")), Optional.of(PARTITION)); + assertEquals(hiveMetastore.getPartitionNames("database", "table"), Optional.of(ImmutableList.of("value"))); + assertEquals(hiveMetastore.getPartitionNamesByParts("database", "table", ImmutableList.of("value")), Optional.of(ImmutableList.of("value"))); + assertEquals(hiveMetastore.getPartitionsByNames("database", "table", ImmutableList.of("value")), ImmutableMap.of("value", Optional.of(PARTITION))); + assertEquals(hiveMetastore.getRoles("user"), ImmutableSet.of("role1", "role2")); + assertEquals(hiveMetastore.getDatabasePrivileges("user", "database"), ImmutableSet.of(PRIVILEGE_INFO)); + assertEquals(hiveMetastore.getTablePrivileges("user", "database", "table"), ImmutableSet.of(PRIVILEGE_INFO)); + } + + private static class TestingHiveMetastore + extends UnimplementedHiveMetastore + { + @Override + public Optional getDatabase(String databaseName) + { + if (databaseName.equals("database")) { + return Optional.of(DATABASE); + } + + return Optional.empty(); + } + + @Override + public List getAllDatabases() + { + return ImmutableList.of("database"); + } + + @Override + public Optional
getTable(String databaseName, String tableName) + { + if (databaseName.equals("database") && tableName.equals("table")) { + return Optional.of(TABLE); + } + + return Optional.empty(); + } + + @Override + public Set getSupportedColumnStatistics(Type type) + { + if (type.equals(createVarcharType(123))) { + return ImmutableSet.of(MIN_VALUE, MAX_VALUE); + } + + return ImmutableSet.of(); + } + + @Override + public PartitionStatistics getTableStatistics(String databaseName, String tableName) + { + if (databaseName.equals("database") && tableName.equals("table")) { + return PARTITION_STATISTICS; + } + + return new PartitionStatistics(createEmptyStatistics(), ImmutableMap.of()); + } + + @Override + public Map getPartitionStatistics(String databaseName, String tableName, Set partitionNames) + { + if (databaseName.equals("database") && tableName.equals("table") && partitionNames.contains("value")) { + return ImmutableMap.of("value", PARTITION_STATISTICS); + } + + return ImmutableMap.of(); + } + + @Override + public Optional> getAllTables(String databaseName) + { + if (databaseName.equals("database")) { + return Optional.of(ImmutableList.of("table")); + } + + return Optional.empty(); + } + + @Override + public Optional> getAllViews(String databaseName) + { + return Optional.empty(); + } + + @Override + public void dropDatabase(String databaseName) + { + // noop for test purpose + } + + @Override + public Optional getPartition(String databaseName, String tableName, List partitionValues) + { + if (databaseName.equals("database") && tableName.equals("table") && partitionValues.equals(ImmutableList.of("value"))) { + return Optional.of(PARTITION); + } + + return Optional.empty(); + } + + @Override + public Optional> getPartitionNames(String databaseName, String tableName) + { + if (databaseName.equals("database") && tableName.equals("table")) { + return Optional.of(ImmutableList.of("value")); + } + + return Optional.empty(); + } + + @Override + public Optional> getPartitionNamesByParts(String databaseName, String tableName, List parts) + { + if (databaseName.equals("database") && tableName.equals("table") && parts.equals(ImmutableList.of("value"))) { + return Optional.of(ImmutableList.of("value")); + } + + return Optional.empty(); + } + + @Override + public Map> getPartitionsByNames(String databaseName, String tableName, List partitionNames) + { + if (databaseName.equals("database") && tableName.equals("table") && partitionNames.contains("value")) { + return ImmutableMap.of("value", Optional.of(PARTITION)); + } + + return ImmutableMap.of(); + } + + @Override + public Set getRoles(String user) + { + return ImmutableSet.of("role1", "role2"); + } + + @Override + public Set getDatabasePrivileges(String user, String databaseName) + { + if (user.equals("user") && databaseName.equals("database")) { + return ImmutableSet.of(PRIVILEGE_INFO); + } + + return ImmutableSet.of(); + } + + @Override + public Set getTablePrivileges(String user, String databaseName, String tableName) + { + if (user.equals("user") && databaseName.equals("database") && tableName.equals("table")) { + return ImmutableSet.of(PRIVILEGE_INFO); + } + + return ImmutableSet.of(); + } + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestStorage.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestStorage.java new file mode 100644 index 0000000000000..82ae9e122877b --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/TestStorage.java @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import io.airlift.json.JsonCodec; +import org.testng.annotations.Test; + +import static io.airlift.json.JsonCodec.jsonCodec; +import static org.testng.Assert.assertEquals; + +public class TestStorage +{ + private static final JsonCodec CODEC = jsonCodec(Storage.class); + + @Test + public void testRoundTrip() + { + Storage storage = Storage.builder() + .setStorageFormat(StorageFormat.create("abc", "in", "out")) + .setLocation("/test") + .build(); + + assertEquals(CODEC.fromJson(CODEC.toJson(storage)), storage); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/UnimplementedHiveMetastore.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/UnimplementedHiveMetastore.java new file mode 100644 index 0000000000000..4dedd4302f7eb --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/UnimplementedHiveMetastore.java @@ -0,0 +1,221 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.metastore; + +import com.facebook.presto.hive.HiveType; +import com.facebook.presto.hive.PartitionStatistics; +import com.facebook.presto.spi.statistics.ColumnStatisticType; +import com.facebook.presto.spi.type.Type; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +class UnimplementedHiveMetastore + implements ExtendedHiveMetastore +{ + @Override + public Optional getDatabase(String databaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public List getAllDatabases() + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional
getTable(String databaseName, String tableName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Set getSupportedColumnStatistics(Type type) + { + throw new UnsupportedOperationException(); + } + + @Override + public PartitionStatistics getTableStatistics(String databaseName, String tableName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Map getPartitionStatistics(String databaseName, String tableName, Set partitionNames) + { + throw new UnsupportedOperationException(); + } + + @Override + public void updateTableStatistics(String databaseName, String tableName, Function update) + { + throw new UnsupportedOperationException(); + } + + @Override + public void updatePartitionStatistics(String databaseName, String tableName, String partitionName, Function update) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> getAllTables(String databaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> getAllViews(String databaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void createDatabase(Database database) + { + throw new UnsupportedOperationException(); + } + + @Override + public void dropDatabase(String databaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void renameDatabase(String databaseName, String newDatabaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void createTable(Table table, PrincipalPrivileges principalPrivileges) + { + throw new UnsupportedOperationException(); + } + + @Override + public void dropTable(String databaseName, String tableName, boolean deleteData) + { + throw new UnsupportedOperationException(); + } + + @Override + public void replaceTable(String databaseName, String tableName, Table newTable, PrincipalPrivileges principalPrivileges) + { + throw new UnsupportedOperationException(); + } + + @Override + public void renameTable(String databaseName, String tableName, String newDatabaseName, String newTableName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void addColumn(String databaseName, String tableName, String columnName, HiveType columnType, String columnComment) + { + throw new UnsupportedOperationException(); + } + + @Override + public void renameColumn(String databaseName, String tableName, String oldColumnName, String newColumnName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void dropColumn(String databaseName, String tableName, String columnName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional getPartition(String databaseName, String tableName, List partitionValues) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> getPartitionNames(String databaseName, String tableName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional> getPartitionNamesByParts(String databaseName, String tableName, List parts) + { + throw new UnsupportedOperationException(); + } + + @Override + public Map> getPartitionsByNames(String databaseName, String tableName, List partitionNames) + { + throw new UnsupportedOperationException(); + } + + @Override + public void addPartitions(String databaseName, String tableName, List partitions) + { + throw new UnsupportedOperationException(); + } + + @Override + public void dropPartition(String databaseName, String tableName, List parts, boolean deleteData) + { + throw new UnsupportedOperationException(); + } + + @Override + public void alterPartition(String databaseName, String tableName, PartitionWithStatistics partition) + { + throw new UnsupportedOperationException(); + } + + @Override + public Set getRoles(String user) + { + throw new UnsupportedOperationException(); + } + + @Override + public Set getDatabasePrivileges(String user, String databaseName) + { + throw new UnsupportedOperationException(); + } + + @Override + public Set getTablePrivileges(String user, String databaseName, String tableName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void grantTablePrivileges(String databaseName, String tableName, String grantee, Set privileges) + { + throw new UnsupportedOperationException(); + } + + @Override + public void revokeTablePrivileges(String databaseName, String tableName, String grantee, Set privileges) + { + throw new UnsupportedOperationException(); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestHiveClientGlueMetastore.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestHiveClientGlueMetastore.java index 287c2c3453e4e..701260f8985ff 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestHiveClientGlueMetastore.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestHiveClientGlueMetastore.java @@ -24,9 +24,17 @@ import java.io.File; +import static java.util.Locale.ENGLISH; +import static java.util.UUID.randomUUID; + public class TestHiveClientGlueMetastore extends AbstractTestHiveClientLocal { + public TestHiveClientGlueMetastore() + { + super("test_glue" + randomUUID().toString().toLowerCase(ENGLISH).replace("-", "")); + } + /** * GlueHiveMetastore currently uses AWS Default Credential Provider Chain, * See https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default @@ -80,4 +88,11 @@ public void testUpdatePartitionColumnStatisticsEmptyOptionalFields() { // column statistics are not supported by Glue } + + @Override + public void testStorePartitionWithStatistics() + throws Exception + { + testStorePartitionWithStatistics(STATISTICS_PARTITIONED_TABLE_COLUMNS, BASIC_STATISTICS_1, BASIC_STATISTICS_2, BASIC_STATISTICS_1, EMPTY_TABLE_STATISTICS); + } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestingMetastoreObjects.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestingMetastoreObjects.java index ee13635e75a1b..2bbbd6df7e37c 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestingMetastoreObjects.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/glue/TestingMetastoreObjects.java @@ -141,9 +141,9 @@ public static com.facebook.presto.hive.metastore.Column getPrestoTestColumn() private static final Consumer STORAGE_CONSUMER = storage -> { storage.setStorageFormat(StorageFormat.create("SerdeLib", "InputFormat", "OutputFormat")) - .setLocation("/test-tbl") - .setBucketProperty(Optional.empty()) - .setSerdeParameters(ImmutableMap.of()); + .setLocation("/test-tbl") + .setBucketProperty(Optional.empty()) + .setSerdeParameters(ImmutableMap.of()); }; private static String generateRandom() diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/InMemoryHiveMetastore.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/InMemoryHiveMetastore.java index 129702828cb2e..b6e1aa75ffcef 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/InMemoryHiveMetastore.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/InMemoryHiveMetastore.java @@ -17,6 +17,7 @@ import com.facebook.presto.hive.SchemaAlreadyExistsException; import com.facebook.presto.hive.TableAlreadyExistsException; import com.facebook.presto.hive.metastore.HivePrivilegeInfo; +import com.facebook.presto.hive.metastore.PartitionWithStatistics; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaNotFoundException; import com.facebook.presto.spi.SchemaTableName; @@ -27,10 +28,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.apache.hadoop.fs.Path; -import org.apache.hadoop.hive.common.FileUtils; import org.apache.hadoop.hive.metastore.TableType; import org.apache.hadoop.hive.metastore.api.Database; -import org.apache.hadoop.hive.metastore.api.FieldSchema; import org.apache.hadoop.hive.metastore.api.Partition; import org.apache.hadoop.hive.metastore.api.PrincipalPrivilegeSet; import org.apache.hadoop.hive.metastore.api.PrincipalType; @@ -58,6 +57,7 @@ import static com.facebook.presto.hive.HiveBasicStatistics.createEmptyStatistics; import static com.facebook.presto.hive.HiveUtil.toPartitionValues; import static com.facebook.presto.hive.metastore.HivePrivilegeInfo.HivePrivilege.OWNERSHIP; +import static com.facebook.presto.hive.metastore.thrift.ThriftMetastoreUtil.toMetastoreApiPartition; import static com.facebook.presto.spi.StandardErrorCode.SCHEMA_NOT_EMPTY; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; @@ -327,34 +327,19 @@ public synchronized Optional getDatabase(String databaseName) } @Override - public synchronized void addPartitions(String databaseName, String tableName, List partitions) + public synchronized void addPartitions(String databaseName, String tableName, List partitionsWithStatistics) { - Optional
table = getTable(databaseName, tableName); - if (!table.isPresent()) { - throw new TableNotFoundException(new SchemaTableName(databaseName, tableName)); - } - for (Partition partition : partitions) { - String partitionName = createPartitionName(partition, table.get()); - partition = partition.deepCopy(); + for (PartitionWithStatistics partitionWithStatistics : partitionsWithStatistics) { + Partition partition = toMetastoreApiPartition(partitionWithStatistics.getPartition()); if (partition.getParameters() == null) { partition.setParameters(ImmutableMap.of()); } - this.partitions.put(PartitionName.partition(databaseName, tableName, partitionName), partition); + PartitionName partitionKey = PartitionName.partition(databaseName, tableName, partitionWithStatistics.getPartitionName()); + partitions.put(partitionKey, partition); + partitionColumnStatistics.put(partitionKey, partitionWithStatistics.getStatistics()); } } - private static String createPartitionName(Partition partition, Table table) - { - return makePartName(table.getPartitionKeys(), partition.getValues()); - } - - private static String makePartName(List partitionColumns, List values) - { - checkArgument(partitionColumns.size() == values.size()); - List partitionColumnNames = partitionColumns.stream().map(FieldSchema::getName).collect(toList()); - return FileUtils.makePartName(partitionColumnNames, values); - } - @Override public synchronized void dropPartition(String databaseName, String tableName, List parts, boolean deleteData) { @@ -363,14 +348,15 @@ public synchronized void dropPartition(String databaseName, String tableName, Li } @Override - public synchronized void alterPartition(String databaseName, String tableName, Partition partition) + public synchronized void alterPartition(String databaseName, String tableName, PartitionWithStatistics partitionWithStatistics) { - Optional
table = getTable(databaseName, tableName); - if (!table.isPresent()) { - throw new TableNotFoundException(new SchemaTableName(databaseName, tableName)); + Partition partition = toMetastoreApiPartition(partitionWithStatistics.getPartition()); + if (partition.getParameters() == null) { + partition.setParameters(ImmutableMap.of()); } - String partitionName = createPartitionName(partition, table.get()); - this.partitions.put(PartitionName.partition(databaseName, tableName, partitionName), partition); + PartitionName partitionKey = PartitionName.partition(databaseName, tableName, partitionWithStatistics.getPartitionName()); + partitions.put(partitionKey, partition); + partitionColumnStatistics.put(partitionKey, partitionWithStatistics.getStatistics()); } @Override diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/MockHiveMetastoreClient.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/MockHiveMetastoreClient.java index 5f853ee27fd73..a8aa4f26c557a 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/MockHiveMetastoreClient.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/MockHiveMetastoreClient.java @@ -130,6 +130,13 @@ public Table getTable(String dbName, String tableName) TableType.MANAGED_TABLE.name()); } + @Override + public List getFields(String databaseName, String tableName) + throws TException + { + return ImmutableList.of(new FieldSchema("key", "string", null)); + } + @Override public List getTableColumnStatistics(String databaseName, String tableName, List columnNames) { diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestStaticHiveCluster.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestStaticHiveCluster.java index 5cf993b8f5561..8f8abc7b0fd5e 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestStaticHiveCluster.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestStaticHiveCluster.java @@ -13,8 +13,8 @@ */ package com.facebook.presto.hive.metastore.thrift; -import com.facebook.presto.spi.PrestoException; import io.airlift.units.Duration; +import org.apache.thrift.TException; import org.testng.annotations.Test; import java.util.List; @@ -48,6 +48,7 @@ public class TestStaticHiveCluster @Test public void testDefaultHiveMetastore() + throws TException { HiveCluster cluster = createHiveCluster(CONFIG_WITH_FALLBACK, singletonList(DEFAULT_CLIENT)); assertEquals(cluster.createMetastoreClient(), DEFAULT_CLIENT); @@ -55,6 +56,7 @@ public void testDefaultHiveMetastore() @Test public void testFallbackHiveMetastore() + throws TException { HiveCluster cluster = createHiveCluster(CONFIG_WITH_FALLBACK, asList(null, null, FALLBACK_CLIENT)); assertEquals(cluster.createMetastoreClient(), FALLBACK_CLIENT); @@ -76,6 +78,7 @@ public void testMetastoreFailedWithoutFallback() @Test public void testFallbackHiveMetastoreWithHiveUser() + throws TException { HiveCluster cluster = createHiveCluster(CONFIG_WITH_FALLBACK_WITH_USER, asList(null, null, FALLBACK_CLIENT)); assertEquals(cluster.createMetastoreClient(), FALLBACK_CLIENT); @@ -94,7 +97,7 @@ private static void assertCreateClientFails(HiveCluster cluster, String message) cluster.createMetastoreClient(); fail("expected exception"); } - catch (PrestoException e) { + catch (TException e) { assertContains(e.getMessage(), message); } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestThriftMetastoreUtil.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestThriftMetastoreUtil.java index a6862a0af2cda..9dde3c8849caa 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestThriftMetastoreUtil.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestThriftMetastoreUtil.java @@ -70,7 +70,7 @@ public void testLongStatsToColumnStatistics() longColumnStatsData.setNumNulls(1); longColumnStatsData.setNumDVs(20); ColumnStatisticsObj columnStatisticsObj = new ColumnStatisticsObj("my_col", BIGINT_TYPE_NAME, longStats(longColumnStatsData)); - HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.empty()); + HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(1000)); assertEquals(actual.getIntegerStatistics(), Optional.of(new IntegerStatistics(OptionalLong.of(0), OptionalLong.of(100)))); assertEquals(actual.getDoubleStatistics(), Optional.empty()); @@ -110,7 +110,7 @@ public void testDoubleStatsToColumnStatistics() doubleColumnStatsData.setNumNulls(1); doubleColumnStatsData.setNumDVs(20); ColumnStatisticsObj columnStatisticsObj = new ColumnStatisticsObj("my_col", DOUBLE_TYPE_NAME, doubleStats(doubleColumnStatsData)); - HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.empty()); + HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(1000)); assertEquals(actual.getIntegerStatistics(), Optional.empty()); assertEquals(actual.getDoubleStatistics(), Optional.of(new DoubleStatistics(OptionalDouble.of(0), OptionalDouble.of(100)))); @@ -152,7 +152,7 @@ public void testDecimalStatsToColumnStatistics() decimalColumnStatsData.setNumNulls(1); decimalColumnStatsData.setNumDVs(20); ColumnStatisticsObj columnStatisticsObj = new ColumnStatisticsObj("my_col", DECIMAL_TYPE_NAME, decimalStats(decimalColumnStatsData)); - HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.empty()); + HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(1000)); assertEquals(actual.getIntegerStatistics(), Optional.empty()); assertEquals(actual.getDoubleStatistics(), Optional.empty()); @@ -231,7 +231,7 @@ public void testDateStatsToColumnStatistics() dateColumnStatsData.setNumNulls(1); dateColumnStatsData.setNumDVs(20); ColumnStatisticsObj columnStatisticsObj = new ColumnStatisticsObj("my_col", DATE_TYPE_NAME, dateStats(dateColumnStatsData)); - HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.empty()); + HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(1000)); assertEquals(actual.getIntegerStatistics(), Optional.empty()); assertEquals(actual.getDoubleStatistics(), Optional.empty()); @@ -281,7 +281,7 @@ public void testStringStatsToColumnStatistics() assertEquals(actual.getMaxValueSizeInBytes(), OptionalLong.of(100)); assertEquals(actual.getTotalSizeInBytes(), OptionalLong.of(23)); assertEquals(actual.getNullsCount(), OptionalLong.of(1)); - assertEquals(actual.getDistinctValuesCount(), OptionalLong.of(19)); + assertEquals(actual.getDistinctValuesCount(), OptionalLong.of(1)); } @Test @@ -341,6 +341,28 @@ public void testEmptyBinaryStatsToColumnStatistics() assertEquals(actual.getDistinctValuesCount(), OptionalLong.empty()); } + @Test + public void testSingleDistinctValue() + { + DoubleColumnStatsData doubleColumnStatsData = new DoubleColumnStatsData(); + doubleColumnStatsData.setNumNulls(10); + doubleColumnStatsData.setNumDVs(1); + ColumnStatisticsObj columnStatisticsObj = new ColumnStatisticsObj("my_col", DOUBLE_TYPE_NAME, doubleStats(doubleColumnStatsData)); + HiveColumnStatistics actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(10)); + + assertEquals(actual.getNullsCount(), OptionalLong.of(10)); + assertEquals(actual.getDistinctValuesCount(), OptionalLong.of(0)); + + doubleColumnStatsData = new DoubleColumnStatsData(); + doubleColumnStatsData.setNumNulls(10); + doubleColumnStatsData.setNumDVs(1); + columnStatisticsObj = new ColumnStatisticsObj("my_col", DOUBLE_TYPE_NAME, doubleStats(doubleColumnStatsData)); + actual = fromMetastoreApiColumnStatistics(columnStatisticsObj, OptionalLong.of(11)); + + assertEquals(actual.getNullsCount(), OptionalLong.of(10)); + assertEquals(actual.getDistinctValuesCount(), OptionalLong.of(1)); + } + @Test public void testBasicStatisticsRoundTrip() { diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestingHiveCluster.java b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestingHiveCluster.java index e70c40e4aa75a..441f46048e084 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestingHiveCluster.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/metastore/thrift/TestingHiveCluster.java @@ -16,7 +16,7 @@ import com.facebook.presto.hive.HiveClientConfig; import com.facebook.presto.hive.authentication.NoHiveMetastoreAuthentication; import com.google.common.net.HostAndPort; -import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.TException; import java.util.Objects; @@ -36,13 +36,9 @@ public TestingHiveCluster(HiveClientConfig config, String host, int port) @Override public HiveMetastoreClient createMetastoreClient() + throws TException { - try { - return new HiveMetastoreClientFactory(config, new NoHiveMetastoreAuthentication()).create(address); - } - catch (TTransportException e) { - throw new RuntimeException(e); - } + return new HiveMetastoreClientFactory(config, new NoHiveMetastoreAuthentication()).create(address); } @Override diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/ParquetTester.java b/presto-hive/src/test/java/com/facebook/presto/hive/parquet/ParquetTester.java index 5ee8a3489c513..92011928657e2 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/ParquetTester.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/parquet/ParquetTester.java @@ -18,6 +18,7 @@ import com.facebook.presto.hive.HiveSessionProperties; import com.facebook.presto.hive.HiveStorageFormat; import com.facebook.presto.hive.OrcFileWriterConfig; +import com.facebook.presto.hive.ParquetFileWriterConfig; import com.facebook.presto.hive.benchmark.FileFormat; import com.facebook.presto.hive.parquet.write.MapKeyValuesSchemaConverter; import com.facebook.presto.hive.parquet.write.SingleLevelArrayMapKeyValuesSchemaConverter; @@ -109,12 +110,12 @@ public class ParquetTester { - public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("Asia/Katmandu"); + public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("America/Bahia_Banderas"); private static final boolean OPTIMIZED = true; private static final HiveClientConfig HIVE_CLIENT_CONFIG = createHiveClientConfig(false); private static final HdfsEnvironment HDFS_ENVIRONMENT = createTestHdfsEnvironment(HIVE_CLIENT_CONFIG); - private static final TestingConnectorSession SESSION = new TestingConnectorSession(new HiveSessionProperties(HIVE_CLIENT_CONFIG, new OrcFileWriterConfig()).getSessionProperties()); - private static final TestingConnectorSession SESSION_USE_NAME = new TestingConnectorSession(new HiveSessionProperties(createHiveClientConfig(true), new OrcFileWriterConfig()).getSessionProperties()); + private static final TestingConnectorSession SESSION = new TestingConnectorSession(new HiveSessionProperties(HIVE_CLIENT_CONFIG, new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); + private static final TestingConnectorSession SESSION_USE_NAME = new TestingConnectorSession(new HiveSessionProperties(createHiveClientConfig(true), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); private static final List TEST_COLUMN = singletonList("test"); private Set compressions = ImmutableSet.of(); @@ -410,8 +411,6 @@ private static HiveClientConfig createHiveClientConfig(boolean useParquetColumnN { HiveClientConfig config = new HiveClientConfig(); config.setHiveStorageFormat(HiveStorageFormat.PARQUET) - .setParquetOptimizedReaderEnabled(OPTIMIZED) - .setParquetPredicatePushdownEnabled(OPTIMIZED) .setUseParquetColumnNames(useParquetColumnNames); return config; } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestTupleDomainParquetPredicate.java b/presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestTupleDomainParquetPredicate.java deleted file mode 100644 index e0b6dea1ad06f..0000000000000 --- a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestTupleDomainParquetPredicate.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.hive.parquet; - -import com.facebook.presto.hive.parquet.predicate.ParquetDictionaryDescriptor; -import com.facebook.presto.hive.parquet.predicate.TupleDomainParquetPredicate; -import com.facebook.presto.spi.predicate.Domain; -import com.facebook.presto.spi.predicate.TupleDomain; -import com.facebook.presto.spi.predicate.ValueSet; -import com.facebook.presto.spi.type.VarcharType; -import io.airlift.slice.Slice; -import io.airlift.slice.Slices; -import org.testng.annotations.Test; -import parquet.column.ColumnDescriptor; -import parquet.column.statistics.BinaryStatistics; -import parquet.column.statistics.BooleanStatistics; -import parquet.column.statistics.DoubleStatistics; -import parquet.column.statistics.FloatStatistics; -import parquet.column.statistics.LongStatistics; -import parquet.column.statistics.Statistics; -import parquet.io.api.Binary; -import parquet.schema.PrimitiveType; - -import java.util.Map; -import java.util.Optional; - -import static com.facebook.presto.hive.parquet.ParquetEncoding.PLAIN_DICTIONARY; -import static com.facebook.presto.hive.parquet.predicate.TupleDomainParquetPredicate.getDomain; -import static com.facebook.presto.spi.predicate.Domain.all; -import static com.facebook.presto.spi.predicate.Domain.create; -import static com.facebook.presto.spi.predicate.Domain.notNull; -import static com.facebook.presto.spi.predicate.Domain.singleValue; -import static com.facebook.presto.spi.predicate.Range.range; -import static com.facebook.presto.spi.predicate.TupleDomain.withColumnDomains; -import static com.facebook.presto.spi.type.BigintType.BIGINT; -import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; -import static com.facebook.presto.spi.type.DoubleType.DOUBLE; -import static com.facebook.presto.spi.type.IntegerType.INTEGER; -import static com.facebook.presto.spi.type.RealType.REAL; -import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TinyintType.TINYINT; -import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; -import static com.facebook.presto.spi.type.VarcharType.createVarcharType; -import static io.airlift.slice.Slices.EMPTY_SLICE; -import static io.airlift.slice.Slices.utf8Slice; -import static java.lang.Float.floatToRawIntBits; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; -import static parquet.column.statistics.Statistics.getStatsBasedOnType; -import static parquet.schema.PrimitiveType.PrimitiveTypeName.BINARY; -import static parquet.schema.Type.Repetition.OPTIONAL; - -public class TestTupleDomainParquetPredicate -{ - @Test - public void testBoolean() - { - assertEquals(getDomain(BOOLEAN, 0, null), all(BOOLEAN)); - - assertEquals(getDomain(BOOLEAN, 10, booleanColumnStats(true, true)), singleValue(BOOLEAN, true)); - assertEquals(getDomain(BOOLEAN, 10, booleanColumnStats(false, false)), singleValue(BOOLEAN, false)); - - assertEquals(getDomain(BOOLEAN, 20, booleanColumnStats(false, true)), all(BOOLEAN)); - } - - private static BooleanStatistics booleanColumnStats(boolean minimum, boolean maximum) - { - BooleanStatistics statistics = new BooleanStatistics(); - statistics.setMinMax(minimum, maximum); - return statistics; - } - - @Test - public void testBigint() - { - assertEquals(getDomain(BIGINT, 0, null), all(BIGINT)); - - assertEquals(getDomain(BIGINT, 10, longColumnStats(100L, 100L)), singleValue(BIGINT, 100L)); - - assertEquals(getDomain(BIGINT, 10, longColumnStats(0L, 100L)), create(ValueSet.ofRanges(range(BIGINT, 0L, true, 100L, true)), false)); - } - - private static LongStatistics longColumnStats(long minimum, long maximum) - { - LongStatistics statistics = new LongStatistics(); - statistics.setMinMax(minimum, maximum); - return statistics; - } - - @Test - public void testInteger() - { - assertEquals(getDomain(INTEGER, 0, null), all(INTEGER)); - - assertEquals(getDomain(INTEGER, 10, longColumnStats(100, 100)), singleValue(INTEGER, 100L)); - - assertEquals(getDomain(INTEGER, 10, longColumnStats(0, 100)), create(ValueSet.ofRanges(range(INTEGER, 0L, true, 100L, true)), false)); - - assertEquals(getDomain(INTEGER, 20, longColumnStats(0, 2147483648L)), notNull(INTEGER)); - } - - @Test - public void testSmallint() - { - assertEquals(getDomain(SMALLINT, 0, null), all(SMALLINT)); - - assertEquals(getDomain(SMALLINT, 10, longColumnStats(100, 100)), singleValue(SMALLINT, 100L)); - - assertEquals(getDomain(SMALLINT, 10, longColumnStats(0, 100)), create(ValueSet.ofRanges(range(SMALLINT, 0L, true, 100L, true)), false)); - - assertEquals(getDomain(SMALLINT, 20, longColumnStats(0, 2147483648L)), notNull(SMALLINT)); - } - - @Test - public void testTinyint() - { - assertEquals(getDomain(TINYINT, 0, null), all(TINYINT)); - - assertEquals(getDomain(TINYINT, 10, longColumnStats(100, 100)), singleValue(TINYINT, 100L)); - - assertEquals(getDomain(TINYINT, 10, longColumnStats(0, 100)), create(ValueSet.ofRanges(range(TINYINT, 0L, true, 100L, true)), false)); - - assertEquals(getDomain(TINYINT, 20, longColumnStats(0, 2147483648L)), notNull(TINYINT)); - } - - @Test - public void testDouble() - { - assertEquals(getDomain(DOUBLE, 0, null), all(DOUBLE)); - - assertEquals(getDomain(DOUBLE, 10, doubleColumnStats(42.24, 42.24)), singleValue(DOUBLE, 42.24)); - - assertEquals(getDomain(DOUBLE, 10, doubleColumnStats(3.3, 42.24)), create(ValueSet.ofRanges(range(DOUBLE, 3.3, true, 42.24, true)), false)); - } - - private static DoubleStatistics doubleColumnStats(double minimum, double maximum) - { - DoubleStatistics statistics = new DoubleStatistics(); - statistics.setMinMax(minimum, maximum); - return statistics; - } - - @Test - public void testString() - { - assertEquals(getDomain(createUnboundedVarcharType(), 0, null), all(createUnboundedVarcharType())); - - assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("taco", "taco")), singleValue(createUnboundedVarcharType(), utf8Slice("taco"))); - - assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("apple", "taco")), create(ValueSet.ofRanges(range(createUnboundedVarcharType(), utf8Slice("apple"), true, utf8Slice("taco"), true)), false)); - - assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("中国", "美利坚")), create(ValueSet.ofRanges(range(createUnboundedVarcharType(), utf8Slice("中国"), true, utf8Slice("美利坚"), true)), false)); - } - - private static BinaryStatistics stringColumnStats(String minimum, String maximum) - { - BinaryStatistics statistics = new BinaryStatistics(); - statistics.setMinMax(Binary.fromString(minimum), Binary.fromString(maximum)); - return statistics; - } - - @Test - public void testFloat() - { - assertEquals(getDomain(REAL, 0, null), all(REAL)); - - float minimum = 4.3f; - float maximum = 40.3f; - - assertEquals(getDomain(REAL, 10, floatColumnStats(minimum, minimum)), singleValue(REAL, (long) floatToRawIntBits(minimum))); - - assertEquals( - getDomain(REAL, 10, floatColumnStats(minimum, maximum)), - create(ValueSet.ofRanges(range(REAL, (long) floatToRawIntBits(minimum), true, (long) floatToRawIntBits(maximum), true)), false)); - - assertEquals(getDomain(REAL, 10, floatColumnStats(maximum, minimum)), create(ValueSet.all(REAL), false)); - } - - @Test - public void testMatchesWithStatistics() - { - String value = "Test"; - ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[] {"path"}, BINARY, 0, 0); - RichColumnDescriptor column = new RichColumnDescriptor(columnDescriptor, new PrimitiveType(OPTIONAL, BINARY, "Test column")); - TupleDomain effectivePredicate = getEffectivePredicate(column, createVarcharType(255), utf8Slice(value)); - TupleDomainParquetPredicate parquetPredicate = new TupleDomainParquetPredicate(effectivePredicate, singletonList(column)); - Statistics stats = getStatsBasedOnType(column.getType()); - stats.setNumNulls(1L); - stats.setMinMaxFromBytes(value.getBytes(), value.getBytes()); - assertTrue(parquetPredicate.matches(2, singletonMap(column, stats))); - } - - @Test - public void testMatchesWithDescriptors() - { - ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[] {"path"}, BINARY, 0, 0); - RichColumnDescriptor column = new RichColumnDescriptor(columnDescriptor, new PrimitiveType(OPTIONAL, BINARY, "Test column")); - TupleDomain effectivePredicate = getEffectivePredicate(column, createVarcharType(255), EMPTY_SLICE); - TupleDomainParquetPredicate parquetPredicate = new TupleDomainParquetPredicate(effectivePredicate, singletonList(column)); - ParquetDictionaryPage page = new ParquetDictionaryPage(Slices.wrappedBuffer(new byte[] {0, 0, 0, 0}), 1, PLAIN_DICTIONARY); - assertTrue(parquetPredicate.matches(singletonMap(column, new ParquetDictionaryDescriptor(column, Optional.of(page))))); - } - - private TupleDomain getEffectivePredicate(RichColumnDescriptor column, VarcharType type, Slice value) - { - ColumnDescriptor predicateColumn = new ColumnDescriptor(column.getPath(), column.getType(), 0, 0); - Domain predicateDomain = singleValue(type, value); - Map predicateColumns = singletonMap(predicateColumn, predicateDomain); - return withColumnDomains(predicateColumns); - } - - private static FloatStatistics floatColumnStats(float minimum, float maximum) - { - FloatStatistics statistics = new FloatStatistics(); - statistics.setMinMax(minimum, maximum); - return statistics; - } -} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/predicate/TestParquetPredicateUtils.java b/presto-hive/src/test/java/com/facebook/presto/hive/parquet/predicate/TestParquetPredicateUtils.java index c6e507375aa08..43bf04e5e3be7 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/predicate/TestParquetPredicateUtils.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/parquet/predicate/TestParquetPredicateUtils.java @@ -15,7 +15,7 @@ import com.facebook.presto.hive.HiveColumnHandle; import com.facebook.presto.hive.HiveType; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.ArrayType; @@ -39,9 +39,9 @@ import java.util.Set; import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getDescriptors; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.getParquetTupleDomain; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.isOnlyDictionaryEncodingPages; +import static com.facebook.presto.hive.parquet.ParquetPageSourceFactory.getParquetTupleDomain; +import static com.facebook.presto.parquet.ParquetTypeUtils.getDescriptors; +import static com.facebook.presto.parquet.predicate.PredicateUtils.isOnlyDictionaryEncodingPages; import static com.facebook.presto.spi.block.MethodHandleUtil.methodHandle; import static com.facebook.presto.spi.predicate.TupleDomain.withColumnDomains; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -175,8 +175,8 @@ public void testParquetTupleDomainMap() MessageType fileSchema = new MessageType("hive_schema", new GroupType(OPTIONAL, "my_map", new GroupType(REPEATED, "map", - new PrimitiveType(REQUIRED, INT32, "key"), - new PrimitiveType(OPTIONAL, INT32, "value")))); + new PrimitiveType(REQUIRED, INT32, "key"), + new PrimitiveType(OPTIONAL, INT32, "value")))); Map, RichColumnDescriptor> descriptorsByPath = getDescriptors(fileSchema, fileSchema); TupleDomain tupleDomain = getParquetTupleDomain(descriptorsByPath, domain); diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/s3/MockAmazonS3.java b/presto-hive/src/test/java/com/facebook/presto/hive/s3/MockAmazonS3.java index bd546523ea81f..5f516d1e7bfdc 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/s3/MockAmazonS3.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/s3/MockAmazonS3.java @@ -15,6 +15,7 @@ import com.amazonaws.services.s3.AbstractAmazonS3; import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.GetObjectMetadataRequest; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.ObjectMetadata; @@ -30,6 +31,7 @@ public class MockAmazonS3 private int getObjectHttpCode = SC_OK; private int getObjectMetadataHttpCode = SC_OK; private GetObjectMetadataRequest getObjectMetadataRequest; + private CannedAccessControlList acl; public void setGetObjectHttpErrorCode(int getObjectHttpErrorCode) { @@ -41,6 +43,11 @@ public void setGetObjectMetadataHttpCode(int getObjectMetadataHttpCode) this.getObjectMetadataHttpCode = getObjectMetadataHttpCode; } + public CannedAccessControlList getAcl() + { + return this.acl; + } + public GetObjectMetadataRequest getGetObjectMetadataRequest() { return getObjectMetadataRequest; @@ -72,6 +79,7 @@ public S3Object getObject(GetObjectRequest getObjectRequest) @Override public PutObjectResult putObject(PutObjectRequest putObjectRequest) { + this.acl = putObjectRequest.getCannedAcl(); return new PutObjectResult(); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestHiveS3Config.java b/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestHiveS3Config.java index 6451fcc8f8eec..2d03e8d95c7c4 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestHiveS3Config.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestHiveS3Config.java @@ -57,7 +57,8 @@ public void testDefaults() .setS3MaxConnections(500) .setS3StagingDirectory(new File(StandardSystemProperty.JAVA_IO_TMPDIR.value())) .setPinS3ClientToCurrentRegion(false) - .setS3UserAgentPrefix("")); + .setS3UserAgentPrefix("") + .setS3AclType(PrestoS3AclType.PRIVATE)); } @Test @@ -88,6 +89,7 @@ public void testExplicitPropertyMappings() .put("hive.s3.staging-directory", "/s3-staging") .put("hive.s3.pin-client-to-current-region", "true") .put("hive.s3.user-agent-prefix", "user-agent-prefix") + .put("hive.s3.upload-acl-type", "PUBLIC_READ") .build(); HiveS3Config expected = new HiveS3Config() @@ -114,7 +116,8 @@ public void testExplicitPropertyMappings() .setS3MaxConnections(77) .setS3StagingDirectory(new File("/s3-staging")) .setPinS3ClientToCurrentRegion(true) - .setS3UserAgentPrefix("user-agent-prefix"); + .setS3UserAgentPrefix("user-agent-prefix") + .setS3AclType(PrestoS3AclType.PUBLIC_READ); assertFullMapping(properties, expected); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestPrestoS3FileSystem.java b/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestPrestoS3FileSystem.java index 9224a2b393a0c..5acf318058736 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestPrestoS3FileSystem.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/s3/TestPrestoS3FileSystem.java @@ -23,6 +23,7 @@ import com.amazonaws.services.s3.AmazonS3EncryptionClient; import com.amazonaws.services.s3.S3ClientOptions; import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.amazonaws.services.s3.model.EncryptionMaterials; import com.amazonaws.services.s3.model.EncryptionMaterialsProvider; import com.facebook.presto.hive.s3.PrestoS3FileSystem.UnrecoverableS3OperationException; @@ -44,6 +45,7 @@ import java.util.Map; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ACCESS_KEY; +import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ACL_TYPE; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_CREDENTIALS_PROVIDER; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ENCRYPTION_MATERIALS_PROVIDER; import static com.facebook.presto.hive.s3.S3ConfigurationUpdater.S3_ENDPOINT; @@ -531,4 +533,41 @@ public AWSCredentials getCredentials() @Override public void refresh() {} } + + @Test + public void testDefaultAcl() + throws Exception + { + Configuration config = new Configuration(); + + try (PrestoS3FileSystem fs = new PrestoS3FileSystem()) { + MockAmazonS3 s3 = new MockAmazonS3(); + String expectedBucketName = "test-bucket"; + fs.initialize(new URI("s3n://" + expectedBucketName + "/"), config); + fs.setS3Client(s3); + try (FSDataOutputStream stream = fs.create(new Path("s3n://test-bucket/test"))) { + // initiate an upload by creating a stream & closing it immediately + } + assertEquals(CannedAccessControlList.Private, s3.getAcl()); + } + } + + @Test + public void testFullBucketOwnerControlAcl() + throws Exception + { + Configuration config = new Configuration(); + config.set(S3_ACL_TYPE, "BUCKET_OWNER_FULL_CONTROL"); + + try (PrestoS3FileSystem fs = new PrestoS3FileSystem()) { + MockAmazonS3 s3 = new MockAmazonS3(); + String expectedBucketName = "test-bucket"; + fs.initialize(new URI("s3n://" + expectedBucketName + "/"), config); + fs.setS3Client(s3); + try (FSDataOutputStream stream = fs.create(new Path("s3n://test-bucket/test"))) { + // initiate an upload by creating a stream & closing it immediately + } + assertEquals(CannedAccessControlList.BucketOwnerFullControl, s3.getAcl()); + } + } } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/security/TestLegacyAccessControl.java b/presto-hive/src/test/java/com/facebook/presto/hive/security/TestLegacyAccessControl.java new file mode 100644 index 0000000000000..ded7cecf073d5 --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/security/TestLegacyAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.security; + +import com.facebook.presto.spi.connector.ConnectorAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestLegacyAccessControl +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(ConnectorAccessControl.class, LegacyAccessControl.class); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/security/TestPartitionsAwareAccessControl.java b/presto-hive/src/test/java/com/facebook/presto/hive/security/TestPartitionsAwareAccessControl.java deleted file mode 100644 index fef0446c89c95..0000000000000 --- a/presto-hive/src/test/java/com/facebook/presto/hive/security/TestPartitionsAwareAccessControl.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.hive.security; - -import com.facebook.presto.spi.connector.ConnectorAccessControl; -import org.testng.annotations.Test; - -import java.lang.reflect.Method; - -import static java.lang.String.format; - -public class TestPartitionsAwareAccessControl -{ - @Test - public void testEverythingDelegated() - throws Exception - { - checkEverythingImplemented(ConnectorAccessControl.class, PartitionsAwareAccessControl.class); - } - - private static void checkEverythingImplemented(Class interfaceClass, Class implementationClass) - throws ReflectiveOperationException - { - for (Method interfaceMethod : interfaceClass.getMethods()) { - Method implementationMethod = implementationClass.getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes()); - if (interfaceMethod.equals(implementationMethod) && interfaceMethod.getAnnotation(Deprecated.class) == null) { - throw new AssertionError(format( - "Method should be overridden in %s: %s", - implementationClass, - interfaceMethod)); - } - } - } -} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/security/TestSqlStandardAccessControl.java b/presto-hive/src/test/java/com/facebook/presto/hive/security/TestSqlStandardAccessControl.java new file mode 100644 index 0000000000000..ddecc5c691b8b --- /dev/null +++ b/presto-hive/src/test/java/com/facebook/presto/hive/security/TestSqlStandardAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.hive.security; + +import com.facebook.presto.spi.connector.ConnectorAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestSqlStandardAccessControl +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(ConnectorAccessControl.class, SqlStandardAccessControl.class); + } +} diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/statistics/TestMetastoreHiveStatisticsProvider.java b/presto-hive/src/test/java/com/facebook/presto/hive/statistics/TestMetastoreHiveStatisticsProvider.java index 5120fbf0c57e2..1fec0052ee728 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/statistics/TestMetastoreHiveStatisticsProvider.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/statistics/TestMetastoreHiveStatisticsProvider.java @@ -13,41 +13,898 @@ */ package com.facebook.presto.hive.statistics; +import com.facebook.presto.hive.HiveBasicStatistics; +import com.facebook.presto.hive.HiveClientConfig; +import com.facebook.presto.hive.HiveColumnHandle; import com.facebook.presto.hive.HivePartition; +import com.facebook.presto.hive.HiveSessionProperties; +import com.facebook.presto.hive.OrcFileWriterConfig; +import com.facebook.presto.hive.ParquetFileWriterConfig; +import com.facebook.presto.hive.PartitionStatistics; +import com.facebook.presto.hive.metastore.DateStatistics; +import com.facebook.presto.hive.metastore.DecimalStatistics; +import com.facebook.presto.hive.metastore.DoubleStatistics; +import com.facebook.presto.hive.metastore.HiveColumnStatistics; +import com.facebook.presto.hive.metastore.IntegerStatistics; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaTableName; +import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; +import com.facebook.presto.spi.statistics.Estimate; +import com.facebook.presto.spi.statistics.TableStatistics; +import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.testing.TestingConnectorSession; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.joda.time.DateTimeZone; import org.testng.annotations.Test; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalLong; + +import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.PARTITION_KEY; +import static com.facebook.presto.hive.HiveColumnHandle.ColumnType.REGULAR; +import static com.facebook.presto.hive.HiveErrorCode.HIVE_CORRUPTED_COLUMN_STATISTICS; +import static com.facebook.presto.hive.HivePartition.UNPARTITIONED_ID; +import static com.facebook.presto.hive.HivePartitionManager.parsePartition; +import static com.facebook.presto.hive.HiveType.HIVE_LONG; +import static com.facebook.presto.hive.HiveType.HIVE_STRING; +import static com.facebook.presto.hive.HiveUtil.parsePartitionValue; +import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createBooleanColumnStatistics; +import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createDateColumnStatistics; +import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createDecimalColumnStatistics; +import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createDoubleColumnStatistics; +import static com.facebook.presto.hive.metastore.HiveColumnStatistics.createIntegerColumnStatistics; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateAverageRowsPerPartition; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateDataSize; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateDataSizeForPartitioningKey; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateDistinctPartitionKeys; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateDistinctValuesCount; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateNullsFraction; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateNullsFractionForPartitioningKey; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateRange; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.calculateRangeForPartitioningKey; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.convertPartitionValueToDouble; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.createDataColumnStatistics; import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.getPartitionsSample; +import static com.facebook.presto.hive.statistics.MetastoreHiveStatisticsProvider.validatePartitionStatistics; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.DecimalType.createDecimalType; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.spi.type.SmallintType.SMALLINT; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static java.lang.Double.NaN; +import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.testng.Assert.assertEquals; public class TestMetastoreHiveStatisticsProvider { + private static final SchemaTableName TABLE = new SchemaTableName("schema", "table"); + private static final String PARTITION = "partition"; + private static final String COLUMN = "column"; + private static final DecimalType DECIMAL = createDecimalType(5, 3); + + private static final HiveColumnHandle PARTITION_COLUMN_1 = new HiveColumnHandle("p1", HIVE_STRING, VARCHAR.getTypeSignature(), 0, PARTITION_KEY, Optional.empty()); + private static final HiveColumnHandle PARTITION_COLUMN_2 = new HiveColumnHandle("p2", HIVE_LONG, BIGINT.getTypeSignature(), 1, PARTITION_KEY, Optional.empty()); + @Test public void testGetPartitionsSample() { - assertEquals(getPartitionsSample(ImmutableList.of(partition("p1")), 1), ImmutableList.of(partition("p1"))); - assertEquals(getPartitionsSample(ImmutableList.of(partition("p1")), 2), ImmutableList.of(partition("p1"))); + HivePartition p1 = partition("p1=string1/p2=1234"); + HivePartition p2 = partition("p1=string2/p2=2345"); + HivePartition p3 = partition("p1=string3/p2=3456"); + HivePartition p4 = partition("p1=string4/p2=4567"); + HivePartition p5 = partition("p1=string5/p2=5678"); + + assertEquals(getPartitionsSample(ImmutableList.of(p1), 1), ImmutableList.of(p1)); + assertEquals(getPartitionsSample(ImmutableList.of(p1), 2), ImmutableList.of(p1)); + assertEquals(getPartitionsSample(ImmutableList.of(p1, p2), 2), ImmutableList.of(p1, p2)); + assertEquals(getPartitionsSample(ImmutableList.of(p1, p2, p3), 2), ImmutableList.of(p1, p3)); + assertEquals(getPartitionsSample(ImmutableList.of(p1, p2, p3, p4), 1), getPartitionsSample(ImmutableList.of(p1, p2, p3, p4), 1)); + assertEquals(getPartitionsSample(ImmutableList.of(p1, p2, p3, p4), 3), getPartitionsSample(ImmutableList.of(p1, p2, p3, p4), 3)); + assertEquals(getPartitionsSample(ImmutableList.of(p1, p2, p3, p4, p5), 3), ImmutableList.of(p1, p5, p4)); + } + + @Test + public void testValidatePartitionStatistics() + { + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(-1, 0, 0, 0)) + .build(), + invalidPartitionStatistics("fileCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, -1, 0, 0)) + .build(), + invalidPartitionStatistics("rowCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, -1, 0)) + .build(), + invalidPartitionStatistics("inMemoryDataSizeInBytes must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, -1)) + .build(), + invalidPartitionStatistics("onDiskDataSizeInBytes must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setMaxValueSizeInBytes(-1).build())) + .build(), + invalidColumnStatistics("maxValueSizeInBytes must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setTotalSizeInBytes(-1).build())) + .build(), + invalidColumnStatistics("totalSizeInBytes must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setNullsCount(-1).build())) + .build(), + invalidColumnStatistics("nullsCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setNullsCount(1).build())) + .build(), + invalidColumnStatistics("nullsCount must be less than or equal to rowCount. nullsCount: 1. rowCount: 0.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setDistinctValuesCount(-1).build())) + .build(), + invalidColumnStatistics("distinctValuesCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setDistinctValuesCount(1).build())) + .build(), + invalidColumnStatistics("distinctValuesCount must be less than or equal to rowCount. distinctValuesCount: 1. rowCount: 0.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 1, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setDistinctValuesCount(1).setNullsCount(1).build())) + .build(), + invalidColumnStatistics("distinctValuesCount must be less than or equal to nonNullsCount. distinctValuesCount: 1. nonNullsCount: 0.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createIntegerColumnStatistics(OptionalLong.of(1), OptionalLong.of(-1), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("integerStatistics.min must be less than or equal to integerStatistics.max. integerStatistics.min: 1. integerStatistics.max: -1.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createDoubleColumnStatistics(OptionalDouble.of(1), OptionalDouble.of(-1), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("doubleStatistics.min must be less than or equal to doubleStatistics.max. doubleStatistics.min: 1.0. doubleStatistics.max: -1.0.")); + validatePartitionStatistics( + TABLE, + ImmutableMap.of( + PARTITION, + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createDoubleColumnStatistics(OptionalDouble.of(NaN), OptionalDouble.of(NaN), OptionalLong.empty(), OptionalLong.empty()))) + .build())); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createDecimalColumnStatistics(Optional.of(BigDecimal.valueOf(1)), Optional.of(BigDecimal.valueOf(-1)), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("decimalStatistics.min must be less than or equal to decimalStatistics.max. decimalStatistics.min: 1. decimalStatistics.max: -1.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createDateColumnStatistics(Optional.of(LocalDate.ofEpochDay(1)), Optional.of(LocalDate.ofEpochDay(-1)), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("dateStatistics.min must be less than or equal to dateStatistics.max. dateStatistics.min: 1970-01-02. dateStatistics.max: 1969-12-31.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createBooleanColumnStatistics(OptionalLong.of(-1), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("trueCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createBooleanColumnStatistics(OptionalLong.empty(), OptionalLong.of(-1), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("falseCount must be greater than or equal to zero: -1")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createBooleanColumnStatistics(OptionalLong.of(1), OptionalLong.empty(), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("booleanStatistics.trueCount must be less than or equal to rowCount. booleanStatistics.trueCount: 1. rowCount: 0.")); + assertInvalidStatistics( + PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(0, 0, 0, 0)) + .setColumnStatistics(ImmutableMap.of(COLUMN, createBooleanColumnStatistics(OptionalLong.empty(), OptionalLong.of(1), OptionalLong.empty()))) + .build(), + invalidColumnStatistics("booleanStatistics.falseCount must be less than or equal to rowCount. booleanStatistics.falseCount: 1. rowCount: 0.")); + } + + @Test + public void testCalculateAverageRowsPerPartition() + { + assertThat(calculateAverageRowsPerPartition(ImmutableList.of())).isEmpty(); + assertThat(calculateAverageRowsPerPartition(ImmutableList.of(PartitionStatistics.empty()))).isEmpty(); + assertThat(calculateAverageRowsPerPartition(ImmutableList.of(PartitionStatistics.empty(), PartitionStatistics.empty()))).isEmpty(); + assertEquals(calculateAverageRowsPerPartition(ImmutableList.of(rowsCount(10))), OptionalDouble.of(10)); + assertEquals(calculateAverageRowsPerPartition(ImmutableList.of(rowsCount(10), PartitionStatistics.empty())), OptionalDouble.of(10)); + assertEquals(calculateAverageRowsPerPartition(ImmutableList.of(rowsCount(10), rowsCount(20))), OptionalDouble.of(15)); + assertEquals(calculateAverageRowsPerPartition(ImmutableList.of(rowsCount(10), rowsCount(20), PartitionStatistics.empty())), OptionalDouble.of(15)); + } + + @Test + public void testCalculateDistinctPartitionKeys() + { + assertEquals(calculateDistinctPartitionKeys(PARTITION_COLUMN_1, ImmutableList.of(), ImmutableMap.of(), 1000), 0); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=string2/p2=1234", rowsCount(1)), + 2000), + 2); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=string2/p2=1234", rowsCount(0)), + 2000), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000), + 2); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 0), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_2, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=string2/p2=1234", rowsCount(1)), + 2000), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_2, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string1/p2=1235")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=string1/p2=1235", rowsCount(1)), + 2000), + 2); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234"), partition("p1=string1/p2=1235")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", rowsCount(1000), "p1=string1/p2=1235", rowsCount(1)), + 2000), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_2, + ImmutableList.of(partition("p1=123/p2=__HIVE_DEFAULT_PARTITION__"), partition("p1=string1/p2=1235")), + ImmutableMap.of("p1=123/p2=__HIVE_DEFAULT_PARTITION__", rowsCount(1000), "p1=string1/p2=1235", rowsCount(1)), + 2000), + 1); + assertEquals( + calculateDistinctPartitionKeys( + PARTITION_COLUMN_2, + ImmutableList.of(partition("p1=123/p2=__HIVE_DEFAULT_PARTITION__"), partition("p1=string1/p2=__HIVE_DEFAULT_PARTITION__")), + ImmutableMap.of("p1=123/p2=__HIVE_DEFAULT_PARTITION__", rowsCount(1000), "p1=string1/p2=__HIVE_DEFAULT_PARTITION__", rowsCount(1)), + 2000), + 0); + } + + @Test + public void testCalculateNullsFractionForPartitioningKey() + { + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000, + 0), + 0.0); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000, + 4000), + 0.0); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", rowsCount(1000)), + 2000, + 4000), + 0.25); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", PartitionStatistics.empty()), + 2000, + 4000), + 0.5); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234")), + ImmutableMap.of(), + 2000, + 4000), + 0.5); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234"), partition("p1=__HIVE_DEFAULT_PARTITION__/p2=4321")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", rowsCount(1000), "p1=__HIVE_DEFAULT_PARTITION__/p2=4321", rowsCount(2000)), + 3000, + 4000), + 0.75); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234"), partition("p1=__HIVE_DEFAULT_PARTITION__/p2=4321")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", rowsCount(1000), "p1=__HIVE_DEFAULT_PARTITION__/p2=4321", PartitionStatistics.empty()), + 3000, + 4000), + 1.0); + assertEquals( + calculateNullsFractionForPartitioningKey( + PARTITION_COLUMN_1, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234"), partition("p1=__HIVE_DEFAULT_PARTITION__/p2=4321")), + ImmutableMap.of("p1=__HIVE_DEFAULT_PARTITION__/p2=1234", rowsCount(1000), "p1=__HIVE_DEFAULT_PARTITION__/p2=4321", PartitionStatistics.empty()), + 4000, + 4000), + 1.0); + } + + @Test + public void testCalculateDataSizeForPartitioningKey() + { assertEquals( - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2")), 2), - ImmutableList.of(partition("p1"), partition("p2"))); + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000), + Estimate.unknown()); assertEquals( - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3")), 2), - ImmutableList.of(partition("p1"), partition("p3"))); + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000)), + 2000), + Estimate.of(7000)); + assertEquals( + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", PartitionStatistics.empty()), + 2000), + Estimate.of(14000)); + assertEquals( + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=str2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=str2/p2=1234", rowsCount(2000)), + 3000), + Estimate.of(15000)); + assertEquals( + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=str2/p2=1234")), + ImmutableMap.of("p1=string1/p2=1234", rowsCount(1000), "p1=str2/p2=1234", PartitionStatistics.empty()), + 3000), + Estimate.of(19000)); + assertEquals( + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=str2/p2=1234")), + ImmutableMap.of(), + 3000), + Estimate.of(33000)); + assertEquals( + calculateDataSizeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=__HIVE_DEFAULT_PARTITION__/p2=1234"), partition("p1=str2/p2=1234")), + ImmutableMap.of(), + 3000), + Estimate.of(12000)); + } + + @Test + public void testCalculateRangeForPartitioningKey() + { + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_1, + VARCHAR, + ImmutableList.of(partition("p1=string1/p2=1234"))), + Optional.empty()); + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=__HIVE_DEFAULT_PARTITION__"))), + Optional.empty()); + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=__HIVE_DEFAULT_PARTITION__"), partition("p1=string1/p2=__HIVE_DEFAULT_PARTITION__"))), + Optional.empty()); + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=__HIVE_DEFAULT_PARTITION__"), partition("p1=string1/p2=1"))), + Optional.of(new DoubleRange(1, 1))); + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=2"), partition("p1=string1/p2=1"))), + Optional.of(new DoubleRange(1, 2))); + assertEquals( + calculateRangeForPartitioningKey( + PARTITION_COLUMN_2, + BIGINT, + ImmutableList.of(partition("p1=string1/p2=2"), partition("p1=string1/p2=3"), partition("p1=string1/p2=1"))), + Optional.of(new DoubleRange(1, 3))); + } + + @Test + public void testConvertPartitionValueToDouble() + { + assertConvertPartitionValueToDouble(BIGINT, "123456", 123456); + assertConvertPartitionValueToDouble(INTEGER, "12345", 12345); + assertConvertPartitionValueToDouble(SMALLINT, "1234", 1234); + assertConvertPartitionValueToDouble(TINYINT, "123", 123); + assertConvertPartitionValueToDouble(DOUBLE, "0.1", 0.1); + assertConvertPartitionValueToDouble(REAL, "0.2", (double) (float) 0.2); + assertConvertPartitionValueToDouble(createDecimalType(5, 2), "123.45", 123.45); + assertConvertPartitionValueToDouble(createDecimalType(25, 5), "12345678901234567890.12345", 12345678901234567890.12345); + assertConvertPartitionValueToDouble(DATE, "1970-01-02", 1); + } + + private static void assertConvertPartitionValueToDouble(Type type, String value, double expected) + { + Object prestoValue = parsePartitionValue(format("p=%s", value), value, type, DateTimeZone.getDefault()).getValue(); + assertEquals(convertPartitionValueToDouble(type, prestoValue), expected); + } + + @Test + public void testCreateDataColumnStatistics() + { + assertEquals(createDataColumnStatistics(COLUMN, BIGINT, 1000, ImmutableList.of()), ColumnStatistics.empty()); + assertEquals( + createDataColumnStatistics(COLUMN, BIGINT, 1000, ImmutableList.of(PartitionStatistics.empty(), PartitionStatistics.empty())), + ColumnStatistics.empty()); + assertEquals( + createDataColumnStatistics( + COLUMN, + BIGINT, + 1000, + ImmutableList.of(new PartitionStatistics(HiveBasicStatistics.createZeroStatistics(), ImmutableMap.of("column2", HiveColumnStatistics.empty())))), + ColumnStatistics.empty()); + } + + @Test + public void testCalculateDistinctValuesCount() + { + assertEquals(calculateDistinctValuesCount(ImmutableList.of()), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(HiveColumnStatistics.empty())), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(HiveColumnStatistics.empty(), HiveColumnStatistics.empty())), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(distinctValuesCount(1))), Estimate.of(1)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(distinctValuesCount(1), distinctValuesCount(2))), Estimate.of(2)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(distinctValuesCount(1), HiveColumnStatistics.empty())), Estimate.of(1)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.empty(), OptionalLong.empty(), OptionalLong.empty()))), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.of(1), OptionalLong.of(0), OptionalLong.empty()))), Estimate.of(1)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.of(10), OptionalLong.empty(), OptionalLong.empty()))), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.of(10), OptionalLong.of(10), OptionalLong.empty()))), Estimate.of(2)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.empty(), OptionalLong.of(10), OptionalLong.empty()))), Estimate.unknown()); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.of(0), OptionalLong.of(10), OptionalLong.empty()))), Estimate.of(1)); + assertEquals(calculateDistinctValuesCount(ImmutableList.of(createBooleanColumnStatistics(OptionalLong.of(0), OptionalLong.of(0), OptionalLong.empty()))), Estimate.of(0)); + assertEquals( + calculateDistinctValuesCount(ImmutableList.of( + createBooleanColumnStatistics(OptionalLong.of(0), OptionalLong.of(10), OptionalLong.empty()), + createBooleanColumnStatistics(OptionalLong.of(1), OptionalLong.of(10), OptionalLong.empty()))), + Estimate.of(2)); + } + + @Test + public void testCalculateNullsFraction() + { + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of()), Estimate.unknown()); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(PartitionStatistics.empty())), Estimate.unknown()); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(rowsCount(1000))), Estimate.unknown()); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(rowsCount(1000), nullsCount(500))), Estimate.unknown()); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(rowsCount(1000), nullsCount(500), rowsCountAndNullsCount(1000, 500))), Estimate.of(0.5)); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(rowsCountAndNullsCount(2000, 200), rowsCountAndNullsCount(1000, 100))), Estimate.of(0.1)); + assertEquals(calculateNullsFraction(COLUMN, ImmutableList.of(rowsCountAndNullsCount(0, 0), rowsCountAndNullsCount(0, 0))), Estimate.of(0)); + } + + @Test + public void testCalculateDataSize() + { + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(), 0), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(), 1000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(PartitionStatistics.empty()), 1000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(rowsCount(1000)), 1000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(dataSize(1000)), 1000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(dataSize(1000), rowsCount(1000)), 1000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(rowsCountAndDataSize(500, 1000)), 2000), Estimate.of(4000)); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(rowsCountAndDataSize(0, 0)), 2000), Estimate.unknown()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(rowsCountAndDataSize(0, 0)), 0), Estimate.zero()); + assertEquals(calculateDataSize(COLUMN, ImmutableList.of(rowsCountAndDataSize(1000, 0)), 2000), Estimate.of(0)); assertEquals( - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3"), partition("p4")), 1), - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3"), partition("p4")), 1)); + calculateDataSize( + COLUMN, + ImmutableList.of( + rowsCountAndDataSize(500, 1000), + rowsCountAndDataSize(1000, 5000)), + 5000), + Estimate.of(20000)); assertEquals( - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3"), partition("p4")), 3), - getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3"), partition("p4")), 3)); - assertThat(getPartitionsSample(ImmutableList.of(partition("p1"), partition("p2"), partition("p3"), partition("p4")), 3)) - .contains(partition("p1"), partition("p3")); + calculateDataSize( + COLUMN, + ImmutableList.of( + dataSize(1000), + rowsCountAndDataSize(500, 1000), + rowsCount(3000), + rowsCountAndDataSize(1000, 5000)), + 5000), + Estimate.of(20000)); + } + + @Test + public void testCalculateRange() + { + assertEquals(calculateRange(VARCHAR, ImmutableList.of()), Optional.empty()); + assertEquals(calculateRange(VARCHAR, ImmutableList.of(integerRange(OptionalLong.empty(), OptionalLong.empty()))), Optional.empty()); + assertEquals(calculateRange(VARCHAR, ImmutableList.of(integerRange(1, 2))), Optional.empty()); + assertEquals(calculateRange(BIGINT, ImmutableList.of(integerRange(1, 2))), Optional.of(new DoubleRange(1, 2))); + assertEquals(calculateRange(BIGINT, ImmutableList.of(integerRange(Long.MIN_VALUE, Long.MAX_VALUE))), Optional.of(new DoubleRange(Long.MIN_VALUE, Long.MAX_VALUE))); + assertEquals(calculateRange(INTEGER, ImmutableList.of(integerRange(Long.MIN_VALUE, Long.MAX_VALUE))), Optional.of(new DoubleRange(Integer.MIN_VALUE, Integer.MAX_VALUE))); + assertEquals(calculateRange(SMALLINT, ImmutableList.of(integerRange(Long.MIN_VALUE, Long.MAX_VALUE))), Optional.of(new DoubleRange(Short.MIN_VALUE, Short.MAX_VALUE))); + assertEquals(calculateRange(TINYINT, ImmutableList.of(integerRange(Long.MIN_VALUE, Long.MAX_VALUE))), Optional.of(new DoubleRange(Byte.MIN_VALUE, Byte.MAX_VALUE))); + assertEquals(calculateRange(BIGINT, ImmutableList.of(integerRange(1, 5), integerRange(3, 7))), Optional.of(new DoubleRange(1, 7))); + assertEquals(calculateRange(BIGINT, ImmutableList.of(integerRange(OptionalLong.empty(), OptionalLong.empty()), integerRange(3, 7))), Optional.of(new DoubleRange(3, 7))); + assertEquals(calculateRange(BIGINT, ImmutableList.of(integerRange(OptionalLong.empty(), OptionalLong.of(8)), integerRange(3, 7))), Optional.of(new DoubleRange(3, 7))); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(integerRange(1, 2))), Optional.empty()); + assertEquals(calculateRange(REAL, ImmutableList.of(integerRange(1, 2))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(OptionalDouble.empty(), OptionalDouble.empty()))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(0.1, 0.2))), Optional.of(new DoubleRange(0.1, 0.2))); + assertEquals(calculateRange(BIGINT, ImmutableList.of(doubleRange(0.1, 0.2))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(0.1, 0.2), doubleRange(0.15, 0.25))), Optional.of(new DoubleRange(0.1, 0.25))); + assertEquals(calculateRange(REAL, ImmutableList.of(doubleRange(0.1, 0.2), doubleRange(0.15, 0.25))), Optional.of(new DoubleRange(0.1, 0.25))); + assertEquals(calculateRange(REAL, ImmutableList.of(doubleRange(OptionalDouble.empty(), OptionalDouble.of(0.2)), doubleRange(0.15, 0.25))), Optional.of(new DoubleRange(0.15, 0.25))); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(NaN, 0.2))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(0.1, NaN))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(NaN, NaN))), Optional.empty()); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))), Optional.of(new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))); + assertEquals(calculateRange(REAL, ImmutableList.of(doubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))), Optional.of(new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY))), Optional.of(new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))); + assertEquals(calculateRange(DOUBLE, ImmutableList.of(doubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), doubleRange(0.1, 0.2))), Optional.of(new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY))); + assertEquals(calculateRange(DATE, ImmutableList.of(doubleRange(0.1, 0.2))), Optional.empty()); + assertEquals(calculateRange(DATE, ImmutableList.of(dateRange("1970-01-01", "1970-01-02"))), Optional.of(new DoubleRange(0, 1))); + assertEquals(calculateRange(DATE, ImmutableList.of(dateRange(Optional.empty(), Optional.empty()))), Optional.empty()); + assertEquals(calculateRange(DATE, ImmutableList.of(dateRange(Optional.of("1970-01-01"), Optional.empty()))), Optional.empty()); + assertEquals(calculateRange(DATE, ImmutableList.of(dateRange("1970-01-01", "1970-01-05"), dateRange("1970-01-03", "1970-01-07"))), Optional.of(new DoubleRange(0, 6))); + assertEquals(calculateRange(DECIMAL, ImmutableList.of(doubleRange(0.1, 0.2))), Optional.empty()); + assertEquals(calculateRange(DECIMAL, ImmutableList.of(decimalRange(BigDecimal.valueOf(1), BigDecimal.valueOf(5)))), Optional.of(new DoubleRange(1, 5))); + assertEquals(calculateRange(DECIMAL, ImmutableList.of(decimalRange(Optional.empty(), Optional.empty()))), Optional.empty()); + assertEquals(calculateRange(DECIMAL, ImmutableList.of(decimalRange(Optional.of(BigDecimal.valueOf(1)), Optional.empty()))), Optional.empty()); + assertEquals(calculateRange(DECIMAL, ImmutableList.of(decimalRange(BigDecimal.valueOf(1), BigDecimal.valueOf(5)), decimalRange(BigDecimal.valueOf(3), BigDecimal.valueOf(7)))), Optional.of(new DoubleRange(1, 7))); + } + + @Test + public void testGetTableStatistics() + { + String partitionName = "p1=string1/p2=1234"; + PartitionStatistics statistics = PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(OptionalLong.empty(), OptionalLong.of(1000), OptionalLong.empty(), OptionalLong.empty())) + .setColumnStatistics(ImmutableMap.of(COLUMN, createIntegerColumnStatistics(OptionalLong.of(-100), OptionalLong.of(100), OptionalLong.of(500), OptionalLong.of(300)))) + .build(); + MetastoreHiveStatisticsProvider statisticsProvider = new MetastoreHiveStatisticsProvider((table, hivePartitions) -> ImmutableMap.of(partitionName, statistics)); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); + HiveColumnHandle columnHandle = new HiveColumnHandle(COLUMN, HIVE_LONG, BIGINT.getTypeSignature(), 2, REGULAR, Optional.empty()); + TableStatistics expected = TableStatistics.builder() + .setRowCount(Estimate.of(1000)) + .setColumnStatistics( + PARTITION_COLUMN_1, + ColumnStatistics.builder() + .setDataSize(Estimate.of(7000)) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(1)) + .build()) + .setColumnStatistics( + PARTITION_COLUMN_2, + ColumnStatistics.builder() + .setRange(new DoubleRange(1234, 1234)) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(1)) + .build()) + .setColumnStatistics( + columnHandle, + ColumnStatistics.builder() + .setRange(new DoubleRange(-100, 100)) + .setNullsFraction(Estimate.of(0.5)) + .setDistinctValuesCount(Estimate.of(300)) + .build()) + .build(); + assertEquals( + statisticsProvider.getTableStatistics( + session, + TABLE, + ImmutableMap.of( + "p1", PARTITION_COLUMN_1, + "p2", PARTITION_COLUMN_2, + COLUMN, columnHandle), + ImmutableMap.of( + "p1", VARCHAR, + "p2", BIGINT, + COLUMN, BIGINT), + ImmutableList.of(partition(partitionName))), + expected); + } + + @Test + public void testGetTableStatisticsUnpartitioned() + { + PartitionStatistics statistics = PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(OptionalLong.empty(), OptionalLong.of(1000), OptionalLong.empty(), OptionalLong.empty())) + .setColumnStatistics(ImmutableMap.of(COLUMN, createIntegerColumnStatistics(OptionalLong.of(-100), OptionalLong.of(100), OptionalLong.of(500), OptionalLong.of(300)))) + .build(); + MetastoreHiveStatisticsProvider statisticsProvider = new MetastoreHiveStatisticsProvider((table, hivePartitions) -> ImmutableMap.of(UNPARTITIONED_ID, statistics)); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); + HiveColumnHandle columnHandle = new HiveColumnHandle(COLUMN, HIVE_LONG, BIGINT.getTypeSignature(), 2, REGULAR, Optional.empty()); + TableStatistics expected = TableStatistics.builder() + .setRowCount(Estimate.of(1000)) + .setColumnStatistics( + columnHandle, + ColumnStatistics.builder() + .setRange(new DoubleRange(-100, 100)) + .setNullsFraction(Estimate.of(0.5)) + .setDistinctValuesCount(Estimate.of(300)) + .build()) + .build(); + assertEquals( + statisticsProvider.getTableStatistics( + session, + TABLE, + ImmutableMap.of(COLUMN, columnHandle), + ImmutableMap.of(COLUMN, BIGINT), + ImmutableList.of(new HivePartition(TABLE))), + expected); + } + + @Test + public void testGetTableStatisticsEmpty() + { + String partitionName = "p1=string1/p2=1234"; + MetastoreHiveStatisticsProvider statisticsProvider = new MetastoreHiveStatisticsProvider((table, hivePartitions) -> ImmutableMap.of(partitionName, PartitionStatistics.empty())); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties(new HiveClientConfig(), new OrcFileWriterConfig(), new ParquetFileWriterConfig()).getSessionProperties()); + assertEquals( + statisticsProvider.getTableStatistics( + session, + TABLE, + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableList.of(partition(partitionName))), + TableStatistics.empty()); + } + + @Test + public void testGetTableStatisticsSampling() + { + MetastoreHiveStatisticsProvider statisticsProvider = new MetastoreHiveStatisticsProvider((table, hivePartitions) -> { + assertEquals(table, TABLE); + assertEquals(hivePartitions.size(), 1); + return ImmutableMap.of(); + }); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties( + new HiveClientConfig().setPartitionStatisticsSampleSize(1), + new OrcFileWriterConfig(), + new ParquetFileWriterConfig()) + .getSessionProperties()); + statisticsProvider.getTableStatistics( + session, + TABLE, + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableList.of(partition("p1=string1/p2=1234"), partition("p1=string1/p2=1235"))); + } + + @Test + public void testGetTableStatisticsValidationFailure() + { + PartitionStatistics corruptedStatistics = PartitionStatistics.builder() + .setBasicStatistics(new HiveBasicStatistics(-1, 0, 0, 0)) + .build(); + String partitionName = "p1=string1/p2=1234"; + MetastoreHiveStatisticsProvider statisticsProvider = new MetastoreHiveStatisticsProvider((table, hivePartitions) -> ImmutableMap.of(partitionName, corruptedStatistics)); + TestingConnectorSession session = new TestingConnectorSession(new HiveSessionProperties( + new HiveClientConfig().setIgnoreCorruptedStatistics(false), + new OrcFileWriterConfig(), + new ParquetFileWriterConfig()) + .getSessionProperties()); + assertThatThrownBy(() -> statisticsProvider.getTableStatistics( + session, + TABLE, + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableList.of(partition(partitionName)))) + .isInstanceOf(PrestoException.class) + .hasFieldOrPropertyWithValue("errorCode", HIVE_CORRUPTED_COLUMN_STATISTICS.toErrorCode()); + TestingConnectorSession ignoreSession = new TestingConnectorSession(new HiveSessionProperties( + new HiveClientConfig().setIgnoreCorruptedStatistics(true), + new OrcFileWriterConfig(), + new ParquetFileWriterConfig()) + .getSessionProperties()); + assertEquals( + statisticsProvider.getTableStatistics( + ignoreSession, + TABLE, + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableList.of(partition(partitionName))), + TableStatistics.empty()); + } + + private static void assertInvalidStatistics(PartitionStatistics partitionStatistics, String expectedMessage) + { + assertThatThrownBy(() -> validatePartitionStatistics(TABLE, ImmutableMap.of(PARTITION, partitionStatistics))) + .isInstanceOf(PrestoException.class) + .hasFieldOrPropertyWithValue("errorCode", HIVE_CORRUPTED_COLUMN_STATISTICS.toErrorCode()) + .hasMessage(expectedMessage); + } + + private static String invalidPartitionStatistics(String message) + { + return format("Corrupted partition statistics (Table: %s Partition: [%s]): %s", TABLE, PARTITION, message); + } + + private static String invalidColumnStatistics(String message) + { + return format("Corrupted partition statistics (Table: %s Partition: [%s] Column: %s): %s", TABLE, PARTITION, COLUMN, message); } private static HivePartition partition(String name) { - return new HivePartition(new SchemaTableName("schema", "table"), name, ImmutableMap.of()); + return parsePartition(TABLE, name, ImmutableList.of(PARTITION_COLUMN_1, PARTITION_COLUMN_2), ImmutableList.of(VARCHAR, BIGINT), DateTimeZone.getDefault()); + } + + private static PartitionStatistics rowsCount(long rowsCount) + { + return new PartitionStatistics(new HiveBasicStatistics(0, rowsCount, 0, 0), ImmutableMap.of()); + } + + private static PartitionStatistics nullsCount(long nullsCount) + { + return new PartitionStatistics(HiveBasicStatistics.createEmptyStatistics(), ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setNullsCount(nullsCount).build())); + } + + private static PartitionStatistics dataSize(long dataSize) + { + return new PartitionStatistics(HiveBasicStatistics.createEmptyStatistics(), ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setTotalSizeInBytes(dataSize).build())); + } + + private static PartitionStatistics rowsCountAndNullsCount(long rowsCount, long nullsCount) + { + return new PartitionStatistics( + new HiveBasicStatistics(0, rowsCount, 0, 0), + ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setNullsCount(nullsCount).build())); + } + + private static PartitionStatistics rowsCountAndDataSize(long rowsCount, long dataSize) + { + return new PartitionStatistics( + new HiveBasicStatistics(0, rowsCount, 0, 0), + ImmutableMap.of(COLUMN, HiveColumnStatistics.builder().setTotalSizeInBytes(dataSize).build())); + } + + private static HiveColumnStatistics distinctValuesCount(long count) + { + return HiveColumnStatistics.builder() + .setDistinctValuesCount(count) + .build(); + } + + private static HiveColumnStatistics integerRange(long min, long max) + { + return integerRange(OptionalLong.of(min), OptionalLong.of(max)); + } + + private static HiveColumnStatistics integerRange(OptionalLong min, OptionalLong max) + { + return HiveColumnStatistics.builder() + .setIntegerStatistics(new IntegerStatistics(min, max)) + .build(); + } + + private static HiveColumnStatistics doubleRange(double min, double max) + { + return doubleRange(OptionalDouble.of(min), OptionalDouble.of(max)); + } + + private static HiveColumnStatistics doubleRange(OptionalDouble min, OptionalDouble max) + { + return HiveColumnStatistics.builder() + .setDoubleStatistics(new DoubleStatistics(min, max)) + .build(); + } + + private static HiveColumnStatistics dateRange(String min, String max) + { + return dateRange(Optional.of(min), Optional.of(max)); + } + + private static HiveColumnStatistics dateRange(Optional min, Optional max) + { + return HiveColumnStatistics.builder() + .setDateStatistics(new DateStatistics(min.map(TestMetastoreHiveStatisticsProvider::parseDate), max.map(TestMetastoreHiveStatisticsProvider::parseDate))) + .build(); + } + + private static LocalDate parseDate(String date) + { + return LocalDate.parse(date); + } + + private static HiveColumnStatistics decimalRange(BigDecimal min, BigDecimal max) + { + return decimalRange(Optional.of(min), Optional.of(max)); + } + + private static HiveColumnStatistics decimalRange(Optional min, Optional max) + { + return HiveColumnStatistics.builder() + .setDecimalStatistics(new DecimalStatistics(min, max)) + .build(); } } diff --git a/presto-hive/src/test/sql/create-test-s3.sql b/presto-hive/src/test/sql/create-test-s3.sql deleted file mode 100644 index 3d51bc5f11e81..0000000000000 --- a/presto-hive/src/test/sql/create-test-s3.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE EXTERNAL TABLE presto_test_external_fs( - t_bigint BIGINT, - t_string STRING -) -COMMENT 'Presto test S3 table' -ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' -STORED AS TEXTFILE -TBLPROPERTIES ('RETENTION'='-1') -; - -ALTER TABLE presto_test_external_fs -SET LOCATION 's3://presto-test-hive/presto_test_external_fs' -; diff --git a/presto-hive/src/test/sql/drop-test-s3.sql b/presto-hive/src/test/sql/drop-test-s3.sql deleted file mode 100644 index 8fa1e2aea014f..0000000000000 --- a/presto-hive/src/test/sql/drop-test-s3.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE IF EXISTS presto_test_external_fs; diff --git a/presto-jdbc/pom.xml b/presto-jdbc/pom.xml index 4832cf8e3a63c..7baf2dc56bd18 100644 --- a/presto-jdbc/pom.xml +++ b/presto-jdbc/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-jdbc @@ -69,6 +69,12 @@ + + com.google.code.findbugs + jsr305 + true + + com.facebook.presto @@ -145,6 +151,28 @@ + + + maven-source-plugin + + + + attach-sources + + true + + + + + test-only + package + + test-jar-no-fork + + + + + org.apache.maven.plugins maven-shade-plugin @@ -183,6 +211,10 @@ io.airlift ${shadeBase}.airlift + + javax.annotation + ${shadeBase}.javax.annotation + javax.inject ${shadeBase}.inject diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoConnection.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoConnection.java index df58189eaca04..a081d60e76e8e 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoConnection.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoConnection.java @@ -89,6 +89,7 @@ public class PrestoConnection private final Map preparedStatements = new ConcurrentHashMap<>(); private final AtomicReference transactionId = new AtomicReference<>(); private final QueryExecutor queryExecutor; + private final WarningsManager warningsManager = new WarningsManager(); PrestoConnection(PrestoDriverUri uri, QueryExecutor queryExecutor) throws SQLException @@ -702,6 +703,11 @@ void updateSession(StatementClient client) } } + WarningsManager getWarningsManager() + { + return warningsManager; + } + private void checkOpen() throws SQLException { diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDriver.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDriver.java index 454909d478bc5..a8b7fcdbbccda 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDriver.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoDriver.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.jdbc; +import com.facebook.presto.client.SocketChannelSocketFactory; import okhttp3.OkHttpClient; import java.io.Closeable; @@ -41,8 +42,9 @@ public class PrestoDriver private static final String DRIVER_URL_START = "jdbc:presto:"; - private final OkHttpClient httpClient = new OkHttpClient().newBuilder() + private final OkHttpClient httpClient = new OkHttpClient.Builder() .addInterceptor(userAgent(DRIVER_NAME + "/" + DRIVER_VERSION)) + .socketFactory(new SocketChannelSocketFactory()) .build(); static { diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoPreparedStatement.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoPreparedStatement.java index 3dadc2d6d28ee..2e0e0f5d032dd 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoPreparedStatement.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoPreparedStatement.java @@ -80,6 +80,14 @@ public class PrestoPreparedStatement super.execute(format("PREPARE %s FROM %s", statementName, sql)); } + @Override + public void close() + throws SQLException + { + super.execute(format("DEALLOCATE PREPARE %s", statementName)); + super.close(); + } + @Override public ResultSet executeQuery() throws SQLException diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoResultSet.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoResultSet.java index e645bbb27dd16..4fcb1d375b003 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoResultSet.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoResultSet.java @@ -105,8 +105,9 @@ public class PrestoResultSet private final AtomicReference> row = new AtomicReference<>(); private final AtomicBoolean wasNull = new AtomicBoolean(); private final AtomicBoolean closed = new AtomicBoolean(); + private final WarningsManager warningsManager; - PrestoResultSet(StatementClient client, long maxRows, Consumer progressCallback) + PrestoResultSet(StatementClient client, long maxRows, Consumer progressCallback, WarningsManager warningsManager) throws SQLException { this.client = requireNonNull(client, "client is null"); @@ -119,8 +120,9 @@ public class PrestoResultSet this.fieldMap = getFieldMap(columns); this.columnInfoList = getColumnInfo(columns); this.resultSetMetaData = new PrestoResultSetMetaData(columnInfoList); + this.warningsManager = requireNonNull(warningsManager, "warningsManager is null"); - this.results = flatten(new ResultsPageIterator(client, progressCallback), maxRows); + this.results = flatten(new ResultsPageIterator(client, progressCallback, warningsManager), maxRows); } public String getQueryId() @@ -482,7 +484,7 @@ public SQLWarning getWarnings() throws SQLException { checkOpen(); - return null; + return warningsManager.getWarnings(); } @Override @@ -490,6 +492,7 @@ public void clearWarnings() throws SQLException { checkOpen(); + warningsManager.clearWarnings(); } @Override @@ -1758,26 +1761,52 @@ private static class ResultsPageIterator { private final StatementClient client; private final Consumer progressCallback; + private final WarningsManager warningsManager; + private final boolean isQuery; - private ResultsPageIterator(StatementClient client, Consumer progressCallback) + private ResultsPageIterator(StatementClient client, Consumer progressCallback, WarningsManager warningsManager) { this.client = requireNonNull(client, "client is null"); this.progressCallback = requireNonNull(progressCallback, "progressCallback is null"); + this.warningsManager = requireNonNull(warningsManager, "warningsManager is null"); + this.isQuery = isQuery(client); + } + + private static boolean isQuery(StatementClient client) + { + String updateType; + if (client.isRunning()) { + updateType = client.currentStatusInfo().getUpdateType(); + } + else { + updateType = client.finalStatusInfo().getUpdateType(); + } + return updateType == null; } @Override protected Iterable> computeNext() { + if (isQuery) { + // Clear the warnings if this is a query, per ResultSet javadoc + warningsManager.clearWarnings(); + } while (client.isRunning()) { - if (Thread.currentThread().isInterrupted()) { - client.close(); - throw new RuntimeException(new SQLException("ResultSet thread was interrupted")); - } + checkInterruption(null); QueryStatusInfo results = client.currentStatusInfo(); progressCallback.accept(QueryStats.create(results.getId(), results.getStats())); + warningsManager.addWarnings(results.getWarnings()); Iterable> data = client.currentData().getData(); - client.advance(); + + try { + client.advance(); + } + catch (RuntimeException e) { + checkInterruption(e); + throw e; + } + if (data != null) { return data; } @@ -1786,13 +1815,21 @@ protected Iterable> computeNext() verify(client.isFinished()); QueryStatusInfo results = client.finalStatusInfo(); progressCallback.accept(QueryStats.create(results.getId(), results.getStats())); - + warningsManager.addWarnings(results.getWarnings()); if (results.getError() != null) { throw new RuntimeException(resultsException(results)); } return endOfData(); } + + private void checkInterruption(Throwable t) + { + if (Thread.currentThread().isInterrupted()) { + client.close(); + throw new RuntimeException(new SQLException("ResultSet thread was interrupted", t)); + } + } } static SQLException resultsException(QueryStatusInfo results) diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoSqlWarning.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoSqlWarning.java new file mode 100644 index 0000000000000..0ca174279e04d --- /dev/null +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoSqlWarning.java @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.jdbc; + +import com.facebook.presto.spi.PrestoWarning; + +import java.sql.SQLWarning; + +public class PrestoSqlWarning + extends SQLWarning +{ + public PrestoSqlWarning() + { + super(); + } + + public PrestoSqlWarning(PrestoWarning warning) + { + //TODO: enforce that sqlState is 01[5,6,7,8,9,I-Z][0-9A-Z]{3} + // From the SQL Standard ISO_IEC_9075-2E_2016 24.1 SQLState: warning codes have class 01 + // warning subclasses defined in the sql state will start with [0-4A-H] and contain 3 characters + // An example of vendor specific warning codes can be found here: + // https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/codes/src/tpc/db2z_sqlstatevalues.html#db2z_sqlstatevalues__code01 + // Note that the subclass begins with 5 which indicates it is an implementation defined subclass + super(warning.getMessage(), warning.getWarningCode().getName(), warning.getWarningCode().getCode()); + } +} diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java index 0e71dc76733a2..b393619d1c5e4 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/PrestoStatement.java @@ -48,6 +48,7 @@ public class PrestoStatement private final AtomicReference connection; private final AtomicReference executingClient = new AtomicReference<>(); private final AtomicReference currentResult = new AtomicReference<>(); + private final AtomicReference> currentWarningsManager = new AtomicReference<>(Optional.empty()); private final AtomicLong currentUpdateCount = new AtomicLong(-1); private final AtomicReference currentUpdateType = new AtomicReference<>(); private final AtomicReference>> progressCallback = new AtomicReference<>(Optional.empty()); @@ -188,7 +189,7 @@ public SQLWarning getWarnings() throws SQLException { checkOpen(); - return null; + return currentWarningsManager.get().map(WarningsManager::getWarnings).orElse(null); } @Override @@ -196,6 +197,7 @@ public void clearWarnings() throws SQLException { checkOpen(); + currentWarningsManager.get().ifPresent(WarningsManager::clearWarnings); } @Override @@ -242,8 +244,9 @@ final boolean internalExecute(String sql) } } executingClient.set(client); - - resultSet = new PrestoResultSet(client, maxRows.get(), progressConsumer); + WarningsManager warningsManager = new WarningsManager(); + currentWarningsManager.set(Optional.of(warningsManager)); + resultSet = new PrestoResultSet(client, maxRows.get(), progressConsumer, warningsManager); // check if this is a query if (client.currentStatusInfo().getUpdateType() == null) { @@ -261,7 +264,7 @@ final boolean internalExecute(String sql) Long updateCount = client.finalStatusInfo().getUpdateCount(); currentUpdateCount.set((updateCount != null) ? updateCount : 0); currentUpdateType.set(client.finalStatusInfo().getUpdateType()); - + warningsManager.addWarnings(client.finalStatusInfo().getWarnings()); return false; } catch (ClientException e) { @@ -288,6 +291,7 @@ private void clearCurrentResults() currentResult.set(null); currentUpdateCount.set(-1); currentUpdateType.set(null); + currentWarningsManager.set(Optional.empty()); } @Override @@ -317,9 +321,7 @@ public long getLargeUpdateCount() public boolean getMoreResults() throws SQLException { - checkOpen(); - closeResultSet(); - return false; + return getMoreResults(CLOSE_CURRENT_RESULT); } @Override @@ -413,6 +415,9 @@ public boolean getMoreResults(int current) { checkOpen(); + currentUpdateCount.set(-1); + currentUpdateType.set(null); + if (current == CLOSE_CURRENT_RESULT) { closeResultSet(); return false; diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/QueryStats.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/QueryStats.java index ec31960ecbdf5..f0cc4dad67f24 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/QueryStats.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/QueryStats.java @@ -32,7 +32,6 @@ public final class QueryStats private final int queuedSplits; private final int runningSplits; private final int completedSplits; - private final long userTimeMillis; private final long cpuTimeMillis; private final long wallTimeMillis; private final long queuedTimeMillis; @@ -52,7 +51,6 @@ public QueryStats( int queuedSplits, int runningSplits, int completedSplits, - long userTimeMillis, long cpuTimeMillis, long wallTimeMillis, long queuedTimeMillis, @@ -71,7 +69,6 @@ public QueryStats( this.queuedSplits = queuedSplits; this.runningSplits = runningSplits; this.completedSplits = completedSplits; - this.userTimeMillis = userTimeMillis; this.cpuTimeMillis = cpuTimeMillis; this.wallTimeMillis = wallTimeMillis; this.queuedTimeMillis = queuedTimeMillis; @@ -94,7 +91,6 @@ static QueryStats create(String queryId, StatementStats stats) stats.getQueuedSplits(), stats.getRunningSplits(), stats.getCompletedSplits(), - stats.getUserTimeMillis(), stats.getCpuTimeMillis(), stats.getWallTimeMillis(), stats.getQueuedTimeMillis(), @@ -150,11 +146,6 @@ public int getCompletedSplits() return completedSplits; } - public long getUserTimeMillis() - { - return userTimeMillis; - } - public long getCpuTimeMillis() { return cpuTimeMillis; diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/StageStats.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/StageStats.java index 9205b69b919e8..98b54e36fc092 100644 --- a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/StageStats.java +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/StageStats.java @@ -30,7 +30,6 @@ public final class StageStats private final int queuedSplits; private final int runningSplits; private final int completedSplits; - private final long userTimeMillis; private final long cpuTimeMillis; private final long wallTimeMillis; private final long processedRows; @@ -46,7 +45,6 @@ public StageStats( int queuedSplits, int runningSplits, int completedSplits, - long userTimeMillis, long cpuTimeMillis, long wallTimeMillis, long processedRows, @@ -61,7 +59,6 @@ public StageStats( this.queuedSplits = queuedSplits; this.runningSplits = runningSplits; this.completedSplits = completedSplits; - this.userTimeMillis = userTimeMillis; this.cpuTimeMillis = cpuTimeMillis; this.wallTimeMillis = wallTimeMillis; this.processedRows = processedRows; @@ -80,7 +77,6 @@ static StageStats create(com.facebook.presto.client.StageStats stats) stats.getQueuedSplits(), stats.getRunningSplits(), stats.getCompletedSplits(), - stats.getUserTimeMillis(), stats.getCpuTimeMillis(), stats.getWallTimeMillis(), stats.getProcessedRows(), @@ -130,11 +126,6 @@ public int getCompletedSplits() return completedSplits; } - public long getUserTimeMillis() - { - return userTimeMillis; - } - public long getCpuTimeMillis() { return cpuTimeMillis; diff --git a/presto-jdbc/src/main/java/com/facebook/presto/jdbc/WarningsManager.java b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/WarningsManager.java new file mode 100644 index 0000000000000..2c2998a541073 --- /dev/null +++ b/presto-jdbc/src/main/java/com/facebook/presto/jdbc/WarningsManager.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.jdbc; + +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import java.sql.SQLWarning; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +@ThreadSafe +public class WarningsManager +{ + @GuardedBy("this") + private Set warningsSeen = new HashSet<>(); + + @GuardedBy("this") + private SQLWarning firstWarning; + + // To avoid O(n) behavior of SQLException.setNextException keep a reference to the last warning added: + @GuardedBy("this") + private SQLWarning lastWarning; + + private synchronized void addWarning(PrestoWarning warning) + { + requireNonNull(warning, "warning is null"); + if (lastWarning == null) { + lastWarning = new PrestoSqlWarning(warning); + } + else { + lastWarning.setNextWarning(new PrestoSqlWarning(warning)); + } + if (firstWarning == null) { + firstWarning = lastWarning; + } + else { + lastWarning = lastWarning.getNextWarning(); + } + } + + public synchronized void addWarnings(List warnings) + { + for (PrestoWarning warning : warnings) { + if (warningsSeen.add(warning.getWarningCode())) { + addWarning(warning); + } + } + } + + public synchronized SQLWarning getWarnings() + { + return firstWarning; + } + + public synchronized void clearWarnings() + { + firstWarning = null; + lastWarning = null; + } +} diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcPreparedStatement.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcPreparedStatement.java index 140a8fb5beb1a..ab9f694764ed5 100644 --- a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcPreparedStatement.java +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcPreparedStatement.java @@ -39,6 +39,7 @@ import static com.facebook.presto.jdbc.TestPrestoDriver.closeQuietly; import static com.facebook.presto.jdbc.TestPrestoDriver.waitForNodeRefresh; +import static com.google.common.base.Strings.repeat; import static com.google.common.primitives.Ints.asList; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; @@ -100,6 +101,22 @@ public void testExecuteQuery() } } + @Test + public void testDeallocate() + throws Exception + { + try (Connection connection = createConnection()) { + for (int i = 0; i < 200; i++) { + try { + connection.prepareStatement("SELECT '" + repeat("a", 300) + "'").close(); + } + catch (Exception e) { + throw new RuntimeException("Failed at " + i, e); + } + } + } + } + @Test public void testExecuteUpdate() throws Exception diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcResultSet.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcResultSet.java index 769fb8f5ad09a..984f7111b21f3 100644 --- a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcResultSet.java +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcResultSet.java @@ -38,6 +38,8 @@ import static com.facebook.presto.jdbc.TestPrestoDriver.closeQuietly; import static java.lang.String.format; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -131,10 +133,39 @@ public void testObjectTypes() assertThrows(() -> rs.getTimestamp(column)); }); + // TODO #7122: line 1:8: '00:39:05' is not a valid time literal +// checkRepresentation("TIME '00:39:05'", Types.TIME, (rs, column) -> { +// ... +// }); + checkRepresentation("TIME '09:39:07 +01:00'", Types.TIME /* TODO TIME_WITH_TIMEZONE */, (rs, column) -> { - assertEquals(rs.getObject(column), Time.valueOf(LocalTime.of(14, 9, 7))); // TODO this should represent TIME '09:39:07 +01:00' + assertEquals(rs.getObject(column), Time.valueOf(LocalTime.of(1, 39, 7))); // TODO this should represent TIME '09:39:07 +01:00' + assertThrows(() -> rs.getDate(column)); + assertEquals(rs.getTime(column), Time.valueOf(LocalTime.of(1, 39, 7))); // TODO this should fail, or represent TIME '09:39:07' + assertThrows(() -> rs.getTimestamp(column)); + }); + + checkRepresentation("TIME '01:39:07 +01:00'", Types.TIME /* TODO TIME_WITH_TIMEZONE */, (rs, column) -> { + Time someBogusValue = new Time( + Time.valueOf( + LocalTime.of(16, 39, 7)).getTime() /* 16:39:07 = 01:39:07 - +01:00 shift + Bahia_Banderas's shift (-8) (modulo 24h which we "un-modulo" below) */ + - DAYS.toMillis(1) /* because we use currently 'shifted' representation, not possible to create just using LocalTime */ + + HOURS.toMillis(1) /* because there was offset shift on 1970-01-01 in America/Bahia_Banderas */); + assertEquals(rs.getObject(column), someBogusValue); // TODO this should represent TIME '01:39:07 +01:00' + assertThrows(() -> rs.getDate(column)); + assertEquals(rs.getTime(column), someBogusValue); // TODO this should fail, or represent TIME '01:39:07' + assertThrows(() -> rs.getTimestamp(column)); + }); + + checkRepresentation("TIME '00:39:07 +01:00'", Types.TIME /* TODO TIME_WITH_TIMEZONE */, (rs, column) -> { + Time someBogusValue = new Time( + Time.valueOf( + LocalTime.of(15, 39, 7)).getTime() /* 15:39:07 = 00:39:07 - +01:00 shift + Bahia_Banderas's shift (-8) (modulo 24h which we "un-modulo" below) */ + - DAYS.toMillis(1) /* because we use currently 'shifted' representation, not possible to create just using LocalTime */ + + HOURS.toMillis(1) /* because there was offset shift on 1970-01-01 in America/Bahia_Banderas */); + assertEquals(rs.getObject(column), someBogusValue); // TODO this should represent TIME '00:39:07 +01:00' assertThrows(() -> rs.getDate(column)); - assertEquals(rs.getTime(column), Time.valueOf(LocalTime.of(14, 9, 7))); // TODO this should fail, or represent TIME '09:39:07 +01:00' + assertEquals(rs.getTime(column), someBogusValue); // TODO this should fail, as there no java.sql.Time representation for TIME '00:39:07' in America/Bahia_Banderas assertThrows(() -> rs.getTimestamp(column)); }); @@ -145,11 +176,31 @@ public void testObjectTypes() assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 13, 14, 15, 123_000_000))); }); + // TODO #7122: line 1:8: '1970-01-01 00:14:15.123' is not a valid timestamp literal; the expected values will pro +// checkRepresentation("TIMESTAMP '1970-01-01 00:14:15.123'", Types.TIMESTAMP, (rs, column) -> { +// ... +// }); + checkRepresentation("TIMESTAMP '2018-02-13 13:14:15.227 Europe/Warsaw'", Types.TIMESTAMP /* TODO TIMESTAMP_WITH_TIMEZONE */, (rs, column) -> { - assertEquals(rs.getObject(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 17, 59, 15, 227_000_000))); // TODO this should represent TIMESTAMP '2018-02-13 13:14:15.227 Europe/Warsaw' + assertEquals(rs.getObject(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 6, 14, 15, 227_000_000))); // TODO this should represent TIMESTAMP '2018-02-13 13:14:15.227 Europe/Warsaw' + assertThrows(() -> rs.getDate(column)); + assertThrows(() -> rs.getTime(column)); + assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 6, 14, 15, 227_000_000))); // TODO this should fail, or represent TIMESTAMP '2018-02-13 13:14:15.227' + }); + + checkRepresentation("TIMESTAMP '1970-01-01 09:14:15.227 Europe/Warsaw'", Types.TIMESTAMP /* TODO TIMESTAMP_WITH_TIMEZONE */, (rs, column) -> { + assertEquals(rs.getObject(column), Timestamp.valueOf(LocalDateTime.of(1970, 1, 1, 1, 14, 15, 227_000_000))); // TODO this should represent TIMESTAMP '1970-01-01 09:14:15.227 Europe/Warsaw' + assertThrows(() -> rs.getDate(column)); + assertThrows(() -> rs.getTime(column)); + assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(1970, 1, 1, 1, 14, 15, 227_000_000))); // TODO this should fail, or represent TIMESTAMP '1970-01-01 09:14:15.227' + }); + + checkRepresentation("TIMESTAMP '1970-01-01 00:14:15.227 Europe/Warsaw'", Types.TIMESTAMP /* TODO TIMESTAMP_WITH_TIMEZONE */, (rs, column) -> { + assertEquals(rs.getObject(column), Timestamp.valueOf(LocalDateTime.of(1969, 12, 31, 15, 14, 15, 227_000_000))); // TODO this should represent TIMESTAMP '1970-01-01 00:14:15.227 Europe/Warsaw' assertThrows(() -> rs.getDate(column)); assertThrows(() -> rs.getTime(column)); - assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(2018, 2, 13, 17, 59, 15, 227_000_000))); // TODO this should fail or represent TIMESTAMP '2018-02-13 13:14:15.227 Europe/Warsaw' + // TODO this should fail, as there no java.sql.Timestamp representation for TIMESTAMP '1970-01-01 00:14:15.227ó' in America/Bahia_Banderas + assertEquals(rs.getTimestamp(column), Timestamp.valueOf(LocalDateTime.of(1969, 12, 31, 15, 14, 15, 227_000_000))); }); } diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcWarnings.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcWarnings.java new file mode 100644 index 0000000000000..2670c872f940b --- /dev/null +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestJdbcWarnings.java @@ -0,0 +1,355 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.jdbc; + +import com.facebook.presto.execution.QueryInfo; +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.plugin.blackhole.BlackHolePlugin; +import com.facebook.presto.server.testing.TestingPrestoServer; +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import com.facebook.presto.sql.parser.SqlParserOptions; +import com.facebook.presto.testing.TestingWarningCollector; +import com.facebook.presto.testing.TestingWarningCollectorConfig; +import com.facebook.presto.tpch.TpchPlugin; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; +import com.google.common.util.concurrent.AbstractFuture; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +import static com.facebook.presto.jdbc.TestPrestoDriver.closeQuietly; +import static com.facebook.presto.jdbc.TestPrestoDriver.waitForNodeRefresh; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +@Test(singleThreaded = true) +public class TestJdbcWarnings +{ + // Number of warnings preloaded to the testing warning collector before a query runs + private static final int PRELOADED_WARNINGS = 5; + + private TestingPrestoServer server; + private Connection connection; + private Statement statement; + + @BeforeClass + public void setupServer() + throws Exception + { + server = new TestingPrestoServer( + true, + ImmutableMap.builder() + .put("testing-warning-collector.add-warnings", "true") + .put("testing-warning-collector.preloaded-warnings", String.valueOf(PRELOADED_WARNINGS)) + .build(), + null, + null, + new SqlParserOptions(), + ImmutableList.of()); + server.installPlugin(new TpchPlugin()); + server.createCatalog("tpch", "tpch"); + server.installPlugin(new BlackHolePlugin()); + server.createCatalog("blackhole", "blackhole"); + waitForNodeRefresh(server); + } + + @AfterClass(alwaysRun = true) + public void teardownServer() + { + closeQuietly(server); + } + + @SuppressWarnings("JDBCResourceOpenedButNotSafelyClosed") + @BeforeMethod + public void setup() + throws Exception + { + connection = createConnection(); + statement = connection.createStatement(); + } + + @AfterMethod + public void teardown() + { + closeQuietly(statement); + closeQuietly(connection); + } + + @Test + public void testStatementWarnings() + throws SQLException + { + assertFalse(statement.execute("CREATE SCHEMA blackhole.test_schema")); + SQLWarning warning = statement.getWarnings(); + assertNotNull(warning); + TestingWarningCollectorConfig warningCollectorConfig = new TestingWarningCollectorConfig().setPreloadedWarnings(PRELOADED_WARNINGS); + TestingWarningCollector warningCollector = new TestingWarningCollector(new WarningCollectorConfig(), warningCollectorConfig); + List expectedWarnings = warningCollector.getWarnings(); + assertStartsWithExpectedWarnings(warning, fromPrestoWarnings(expectedWarnings)); + statement.clearWarnings(); + assertNull(statement.getWarnings()); + } + + @Test + public void testLongRunningStatement() + throws SQLException, InterruptedException + { + ExecutorService queryExecutor = newSingleThreadExecutor(daemonThreadsNamed("test-%s")); + QueryCreationFuture queryCreationFuture = new QueryCreationFuture(); + queryExecutor.submit(() -> { + try { + statement.execute("CREATE SCHEMA blackhole.blackhole"); + statement.execute("CREATE TABLE blackhole.blackhole.test_table AS SELECT 1 AS col1 FROM tpch.sf1.lineitem CROSS JOIN tpch.sf1.lineitem"); + queryCreationFuture.set(null); + } + catch (Throwable e) { + queryCreationFuture.setException(e); + } + }); + while (statement.getWarnings() == null) { + Thread.sleep(100); + } + SQLWarning warning = statement.getWarnings(); + Set currentWarnings = new HashSet<>(); + assertTrue(currentWarnings.add(new WarningEntry(warning))); + for (int warnings = 1; !queryCreationFuture.isDone() && warnings < 100; warnings++) { + for (SQLWarning nextWarning = warning.getNextWarning(); nextWarning == null; nextWarning = warning.getNextWarning()) { + // Wait for new warnings + } + warning = warning.getNextWarning(); + assertTrue(currentWarnings.add(new WarningEntry(warning))); + Thread.sleep(100); + } + assertEquals(currentWarnings.size(), 100); + queryExecutor.shutdownNow(); + } + + @Test + public void testLongRunningQuery() + throws SQLException, InterruptedException + { + ExecutorService queryExecutor = newSingleThreadExecutor(daemonThreadsNamed("test-%s")); + QueryCreationFuture queryCreationFuture = new QueryCreationFuture(); + queryExecutor.submit(() -> { + try { + statement.execute("SELECT 1 AS col1 FROM tpch.sf1.lineitem CROSS JOIN tpch.sf1.lineitem"); + queryCreationFuture.set(null); + } + catch (Throwable e) { + queryCreationFuture.setException(e); + } + }); + while (statement.getResultSet() == null) { + Thread.sleep(100); + } + ResultSet resultSet = statement.getResultSet(); + Set currentWarnings = new HashSet<>(); + for (int rows = 0; !queryCreationFuture.isDone() && rows < 10; ) { + if (resultSet.next()) { + for (SQLWarning warning = resultSet.getWarnings(); warning.getNextWarning() != null; warning = warning.getNextWarning()) { + assertTrue(currentWarnings.add(new WarningEntry(warning.getNextWarning()))); + } + } + else { + break; + } + Thread.sleep(100); + } + queryExecutor.shutdownNow(); + } + + @Test + public void testExecuteQueryWarnings() + throws SQLException + { + try (ResultSet rs = statement.executeQuery("SELECT a FROM (VALUES 1, 2, 3) t(a)")) { + assertNull(statement.getConnection().getWarnings()); + assertNull(statement.getWarnings()); + assertNull(rs.getWarnings()); + Set currentWarnings = new HashSet<>(); + while (rs.next()) { + assertWarnings(rs.getWarnings(), currentWarnings); + } + + TestingWarningCollectorConfig warningCollectorConfig = new TestingWarningCollectorConfig().setPreloadedWarnings(PRELOADED_WARNINGS).setAddWarnings(true); + TestingWarningCollector warningCollector = new TestingWarningCollector(new WarningCollectorConfig(), warningCollectorConfig); + List expectedWarnings = warningCollector.getWarnings(); + for (PrestoWarning prestoWarning : expectedWarnings) { + assertTrue(currentWarnings.contains(new WarningEntry(new PrestoSqlWarning(prestoWarning)))); + } + } + } + + @Test + public void testSqlWarning() + { + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < 3; i++) { + builder.add(new PrestoWarning(new WarningCode(i, "CODE_" + i), "warning message " + i)); + } + List warnings = builder.build(); + SQLWarning warning = fromPrestoWarnings(warnings); + assertEquals(Iterators.size(warning.iterator()), warnings.size()); + assertWarningsEqual(warning, new PrestoSqlWarning(warnings.get(0))); + assertWarningsEqual(warning.getNextWarning(), new PrestoSqlWarning(warnings.get(1))); + assertWarningsEqual(warning.getNextWarning().getNextWarning(), new PrestoSqlWarning(warnings.get(2))); + } + + private static SQLWarning fromPrestoWarnings(List warnings) + { + requireNonNull(warnings, "warnings is null"); + assertFalse(warnings.isEmpty()); + Iterator iterator = warnings.iterator(); + PrestoSqlWarning first = new PrestoSqlWarning(iterator.next()); + SQLWarning current = first; + while (iterator.hasNext()) { + current.setNextWarning(new PrestoSqlWarning(iterator.next())); + current = current.getNextWarning(); + } + return first; + } + + private static void assertWarningsEqual(SQLWarning actual, SQLWarning expected) + { + assertEquals(actual.getMessage(), expected.getMessage()); + assertEquals(actual.getSQLState(), expected.getSQLState()); + assertEquals(actual.getErrorCode(), expected.getErrorCode()); + } + + private static void addWarnings(Set currentWarnings, SQLWarning newWarning) + { + if (newWarning == null) { + return; + } + for (Throwable warning : newWarning) { + WarningEntry entry = new WarningEntry(warning); + currentWarnings.add(entry); + } + } + + //TODO: this method seems to be copied in multiple test classes in this package, should it be moved to a utility? + private Connection createConnection() + throws SQLException + { + String url = format("jdbc:presto://%s", server.getAddress(), "blackhole", "blackhole"); + return DriverManager.getConnection(url, "test", null); + } + + private static void assertWarnings(SQLWarning warning, Set currentWarnings) + { + assertNotNull(warning); + int previousSize = currentWarnings.size(); + addWarnings(currentWarnings, warning); + assertTrue(currentWarnings.size() >= previousSize); + } + + private static void assertStartsWithExpectedWarnings(SQLWarning warning, SQLWarning expected) + { + assertNotNull(expected); + assertNotNull(warning); + while (true) { + assertWarningsEqual(warning, expected); + warning = warning.getNextWarning(); + expected = expected.getNextWarning(); + if (expected == null) { + return; + } + assertNotNull(warning); + } + } + + private static class WarningEntry + { + public final int vendorCode; + public final String sqlState; + public final String message; + + public WarningEntry(Throwable throwable) + { + requireNonNull(throwable, "throwable is null"); + assertTrue(throwable instanceof SQLWarning); + SQLWarning warning = (SQLWarning) throwable; + this.vendorCode = warning.getErrorCode(); + this.sqlState = requireNonNull(warning.getSQLState(), "SQLState is null"); + this.message = requireNonNull(warning.getMessage(), "message is null"); + } + + @Override + public boolean equals(Object other) + { + if (this == other) { + return true; + } + if (!(other instanceof WarningEntry)) { + return false; + } + WarningEntry that = (WarningEntry) other; + return vendorCode == that.vendorCode && sqlState.equals(that.sqlState) && message.equals(that.message); + } + + @Override + public int hashCode() + { + return Objects.hash(vendorCode, sqlState, message); + } + } + + private static class QueryCreationFuture + extends AbstractFuture + { + @Override + protected boolean set(QueryInfo value) + { + return super.set(value); + } + + @Override + protected boolean setException(Throwable throwable) + { + return super.setException(throwable); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + // query submission can not be canceled + return false; + } + } +} diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDatabaseMetaData.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDatabaseMetaData.java index a54fdbd4dc7fe..d3459db7f4933 100644 --- a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDatabaseMetaData.java +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDatabaseMetaData.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.jdbc; -import com.facebook.presto.execution.QueryInfo; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.QueryId; import io.airlift.log.Logging; @@ -157,15 +157,15 @@ private static void assertColumnSpec(ResultSet rs, int dataType, Long precision, private Set captureQueries(Callable action) throws Exception { - Set queryIdsBefore = server.getQueryManager().getAllQueryInfo().stream() - .map(QueryInfo::getQueryId) + Set queryIdsBefore = server.getQueryManager().getQueries().stream() + .map(BasicQueryInfo::getQueryId) .collect(toImmutableSet()); action.call(); - return server.getQueryManager().getAllQueryInfo().stream() + return server.getQueryManager().getQueries().stream() .filter(queryInfo -> !queryIdsBefore.contains(queryInfo.getQueryId())) - .map(QueryInfo::getQuery) + .map(BasicQueryInfo::getQuery) .collect(toImmutableSet()); } diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDriver.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDriver.java index 483b09e5df235..29dcbf5ebc6d3 100644 --- a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDriver.java +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestPrestoDriver.java @@ -92,6 +92,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -1236,6 +1237,27 @@ public void testGetMoreResultsException() } } + @Test + public void testGetMoreResultsClearsUpdateCount() + throws Exception + { + try (Connection connection = createConnection("blackhole", "default")) { + try (PrestoStatement statement = connection.createStatement().unwrap(PrestoStatement.class)) { + assertFalse(statement.execute("CREATE TABLE test_more_results_clears_update_count (id bigint)")); + assertEquals(statement.getUpdateCount(), 0); + assertEquals(statement.getUpdateType(), "CREATE TABLE"); + assertFalse(statement.getMoreResults()); + assertEquals(statement.getUpdateCount(), -1); + assertNull(statement.getUpdateType()); + } + finally { + try (Statement statement = connection.createStatement()) { + statement.execute("DROP TABLE test_more_results_clears_update_count"); + } + } + } + } + @Test public void testSetTimeZoneId() throws Exception @@ -1419,7 +1441,9 @@ public void testQueryCancelByInterrupt() // make sure the query was aborted assertTrue(queryFinished.await(10, SECONDS)); - assertNotNull(queryFailure.get()); + assertThat(queryFailure.get()) + .isInstanceOf(SQLException.class) + .hasMessage("ResultSet thread was interrupted"); assertEquals(getQueryState(queryId.get()), FAILED); } diff --git a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestProgressMonitor.java b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestProgressMonitor.java index a8aec4571e24c..8ddb27d4308be 100644 --- a/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestProgressMonitor.java +++ b/presto-jdbc/src/test/java/com/facebook/presto/jdbc/TestProgressMonitor.java @@ -89,8 +89,9 @@ private String newQueryResults(Integer partialCancelId, Integer nextUriId, List< nextUriId == null ? null : server.url(format("/v1/statement/%s/%s", queryId, nextUriId)).uri(), responseColumns, data, - new StatementStats(state, state.equals("QUEUED"), true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null), + new StatementStats(state, state.equals("QUEUED"), true, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null), null, + ImmutableList.of(), null, null); diff --git a/presto-jmx/pom.xml b/presto-jmx/pom.xml index 0b0af359e1553..c4b3c90a6e78f 100644 --- a/presto-jmx/pom.xml +++ b/presto-jmx/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-jmx diff --git a/presto-jmx/src/main/java/com/facebook/presto/connector/jmx/JmxConnectorFactory.java b/presto-jmx/src/main/java/com/facebook/presto/connector/jmx/JmxConnectorFactory.java index 843dd61431dab..9e29a3c041a70 100644 --- a/presto-jmx/src/main/java/com/facebook/presto/connector/jmx/JmxConnectorFactory.java +++ b/presto-jmx/src/main/java/com/facebook/presto/connector/jmx/JmxConnectorFactory.java @@ -54,7 +54,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { try { Bootstrap app = new Bootstrap( diff --git a/presto-kafka/pom.xml b/presto-kafka/pom.xml index bb44f5628a35f..8f9f569c29861 100644 --- a/presto-kafka/pom.xml +++ b/presto-kafka/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-kafka @@ -67,6 +67,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.annotation javax.annotation-api diff --git a/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaConnectorFactory.java b/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaConnectorFactory.java index 96868a86e14ed..03e8d396cc151 100644 --- a/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaConnectorFactory.java +++ b/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaConnectorFactory.java @@ -59,9 +59,9 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { - requireNonNull(connectorId, "connectorId is null"); + requireNonNull(catalogName, "catalogName is null"); requireNonNull(config, "config is null"); try { @@ -69,7 +69,7 @@ public Connector create(String connectorId, Map config, Connecto new JsonModule(), new KafkaConnectorModule(), binder -> { - binder.bind(KafkaConnectorId.class).toInstance(new KafkaConnectorId(connectorId)); + binder.bind(KafkaConnectorId.class).toInstance(new KafkaConnectorId(catalogName)); binder.bind(TypeManager.class).toInstance(context.getTypeManager()); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); diff --git a/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaSplitManager.java b/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaSplitManager.java index ad0d9bddb5840..9dc108bb7f23b 100644 --- a/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaSplitManager.java +++ b/presto-kafka/src/main/java/com/facebook/presto/kafka/KafkaSplitManager.java @@ -158,7 +158,7 @@ private static String readSchema(String dataSchemaLocation) return CharStreams.toString(new InputStreamReader(inputStream, UTF_8)); } catch (IOException e) { - throw new PrestoException(GENERIC_INTERNAL_ERROR, "Could not parse the AVRO schema at: " + dataSchemaLocation, e); + throw new PrestoException(GENERIC_INTERNAL_ERROR, "Could not parse the Avro schema at: " + dataSchemaLocation, e); } finally { closeQuietly(inputStream); diff --git a/presto-kudu/conf/docker-compose-single-node.yaml b/presto-kudu/conf/docker-compose-single-node.yaml index 2e5e5cf95eca4..b6e02171e5138 100644 --- a/presto-kudu/conf/docker-compose-single-node.yaml +++ b/presto-kudu/conf/docker-compose-single-node.yaml @@ -2,7 +2,7 @@ version: '2' services: kudu: hostname: kudu - image: 'usuresearch/kudu-docker-slim:release-v1.7.0-2' + image: 'usuresearch/kudu-docker-slim:release-v1.8.0-1' ports: - '18050:8050' - '18051:8051' diff --git a/presto-kudu/conf/docker-compose-three-nodes.yaml b/presto-kudu/conf/docker-compose-three-nodes.yaml index 5f1972bd8dbb3..e73f680bb4d85 100644 --- a/presto-kudu/conf/docker-compose-three-nodes.yaml +++ b/presto-kudu/conf/docker-compose-three-nodes.yaml @@ -2,7 +2,7 @@ version: '2' services: kudu1: hostname: kudu1 - image: 'usuresearch/kudu-docker-slim:release-v1.7.0-2' + image: 'usuresearch/kudu-docker-slim:release-v1.8.0-1' ports: - '18050:8050' - '18051:8051' diff --git a/presto-kudu/pom.xml b/presto-kudu/pom.xml index 6c85e582af485..e41201158cbc8 100644 --- a/presto-kudu/pom.xml +++ b/presto-kudu/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-kudu @@ -13,7 +13,7 @@ ${project.parent.basedir} - 1.7.0 + 1.8.0 @@ -53,6 +53,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + io.airlift configuration @@ -173,4 +179,25 @@ + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + org.apache.yetus + audience-annotations + 0.8.0 + + + + + + + diff --git a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduConnectorFactory.java b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduConnectorFactory.java index bb64eafc93fa0..bb8df46a2b4a0 100755 --- a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduConnectorFactory.java +++ b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduConnectorFactory.java @@ -45,13 +45,13 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { requireNonNull(config, "config is null"); try { Bootstrap app = new Bootstrap(new JsonModule(), - new KuduModule(connectorId, context.getTypeManager())); + new KuduModule(catalogName, context.getTypeManager())); Injector injector = app.strictConfig().doNotInitializeLogging().setRequiredConfigurationProperties(config) diff --git a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduModule.java b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduModule.java index cfaaef20fdaed..017062b2c229c 100755 --- a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduModule.java +++ b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduModule.java @@ -40,12 +40,12 @@ public class KuduModule extends AbstractModule { - private final String connectorId; + private final String catalogName; private final TypeManager typeManager; - public KuduModule(String connectorId, TypeManager typeManager) + public KuduModule(String catalogName, TypeManager typeManager) { - this.connectorId = requireNonNull(connectorId, "connector id is null"); + this.catalogName = requireNonNull(catalogName, "catalogName qis null"); this.typeManager = requireNonNull(typeManager, "typeManager is null"); } @@ -57,7 +57,7 @@ protected void configure() bind(TypeManager.class).toInstance(typeManager); bind(KuduConnector.class).in(Scopes.SINGLETON); - bind(KuduConnectorId.class).toInstance(new KuduConnectorId(connectorId)); + bind(KuduConnectorId.class).toInstance(new KuduConnectorId(catalogName)); bind(KuduMetadata.class).in(Scopes.SINGLETON); bind(KuduTableProperties.class).in(Scopes.SINGLETON); bind(ConnectorSplitManager.class).to(KuduSplitManager.class).in(Scopes.SINGLETON); diff --git a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduRecordCursor.java b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduRecordCursor.java index 7aba97f42665a..df29f070f06ff 100755 --- a/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduRecordCursor.java +++ b/presto-kudu/src/main/java/com/facebook/presto/kudu/KuduRecordCursor.java @@ -101,7 +101,8 @@ public boolean advanceNextPosition() } nextRows = scanner.nextRows(); - } while (!nextRows.hasNext()); + } + while (!nextRows.hasNext()); log.debug("Fetched " + nextRows.getNumRows() + " rows"); } catch (KuduException e) { diff --git a/presto-kudu/src/main/java/com/facebook/presto/kudu/properties/KuduTableProperties.java b/presto-kudu/src/main/java/com/facebook/presto/kudu/properties/KuduTableProperties.java index be5a646efbafb..77b2224526b8f 100644 --- a/presto-kudu/src/main/java/com/facebook/presto/kudu/properties/KuduTableProperties.java +++ b/presto-kudu/src/main/java/com/facebook/presto/kudu/properties/KuduTableProperties.java @@ -65,7 +65,7 @@ public final class KuduTableProperties public static final String PARTITION_BY_HASH_BUCKETS_2 = "partition_by_second_hash_buckets"; public static final String PARTITION_BY_RANGE_COLUMNS = "partition_by_range_columns"; public static final String RANGE_PARTITIONS = "range_partitions"; - public static final String NUM_REPLICAS = "num_replicas"; + public static final String NUM_REPLICAS = "number_of_replicas"; public static final String PRIMARY_KEY = "primary_key"; public static final String NULLABLE = "nullable"; public static final String ENCODING = "encoding"; @@ -321,7 +321,7 @@ public static Map toMap(KuduTable table) String partitionRangesValue = mapper.writeValueAsString(rangePartitionList); properties.put(RANGE_PARTITIONS, partitionRangesValue); - // currently no access to numReplicas? + properties.put(NUM_REPLICAS, table.getNumReplicas()); return properties; } diff --git a/presto-kudu/src/test/java/com/facebook/presto/kudu/TestKuduIntegrationSmoke.java b/presto-kudu/src/test/java/com/facebook/presto/kudu/TestKuduIntegrationSmoke.java index b2d1e007aae94..9cb22793b57be 100644 --- a/presto-kudu/src/test/java/com/facebook/presto/kudu/TestKuduIntegrationSmoke.java +++ b/presto-kudu/src/test/java/com/facebook/presto/kudu/TestKuduIntegrationSmoke.java @@ -22,8 +22,11 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.util.regex.Pattern; + import static com.facebook.presto.testing.assertions.Assert.assertEquals; import static io.airlift.tpch.TpchTable.ORDERS; +import static org.testng.Assert.assertTrue; public class TestKuduIntegrationSmoke extends AbstractTestIntegrationSmokeTest @@ -70,6 +73,32 @@ public void testDescribeTable() assertEquals(filteredActual, expectedColumns, String.format("%s != %s", filteredActual, expectedColumns)); } + @Test + public void testShowCreateTable() + { + queryRunner.execute("CREATE TABLE IF NOT EXISTS test_show_create_table (\n" + + "id INT WITH (primary_key=true),\n" + + "user_name VARCHAR\n" + + ") WITH (\n" + + " partition_by_hash_columns = ARRAY['id'],\n" + + " partition_by_hash_buckets = 2," + + " number_of_replicas = 1\n" + + ")"); + + MaterializedResult result = queryRunner.execute("SHOW CREATE TABLE test_show_create_table"); + String sqlStatement = (String) result.getOnlyValue(); + String tableProperties = sqlStatement.split("\\)\\s*WITH\\s*\\(")[1]; + assertTableProperty(tableProperties, "number_of_replicas", "1"); + assertTableProperty(tableProperties, "partition_by_hash_columns", Pattern.quote("ARRAY['id']")); + assertTableProperty(tableProperties, "partition_by_hash_buckets", "2"); + } + + private void assertTableProperty(String tableProperties, String key, String regexValue) + { + assertTrue(Pattern.compile(key + "\\s*=\\s*" + regexValue + ",?\\s+").matcher(tableProperties).find(), + "Not found: " + key + " = " + regexValue + " in " + tableProperties); + } + @AfterClass(alwaysRun = true) public final void destroy() { diff --git a/presto-local-file/pom.xml b/presto-local-file/pom.xml index cdf97bae86be6..93e515bf2269c 100644 --- a/presto-local-file/pom.xml +++ b/presto-local-file/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-local-file diff --git a/presto-local-file/src/main/java/com/facebook/presto/localfile/LocalFileConnectorFactory.java b/presto-local-file/src/main/java/com/facebook/presto/localfile/LocalFileConnectorFactory.java index bf8b0a0e07479..b1ffe7041d9e8 100644 --- a/presto-local-file/src/main/java/com/facebook/presto/localfile/LocalFileConnectorFactory.java +++ b/presto-local-file/src/main/java/com/facebook/presto/localfile/LocalFileConnectorFactory.java @@ -42,14 +42,14 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { requireNonNull(config, "config is null"); try { Bootstrap app = new Bootstrap( binder -> binder.bind(NodeManager.class).toInstance(context.getNodeManager()), - new LocalFileModule(connectorId)); + new LocalFileModule(catalogName)); Injector injector = app .strictConfig() diff --git a/presto-main/bin/check_webui.sh b/presto-main/bin/check_webui.sh new file mode 100755 index 0000000000000..5f349f7a1dbfb --- /dev/null +++ b/presto-main/bin/check_webui.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Perform basic validations on the Web UI Javascript code +# +# 1. Validate that the generated files that have been checked in to the webapp folder are in sync +# with the source. +# 2. Make sure there are no type checker warnings reported by Flow + +set -euo pipefail + +WEBUI_ROOT="$(pwd)/${BASH_SOURCE%/*}/../src/main/resources/webapp" + +# Fail if running the command to generate the `dist` folder again produces different results. +# The lockfile is generated code also, and must go through the same process. + +pushd $(mktemp -d) + +cp "${WEBUI_ROOT}/src/yarn.lock" . +cp -r "${WEBUI_ROOT}/dist" . + +yarn --cwd ${WEBUI_ROOT}/src/ install + +if ! diff -u ${WEBUI_ROOT}/src/yarn.lock yarn.lock; then + echo "Generated lockfile did not match checked-in version" + echo "Refer to the root README.md for instructions" + exit 1 +fi + +if ! diff -u ${WEBUI_ROOT}/dist dist; then + echo "ERROR: Generated dist folder did not match checked-in version" + echo "Refer to the root README.md for instructions on generating Web UI" + exit 1 +fi + +popd + + +# Fail on flow warnings + +if ! yarn --cwd ${WEBUI_ROOT}/src/ run flow; then + echo "ERROR: Flow found type errors while performing static analysis" + exit 1 +fi diff --git a/presto-main/etc/access-control.properties b/presto-main/etc/access-control.properties new file mode 100644 index 0000000000000..bb7f9d719e29e --- /dev/null +++ b/presto-main/etc/access-control.properties @@ -0,0 +1 @@ +access-control.name=allow-all diff --git a/presto-main/pom.xml b/presto-main/pom.xml index 59794482b0a7b..fdccb75a092d8 100644 --- a/presto-main/pom.xml +++ b/presto-main/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-main @@ -186,6 +186,11 @@ joni + + io.airlift + joda-to-java-time-bridge + + com.teradata re2j-td @@ -376,6 +381,10 @@ jaxrs-testing test + + com.facebook.presto + presto-plugin-toolkit + @@ -383,10 +392,10 @@ org.gaul modernizer-maven-plugin - - - com.facebook.presto.testing.assertions - + + + com.facebook.presto.testing.assertions + @@ -397,6 +406,9 @@ **/TestLocalExecutionPlanner.java + + io.airlift.jodabridge.JdkBasedZoneInfoProvider + @@ -425,7 +437,7 @@ -Dmyproperty=myvalue -classpath - com.facebook.presto + com.facebook.presto.benchmark.BenchmarkSuite test diff --git a/presto-main/src/main/java/com/facebook/presto/ExceededMemoryLimitException.java b/presto-main/src/main/java/com/facebook/presto/ExceededMemoryLimitException.java index 5e63b6413c8e5..23b39c9d678bd 100644 --- a/presto-main/src/main/java/com/facebook/presto/ExceededMemoryLimitException.java +++ b/presto-main/src/main/java/com/facebook/presto/ExceededMemoryLimitException.java @@ -14,44 +14,46 @@ package com.facebook.presto; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.StandardErrorCode; import io.airlift.units.DataSize; -import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_MEMORY_LIMIT; +import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_GLOBAL_MEMORY_LIMIT; +import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_LOCAL_MEMORY_LIMIT; import static java.lang.String.format; public class ExceededMemoryLimitException extends PrestoException { - private final DataSize maxMemory; - public static ExceededMemoryLimitException exceededGlobalUserLimit(DataSize maxMemory) { - return new ExceededMemoryLimitException(maxMemory, format("Query exceeded max user memory size of %s", maxMemory)); + return new ExceededMemoryLimitException(EXCEEDED_GLOBAL_MEMORY_LIMIT, format("Query exceeded distributed user memory limit of %s", maxMemory)); } public static ExceededMemoryLimitException exceededGlobalTotalLimit(DataSize maxMemory) { - return new ExceededMemoryLimitException(maxMemory, format("Query exceeded max total memory size of %s", maxMemory)); - } - - public static ExceededMemoryLimitException exceededLocalUserMemoryLimit(DataSize maxMemory) - { - return new ExceededMemoryLimitException(maxMemory, format("Query exceeded local user memory limit of %s", maxMemory)); + return new ExceededMemoryLimitException(EXCEEDED_GLOBAL_MEMORY_LIMIT, format("Query exceeded distributed total memory limit of %s", maxMemory)); } - public static ExceededMemoryLimitException exceededLocalTotalMemoryLimit(DataSize maxMemory) + public static ExceededMemoryLimitException exceededLocalUserMemoryLimit(DataSize maxMemory, DataSize allocated, DataSize delta) { - return new ExceededMemoryLimitException(maxMemory, format("Query exceeded local total memory limit of %s", maxMemory)); + return new ExceededMemoryLimitException(EXCEEDED_LOCAL_MEMORY_LIMIT, format( + "Query exceeded per-node user memory limit of %s when increasing allocation of %s by %s", + maxMemory, + allocated, + delta)); } - private ExceededMemoryLimitException(DataSize maxMemory, String message) + public static ExceededMemoryLimitException exceededLocalTotalMemoryLimit(DataSize maxMemory, DataSize allocated, DataSize delta) { - super(EXCEEDED_MEMORY_LIMIT, message); - this.maxMemory = maxMemory; + return new ExceededMemoryLimitException(EXCEEDED_LOCAL_MEMORY_LIMIT, format( + "Query exceeded per-node total memory limit of %s when increasing allocation of %s by %s", + maxMemory, + allocated, + delta)); } - public DataSize getMaxMemory() + private ExceededMemoryLimitException(StandardErrorCode errorCode, String message) { - return maxMemory; + super(errorCode, message); } } diff --git a/presto-main/src/main/java/com/facebook/presto/FullConnectorSession.java b/presto-main/src/main/java/com/facebook/presto/FullConnectorSession.java index 00db4ddcb22c3..10da2168d6621 100644 --- a/presto-main/src/main/java/com/facebook/presto/FullConnectorSession.java +++ b/presto-main/src/main/java/com/facebook/presto/FullConnectorSession.java @@ -39,7 +39,6 @@ public class FullConnectorSession private final String catalog; private final SessionPropertyManager sessionPropertyManager; private final boolean isLegacyTimestamp; - private final boolean isLegacyRoundNBigint; public FullConnectorSession(Session session) { @@ -49,7 +48,6 @@ public FullConnectorSession(Session session) this.catalog = null; this.sessionPropertyManager = null; this.isLegacyTimestamp = SystemSessionProperties.isLegacyTimestamp(session); - this.isLegacyRoundNBigint = SystemSessionProperties.isLegacyRoundNBigint(session); } public FullConnectorSession( @@ -65,7 +63,6 @@ public FullConnectorSession( this.catalog = requireNonNull(catalog, "catalog is null"); this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); this.isLegacyTimestamp = SystemSessionProperties.isLegacyTimestamp(session); - this.isLegacyRoundNBigint = SystemSessionProperties.isLegacyRoundNBigint(session); } public Session getSession() @@ -85,12 +82,6 @@ public Optional getSource() return session.getSource(); } - @Override - public String getPath() - { - return session.getPath().toString(); - } - @Override public Identity getIdentity() { @@ -127,12 +118,6 @@ public boolean isLegacyTimestamp() return isLegacyTimestamp; } - @Override - public boolean isLegacyRoundNBigint() - { - return isLegacyRoundNBigint; - } - @Override public T getProperty(String propertyName, Class type) { diff --git a/presto-main/src/main/java/com/facebook/presto/Session.java b/presto-main/src/main/java/com/facebook/presto/Session.java index 9148c27fcfa14..1cd64dde0f176 100644 --- a/presto-main/src/main/java/com/facebook/presto/Session.java +++ b/presto-main/src/main/java/com/facebook/presto/Session.java @@ -41,6 +41,7 @@ import java.util.Optional; import java.util.Set; import java.util.TimeZone; +import java.util.stream.Collectors; import static com.facebook.presto.spi.StandardErrorCode.NOT_FOUND; import static com.facebook.presto.util.Failures.checkCondition; @@ -344,6 +345,57 @@ public Session beginTransactionId(TransactionId transactionId, TransactionManage preparedStatements); } + public Session withDefaultProperties(Map systemPropertyDefaults, Map> catalogPropertyDefaults) + { + requireNonNull(systemPropertyDefaults, "systemPropertyDefaults is null"); + requireNonNull(catalogPropertyDefaults, "catalogPropertyDefaults is null"); + + // to remove this check properties must be authenticated and validated as in beginTransactionId + checkState( + !this.transactionId.isPresent() && this.connectorProperties.isEmpty(), + "Session properties cannot be overridden once a transaction is active"); + + Map systemProperties = new HashMap<>(); + systemProperties.putAll(systemPropertyDefaults); + systemProperties.putAll(this.systemProperties); + + Map> connectorProperties = catalogPropertyDefaults.entrySet().stream() + .map(entry -> Maps.immutableEntry(entry.getKey(), new HashMap<>(entry.getValue()))) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + for (Entry> catalogProperties : this.unprocessedCatalogProperties.entrySet()) { + String catalog = catalogProperties.getKey(); + for (Entry entry : catalogProperties.getValue().entrySet()) { + connectorProperties.computeIfAbsent(catalog, id -> new HashMap<>()) + .put(entry.getKey(), entry.getValue()); + } + } + + return new Session( + queryId, + transactionId, + clientTransactionSupport, + identity, + source, + catalog, + schema, + path, + traceToken, + timeZoneKey, + locale, + remoteUserAddress, + userAgent, + clientInfo, + clientTags, + clientCapabilities, + resourceEstimates, + startTime, + systemProperties, + ImmutableMap.of(), + connectorProperties, + sessionPropertyManager, + preparedStatements); + } + public ConnectorSession toConnectorSession() { return new FullConnectorSession(this); @@ -384,6 +436,7 @@ public SessionRepresentation toSessionRepresentation() startTime, systemProperties, connectorProperties, + unprocessedCatalogProperties, preparedStatements); } diff --git a/presto-main/src/main/java/com/facebook/presto/SessionRepresentation.java b/presto-main/src/main/java/com/facebook/presto/SessionRepresentation.java index d809380a6cf5a..218f5e17258cd 100644 --- a/presto-main/src/main/java/com/facebook/presto/SessionRepresentation.java +++ b/presto-main/src/main/java/com/facebook/presto/SessionRepresentation.java @@ -57,6 +57,7 @@ public final class SessionRepresentation private final ResourceEstimates resourceEstimates; private final Map systemProperties; private final Map> catalogProperties; + private final Map> unprocessedCatalogProperties; private final Map preparedStatements; @JsonCreator @@ -82,6 +83,7 @@ public SessionRepresentation( @JsonProperty("startTime") long startTime, @JsonProperty("systemProperties") Map systemProperties, @JsonProperty("catalogProperties") Map> catalogProperties, + @JsonProperty("unprocessedCatalogProperties") Map> unprocessedCatalogProperties, @JsonProperty("preparedStatements") Map preparedStatements) { this.queryId = requireNonNull(queryId, "queryId is null"); @@ -111,6 +113,12 @@ public SessionRepresentation( catalogPropertiesBuilder.put(entry.getKey(), ImmutableMap.copyOf(entry.getValue())); } this.catalogProperties = catalogPropertiesBuilder.build(); + + ImmutableMap.Builder> unprocessedCatalogPropertiesBuilder = ImmutableMap.builder(); + for (Entry> entry : unprocessedCatalogProperties.entrySet()) { + unprocessedCatalogPropertiesBuilder.put(entry.getKey(), ImmutableMap.copyOf(entry.getValue())); + } + this.unprocessedCatalogProperties = unprocessedCatalogPropertiesBuilder.build(); } @JsonProperty @@ -239,6 +247,12 @@ public Map> getCatalogProperties() return catalogProperties; } + @JsonProperty + public Map> getUnprocessedCatalogProperties() + { + return unprocessedCatalogProperties; + } + @JsonProperty public Map getPreparedStatements() { @@ -268,7 +282,7 @@ public Session toSession(SessionPropertyManager sessionPropertyManager) startTime, systemProperties, catalogProperties, - ImmutableMap.of(), + unprocessedCatalogProperties, sessionPropertyManager, preparedStatements); } diff --git a/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java b/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java index ca5a4fc6a0b08..52368f9acc1d2 100644 --- a/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java +++ b/presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java @@ -28,6 +28,7 @@ import javax.inject.Inject; import java.util.List; +import java.util.Optional; import java.util.OptionalInt; import java.util.stream.Stream; @@ -37,12 +38,14 @@ import static com.facebook.presto.spi.session.PropertyMetadata.stringProperty; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.BROADCAST; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.PARTITIONED; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinReorderingStrategy.ELIMINATE_CROSS_JOINS; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinReorderingStrategy.NONE; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Math.min; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; @@ -51,10 +54,12 @@ public final class SystemSessionProperties { public static final String OPTIMIZE_HASH_GENERATION = "optimize_hash_generation"; public static final String JOIN_DISTRIBUTION_TYPE = "join_distribution_type"; + public static final String JOIN_MAX_BROADCAST_TABLE_SIZE = "join_max_broadcast_table_size"; public static final String DISTRIBUTED_JOIN = "distributed_join"; public static final String DISTRIBUTED_INDEX_JOIN = "distributed_index_join"; public static final String HASH_PARTITION_COUNT = "hash_partition_count"; public static final String GROUPED_EXECUTION_FOR_AGGREGATION = "grouped_execution_for_aggregation"; + public static final String DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION = "dynamic_schedule_for_grouped_execution"; public static final String PREFER_STREAMING_OPERATORS = "prefer_streaming_operators"; public static final String TASK_WRITER_COUNT = "task_writer_count"; public static final String TASK_CONCURRENCY = "task_concurrency"; @@ -74,6 +79,7 @@ public final class SystemSessionProperties public static final String DICTIONARY_AGGREGATION = "dictionary_aggregation"; public static final String PLAN_WITH_TABLE_NODE_PARTITIONING = "plan_with_table_node_partitioning"; public static final String SPATIAL_JOIN = "spatial_join"; + public static final String SPATIAL_PARTITIONING_TABLE_NAME = "spatial_partitioning_table_name"; public static final String COLOCATED_JOIN = "colocated_join"; public static final String CONCURRENT_LIFESPANS_PER_NODE = "concurrent_lifespans_per_task"; public static final String REORDER_JOINS = "reorder_joins"; @@ -87,7 +93,6 @@ public final class SystemSessionProperties public static final String SPILL_ENABLED = "spill_enabled"; public static final String AGGREGATION_OPERATOR_UNSPILL_MEMORY_LIMIT = "aggregation_operator_unspill_memory_limit"; public static final String OPTIMIZE_DISTINCT_AGGREGATIONS = "optimize_mixed_distinct_aggregations"; - public static final String LEGACY_ROUND_N_BIGINT = "legacy_round_n_bigint"; public static final String LEGACY_ROW_FIELD_ORDINAL_ACCESS = "legacy_row_field_ordinal_access"; public static final String ITERATIVE_OPTIMIZER = "iterative_optimizer_enabled"; public static final String ITERATIVE_OPTIMIZER_TIMEOUT = "iterative_optimizer_timeout"; @@ -102,9 +107,15 @@ public final class SystemSessionProperties public static final String FILTER_AND_PROJECT_MIN_OUTPUT_PAGE_ROW_COUNT = "filter_and_project_min_output_page_row_count"; public static final String DISTRIBUTED_SORT = "distributed_sort"; public static final String USE_MARK_DISTINCT = "use_mark_distinct"; - public static final String PREFER_PARTITIAL_AGGREGATION = "prefer_partial_aggregation"; + public static final String PREFER_PARTIAL_AGGREGATION = "prefer_partial_aggregation"; + public static final String OPTIMIZE_TOP_N_ROW_NUMBER = "optimize_top_n_row_number"; public static final String MAX_GROUPING_SETS = "max_grouping_sets"; public static final String LEGACY_UNNEST = "legacy_unnest"; + public static final String STATISTICS_CPU_TIMER_ENABLED = "statistics_cpu_timer_enabled"; + public static final String ENABLE_STATS_CALCULATOR = "enable_stats_calculator"; + public static final String IGNORE_STATS_CALCULATOR_FAILURES = "ignore_stats_calculator_failures"; + public static final String MAX_DRIVERS_PER_TASK = "max_drivers_per_task"; + public static final String DEFAULT_FILTER_FACTOR_ENABLED = "default_filter_factor_enabled"; private final List> sessionProperties; @@ -148,6 +159,15 @@ public SystemSessionProperties( false, value -> JoinDistributionType.valueOf(((String) value).toUpperCase()), JoinDistributionType::name), + new PropertyMetadata<>( + JOIN_MAX_BROADCAST_TABLE_SIZE, + "Maximum estimated size of a table that can be broadcast for JOIN.", + VARCHAR, + DataSize.class, + featuresConfig.getJoinMaxBroadcastTableSize(), + true, + value -> DataSize.valueOf((String) value), + DataSize::toString), booleanProperty( DISTRIBUTED_INDEX_JOIN, "Distribute index joins on join keys instead of executing inline", @@ -163,6 +183,11 @@ public SystemSessionProperties( "Use grouped execution for aggregation when possible", featuresConfig.isGroupedExecutionForAggregationEnabled(), false), + booleanProperty( + DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, + "Experimental: Use dynamic schedule for grouped execution when possible", + false, + featuresConfig.isDynamicScheduleForGroupedExecutionEnabled()), booleanProperty( PREFER_STREAMING_OPERATORS, "Prefer source table layouts that produce streaming operators", @@ -351,6 +376,11 @@ public SystemSessionProperties( "Use spatial index for spatial join when possible", featuresConfig.isSpatialJoinsEnabled(), false), + stringProperty( + SPATIAL_PARTITIONING_TABLE_NAME, + "Name of the table containing spatial partitioning scheme", + null, + false), integerProperty( CONCURRENT_LIFESPANS_PER_NODE, "Experimental: Run a fixed number of groups concurrently for eligible JOINs", @@ -387,11 +417,6 @@ public SystemSessionProperties( "Optimize mixed non-distinct and distinct aggregations", featuresConfig.isOptimizeMixedDistinctAggregations(), false), - booleanProperty( - LEGACY_ROUND_N_BIGINT, - "Allow ROUND(x, d) to accept d being BIGINT", - featuresConfig.isLegacyRoundNBigint(), - true), booleanProperty( LEGACY_ROW_FIELD_ORDINAL_ACCESS, "Allow accessing anonymous row field with .field0, .field1, ...", @@ -471,10 +496,15 @@ public SystemSessionProperties( featuresConfig.isUseMarkDistinct(), false), booleanProperty( - PREFER_PARTITIAL_AGGREGATION, + PREFER_PARTIAL_AGGREGATION, "Prefer splitting aggregations into partial and final stages", featuresConfig.isPreferPartialAggregation(), false), + booleanProperty( + OPTIMIZE_TOP_N_ROW_NUMBER, + "Use top N row number optimization", + featuresConfig.isOptimizeTopNRowNumber(), + false), integerProperty( MAX_GROUPING_SETS, "Maximum number of grouping sets in a GROUP BY", @@ -484,6 +514,35 @@ public SystemSessionProperties( LEGACY_UNNEST, "Using legacy unnest semantic, where unnest(array(row)) will create one column of type row", featuresConfig.isLegacyUnnestArrayRows(), + false), + booleanProperty( + STATISTICS_CPU_TIMER_ENABLED, + "Experimental: Enable cpu time tracking for automatic column statistics collection on write", + taskManagerConfig.isStatisticsCpuTimerEnabled(), + false), + booleanProperty( + ENABLE_STATS_CALCULATOR, + "Experimental: Enable statistics calculator", + featuresConfig.isEnableStatsCalculator(), + false), + new PropertyMetadata<>( + MAX_DRIVERS_PER_TASK, + "Maximum number of drivers per task", + INTEGER, + Integer.class, + null, + false, + value -> min(taskManagerConfig.getMaxDriversPerTask(), validateNullablePositiveIntegerValue(value, MAX_DRIVERS_PER_TASK)), + object -> object), + booleanProperty( + IGNORE_STATS_CALCULATOR_FAILURES, + "Ignore statistics calculator failures", + featuresConfig.isIgnoreStatsCalculatorFailures(), + false), + booleanProperty( + DEFAULT_FILTER_FACTOR_ENABLED, + "use a default filter factor for unknown filters in a filter node", + featuresConfig.isDefaultFilterFactorEnabled(), false)); } @@ -516,6 +575,11 @@ public static JoinDistributionType getJoinDistributionType(Session session) return session.getSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.class); } + public static Optional getJoinMaxBroadcastTableSize(Session session) + { + return Optional.ofNullable(session.getSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, DataSize.class)); + } + public static boolean isDistributedIndexJoinEnabled(Session session) { return session.getSystemProperty(DISTRIBUTED_INDEX_JOIN, Boolean.class); @@ -526,11 +590,16 @@ public static int getHashPartitionCount(Session session) return session.getSystemProperty(HASH_PARTITION_COUNT, Integer.class); } - public static boolean isGroupedExecutionForJoinEnabled(Session session) + public static boolean isGroupedExecutionForAggregationEnabled(Session session) { return session.getSystemProperty(GROUPED_EXECUTION_FOR_AGGREGATION, Boolean.class); } + public static boolean isDynamicSchduleForGroupedExecution(Session session) + { + return session.getSystemProperty(DYNAMIC_SCHEDULE_FOR_GROUPED_EXECUTION, Boolean.class); + } + public static boolean preferStreamingOperators(Session session) { return session.getSystemProperty(PREFER_STREAMING_OPERATORS, Boolean.class); @@ -648,6 +717,11 @@ public static boolean isSpatialJoinEnabled(Session session) return session.getSystemProperty(SPATIAL_JOIN, Boolean.class); } + public static Optional getSpatialPartitioningTableName(Session session) + { + return Optional.ofNullable(session.getSystemProperty(SPATIAL_PARTITIONING_TABLE_NAME, String.class)); + } + public static OptionalInt getConcurrentLifespansPerNode(Session session) { Integer result = session.getSystemProperty(CONCURRENT_LIFESPANS_PER_NODE, Integer.class); @@ -699,12 +773,6 @@ public static boolean isOptimizeDistinctAggregationEnabled(Session session) return session.getSystemProperty(OPTIMIZE_DISTINCT_AGGREGATIONS, Boolean.class); } - @Deprecated - public static boolean isLegacyRoundNBigint(Session session) - { - return session.getSystemProperty(LEGACY_ROUND_N_BIGINT, Boolean.class); - } - public static boolean isLegacyRowFieldOrdinalAccessEnabled(Session session) { return session.getSystemProperty(LEGACY_ROW_FIELD_ORDINAL_ACCESS, Boolean.class); @@ -773,7 +841,12 @@ public static boolean useMarkDistinct(Session session) public static boolean preferPartialAggregation(Session session) { - return session.getSystemProperty(PREFER_PARTITIAL_AGGREGATION, Boolean.class); + return session.getSystemProperty(PREFER_PARTIAL_AGGREGATION, Boolean.class); + } + + public static boolean isOptimizeTopNRowNumber(Session session) + { + return session.getSystemProperty(OPTIMIZE_TOP_N_ROW_NUMBER, Boolean.class); } public static boolean isDistributedSortEnabled(Session session) @@ -791,6 +864,15 @@ public static boolean isLegacyUnnest(Session session) return session.getSystemProperty(LEGACY_UNNEST, Boolean.class); } + public static OptionalInt getMaxDriversPerTask(Session session) + { + Integer value = session.getSystemProperty(MAX_DRIVERS_PER_TASK, Integer.class); + if (value == null) { + return OptionalInt.empty(); + } + return OptionalInt.of(value); + } + private static int validateValueIsPowerOfTwo(Object value, String property) { int intValue = ((Number) requireNonNull(value, "value is null")).intValue(); @@ -801,4 +883,46 @@ private static int validateValueIsPowerOfTwo(Object value, String property) } return intValue; } + + private static Integer validateNullablePositiveIntegerValue(Object value, String property) + { + return validateIntegerValue(value, property, 1, true); + } + + private static Integer validateIntegerValue(Object value, String property, int lowerBoundIncluded, boolean allowNull) + { + if (value == null && !allowNull) { + throw new PrestoException(INVALID_SESSION_PROPERTY, format("%s must be non-null", property)); + } + + if (value == null) { + return null; + } + + int intValue = ((Number) value).intValue(); + if (intValue < lowerBoundIncluded) { + throw new PrestoException(INVALID_SESSION_PROPERTY, format("%s must be equal or greater than %s", property, lowerBoundIncluded)); + } + return intValue; + } + + public static boolean isStatisticsCpuTimerEnabled(Session session) + { + return session.getSystemProperty(STATISTICS_CPU_TIMER_ENABLED, Boolean.class); + } + + public static boolean isEnableStatsCalculator(Session session) + { + return session.getSystemProperty(ENABLE_STATS_CALCULATOR, Boolean.class); + } + + public static boolean isIgnoreStatsCalculatorFailures(Session session) + { + return session.getSystemProperty(IGNORE_STATS_CALCULATOR_FAILURES, Boolean.class); + } + + public static boolean isDefaultFilterFactorEnabled(Session session) + { + return session.getSystemProperty(DEFAULT_FILTER_FACTOR_ENABLED, Boolean.class); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/connector/ConnectorManager.java b/presto-main/src/main/java/com/facebook/presto/connector/ConnectorManager.java index 57db7f0dab3f3..0af56e0ac0b5c 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/ConnectorManager.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/ConnectorManager.java @@ -292,6 +292,7 @@ private synchronized void removeConnectorInternal(ConnectorId connectorId) metadataManager.getProcedureRegistry().removeProcedures(connectorId); accessControlManager.removeCatalogAccessControl(connectorId); metadataManager.getTablePropertyManager().removeProperties(connectorId); + metadataManager.getColumnPropertyManager().removeProperties(connectorId); metadataManager.getSchemaPropertyManager().removeProperties(connectorId); metadataManager.getSessionPropertyManager().removeConnectorSessionProperties(connectorId); diff --git a/presto-main/src/main/java/com/facebook/presto/connector/informationSchema/InformationSchemaMetadata.java b/presto-main/src/main/java/com/facebook/presto/connector/informationSchema/InformationSchemaMetadata.java index 9c623e8a06f85..6b0fc2146c7ba 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/informationSchema/InformationSchemaMetadata.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/informationSchema/InformationSchemaMetadata.java @@ -59,6 +59,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static io.airlift.slice.Slices.utf8Slice; +import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; @@ -249,6 +250,7 @@ private Set calculatePrefixesWithSchemaName( Optional> schemas = filterString(constraint, SCHEMA_COLUMN_HANDLE); if (schemas.isPresent()) { return schemas.get().stream() + .filter(this::isLowerCase) .map(schema -> new QualifiedTablePrefix(catalogName, schema)) .collect(toImmutableSet()); } @@ -272,6 +274,8 @@ public Set calculatePrefixesWithTableName( if (tables.isPresent()) { return prefixes.stream() .flatMap(prefix -> tables.get().stream() + .filter(this::isLowerCase) + .map(table -> table.toLowerCase(ENGLISH)) .map(table -> new QualifiedObjectName(catalogName, prefix.getSchemaName().get(), table))) .filter(objectName -> metadata.getTableHandle(session, objectName).isPresent() || metadata.getView(session, objectName).isPresent()) .map(QualifiedObjectName::asQualifiedTablePrefix) @@ -335,4 +339,9 @@ static List informationSchemaTableColumns(SchemaTableName tableN checkArgument(TABLES.containsKey(tableName), "table does not exist: %s", tableName); return TABLES.get(tableName).getColumns(); } + + private boolean isLowerCase(String value) + { + return value.toLowerCase(ENGLISH).equals(value); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/connector/system/GlobalSystemConnectorFactory.java b/presto-main/src/main/java/com/facebook/presto/connector/system/GlobalSystemConnectorFactory.java index 82f1c730b2b9a..07cd6ad48a90c 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/system/GlobalSystemConnectorFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/system/GlobalSystemConnectorFactory.java @@ -54,8 +54,8 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { - return new GlobalSystemConnector(connectorId, tables, procedures); + return new GlobalSystemConnector(catalogName, tables, procedures); } } diff --git a/presto-main/src/main/java/com/facebook/presto/connector/system/KillQueryProcedure.java b/presto-main/src/main/java/com/facebook/presto/connector/system/KillQueryProcedure.java index 4eee5b6e18dd6..c9262823030e9 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/system/KillQueryProcedure.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/system/KillQueryProcedure.java @@ -14,7 +14,6 @@ package com.facebook.presto.connector.system; import com.facebook.presto.annotation.UsedByGeneratedCode; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryState; import com.facebook.presto.spi.PrestoException; @@ -29,6 +28,7 @@ import java.util.NoSuchElementException; import static com.facebook.presto.spi.StandardErrorCode.ADMINISTRATIVELY_KILLED; +import static com.facebook.presto.spi.StandardErrorCode.ADMINISTRATIVELY_PREEMPTED; import static com.facebook.presto.spi.StandardErrorCode.INVALID_PROCEDURE_ARGUMENT; import static com.facebook.presto.spi.StandardErrorCode.NOT_FOUND; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; @@ -55,19 +55,17 @@ public void killQuery(String queryId, String message) QueryId query = parseQueryId(queryId); try { - QueryInfo queryInfo = queryManager.getQueryInfo(query); + QueryState state = queryManager.getQueryState(query); // check before killing to provide the proper error message (this is racy) - if (queryInfo.getState().isDone()) { + if (state.isDone()) { throw new PrestoException(NOT_SUPPORTED, "Target query is not running: " + queryId); } queryManager.failQuery(query, createKillQueryException(message)); // verify if the query was killed (if not, we lost the race) - queryInfo = queryManager.getQueryInfo(query); - if ((queryInfo.getState() != QueryState.FAILED) || - !ADMINISTRATIVELY_KILLED.toErrorCode().equals(queryInfo.getErrorCode())) { + if (!ADMINISTRATIVELY_KILLED.toErrorCode().equals(queryManager.getQueryInfo(query).getErrorCode())) { throw new PrestoException(NOT_SUPPORTED, "Target query is not running: " + queryId); } } @@ -94,6 +92,12 @@ public static PrestoException createKillQueryException(String message) (isNullOrEmpty(message) ? "No message provided." : "Message: " + message)); } + public static PrestoException createPreemptQueryException(String message) + { + return new PrestoException(ADMINISTRATIVELY_PREEMPTED, "Query preempted. " + + (isNullOrEmpty(message) ? "No message provided." : "Message: " + message)); + } + private static QueryId parseQueryId(String queryId) { try { diff --git a/presto-main/src/main/java/com/facebook/presto/connector/system/QuerySystemTable.java b/presto-main/src/main/java/com/facebook/presto/connector/system/QuerySystemTable.java index 6ed53fbc4ca99..b508b86596d80 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/system/QuerySystemTable.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/system/QuerySystemTable.java @@ -16,6 +16,7 @@ import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryStats; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorTableMetadata; import com.facebook.presto.spi.InMemoryRecordSet; @@ -29,19 +30,21 @@ import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.type.ArrayType; -import io.airlift.node.NodeInfo; import io.airlift.units.Duration; import org.joda.time.DateTime; import javax.inject.Inject; import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; import static com.facebook.presto.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder; import static com.facebook.presto.spi.SystemTable.Distribution.ALL_COORDINATORS; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; +import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.slice.Slices.utf8Slice; import static java.util.Objects.requireNonNull; @@ -51,7 +54,6 @@ public class QuerySystemTable public static final SchemaTableName QUERY_TABLE_NAME = new SchemaTableName("runtime", "queries"); public static final ConnectorTableMetadata QUERY_TABLE = tableMetadataBuilder(QUERY_TABLE_NAME) - .column("node_id", createUnboundedVarcharType()) .column("query_id", createUnboundedVarcharType()) .column("state", createUnboundedVarcharType()) .column("user", createUnboundedVarcharType()) @@ -70,13 +72,11 @@ public class QuerySystemTable .build(); private final QueryManager queryManager; - private final String nodeId; @Inject - public QuerySystemTable(QueryManager queryManager, NodeInfo nodeInfo) + public QuerySystemTable(QueryManager queryManager) { this.queryManager = queryManager; - this.nodeId = nodeInfo.getNodeId(); } @Override @@ -95,10 +95,21 @@ public ConnectorTableMetadata getTableMetadata() public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, ConnectorSession session, TupleDomain constraint) { Builder table = InMemoryRecordSet.builder(QUERY_TABLE); - for (QueryInfo queryInfo : queryManager.getAllQueryInfo()) { + List queryInfos = queryManager.getQueries().stream() + .map(BasicQueryInfo::getQueryId) + .map(queryId -> { + try { + return queryManager.getFullQueryInfo(queryId); + } + catch (NoSuchElementException e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(toImmutableList()); + for (QueryInfo queryInfo : queryInfos) { QueryStats queryStats = queryInfo.getQueryStats(); table.addRow( - nodeId, queryInfo.getQueryId().toString(), queryInfo.getState().toString(), queryInfo.getSession().getUser(), diff --git a/presto-main/src/main/java/com/facebook/presto/connector/system/TaskSystemTable.java b/presto-main/src/main/java/com/facebook/presto/connector/system/TaskSystemTable.java index 54a62bd9d1388..fa08ad5864011 100644 --- a/presto-main/src/main/java/com/facebook/presto/connector/system/TaskSystemTable.java +++ b/presto-main/src/main/java/com/facebook/presto/connector/system/TaskSystemTable.java @@ -59,7 +59,6 @@ public class TaskSystemTable .column("split_scheduled_time_ms", BIGINT) .column("split_cpu_time_ms", BIGINT) - .column("split_user_time_ms", BIGINT) .column("split_blocked_time_ms", BIGINT) .column("raw_input_bytes", BIGINT) @@ -123,7 +122,6 @@ public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, Connect toMillis(stats.getTotalScheduledTime()), toMillis(stats.getTotalCpuTime()), - toMillis(stats.getTotalUserTime()), toMillis(stats.getTotalBlockedTime()), toBytes(stats.getRawInputDataSize()), diff --git a/presto-main/src/main/java/com/facebook/presto/cost/AggregationStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/AggregationStatsRule.java index a6e8155d20bb7..6f69e0bd178c3 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/AggregationStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/AggregationStatsRule.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Optional; +import static com.facebook.presto.sql.planner.plan.AggregationNode.Step.SINGLE; import static com.facebook.presto.sql.planner.plan.Patterns.aggregation; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; @@ -52,6 +53,10 @@ protected Optional doCalculate(AggregationNode node, Stat return Optional.empty(); } + if (node.getStep() != SINGLE) { + return Optional.empty(); + } + return Optional.of(groupBy( statsProvider.getStats(node.getSource()), node.getGroupingKeys(), @@ -92,6 +97,6 @@ private static SymbolStatsEstimate estimateAggregationStats(Aggregation aggregat requireNonNull(sourceStats, "sourceStats is null"); // TODO implement simple aggregations like: min, max, count, sum - return SymbolStatsEstimate.UNKNOWN_STATS; + return SymbolStatsEstimate.unknown(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/CachingCostProvider.java b/presto-main/src/main/java/com/facebook/presto/cost/CachingCostProvider.java index f6e3acf007ed7..c5fbf679e91a0 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/CachingCostProvider.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/CachingCostProvider.java @@ -16,26 +16,27 @@ import com.facebook.presto.Session; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.GroupReference; -import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.iterative.Memo; import com.facebook.presto.sql.planner.plan.PlanNode; +import io.airlift.log.Logger; import java.util.IdentityHashMap; import java.util.Map; import java.util.Optional; -import static com.facebook.presto.cost.PlanNodeCostEstimate.ZERO_COST; -import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; +import static com.facebook.presto.SystemSessionProperties.isEnableStatsCalculator; +import static com.facebook.presto.SystemSessionProperties.isIgnoreStatsCalculatorFailures; import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; public class CachingCostProvider implements CostProvider { + private static final Logger log = Logger.get(CachingCostProvider.class); + private final CostCalculator costCalculator; private final StatsProvider statsProvider; private final Optional memo; - private final Lookup lookup; private final Session session; private final TypeProvider types; @@ -43,15 +44,14 @@ public class CachingCostProvider public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Session session, TypeProvider types) { - this(costCalculator, statsProvider, Optional.empty(), noLookup(), session, types); + this(costCalculator, statsProvider, Optional.empty(), session, types); } - public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Optional memo, Lookup lookup, Session session, TypeProvider types) + public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Optional memo, Session session, TypeProvider types) { this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); this.statsProvider = requireNonNull(statsProvider, "statsProvider is null"); this.memo = requireNonNull(memo, "memo is null"); - this.lookup = requireNonNull(lookup, "lookup is null"); this.session = requireNonNull(session, "session is null"); this.types = requireNonNull(types, "types is null"); } @@ -59,20 +59,33 @@ public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsPro @Override public PlanNodeCostEstimate getCumulativeCost(PlanNode node) { + if (!isEnableStatsCalculator(session)) { + return PlanNodeCostEstimate.unknown(); + } + requireNonNull(node, "node is null"); - if (node instanceof GroupReference) { - return getGroupCost((GroupReference) node); - } + try { + if (node instanceof GroupReference) { + return getGroupCost((GroupReference) node); + } - PlanNodeCostEstimate cumulativeCost = cache.get(node); - if (cumulativeCost != null) { + PlanNodeCostEstimate cumulativeCost = cache.get(node); + if (cumulativeCost != null) { + return cumulativeCost; + } + + cumulativeCost = calculateCumulativeCost(node); + verify(cache.put(node, cumulativeCost) == null, "Cost already set"); return cumulativeCost; } - - cumulativeCost = calculateCumulativeCost(node); - verify(cache.put(node, cumulativeCost) == null, "Cost already set"); - return cumulativeCost; + catch (RuntimeException e) { + if (isIgnoreStatsCalculatorFailures(session)) { + log.error(e, "Error occurred when computing cost for query %s", session.getQueryId()); + return PlanNodeCostEstimate.unknown(); + } + throw e; + } } private PlanNodeCostEstimate getGroupCost(GroupReference groupReference) @@ -93,11 +106,11 @@ private PlanNodeCostEstimate getGroupCost(GroupReference groupReference) private PlanNodeCostEstimate calculateCumulativeCost(PlanNode node) { - PlanNodeCostEstimate localCosts = costCalculator.calculateCost(node, statsProvider, lookup, session, types); + PlanNodeCostEstimate localCosts = costCalculator.calculateCost(node, statsProvider, session, types); PlanNodeCostEstimate sourcesCost = node.getSources().stream() .map(this::getCumulativeCost) - .reduce(ZERO_COST, PlanNodeCostEstimate::add); + .reduce(PlanNodeCostEstimate.zero(), PlanNodeCostEstimate::add); PlanNodeCostEstimate cumulativeCost = localCosts.add(sourcesCost); return cumulativeCost; diff --git a/presto-main/src/main/java/com/facebook/presto/cost/CachingStatsProvider.java b/presto-main/src/main/java/com/facebook/presto/cost/CachingStatsProvider.java index 6c6cef4a1eaf1..31e3335fc8682 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/CachingStatsProvider.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/CachingStatsProvider.java @@ -19,11 +19,14 @@ import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.iterative.Memo; import com.facebook.presto.sql.planner.plan.PlanNode; +import io.airlift.log.Logger; import java.util.IdentityHashMap; import java.util.Map; import java.util.Optional; +import static com.facebook.presto.SystemSessionProperties.isEnableStatsCalculator; +import static com.facebook.presto.SystemSessionProperties.isIgnoreStatsCalculatorFailures; import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; @@ -31,6 +34,8 @@ public final class CachingStatsProvider implements StatsProvider { + private static final Logger log = Logger.get(CachingStatsProvider.class); + private final StatsCalculator statsCalculator; private final Optional memo; private final Lookup lookup; @@ -56,20 +61,33 @@ public CachingStatsProvider(StatsCalculator statsCalculator, Optional memo @Override public PlanNodeStatsEstimate getStats(PlanNode node) { + if (!isEnableStatsCalculator(session)) { + return PlanNodeStatsEstimate.unknown(); + } + requireNonNull(node, "node is null"); - if (node instanceof GroupReference) { - return getGroupStats((GroupReference) node); - } + try { + if (node instanceof GroupReference) { + return getGroupStats((GroupReference) node); + } - PlanNodeStatsEstimate stats = cache.get(node); - if (stats != null) { + PlanNodeStatsEstimate stats = cache.get(node); + if (stats != null) { + return stats; + } + + stats = statsCalculator.calculateStats(node, this, lookup, session, types); + verify(cache.put(node, stats) == null, "Stats already set"); return stats; } - - stats = statsCalculator.calculateStats(node, this, lookup, session, types); - verify(cache.put(node, stats) == null, "Stats already set"); - return stats; + catch (RuntimeException e) { + if (isIgnoreStatsCalculatorFailures(session)) { + log.error(e, "Error occurred when computing stats for query %s", session.getQueryId()); + return PlanNodeStatsEstimate.unknown(); + } + throw e; + } } private PlanNodeStatsEstimate getGroupStats(GroupReference groupReference) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java index d3010ac062986..5625074e25695 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/ComparisonStatsCalculator.java @@ -33,179 +33,213 @@ public final class ComparisonStatsCalculator { private ComparisonStatsCalculator() {} - public static Optional comparisonExpressionToLiteralStats( + public static PlanNodeStatsEstimate estimateExpressionToLiteralComparison( PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - OptionalDouble doubleLiteral, + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + OptionalDouble literalValue, ComparisonExpression.Operator operator) { switch (operator) { case EQUAL: - return expressionToLiteralEquality(inputStatistics, symbol, expressionStats, doubleLiteral); + return estimateExpressionEqualToLiteral(inputStatistics, expressionStatistics, expressionSymbol, literalValue); case NOT_EQUAL: - return expressionToLiteralNonEquality(inputStatistics, symbol, expressionStats, doubleLiteral); + return estimateExpressionNotEqualToLiteral(inputStatistics, expressionStatistics, expressionSymbol, literalValue); case LESS_THAN: case LESS_THAN_OR_EQUAL: - return expressionToLiteralLessThan(inputStatistics, symbol, expressionStats, doubleLiteral); + return estimateExpressionLessThanLiteral(inputStatistics, expressionStatistics, expressionSymbol, literalValue); case GREATER_THAN: case GREATER_THAN_OR_EQUAL: - return expressionToLiteralGreaterThan(inputStatistics, symbol, expressionStats, doubleLiteral); + return estimateExpressionGreaterThanLiteral(inputStatistics, expressionStatistics, expressionSymbol, literalValue); case IS_DISTINCT_FROM: + return PlanNodeStatsEstimate.unknown(); default: - return Optional.empty(); + throw new IllegalArgumentException("Unexpected comparison operator: " + operator); } } - private static Optional expressionToLiteralRangeComparison( + private static PlanNodeStatsEstimate estimateExpressionEqualToLiteral( PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - StatisticRange literalRange) + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + OptionalDouble literalValue) { - StatisticRange range = StatisticRange.from(expressionStats); - StatisticRange intersectRange = range.intersect(literalRange); - - double filterFactor = range.overlapPercentWith(intersectRange); - - PlanNodeStatsEstimate estimate = inputStatistics.mapOutputRowCount(rowCount -> filterFactor * (1 - expressionStats.getNullsFraction()) * rowCount); - if (symbol.isPresent()) { - SymbolStatsEstimate symbolNewEstimate = - SymbolStatsEstimate.builder() - .setAverageRowSize(expressionStats.getAverageRowSize()) - .setStatisticsRange(intersectRange) - .setNullsFraction(0.0) - .build(); - estimate = estimate.mapSymbolColumnStatistics(symbol.get(), oldStats -> symbolNewEstimate); - } - return Optional.of(estimate); - } - - private static Optional expressionToLiteralEquality( - PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - OptionalDouble literal) - { - StatisticRange literalRange; - if (literal.isPresent()) { - literalRange = new StatisticRange(literal.getAsDouble(), literal.getAsDouble(), 1); + StatisticRange filterRange; + if (literalValue.isPresent()) { + filterRange = new StatisticRange(literalValue.getAsDouble(), literalValue.getAsDouble(), 1); } else { - literalRange = new StatisticRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, 1); + filterRange = new StatisticRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, 1); } - return expressionToLiteralRangeComparison(inputStatistics, symbol, expressionStats, literalRange); + return estimateFilterRange(inputStatistics, expressionStatistics, expressionSymbol, filterRange); } - private static Optional expressionToLiteralNonEquality( + private static PlanNodeStatsEstimate estimateExpressionNotEqualToLiteral( PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - OptionalDouble literal) + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + OptionalDouble literalValue) { - StatisticRange range = StatisticRange.from(expressionStats); + StatisticRange expressionRange = StatisticRange.from(expressionStatistics); - StatisticRange literalRange; - if (literal.isPresent()) { - literalRange = new StatisticRange(literal.getAsDouble(), literal.getAsDouble(), 1); + StatisticRange filterRange; + if (literalValue.isPresent()) { + filterRange = new StatisticRange(literalValue.getAsDouble(), literalValue.getAsDouble(), 1); } else { - literalRange = new StatisticRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, 1); + filterRange = new StatisticRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, 1); } - StatisticRange intersectRange = range.intersect(literalRange); - double filterFactor = 1 - range.overlapPercentWith(intersectRange); + StatisticRange intersectRange = expressionRange.intersect(filterRange); + double filterFactor = 1 - expressionRange.overlapPercentWith(intersectRange); PlanNodeStatsEstimate.Builder estimate = PlanNodeStatsEstimate.buildFrom(inputStatistics); - estimate.setOutputRowCount(filterFactor * (1 - expressionStats.getNullsFraction()) * inputStatistics.getOutputRowCount()); - if (symbol.isPresent()) { - SymbolStatsEstimate symbolNewEstimate = buildFrom(expressionStats) + estimate.setOutputRowCount(filterFactor * (1 - expressionStatistics.getNullsFraction()) * inputStatistics.getOutputRowCount()); + if (expressionSymbol.isPresent()) { + SymbolStatsEstimate symbolNewEstimate = buildFrom(expressionStatistics) .setNullsFraction(0.0) - .setDistinctValuesCount(max(expressionStats.getDistinctValuesCount() - 1, 0)) + .setDistinctValuesCount(max(expressionStatistics.getDistinctValuesCount() - 1, 0)) .build(); - estimate = estimate.addSymbolStatistics(symbol.get(), symbolNewEstimate); + estimate = estimate.addSymbolStatistics(expressionSymbol.get(), symbolNewEstimate); } - return Optional.of(estimate.build()); + return estimate.build(); } - private static Optional expressionToLiteralLessThan( + private static PlanNodeStatsEstimate estimateExpressionLessThanLiteral( PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - OptionalDouble literal) + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + OptionalDouble literalValue) { - return expressionToLiteralRangeComparison(inputStatistics, symbol, expressionStats, new StatisticRange(NEGATIVE_INFINITY, literal.orElse(POSITIVE_INFINITY), NaN)); + StatisticRange filterRange = new StatisticRange(NEGATIVE_INFINITY, literalValue.orElse(POSITIVE_INFINITY), NaN); + return estimateFilterRange(inputStatistics, expressionStatistics, expressionSymbol, filterRange); } - private static Optional expressionToLiteralGreaterThan( + private static PlanNodeStatsEstimate estimateExpressionGreaterThanLiteral( PlanNodeStatsEstimate inputStatistics, - Optional symbol, - SymbolStatsEstimate expressionStats, - OptionalDouble literal) + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + OptionalDouble literalValue) { - return expressionToLiteralRangeComparison(inputStatistics, symbol, expressionStats, new StatisticRange(literal.orElse(NEGATIVE_INFINITY), POSITIVE_INFINITY, NaN)); + StatisticRange filterRange = new StatisticRange(literalValue.orElse(NEGATIVE_INFINITY), POSITIVE_INFINITY, NaN); + return estimateFilterRange(inputStatistics, expressionStatistics, expressionSymbol, filterRange); } - public static Optional comparisonExpressionToExpressionStats( + private static PlanNodeStatsEstimate estimateFilterRange( PlanNodeStatsEstimate inputStatistics, - Optional left, - SymbolStatsEstimate leftStats, - Optional right, - SymbolStatsEstimate rightStats, + SymbolStatsEstimate expressionStatistics, + Optional expressionSymbol, + StatisticRange filterRange) + { + StatisticRange expressionRange = StatisticRange.from(expressionStatistics); + StatisticRange intersectRange = expressionRange.intersect(filterRange); + + double filterFactor = expressionRange.overlapPercentWith(intersectRange); + + PlanNodeStatsEstimate estimate = inputStatistics.mapOutputRowCount(rowCount -> filterFactor * (1 - expressionStatistics.getNullsFraction()) * rowCount); + if (expressionSymbol.isPresent()) { + SymbolStatsEstimate symbolNewEstimate = + SymbolStatsEstimate.builder() + .setAverageRowSize(expressionStatistics.getAverageRowSize()) + .setStatisticsRange(intersectRange) + .setNullsFraction(0.0) + .build(); + estimate = estimate.mapSymbolColumnStatistics(expressionSymbol.get(), oldStats -> symbolNewEstimate); + } + return estimate; + } + + public static PlanNodeStatsEstimate estimateExpressionToExpressionComparison( + PlanNodeStatsEstimate inputStatistics, + SymbolStatsEstimate leftExpressionStatistics, + Optional leftExpressionSymbol, + SymbolStatsEstimate rightExpressionStatistics, + Optional rightExpressionSymbol, ComparisonExpression.Operator operator) { switch (operator) { case EQUAL: - return expressionToExpressionEquality(inputStatistics, left, leftStats, right, rightStats); + return estimateExpressionEqualToExpression(inputStatistics, leftExpressionStatistics, leftExpressionSymbol, rightExpressionStatistics, rightExpressionSymbol); case NOT_EQUAL: - return expressionToExpressionNonEquality(inputStatistics, left, leftStats, right, rightStats); + return estimateExpressionNotEqualToExpression(inputStatistics, leftExpressionStatistics, leftExpressionSymbol, rightExpressionStatistics, rightExpressionSymbol); case LESS_THAN: case LESS_THAN_OR_EQUAL: case GREATER_THAN: case GREATER_THAN_OR_EQUAL: case IS_DISTINCT_FROM: + return PlanNodeStatsEstimate.unknown(); default: - return Optional.empty(); + throw new IllegalArgumentException("Unexpected comparison operator: " + operator); } } - private static Optional expressionToExpressionEquality( + private static PlanNodeStatsEstimate estimateExpressionEqualToExpression( PlanNodeStatsEstimate inputStatistics, - Optional left, - SymbolStatsEstimate leftStats, - Optional right, - SymbolStatsEstimate rightStats) + SymbolStatsEstimate leftExpressionStatistics, + Optional leftExpressionSymbol, + SymbolStatsEstimate rightExpressionStatistics, + Optional rightExpressionSymbol) { - if (isNaN(leftStats.getDistinctValuesCount()) || isNaN(rightStats.getDistinctValuesCount())) { - return Optional.empty(); + if (isNaN(leftExpressionStatistics.getDistinctValuesCount()) || isNaN(rightExpressionStatistics.getDistinctValuesCount())) { + return PlanNodeStatsEstimate.unknown(); } - StatisticRange leftRange = StatisticRange.from(leftStats); - StatisticRange rightRange = StatisticRange.from(rightStats); + StatisticRange leftExpressionRange = StatisticRange.from(leftExpressionStatistics); + StatisticRange rightExpressionRange = StatisticRange.from(rightExpressionStatistics); - StatisticRange intersect = leftRange.intersect(rightRange); + StatisticRange intersect = leftExpressionRange.intersect(rightExpressionRange); - double nullsFilterFactor = (1 - leftStats.getNullsFraction()) * (1 - rightStats.getNullsFraction()); - double leftFilterFactor = firstNonNaN(leftRange.overlapPercentWith(intersect), 1); - double rightFilterFactor = firstNonNaN(rightRange.overlapPercentWith(intersect), 1); - double leftNdvInRange = leftFilterFactor * leftRange.getDistinctValuesCount(); - double rightNdvInRange = rightFilterFactor * rightRange.getDistinctValuesCount(); - double filterFactor = 1 * leftFilterFactor * rightFilterFactor / max(leftNdvInRange, rightNdvInRange, 1); - double retainedNdv = min(leftNdvInRange, rightNdvInRange); + double nullsFilterFactor = (1 - leftExpressionStatistics.getNullsFraction()) * (1 - rightExpressionStatistics.getNullsFraction()); + double leftNdv = leftExpressionRange.getDistinctValuesCount(); + double rightNdv = rightExpressionRange.getDistinctValuesCount(); + double filterFactor = 1.0 / max(leftNdv, rightNdv, 1); + double retainedNdv = min(leftNdv, rightNdv); PlanNodeStatsEstimate.Builder estimate = PlanNodeStatsEstimate.buildFrom(inputStatistics) .setOutputRowCount(inputStatistics.getOutputRowCount() * nullsFilterFactor * filterFactor); SymbolStatsEstimate equalityStats = SymbolStatsEstimate.builder() - .setAverageRowSize(averageExcludingNaNs(leftStats.getAverageRowSize(), rightStats.getAverageRowSize())) + .setAverageRowSize(averageExcludingNaNs(leftExpressionStatistics.getAverageRowSize(), rightExpressionStatistics.getAverageRowSize())) .setNullsFraction(0) .setStatisticsRange(intersect) .setDistinctValuesCount(retainedNdv) .build(); - left.ifPresent(symbol -> estimate.addSymbolStatistics(symbol, equalityStats)); - right.ifPresent(symbol -> estimate.addSymbolStatistics(symbol, equalityStats)); + leftExpressionSymbol.ifPresent(symbol -> estimate.addSymbolStatistics(symbol, equalityStats)); + rightExpressionSymbol.ifPresent(symbol -> estimate.addSymbolStatistics(symbol, equalityStats)); + + return estimate.build(); + } + + private static PlanNodeStatsEstimate estimateExpressionNotEqualToExpression( + PlanNodeStatsEstimate inputStatistics, + SymbolStatsEstimate leftExpressionStatistics, + Optional leftExpressionSymbol, + SymbolStatsEstimate rightExpressionStatistics, + Optional rightExpressionSymbol) + { + double nullsFilterFactor = (1 - leftExpressionStatistics.getNullsFraction()) * (1 - rightExpressionStatistics.getNullsFraction()); + PlanNodeStatsEstimate inputNullsFiltered = inputStatistics.mapOutputRowCount(size -> size * nullsFilterFactor); + SymbolStatsEstimate leftNullsFiltered = leftExpressionStatistics.mapNullsFraction(nullsFraction -> 0.0); + SymbolStatsEstimate rightNullsFiltered = rightExpressionStatistics.mapNullsFraction(nullsFraction -> 0.0); + PlanNodeStatsEstimate equalityStats = estimateExpressionEqualToExpression( + inputNullsFiltered, + leftNullsFiltered, + leftExpressionSymbol, + rightNullsFiltered, + rightExpressionSymbol); + if (equalityStats.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } - return Optional.of(estimate.build()); + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(inputNullsFiltered); + double equalityFilterFactor = equalityStats.getOutputRowCount() / inputNullsFiltered.getOutputRowCount(); + if (!isFinite(equalityFilterFactor)) { + equalityFilterFactor = 0.0; + } + result.setOutputRowCount(inputNullsFiltered.getOutputRowCount() * (1 - equalityFilterFactor)); + leftExpressionSymbol.ifPresent(symbol -> result.addSymbolStatistics(symbol, leftNullsFiltered)); + rightExpressionSymbol.ifPresent(symbol -> result.addSymbolStatistics(symbol, rightNullsFiltered)); + return result.build(); } private static double averageExcludingNaNs(double first, double second) @@ -218,36 +252,4 @@ private static double averageExcludingNaNs(double first, double second) } return firstNonNaN(first, second); } - - private static Optional expressionToExpressionNonEquality( - PlanNodeStatsEstimate inputStatistics, - Optional left, - SymbolStatsEstimate leftStats, - Optional right, - SymbolStatsEstimate rightStats) - { - double nullsFilterFactor = (1 - leftStats.getNullsFraction()) * (1 - rightStats.getNullsFraction()); - PlanNodeStatsEstimate inputNullsFiltered = inputStatistics.mapOutputRowCount(size -> size * nullsFilterFactor); - SymbolStatsEstimate leftNullsFiltered = leftStats.mapNullsFraction(nullsFraction -> 0.0); - SymbolStatsEstimate rightNullsFiltered = rightStats.mapNullsFraction(nullsFration -> 0.0); - Optional equalityStats = expressionToExpressionEquality(inputNullsFiltered, left, leftNullsFiltered, right, rightNullsFiltered); - if (!equalityStats.isPresent()) { - return Optional.empty(); - } - PlanNodeStatsEstimate resultStats = inputNullsFiltered.mapOutputRowCount(rowCount -> { - double equalityFilterFactor = equalityStats.get().getOutputRowCount() / inputNullsFiltered.getOutputRowCount(); - if (!isFinite(equalityFilterFactor)) { - equalityFilterFactor = 0.0; - } - return rowCount * (1 - equalityFilterFactor); - }); - if (left.isPresent()) { - resultStats = resultStats.mapSymbolColumnStatistics(left.get(), stats -> leftNullsFiltered); - } - if (right.isPresent()) { - resultStats = resultStats.mapSymbolColumnStatistics(right.get(), stats -> rightNullsFiltered); - } - - return Optional.of(resultStats); - } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/ComposableStatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/ComposableStatsCalculator.java index 7895df4baff4c..8fa93978aeb78 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/ComposableStatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/ComposableStatsCalculator.java @@ -72,7 +72,7 @@ public PlanNodeStatsEstimate calculateStats(PlanNode node, StatsProvider sourceS return calculatedStats.get(); } } - return PlanNodeStatsEstimate.UNKNOWN_STATS; + return PlanNodeStatsEstimate.unknown(); } private static Optional calculateStats(Rule rule, PlanNode node, StatsProvider sourceStats, Lookup lookup, Session session, TypeProvider types) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculator.java index dfad20c97374f..2c71bd3a7682c 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculator.java @@ -16,8 +16,6 @@ import com.facebook.presto.Session; import com.facebook.presto.sql.planner.TypeProvider; -import com.facebook.presto.sql.planner.iterative.IterativeOptimizer; -import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.plan.PlanNode; import com.google.inject.BindingAnnotation; @@ -37,12 +35,10 @@ public interface CostCalculator * * @param node The node to compute cost for. * @param stats The stats provider for node's stats and child nodes' stats, to be used if stats are needed to compute cost for the {@code node} - * @param lookup Lookup to be used when resolving source nodes, allowing cost calculation to work within {@link IterativeOptimizer} */ PlanNodeCostEstimate calculateCost( PlanNode node, StatsProvider stats, - Lookup lookup, Session session, TypeProvider types); diff --git a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorUsingExchanges.java b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorUsingExchanges.java index d10c99883ff13..04383be0e7ad9 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorUsingExchanges.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorUsingExchanges.java @@ -15,13 +15,9 @@ package com.facebook.presto.cost; import com.facebook.presto.Session; -import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; -import com.facebook.presto.metadata.InternalNodeManager; -import com.facebook.presto.spi.Node; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.GroupReference; -import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.plan.AggregationNode; import com.facebook.presto.sql.planner.plan.AssignUniqueId; import com.facebook.presto.sql.planner.plan.EnforceSingleRowNode; @@ -33,8 +29,11 @@ import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.PlanVisitor; import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.sql.planner.plan.RowNumberNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableScanNode; +import com.facebook.presto.sql.planner.plan.UnionNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.google.common.collect.ImmutableList; @@ -44,15 +43,15 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.function.IntSupplier; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; -import static com.facebook.presto.cost.PlanNodeCostEstimate.ZERO_COST; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateJoinInputCost; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateLocalRepartitionCost; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateRemoteGatherCost; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateRemoteRepartitionCost; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateRemoteReplicateCost; import static com.facebook.presto.cost.PlanNodeCostEstimate.cpuCost; -import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.LOCAL; -import static java.lang.Math.toIntExact; -import static java.lang.String.format; +import static com.facebook.presto.sql.planner.plan.AggregationNode.Step.FINAL; +import static com.facebook.presto.sql.planner.plan.AggregationNode.Step.SINGLE; import static java.util.Objects.requireNonNull; /** @@ -62,58 +61,39 @@ public class CostCalculatorUsingExchanges implements CostCalculator { - private final IntSupplier numberOfNodes; + private final TaskCountEstimator taskCountEstimator; @Inject - public CostCalculatorUsingExchanges(NodeSchedulerConfig nodeSchedulerConfig, InternalNodeManager nodeManager) + public CostCalculatorUsingExchanges(TaskCountEstimator taskCountEstimator) { - this(currentNumberOfWorkerNodes(nodeSchedulerConfig.isIncludeCoordinator(), nodeManager)); - } - - static IntSupplier currentNumberOfWorkerNodes(boolean includeCoordinator, InternalNodeManager nodeManager) - { - requireNonNull(nodeManager, "nodeManager is null"); - return () -> { - Set activeNodes = nodeManager.getAllNodes().getActiveNodes(); - if (includeCoordinator) { - return activeNodes.size(); - } - return toIntExact(activeNodes.stream() - .filter(node -> !node.isCoordinator()) - .count()); - }; - } - - public CostCalculatorUsingExchanges(IntSupplier numberOfNodes) - { - this.numberOfNodes = requireNonNull(numberOfNodes, "numberOfNodes is null"); + this.taskCountEstimator = requireNonNull(taskCountEstimator, "taskCountEstimator is null"); } @Override - public PlanNodeCostEstimate calculateCost(PlanNode node, StatsProvider stats, Lookup lookup, Session session, TypeProvider types) + public PlanNodeCostEstimate calculateCost(PlanNode node, StatsProvider stats, Session session, TypeProvider types) { - CostEstimator costEstimator = new CostEstimator(numberOfNodes.getAsInt(), stats, types); + CostEstimator costEstimator = new CostEstimator(stats, types, taskCountEstimator); return node.accept(costEstimator, null); } private static class CostEstimator extends PlanVisitor { - private final int numberOfNodes; private final StatsProvider stats; private final TypeProvider types; + private final TaskCountEstimator taskCountEstimator; - CostEstimator(int numberOfNodes, StatsProvider stats, TypeProvider types) + CostEstimator(StatsProvider stats, TypeProvider types, TaskCountEstimator taskCountEstimator) { - this.numberOfNodes = numberOfNodes; this.stats = requireNonNull(stats, "stats is null"); this.types = requireNonNull(types, "types is null"); + this.taskCountEstimator = requireNonNull(taskCountEstimator, "taskCountEstimator is null"); } @Override protected PlanNodeCostEstimate visitPlan(PlanNode node, Void context) { - return UNKNOWN_COST; + return PlanNodeCostEstimate.unknown(); } @Override @@ -128,10 +108,28 @@ public PlanNodeCostEstimate visitAssignUniqueId(AssignUniqueId node, Void contex return cpuCost(getStats(node).getOutputSizeInBytes(ImmutableList.of(node.getIdColumn()), types)); } + @Override + public PlanNodeCostEstimate visitRowNumber(RowNumberNode node, Void context) + { + List symbols = node.getOutputSymbols(); + // when maxRowCountPerPartition is set, the RowNumberOperator + // copies values for all the columns into a page builder + if (!node.getMaxRowCountPerPartition().isPresent()) { + symbols = ImmutableList.builder() + .addAll(node.getPartitionBy()) + .add(node.getRowNumberSymbol()) + .build(); + } + PlanNodeStatsEstimate stats = getStats(node); + double cpuCost = stats.getOutputSizeInBytes(symbols, types); + double memoryCost = node.getPartitionBy().isEmpty() ? 0 : stats.getOutputSizeInBytes(node.getSource().getOutputSymbols(), types); + return new PlanNodeCostEstimate(cpuCost, memoryCost, 0); + } + @Override public PlanNodeCostEstimate visitOutput(OutputNode node, Void context) { - return ZERO_COST; + return PlanNodeCostEstimate.zero(); } @Override @@ -156,6 +154,9 @@ public PlanNodeCostEstimate visitProject(ProjectNode node, Void context) @Override public PlanNodeCostEstimate visitAggregation(AggregationNode node, Void context) { + if (node.getStep() != FINAL && node.getStep() != SINGLE) { + return PlanNodeCostEstimate.unknown(); + } PlanNodeStatsEstimate aggregationStats = getStats(node); PlanNodeStatsEstimate sourceStats = getStats(node.getSource()); double cpuCost = sourceStats.getOutputSizeInBytes(node.getSource().getOutputSymbols(), types); @@ -175,35 +176,57 @@ public PlanNodeCostEstimate visitJoin(JoinNode node, Void context) private PlanNodeCostEstimate calculateJoinCost(PlanNode join, PlanNode probe, PlanNode build, boolean replicated) { - int numberOfNodesMultiplier = replicated ? numberOfNodes : 1; + PlanNodeCostEstimate joinInputCost = calculateJoinInputCost( + probe, + build, + stats, + types, + replicated, + taskCountEstimator.estimateSourceDistributedTaskCount()); + PlanNodeCostEstimate joinOutputCost = calculateJoinOutputCost(join); + return joinInputCost.add(joinOutputCost); + } - PlanNodeStatsEstimate probeStats = getStats(probe); - PlanNodeStatsEstimate buildStats = getStats(build); + private PlanNodeCostEstimate calculateJoinOutputCost(PlanNode join) + { PlanNodeStatsEstimate outputStats = getStats(join); - - double buildSideSize = buildStats.getOutputSizeInBytes(build.getOutputSymbols(), types); - double probeSideSize = probeStats.getOutputSizeInBytes(probe.getOutputSymbols(), types); double joinOutputSize = outputStats.getOutputSizeInBytes(join.getOutputSymbols(), types); - - double cpuCost = probeSideSize + - buildSideSize * numberOfNodesMultiplier + - joinOutputSize; - - if (replicated) { - // add the cost of a local repartitioning of build side copies - // cost of the repartitioning of a single data copy has been already added in calculateExchangeCost - cpuCost += buildSideSize * (numberOfNodesMultiplier - 1); - } - - double memoryCost = buildSideSize * numberOfNodesMultiplier; - - return new PlanNodeCostEstimate(cpuCost, memoryCost, 0); + return cpuCost(joinOutputSize); } @Override public PlanNodeCostEstimate visitExchange(ExchangeNode node, Void context) { - return calculateExchangeCost(numberOfNodes, getStats(node), node.getOutputSymbols(), node.getType(), node.getScope(), types); + double inputSizeInBytes = getStats(node).getOutputSizeInBytes(node.getOutputSymbols(), types); + switch (node.getScope()) { + case LOCAL: + switch (node.getType()) { + case GATHER: + return PlanNodeCostEstimate.zero(); + case REPARTITION: + return calculateLocalRepartitionCost(inputSizeInBytes); + case REPLICATE: + return PlanNodeCostEstimate.zero(); + default: + throw new IllegalArgumentException("Unexpected type: " + node.getType()); + } + case REMOTE: + switch (node.getType()) { + case GATHER: + return calculateRemoteGatherCost(inputSizeInBytes); + case REPARTITION: + return calculateRemoteRepartitionCost(inputSizeInBytes); + case REPLICATE: + // assuming that destination is always source distributed + // it is true as now replicated exchange is used for joins only + // for replicated join probe side is usually source distributed + return calculateRemoteReplicateCost(inputSizeInBytes, taskCountEstimator.estimateSourceDistributedTaskCount()); + default: + throw new IllegalArgumentException("Unexpected type: " + node.getType()); + } + default: + throw new IllegalArgumentException("Unexpected scope: " + node.getScope()); + } } @Override @@ -216,16 +239,26 @@ public PlanNodeCostEstimate visitSemiJoin(SemiJoinNode node, Void context) node.getDistributionType().orElse(SemiJoinNode.DistributionType.PARTITIONED).equals(SemiJoinNode.DistributionType.REPLICATED)); } + @Override + public PlanNodeCostEstimate visitSpatialJoin(SpatialJoinNode node, Void context) + { + return calculateJoinCost( + node, + node.getLeft(), + node.getRight(), + node.getDistributionType() == SpatialJoinNode.DistributionType.REPLICATED); + } + @Override public PlanNodeCostEstimate visitValues(ValuesNode node, Void context) { - return ZERO_COST; + return PlanNodeCostEstimate.zero(); } @Override public PlanNodeCostEstimate visitEnforceSingleRow(EnforceSingleRowNode node, Void context) { - return ZERO_COST; + return PlanNodeCostEstimate.zero(); } @Override @@ -238,44 +271,18 @@ public PlanNodeCostEstimate visitLimit(LimitNode node, Void context) return cpuCost(getStats(node).getOutputSizeInBytes(node.getOutputSymbols(), types)); } - private PlanNodeStatsEstimate getStats(PlanNode node) + @Override + public PlanNodeCostEstimate visitUnion(UnionNode node, Void context) { - return stats.getStats(node); + // Cost will be accounted either in CostCalculatorUsingExchanges#CostEstimator#visitExchanged + // or in CostCalculatorWithEstimatedExchanges#CostEstimator#visitUnion + // This stub is needed just to avoid the cumulative cost being set to unknown + return PlanNodeCostEstimate.zero(); } - } - - public static PlanNodeCostEstimate calculateExchangeCost( - int numberOfNodes, - PlanNodeStatsEstimate exchangeStats, - List symbols, - ExchangeNode.Type type, - ExchangeNode.Scope scope, - TypeProvider types) - { - double exchangeSize = exchangeStats.getOutputSizeInBytes(symbols, types); - - double network; - double cpu = 0; - switch (type) { - case GATHER: - network = exchangeSize; - break; - case REPARTITION: - network = exchangeSize; - cpu = exchangeSize; - break; - case REPLICATE: - network = exchangeSize * numberOfNodes; - break; - default: - throw new UnsupportedOperationException(format("Unsupported type [%s] of the exchange", type)); - } - - if (scope == LOCAL) { - network = 0; + private PlanNodeStatsEstimate getStats(PlanNode node) + { + return stats.getStats(node); } - - return new PlanNodeCostEstimate(cpu, 0, network); } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorWithEstimatedExchanges.java b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorWithEstimatedExchanges.java index 7a96a6e3b1eaf..cb903b01b7c01 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorWithEstimatedExchanges.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/CostCalculatorWithEstimatedExchanges.java @@ -15,85 +15,81 @@ package com.facebook.presto.cost; import com.facebook.presto.Session; -import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; -import com.facebook.presto.metadata.InternalNodeManager; -import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.GroupReference; -import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.plan.AggregationNode; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.PlanVisitor; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; +import com.facebook.presto.sql.planner.plan.UnionNode; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; -import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.function.IntSupplier; - -import static com.facebook.presto.cost.CostCalculatorUsingExchanges.currentNumberOfWorkerNodes; -import static com.facebook.presto.cost.PlanNodeCostEstimate.ZERO_COST; -import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.LOCAL; -import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.REMOTE; -import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.REPARTITION; -import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.REPLICATE; + +import static com.facebook.presto.cost.PlanNodeCostEstimate.cpuCost; +import static com.facebook.presto.cost.PlanNodeCostEstimate.networkCost; import static java.util.Objects.requireNonNull; /** + * HACK! + * * This is a wrapper class around CostCalculator that estimates ExchangeNodes cost. + * + * The ReorderJoins and DetermineJoinDistributionType rules are run before exchanges + * are introduced. This cost calculator adds the implied costs for the exchanges that + * will be added later. It is needed to account for the differences in exchange costs + * for different types of joins. + * + * Ideally the optimizer would produce different variations of a plan with all the + * exchanges already introduced, so that the cost could be computed on the whole plan + * and this class would not be needed. */ @ThreadSafe public class CostCalculatorWithEstimatedExchanges implements CostCalculator { private final CostCalculator costCalculator; - private final IntSupplier numberOfNodes; + private final TaskCountEstimator taskCountEstimator; @Inject - public CostCalculatorWithEstimatedExchanges(CostCalculator costCalculator, NodeSchedulerConfig nodeSchedulerConfig, InternalNodeManager nodeManager) - { - this(costCalculator, currentNumberOfWorkerNodes(nodeSchedulerConfig.isIncludeCoordinator(), nodeManager)); - } - - public CostCalculatorWithEstimatedExchanges(CostCalculator costCalculator, IntSupplier numberOfNodes) + public CostCalculatorWithEstimatedExchanges(CostCalculator costCalculator, TaskCountEstimator taskCountEstimator) { this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); - this.numberOfNodes = requireNonNull(numberOfNodes, "numberOfNodes is null"); + this.taskCountEstimator = requireNonNull(taskCountEstimator, "taskCountEstimator is null"); } @Override - public PlanNodeCostEstimate calculateCost(PlanNode node, StatsProvider stats, Lookup lookup, Session session, TypeProvider types) + public PlanNodeCostEstimate calculateCost(PlanNode node, StatsProvider stats, Session session, TypeProvider types) { - ExchangeCostEstimator exchangeCostEstimator = new ExchangeCostEstimator(numberOfNodes.getAsInt(), stats, lookup, types); + ExchangeCostEstimator exchangeCostEstimator = new ExchangeCostEstimator(stats, types, taskCountEstimator); PlanNodeCostEstimate estimatedExchangeCost = node.accept(exchangeCostEstimator, null); - return costCalculator.calculateCost(node, stats, lookup, session, types).add(estimatedExchangeCost); + return costCalculator.calculateCost(node, stats, session, types).add(estimatedExchangeCost); } private static class ExchangeCostEstimator extends PlanVisitor { - private final int numberOfNodes; private final StatsProvider stats; - private final Lookup lookup; private final TypeProvider types; + private final TaskCountEstimator taskCountEstimator; - ExchangeCostEstimator(int numberOfNodes, StatsProvider stats, Lookup lookup, TypeProvider types) + ExchangeCostEstimator(StatsProvider stats, TypeProvider types, TaskCountEstimator taskCountEstimator) { - this.numberOfNodes = numberOfNodes; this.stats = requireNonNull(stats, "stats is null"); - this.lookup = requireNonNull(lookup, "lookup is null"); this.types = requireNonNull(types, "types is null"); + this.taskCountEstimator = requireNonNull(taskCountEstimator, "taskCountEstimator is null"); } @Override protected PlanNodeCostEstimate visitPlan(PlanNode node, Void context) { - // TODO implement logic for other node types and return UNKNOWN_COST here (or throw) - return ZERO_COST; + // TODO implement logic for other node types and return PlanNodeCostEstimate.unknown() here (or throw) + return PlanNodeCostEstimate.zero(); } @Override @@ -105,23 +101,11 @@ public PlanNodeCostEstimate visitGroupReference(GroupReference node, Void contex @Override public PlanNodeCostEstimate visitAggregation(AggregationNode node, Void context) { - PlanNodeStatsEstimate sourceStats = getStats(node.getSource()); - List sourceSymbols = node.getSource().getOutputSymbols(); - - PlanNodeCostEstimate remoteRepartitionCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - sourceStats, - sourceSymbols, - REPARTITION, - REMOTE, - types); - PlanNodeCostEstimate localRepartitionCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - sourceStats, - sourceSymbols, - REPARTITION, - LOCAL, - types); + PlanNode source = node.getSource(); + double inputSizeInBytes = getStats(source).getOutputSizeInBytes(source.getOutputSymbols(), types); + + PlanNodeCostEstimate remoteRepartitionCost = calculateRemoteRepartitionCost(inputSizeInBytes); + PlanNodeCostEstimate localRepartitionCost = calculateLocalRepartitionCost(inputSizeInBytes); // TODO consider cost of aggregation itself, not only exchanges, based on aggregation's properties return remoteRepartitionCost.add(localRepartitionCost); @@ -130,67 +114,48 @@ public PlanNodeCostEstimate visitAggregation(AggregationNode node, Void context) @Override public PlanNodeCostEstimate visitJoin(JoinNode node, Void context) { - return calculateJoinCost( + return calculateJoinExchangeCost( node.getLeft(), node.getRight(), - Objects.equals(node.getDistributionType(), Optional.of(JoinNode.DistributionType.REPLICATED))); + stats, + types, + Objects.equals(node.getDistributionType(), Optional.of(JoinNode.DistributionType.REPLICATED)), + taskCountEstimator.estimateSourceDistributedTaskCount()); } @Override public PlanNodeCostEstimate visitSemiJoin(SemiJoinNode node, Void context) { - return calculateJoinCost( + return calculateJoinExchangeCost( node.getSource(), node.getFilteringSource(), - Objects.equals(node.getDistributionType(), Optional.of(SemiJoinNode.DistributionType.REPLICATED))); + stats, + types, + Objects.equals(node.getDistributionType(), Optional.of(SemiJoinNode.DistributionType.REPLICATED)), + taskCountEstimator.estimateSourceDistributedTaskCount()); } - private PlanNodeCostEstimate calculateJoinCost(PlanNode probe, PlanNode build, boolean replicated) + @Override + public PlanNodeCostEstimate visitSpatialJoin(SpatialJoinNode node, Void context) { - if (replicated) { - PlanNodeCostEstimate replicateCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - getStats(build), - build.getOutputSymbols(), - REPLICATE, - REMOTE, - types); - // cost of the copies repartitioning is added in CostCalculatorUsingExchanges#calculateJoinCost - PlanNodeCostEstimate localRepartitionCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - getStats(build), - build.getOutputSymbols(), - REPARTITION, - LOCAL, - types); - return replicateCost.add(localRepartitionCost); - } - else { - PlanNodeCostEstimate probeCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - getStats(probe), - probe.getOutputSymbols(), - REPARTITION, - REMOTE, - types); - PlanNodeCostEstimate buildRemoteRepartitionCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - getStats(build), - build.getOutputSymbols(), - REPARTITION, - REMOTE, - types); - PlanNodeCostEstimate buildLocalRepartitionCost = CostCalculatorUsingExchanges.calculateExchangeCost( - numberOfNodes, - getStats(build), - build.getOutputSymbols(), - REPARTITION, - LOCAL, - types); - return probeCost - .add(buildRemoteRepartitionCost) - .add(buildLocalRepartitionCost); - } + return calculateJoinExchangeCost( + node.getLeft(), + node.getRight(), + stats, + types, + node.getDistributionType() == SpatialJoinNode.DistributionType.REPLICATED, + taskCountEstimator.estimateSourceDistributedTaskCount()); + } + + @Override + public PlanNodeCostEstimate visitUnion(UnionNode node, Void context) + { + // this assumes that all union inputs will be gathered over the network + // that is not aways true + // but this estimate is better that returning UNKNOWN, as it sets + // cumulative cost to unknown + double inputSizeInBytes = getStats(node).getOutputSizeInBytes(node.getOutputSymbols(), types); + return calculateRemoteGatherCost(inputSizeInBytes); } private PlanNodeStatsEstimate getStats(PlanNode node) @@ -198,4 +163,90 @@ private PlanNodeStatsEstimate getStats(PlanNode node) return stats.getStats(node); } } + + public static PlanNodeCostEstimate calculateRemoteGatherCost(double inputSizeInBytes) + { + return networkCost(inputSizeInBytes); + } + + public static PlanNodeCostEstimate calculateRemoteRepartitionCost(double inputSizeInBytes) + { + return new PlanNodeCostEstimate(inputSizeInBytes, 0, inputSizeInBytes); + } + + public static PlanNodeCostEstimate calculateLocalRepartitionCost(double inputSizeInBytes) + { + return cpuCost(inputSizeInBytes); + } + + public static PlanNodeCostEstimate calculateRemoteReplicateCost(double inputSizeInBytes, int destinationTaskCount) + { + return networkCost(inputSizeInBytes * destinationTaskCount); + } + + public static PlanNodeCostEstimate calculateJoinExchangeCost( + PlanNode probe, + PlanNode build, + StatsProvider stats, + TypeProvider types, + boolean replicated, + int estimatedSourceDistributedTaskCount) + { + double probeSizeInBytes = stats.getStats(probe).getOutputSizeInBytes(probe.getOutputSymbols(), types); + double buildSizeInBytes = stats.getStats(build).getOutputSizeInBytes(build.getOutputSymbols(), types); + if (replicated) { + // assuming the probe side of a replicated join is always source distributed + PlanNodeCostEstimate replicateCost = calculateRemoteReplicateCost(buildSizeInBytes, estimatedSourceDistributedTaskCount); + // cost of the copies repartitioning is added in CostCalculatorUsingExchanges#calculateJoinCost + PlanNodeCostEstimate localRepartitionCost = calculateLocalRepartitionCost(buildSizeInBytes); + return replicateCost.add(localRepartitionCost); + } + else { + PlanNodeCostEstimate probeCost = calculateRemoteRepartitionCost(probeSizeInBytes); + PlanNodeCostEstimate buildRemoteRepartitionCost = calculateRemoteRepartitionCost(buildSizeInBytes); + PlanNodeCostEstimate buildLocalRepartitionCost = calculateLocalRepartitionCost(buildSizeInBytes); + return probeCost + .add(buildRemoteRepartitionCost) + .add(buildLocalRepartitionCost); + } + } + + public static PlanNodeCostEstimate calculateJoinInputCost( + PlanNode probe, + PlanNode build, + StatsProvider stats, + TypeProvider types, + boolean replicated, + int estimatedSourceDistributedTaskCount) + { + int buildSizeMultiplier = replicated ? estimatedSourceDistributedTaskCount : 1; + + PlanNodeStatsEstimate probeStats = stats.getStats(probe); + PlanNodeStatsEstimate buildStats = stats.getStats(build); + + double buildSideSize = buildStats.getOutputSizeInBytes(build.getOutputSymbols(), types); + double probeSideSize = probeStats.getOutputSizeInBytes(probe.getOutputSymbols(), types); + + double cpuCost = probeSideSize + buildSideSize * buildSizeMultiplier; + + /* + * HACK! + * + * Stats model doesn't multiply the number of rows by the number of tasks for replicated + * exchange to avoid misestimation of the JOIN output. + * + * Thus the cost estimation for the operations that come after a replicated exchange is + * underestimated. And the cost of operations over the replicated copies must be explicitly + * added here. + */ + if (replicated) { + // add the cost of a local repartitioning of build side copies + // cost of the repartitioning of a single data copy has been already added in calculateExchangeCost + cpuCost += buildSideSize * (buildSizeMultiplier - 1); + } + + double memoryCost = buildSideSize * buildSizeMultiplier; + + return new PlanNodeCostEstimate(cpuCost, memoryCost, 0); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java index cfef6c5d658e2..0f21a3434665e 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsCalculator.java @@ -14,11 +14,15 @@ package com.facebook.presto.cost; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.analyzer.ExpressionAnalyzer; import com.facebook.presto.sql.analyzer.Scope; +import com.facebook.presto.sql.planner.ExpressionInterpreter; +import com.facebook.presto.sql.planner.LiteralEncoder; import com.facebook.presto.sql.planner.LiteralInterpreter; +import com.facebook.presto.sql.planner.NoOpSymbolResolver; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.tree.AstVisitor; @@ -33,34 +37,37 @@ import com.facebook.presto.sql.tree.Literal; import com.facebook.presto.sql.tree.LogicalBinaryExpression; import com.facebook.presto.sql.tree.Node; +import com.facebook.presto.sql.tree.NodeRef; import com.facebook.presto.sql.tree.NotExpression; import com.facebook.presto.sql.tree.SymbolReference; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import javax.annotation.Nullable; -import java.util.Objects; +import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; -import static com.facebook.presto.cost.ComparisonStatsCalculator.comparisonExpressionToExpressionStats; -import static com.facebook.presto.cost.ComparisonStatsCalculator.comparisonExpressionToLiteralStats; +import static com.facebook.presto.cost.ComparisonStatsCalculator.estimateExpressionToExpressionComparison; +import static com.facebook.presto.cost.ComparisonStatsCalculator.estimateExpressionToLiteralComparison; import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.addStatsAndSumDistinctValues; -import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.differenceInNonRangeStats; -import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.differenceInStats; +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.capStats; +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.subtractSubsetStats; import static com.facebook.presto.cost.StatsUtil.toStatsRepresentation; -import static com.facebook.presto.cost.SymbolStatsEstimate.UNKNOWN_STATS; -import static com.facebook.presto.cost.SymbolStatsEstimate.ZERO_STATS; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.sql.ExpressionUtils.and; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.EQUAL; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.GREATER_THAN_OR_EQUAL; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.lang.Double.NaN; import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; import static java.lang.Double.min; import static java.lang.String.format; +import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; public class FilterStatsCalculator @@ -70,12 +77,14 @@ public class FilterStatsCalculator private final Metadata metadata; private final ScalarStatsCalculator scalarStatsCalculator; private final StatsNormalizer normalizer; + private final LiteralEncoder literalEncoder; public FilterStatsCalculator(Metadata metadata, ScalarStatsCalculator scalarStatsCalculator, StatsNormalizer normalizer) { this.metadata = requireNonNull(metadata, "metadata is null"); this.scalarStatsCalculator = requireNonNull(scalarStatsCalculator, "scalarStatsCalculator is null"); this.normalizer = requireNonNull(normalizer, "normalizer is null"); + this.literalEncoder = new LiteralEncoder(metadata.getBlockEncodingSerde()); } public PlanNodeStatsEstimate filterStats( @@ -84,17 +93,43 @@ public PlanNodeStatsEstimate filterStats( Session session, TypeProvider types) { - return new FilterExpressionStatsCalculatingVisitor(statsEstimate, session, types).process(predicate) - .orElseGet(() -> normalizer.normalize(filterStatsForUnknownExpression(statsEstimate), types)); + Expression simplifiedExpression = simplifyExpression(session, predicate, types); + return new FilterExpressionStatsCalculatingVisitor(statsEstimate, session, types) + .process(simplifiedExpression); } - private static PlanNodeStatsEstimate filterStatsForUnknownExpression(PlanNodeStatsEstimate inputStatistics) + private Expression simplifyExpression(Session session, Expression predicate, TypeProvider types) { - return inputStatistics.mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT); + // TODO reuse com.facebook.presto.sql.planner.iterative.rule.SimplifyExpressions.rewrite + + Map, Type> expressionTypes = getExpressionTypes(session, predicate, types); + ExpressionInterpreter interpreter = ExpressionInterpreter.expressionOptimizer(predicate, metadata, session, expressionTypes); + Object value = interpreter.optimize(NoOpSymbolResolver.INSTANCE); + + if (value == null) { + // Expression evaluates to SQL null, which in Filter is equivalent to false. This assumes the expression is a top-level expression (eg. not in NOT). + value = false; + } + return literalEncoder.toExpression(value, BOOLEAN); + } + + private Map, Type> getExpressionTypes(Session session, Expression expression, TypeProvider types) + { + ExpressionAnalyzer expressionAnalyzer = ExpressionAnalyzer.createWithoutSubqueries( + metadata.getFunctionRegistry(), + metadata.getTypeManager(), + session, + types, + emptyList(), + node -> new IllegalStateException("Unexpected node: %s" + node), + WarningCollector.NOOP, + false); + expressionAnalyzer.analyze(expression, Scope.create()); + return expressionAnalyzer.getExpressionTypes(); } private class FilterExpressionStatsCalculatingVisitor - extends AstVisitor, Void> + extends AstVisitor { private final PlanNodeStatsEstimate input; private final Session session; @@ -108,132 +143,150 @@ private class FilterExpressionStatsCalculatingVisitor } @Override - public Optional process(Node node, @Nullable Void context) + public PlanNodeStatsEstimate process(Node node, @Nullable Void context) { - return super.process(node, context) - .map(estimate -> normalizer.normalize(estimate, types)); + return normalizer.normalize(super.process(node, context), types); } @Override - protected Optional visitExpression(Expression node, Void context) - { - return Optional.empty(); - } - - private Optional filterForFalseExpression() + protected PlanNodeStatsEstimate visitExpression(Expression node, Void context) { - PlanNodeStatsEstimate.Builder falseStatsBuilder = PlanNodeStatsEstimate.builder(); - input.getSymbolsWithKnownStatistics().forEach(symbol -> falseStatsBuilder.addSymbolStatistics(symbol, ZERO_STATS)); - return Optional.of(falseStatsBuilder - .setOutputRowCount(0.0) - .build()); + return PlanNodeStatsEstimate.unknown(); } @Override - protected Optional visitNotExpression(NotExpression node, Void context) + protected PlanNodeStatsEstimate visitNotExpression(NotExpression node, Void context) { if (node.getValue() instanceof IsNullPredicate) { return process(new IsNotNullPredicate(((IsNullPredicate) node.getValue()).getValue())); } - return process(node.getValue()).map(childStats -> differenceInStats(input, childStats)); + return subtractSubsetStats(input, process(node.getValue())); } @Override - protected Optional visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) + protected PlanNodeStatsEstimate visitLogicalBinaryExpression(LogicalBinaryExpression node, Void context) { switch (node.getOperator()) { case AND: - return visitLogicalBinaryAnd(node.getLeft(), node.getRight()); + return estimateLogicalAnd(node.getLeft(), node.getRight()); case OR: - return visitLogicalBinaryOr(node.getLeft(), node.getRight()); + return estimateLogicalOr(node.getLeft(), node.getRight()); default: - throw new IllegalStateException("Unimplemented logical binary operator expression " + node.getOperator()); + throw new IllegalArgumentException("Unexpected binary operator: " + node.getOperator()); } } - private Optional visitLogicalBinaryAnd(Expression left, Expression right) + private PlanNodeStatsEstimate estimateLogicalAnd(Expression left, Expression right) { - Optional leftStats = process(left); - if (leftStats.isPresent()) { - Optional andStats = new FilterExpressionStatsCalculatingVisitor(leftStats.get(), session, types).process(right); - if (andStats.isPresent()) { - return andStats; + // first try to estimate in the fair way + PlanNodeStatsEstimate leftEstimate = process(left); + if (!leftEstimate.isOutputRowCountUnknown()) { + PlanNodeStatsEstimate logicalAndEstimate = new FilterExpressionStatsCalculatingVisitor(leftEstimate, session, types).process(right); + if (!logicalAndEstimate.isOutputRowCountUnknown()) { + return logicalAndEstimate; } - return leftStats.map(FilterStatsCalculator::filterStatsForUnknownExpression); } - Optional rightStats = process(right); - return rightStats.map(FilterStatsCalculator::filterStatsForUnknownExpression); + // If some of the filters cannot be estimated, take the smallest estimate. + // Apply 0.9 filter factor as "unknown filter" factor. + PlanNodeStatsEstimate rightEstimate = process(right); + PlanNodeStatsEstimate smallestKnownEstimate; + if (leftEstimate.isOutputRowCountUnknown()) { + smallestKnownEstimate = rightEstimate; + } + else if (rightEstimate.isOutputRowCountUnknown()) { + smallestKnownEstimate = leftEstimate; + } + else { + smallestKnownEstimate = leftEstimate.getOutputRowCount() <= rightEstimate.getOutputRowCount() ? leftEstimate : rightEstimate; + } + if (smallestKnownEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + return smallestKnownEstimate.mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT); } - private Optional visitLogicalBinaryOr(Expression left, Expression right) + private PlanNodeStatsEstimate estimateLogicalOr(Expression left, Expression right) { - Optional leftStats = process(left); - if (!leftStats.isPresent()) { - return Optional.empty(); + PlanNodeStatsEstimate leftEstimate = process(left); + if (leftEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); } - Optional rightStats = process(right); - if (!rightStats.isPresent()) { - return Optional.empty(); + PlanNodeStatsEstimate rightEstimate = process(right); + if (rightEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); } - Optional andStats = new FilterExpressionStatsCalculatingVisitor(leftStats.get(), session, types).process(right); - if (!andStats.isPresent()) { - return Optional.empty(); + PlanNodeStatsEstimate andEstimate = new FilterExpressionStatsCalculatingVisitor(leftEstimate, session, types).process(right); + if (andEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); } - PlanNodeStatsEstimate sumStats = addStatsAndSumDistinctValues(leftStats.get(), rightStats.get()); - return Optional.of(differenceInNonRangeStats(sumStats, andStats.get())); + + return capStats( + subtractSubsetStats( + addStatsAndSumDistinctValues(leftEstimate, rightEstimate), + andEstimate), + input); } @Override - protected Optional visitBooleanLiteral(BooleanLiteral node, Void context) + protected PlanNodeStatsEstimate visitBooleanLiteral(BooleanLiteral node, Void context) { - if (node.equals(BooleanLiteral.TRUE_LITERAL)) { - return Optional.of(input); + if (node.getValue()) { + return input; } - return filterForFalseExpression(); + + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder(); + result.setOutputRowCount(0.0); + input.getSymbolsWithKnownStatistics().forEach(symbol -> result.addSymbolStatistics(symbol, SymbolStatsEstimate.zero())); + return result.build(); } @Override - protected Optional visitIsNotNullPredicate(IsNotNullPredicate node, Void context) + protected PlanNodeStatsEstimate visitIsNotNullPredicate(IsNotNullPredicate node, Void context) { if (node.getValue() instanceof SymbolReference) { Symbol symbol = Symbol.from(node.getValue()); - SymbolStatsEstimate symbolStatsEstimate = input.getSymbolStatistics(symbol); - return Optional.of(input.mapOutputRowCount(rowCount -> rowCount * (1 - symbolStatsEstimate.getNullsFraction())) - .mapSymbolColumnStatistics(symbol, statsEstimate -> statsEstimate.mapNullsFraction(x -> 0.0))); + SymbolStatsEstimate symbolStats = input.getSymbolStatistics(symbol); + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(input); + result.setOutputRowCount(input.getOutputRowCount() * (1 - symbolStats.getNullsFraction())); + result.addSymbolStatistics(symbol, symbolStats.mapNullsFraction(x -> 0.0)); + return result.build(); } - return visitExpression(node, context); + return PlanNodeStatsEstimate.unknown(); } @Override - protected Optional visitIsNullPredicate(IsNullPredicate node, Void context) + protected PlanNodeStatsEstimate visitIsNullPredicate(IsNullPredicate node, Void context) { if (node.getValue() instanceof SymbolReference) { Symbol symbol = Symbol.from(node.getValue()); - SymbolStatsEstimate symbolStatsEstimate = input.getSymbolStatistics(symbol); - return Optional.of(input.mapOutputRowCount(rowCount -> rowCount * symbolStatsEstimate.getNullsFraction()) - .mapSymbolColumnStatistics(symbol, statsEstimate -> - SymbolStatsEstimate.builder().setNullsFraction(1.0) - .setLowValue(NaN) - .setHighValue(NaN) - .setDistinctValuesCount(0.0).build())); + SymbolStatsEstimate symbolStats = input.getSymbolStatistics(symbol); + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(input); + result.setOutputRowCount(input.getOutputRowCount() * symbolStats.getNullsFraction()); + result.addSymbolStatistics(symbol, SymbolStatsEstimate.builder() + .setNullsFraction(1.0) + .setLowValue(NaN) + .setHighValue(NaN) + .setDistinctValuesCount(0.0) + .build()); + return result.build(); } - return visitExpression(node, context); + return PlanNodeStatsEstimate.unknown(); } @Override - protected Optional visitBetweenPredicate(BetweenPredicate node, Void context) + protected PlanNodeStatsEstimate visitBetweenPredicate(BetweenPredicate node, Void context) { if (!(node.getValue() instanceof SymbolReference)) { - return visitExpression(node, context); + return PlanNodeStatsEstimate.unknown(); } - if (!(node.getMin() instanceof Literal || isSingleValue(getExpressionStats(node.getMin())))) { - return visitExpression(node, context); + if (!getExpressionStats(node.getMin()).isSingleValue()) { + return PlanNodeStatsEstimate.unknown(); } - if (!(node.getMax() instanceof Literal || isSingleValue(getExpressionStats(node.getMax())))) { - return visitExpression(node, context); + if (!getExpressionStats(node.getMax()).isSingleValue()) { + return PlanNodeStatsEstimate.unknown(); } SymbolStatsEstimate valueStats = input.getSymbolStatistics(Symbol.from(node.getValue())); @@ -253,49 +306,50 @@ protected Optional visitBetweenPredicate(BetweenPredicate } @Override - protected Optional visitInPredicate(InPredicate node, Void context) + protected PlanNodeStatsEstimate visitInPredicate(InPredicate node, Void context) { if (!(node.getValueList() instanceof InListExpression)) { - return Optional.empty(); + return PlanNodeStatsEstimate.unknown(); } InListExpression inList = (InListExpression) node.getValueList(); - ImmutableList> valuesEqualityStats = inList.getValues().stream() + ImmutableList equalityEstimates = inList.getValues().stream() .map(inValue -> process(new ComparisonExpression(EQUAL, node.getValue(), inValue))) - .collect(ImmutableList.toImmutableList()); + .collect(toImmutableList()); - if (!valuesEqualityStats.stream().allMatch(Optional::isPresent)) { - return Optional.empty(); + if (equalityEstimates.stream().anyMatch(PlanNodeStatsEstimate::isOutputRowCountUnknown)) { + return PlanNodeStatsEstimate.unknown(); } - PlanNodeStatsEstimate statsSum = valuesEqualityStats.stream() - .map(Optional::get) - .reduce(filterForFalseExpression().get(), PlanNodeStatsEstimateMath::addStatsAndSumDistinctValues); + PlanNodeStatsEstimate inEstimate = equalityEstimates.stream() + .reduce(PlanNodeStatsEstimateMath::addStatsAndSumDistinctValues) + .orElse(PlanNodeStatsEstimate.unknown()); - if (isNaN(statsSum.getOutputRowCount())) { - return Optional.empty(); + if (inEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); } - Optional inValueSymbol = asSymbol(node.getValue()); - SymbolStatsEstimate inValueStats = getExpressionStats(node.getValue()); - if (Objects.equals(inValueStats, UNKNOWN_STATS)) { - return Optional.empty(); + SymbolStatsEstimate valueStats = getExpressionStats(node.getValue()); + if (valueStats.isUnknown()) { + return PlanNodeStatsEstimate.unknown(); } - double notNullValuesBeforeIn = input.getOutputRowCount() * (1 - inValueStats.getNullsFraction()); + double notNullValuesBeforeIn = input.getOutputRowCount() * (1 - valueStats.getNullsFraction()); - PlanNodeStatsEstimate estimate = input.mapOutputRowCount(rowCount -> min(statsSum.getOutputRowCount(), notNullValuesBeforeIn)); + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(input); + result.setOutputRowCount(min(inEstimate.getOutputRowCount(), notNullValuesBeforeIn)); - if (inValueSymbol.isPresent()) { - SymbolStatsEstimate newSymbolStats = statsSum.getSymbolStatistics(inValueSymbol.get()) - .mapDistinctValuesCount(newDistinctValuesCount -> min(newDistinctValuesCount, inValueStats.getDistinctValuesCount())); - estimate = estimate.mapSymbolColumnStatistics(inValueSymbol.get(), oldSymbolStats -> newSymbolStats); + if (node.getValue() instanceof SymbolReference) { + Symbol valueSymbol = Symbol.from(node.getValue()); + SymbolStatsEstimate newSymbolStats = inEstimate.getSymbolStatistics(valueSymbol) + .mapDistinctValuesCount(newDistinctValuesCount -> min(newDistinctValuesCount, valueStats.getDistinctValuesCount())); + result.addSymbolStatistics(valueSymbol, newSymbolStats); } - return Optional.of(estimate); + return result.build(); } @Override - protected Optional visitComparisonExpression(ComparisonExpression node, Void context) + protected PlanNodeStatsEstimate visitComparisonExpression(ComparisonExpression node, Void context) { ComparisonExpression.Operator operator = node.getOperator(); Expression left = node.getLeft(); @@ -313,75 +367,54 @@ protected Optional visitComparisonExpression(ComparisonEx return process(new ComparisonExpression(operator.flip(), right, left)); } - Optional leftSymbol = asSymbol(left); - SymbolStatsEstimate leftStats = getExpressionStats(left); - if (Objects.equals(leftStats, UNKNOWN_STATS)) { - return visitExpression(node, context); + if (left instanceof SymbolReference && left.equals(right)) { + return process(new IsNotNullPredicate(left)); } + SymbolStatsEstimate leftStats = getExpressionStats(left); + Optional leftSymbol = left instanceof SymbolReference ? Optional.of(Symbol.from(left)) : Optional.empty(); if (right instanceof Literal) { OptionalDouble literal = doubleValueFromLiteral(getType(left), (Literal) right); - return comparisonExpressionToLiteralStats(input, leftSymbol, leftStats, literal, operator); + return estimateExpressionToLiteralComparison(input, leftStats, leftSymbol, literal, operator); } - Optional rightSymbol = asSymbol(right); - SymbolStatsEstimate rightStats = getExpressionStats(right); - if (Objects.equals(rightStats, UNKNOWN_STATS)) { - return visitExpression(node, context); - } - - if (left instanceof SymbolReference && Objects.equals(left, right)) { - return process(new IsNotNullPredicate(left)); - } - - if (isSingleValue(rightStats)) { + if (rightStats.isSingleValue()) { OptionalDouble value = isNaN(rightStats.getLowValue()) ? OptionalDouble.empty() : OptionalDouble.of(rightStats.getLowValue()); - return comparisonExpressionToLiteralStats(input, leftSymbol, leftStats, value, operator); + return estimateExpressionToLiteralComparison(input, leftStats, leftSymbol, value, operator); } - return comparisonExpressionToExpressionStats(input, leftSymbol, leftStats, rightSymbol, rightStats, operator); + Optional rightSymbol = right instanceof SymbolReference ? Optional.of(Symbol.from(right)) : Optional.empty(); + return estimateExpressionToExpressionComparison(input, leftStats, leftSymbol, rightStats, rightSymbol, operator); } - private Optional asSymbol(Expression expression) + private Type getType(Expression expression) { if (expression instanceof SymbolReference) { - return Optional.of(Symbol.from(expression)); + Symbol symbol = Symbol.from(expression); + return requireNonNull(types.get(symbol), () -> format("No type for symbol %s", symbol)); } - return Optional.empty(); - } - - private boolean isSingleValue(SymbolStatsEstimate stats) - { - return stats.getDistinctValuesCount() == 1.0 - && Double.compare(stats.getLowValue(), stats.getHighValue()) == 0 - && !isInfinite(stats.getLowValue()); - } - private Type getType(Expression expression) - { - return asSymbol(expression) - .map(symbol -> requireNonNull(types.get(symbol), () -> format("No type for symbol %s", symbol))) - .orElseGet(() -> { - ExpressionAnalyzer expressionAnalyzer = ExpressionAnalyzer.createWithoutSubqueries( - metadata.getFunctionRegistry(), - metadata.getTypeManager(), - session, - types, - ImmutableList.of(), - // At this stage, there should be no subqueries in the plan. - node -> new IllegalStateException("Unexpected Subquery"), - false); - Type type = expressionAnalyzer.analyze(expression, Scope.create()); - return type; - }); + ExpressionAnalyzer expressionAnalyzer = ExpressionAnalyzer.createWithoutSubqueries( + metadata.getFunctionRegistry(), + metadata.getTypeManager(), + session, + types, + ImmutableList.of(), + // At this stage, there should be no subqueries in the plan. + node -> new VerifyException("Unexpected subquery"), + WarningCollector.NOOP, + false); + return expressionAnalyzer.analyze(expression, Scope.create()); } private SymbolStatsEstimate getExpressionStats(Expression expression) { - return asSymbol(expression) - .map(symbol -> requireNonNull(input.getSymbolStatistics(symbol), () -> format("No statistics for symbol %s", symbol))) - .orElseGet(() -> scalarStatsCalculator.calculate(expression, input, session)); + if (expression instanceof SymbolReference) { + Symbol symbol = Symbol.from(expression); + return requireNonNull(input.getSymbolStatistics(symbol), () -> format("No statistics for symbol %s", symbol)); + } + return scalarStatsCalculator.calculate(expression, input, session, types); } private OptionalDouble doubleValueFromLiteral(Type type, Literal literal) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsRule.java index 6ed81f540d872..d82ee353c1ca2 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/FilterStatsRule.java @@ -14,7 +14,6 @@ package com.facebook.presto.cost; import com.facebook.presto.Session; -import com.facebook.presto.cost.ComposableStatsCalculator.Rule; import com.facebook.presto.matching.Pattern; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.Lookup; @@ -22,17 +21,20 @@ import java.util.Optional; +import static com.facebook.presto.SystemSessionProperties.isDefaultFilterFactorEnabled; +import static com.facebook.presto.cost.FilterStatsCalculator.UNKNOWN_FILTER_COEFFICIENT; import static com.facebook.presto.sql.planner.plan.Patterns.filter; public class FilterStatsRule - implements Rule + extends SimpleStatsRule { private static final Pattern PATTERN = filter(); private final FilterStatsCalculator filterStatsCalculator; - public FilterStatsRule(FilterStatsCalculator filterStatsCalculator) + public FilterStatsRule(StatsNormalizer normalizer, FilterStatsCalculator filterStatsCalculator) { + super(normalizer); this.filterStatsCalculator = filterStatsCalculator; } @@ -43,9 +45,13 @@ public Pattern getPattern() } @Override - public Optional calculate(FilterNode node, StatsProvider statsProvider, Lookup lookup, Session session, TypeProvider types) + public Optional doCalculate(FilterNode node, StatsProvider statsProvider, Lookup lookup, Session session, TypeProvider types) { PlanNodeStatsEstimate sourceStats = statsProvider.getStats(node.getSource()); - return Optional.of(filterStatsCalculator.filterStats(sourceStats, node.getPredicate(), session, types)); + PlanNodeStatsEstimate estimate = filterStatsCalculator.filterStats(sourceStats, node.getPredicate(), session, types); + if (isDefaultFilterFactorEnabled(session) && estimate.isOutputRowCountUnknown()) { + estimate = sourceStats.mapOutputRowCount(sourceRowCount -> sourceStats.getOutputRowCount() * UNKNOWN_FILTER_COEFFICIENT); + } + return Optional.of(estimate); } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/JoinStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/JoinStatsRule.java index 74135ca80ee2a..8690c7166be4b 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/JoinStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/JoinStatsRule.java @@ -15,7 +15,6 @@ import com.facebook.presto.Session; import com.facebook.presto.matching.Pattern; -import com.facebook.presto.sql.ExpressionUtils; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.Lookup; @@ -26,17 +25,18 @@ import com.facebook.presto.util.MoreMath; import com.google.common.annotations.VisibleForTesting; -import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.stream.IntStream; +import java.util.Queue; import static com.facebook.presto.cost.FilterStatsCalculator.UNKNOWN_FILTER_COEFFICIENT; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.cost.SymbolStatsEstimate.buildFrom; +import static com.facebook.presto.sql.ExpressionUtils.extractConjuncts; import static com.facebook.presto.sql.planner.plan.Patterns.join; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.EQUAL; -import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Sets.difference; import static java.lang.Double.NaN; @@ -52,6 +52,7 @@ public class JoinStatsRule private static final double DEFAULT_UNMATCHED_JOIN_COMPLEMENT_NDVS_COEFFICIENT = 0.5; private final FilterStatsCalculator filterStatsCalculator; + private final StatsNormalizer normalizer; private final double unmatchedJoinComplementNdvsCoefficient; public JoinStatsRule(FilterStatsCalculator filterStatsCalculator, StatsNormalizer normalizer) @@ -64,6 +65,7 @@ public JoinStatsRule(FilterStatsCalculator filterStatsCalculator, StatsNormalize { super(normalizer); this.filterStatsCalculator = requireNonNull(filterStatsCalculator, "filterStatsCalculator is null"); + this.normalizer = normalizer; this.unmatchedJoinComplementNdvsCoefficient = unmatchedJoinComplementNdvsCoefficient; } @@ -78,7 +80,7 @@ protected Optional doCalculate(JoinNode node, StatsProvid { PlanNodeStatsEstimate leftStats = sourceStats.getStats(node.getLeft()); PlanNodeStatsEstimate rightStats = sourceStats.getStats(node.getRight()); - PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftStats, rightStats); + PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftStats, rightStats, types); switch (node.getType()) { case INNER: @@ -102,7 +104,7 @@ private PlanNodeStatsEstimate computeFullJoinStats( Session session, TypeProvider types) { - PlanNodeStatsEstimate rightJoinComplementStats = calculateJoinComplementStats(node.getFilter(), flippedCriteria(node), rightStats, leftStats); + PlanNodeStatsEstimate rightJoinComplementStats = calculateJoinComplementStats(node.getFilter(), flippedCriteria(node), rightStats, leftStats, types); return addJoinComplementStats( rightStats, computeLeftJoinStats(node, leftStats, rightStats, crossJoinStats, session, types), @@ -118,7 +120,7 @@ private PlanNodeStatsEstimate computeLeftJoinStats( TypeProvider types) { PlanNodeStatsEstimate innerJoinStats = computeInnerJoinStats(node, crossJoinStats, session, types); - PlanNodeStatsEstimate leftJoinComplementStats = calculateJoinComplementStats(node.getFilter(), node.getCriteria(), leftStats, rightStats); + PlanNodeStatsEstimate leftJoinComplementStats = calculateJoinComplementStats(node.getFilter(), node.getCriteria(), leftStats, rightStats, types); return addJoinComplementStats( leftStats, innerJoinStats, @@ -134,7 +136,7 @@ private PlanNodeStatsEstimate computeRightJoinStats( TypeProvider types) { PlanNodeStatsEstimate innerJoinStats = computeInnerJoinStats(node, crossJoinStats, session, types); - PlanNodeStatsEstimate rightJoinComplementStats = calculateJoinComplementStats(node.getFilter(), flippedCriteria(node), rightStats, leftStats); + PlanNodeStatsEstimate rightJoinComplementStats = calculateJoinComplementStats(node.getFilter(), flippedCriteria(node), rightStats, leftStats, types); return addJoinComplementStats( rightStats, innerJoinStats, @@ -143,49 +145,77 @@ private PlanNodeStatsEstimate computeRightJoinStats( private PlanNodeStatsEstimate computeInnerJoinStats(JoinNode node, PlanNodeStatsEstimate crossJoinStats, Session session, TypeProvider types) { - List equiJoinClauses = node.getCriteria(); + List equiJoinCriteria = node.getCriteria(); + if (equiJoinCriteria.isEmpty()) { + if (!node.getFilter().isPresent()) { + return crossJoinStats; + } + // TODO: this might explode stats + return filterStatsCalculator.filterStats(crossJoinStats, node.getFilter().get(), session, types); + } + + PlanNodeStatsEstimate equiJoinEstimate = filterByEquiJoinClauses(crossJoinStats, node.getCriteria(), session, types); + + if (equiJoinEstimate.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + + if (!node.getFilter().isPresent()) { + return equiJoinEstimate; + } + + PlanNodeStatsEstimate filteredEquiJoinEstimate = filterStatsCalculator.filterStats(equiJoinEstimate, node.getFilter().get(), session, types); + + if (filteredEquiJoinEstimate.isOutputRowCountUnknown()) { + return normalizer.normalize(equiJoinEstimate.mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT), types); + } + + return filteredEquiJoinEstimate; + } + + private PlanNodeStatsEstimate filterByEquiJoinClauses( + PlanNodeStatsEstimate stats, + Collection clauses, + Session session, + TypeProvider types) + { + checkArgument(!clauses.isEmpty(), "clauses is empty"); + PlanNodeStatsEstimate result = PlanNodeStatsEstimate.unknown(); // Join equality clauses are usually correlated. Therefore we shouldn't treat each join equality // clause separately because stats estimates would be way off. Instead we choose so called // "driving clause" which mostly reduces join output rows cardinality and apply UNKNOWN_FILTER_COEFFICIENT // for other (auxiliary) clauses. - PlanNodeStatsEstimate equiJoinClausesFilteredStats = IntStream.range(0, equiJoinClauses.size()) - .mapToObj(drivingClauseId -> { - EquiJoinClause drivingClause = equiJoinClauses.get(drivingClauseId); - List remainingClauses = copyWithout(equiJoinClauses, drivingClauseId); - return filterByEquiJoinClauses(crossJoinStats, drivingClause, remainingClauses, session, types); - }) - .min(comparingDouble(PlanNodeStatsEstimate::getOutputRowCount)) - .orElse(crossJoinStats); - - return node.getFilter() - .map(filter -> filterStatsCalculator.filterStats(equiJoinClausesFilteredStats, filter, session, types)) - .orElse(equiJoinClausesFilteredStats); - } + Queue remainingClauses = new LinkedList<>(clauses); + EquiJoinClause drivingClause = remainingClauses.poll(); + for (int i = 0; i < clauses.size(); i++) { + PlanNodeStatsEstimate estimate = filterByEquiJoinClauses(stats, drivingClause, remainingClauses, session, types); + if (result.isOutputRowCountUnknown() || (!estimate.isOutputRowCountUnknown() && estimate.getOutputRowCount() < result.getOutputRowCount())) { + result = estimate; + } + remainingClauses.add(drivingClause); + drivingClause = remainingClauses.poll(); + } - private static List copyWithout(List list, int filteredOutIndex) - { - List copy = new ArrayList<>(list); - copy.remove(filteredOutIndex); - return copy; + return result; } private PlanNodeStatsEstimate filterByEquiJoinClauses( PlanNodeStatsEstimate stats, EquiJoinClause drivingClause, - List auxiliaryClauses, + Collection remainingClauses, Session session, TypeProvider types) { ComparisonExpression drivingPredicate = new ComparisonExpression(EQUAL, drivingClause.getLeft().toSymbolReference(), drivingClause.getRight().toSymbolReference()); PlanNodeStatsEstimate filteredStats = filterStatsCalculator.filterStats(stats, drivingPredicate, session, types); - for (EquiJoinClause clause : auxiliaryClauses) { - filteredStats = filterByAuxiliaryClause(filteredStats, clause); + for (EquiJoinClause clause : remainingClauses) { + filteredStats = filterByAuxiliaryClause(filteredStats, clause, types); } return filteredStats; } - private PlanNodeStatsEstimate filterByAuxiliaryClause(PlanNodeStatsEstimate stats, EquiJoinClause clause) + private PlanNodeStatsEstimate filterByAuxiliaryClause(PlanNodeStatsEstimate stats, EquiJoinClause clause, TypeProvider types) { // we just clear null fraction and adjust ranges here // selectivity is mostly handled by driving clause. We just scale heuristically by UNKNOWN_FILTER_COEFFICIENT here. @@ -214,10 +244,11 @@ private PlanNodeStatsEstimate filterByAuxiliaryClause(PlanNodeStatsEstimate stat .setDistinctValuesCount(retainedNdv) .build(); - return stats - .mapSymbolColumnStatistics(clause.getLeft(), oldLeftStats -> newLeftStats) - .mapSymbolColumnStatistics(clause.getRight(), oldRightStats -> newRightStats) - .mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT); + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.buildFrom(stats) + .setOutputRowCount(stats.getOutputRowCount() * UNKNOWN_FILTER_COEFFICIENT) + .addSymbolStatistics(clause.getLeft(), newLeftStats) + .addSymbolStatistics(clause.getRight(), newRightStats); + return normalizer.normalize(result.build(), types); } private static double firstNonNaN(double... values) @@ -238,7 +269,8 @@ PlanNodeStatsEstimate calculateJoinComplementStats( Optional filter, List criteria, PlanNodeStatsEstimate leftStats, - PlanNodeStatsEstimate rightStats) + PlanNodeStatsEstimate rightStats, + TypeProvider types) { if (rightStats.getOutputRowCount() == 0) { // no left side rows are matched @@ -248,27 +280,24 @@ PlanNodeStatsEstimate calculateJoinComplementStats( if (criteria.isEmpty()) { // TODO: account for non-equi conditions if (filter.isPresent()) { - return UNKNOWN_STATS; + return PlanNodeStatsEstimate.unknown(); } - return leftStats.mapOutputRowCount(rowCount -> 0.0); + return normalizer.normalize(leftStats.mapOutputRowCount(rowCount -> 0.0), types); } // TODO: add support for non-equality conditions (e.g: <=, !=, >) - int numberOfFilterClauses = filter.map( - exression -> ExpressionUtils.extractConjuncts(exression).size()) - .orElse(0); + int numberOfFilterClauses = filter.map(expression -> extractConjuncts(expression).size()).orElse(0); // Heuristics: select the most selective criteria for join complement clause. // Principals behind this heuristics is the same as in computeInnerJoinStats: // select "driving join clause" that reduces matched rows the most. - return IntStream.range(0, criteria.size()) - .mapToObj(drivingClauseId -> { - EquiJoinClause drivingClause = criteria.get(drivingClauseId); - return calculateJoinComplementStats(leftStats, rightStats, drivingClause, criteria.size() - 1 + numberOfFilterClauses); - }) + return criteria.stream() + .map(drivingClause -> calculateJoinComplementStats(leftStats, rightStats, drivingClause, criteria.size() - 1 + numberOfFilterClauses)) + .filter(estimate -> !estimate.isOutputRowCountUnknown()) .max(comparingDouble(PlanNodeStatsEstimate::getOutputRowCount)) - .get(); + .map(estimate -> normalizer.normalize(estimate, types)) + .orElse(PlanNodeStatsEstimate.unknown()); } private PlanNodeStatsEstimate calculateJoinComplementStats( @@ -313,7 +342,7 @@ else if (leftNDV <= matchingRightNDV) { } else { // either leftNDV or rightNDV is NaN - return UNKNOWN_STATS; + return PlanNodeStatsEstimate.unknown(); } // limit the number of complement rows (to left row count) and account for remaining clauses @@ -325,45 +354,47 @@ else if (leftNDV <= matchingRightNDV) { @VisibleForTesting PlanNodeStatsEstimate addJoinComplementStats( PlanNodeStatsEstimate sourceStats, - PlanNodeStatsEstimate baseJoinStats, + PlanNodeStatsEstimate innerJoinStats, PlanNodeStatsEstimate joinComplementStats) { - checkState(baseJoinStats.getSymbolsWithKnownStatistics().containsAll(joinComplementStats.getSymbolsWithKnownStatistics())); + double innerJoinRowCount = innerJoinStats.getOutputRowCount(); + double joinComplementRowCount = joinComplementStats.getOutputRowCount(); + if (joinComplementRowCount == 0) { + return innerJoinStats; + } - double joinOutputRowCount = baseJoinStats.getOutputRowCount(); - double joinComplementOutputRowCount = joinComplementStats.getOutputRowCount(); - double totalRowCount = joinOutputRowCount + joinComplementOutputRowCount; + double outputRowCount = innerJoinRowCount + joinComplementRowCount; - PlanNodeStatsEstimate.Builder outputStats = PlanNodeStatsEstimate.buildFrom(baseJoinStats); - outputStats.setOutputRowCount(joinOutputRowCount + joinComplementOutputRowCount); + PlanNodeStatsEstimate.Builder outputStats = PlanNodeStatsEstimate.buildFrom(innerJoinStats); + outputStats.setOutputRowCount(outputRowCount); for (Symbol symbol : joinComplementStats.getSymbolsWithKnownStatistics()) { - SymbolStatsEstimate sourceSymbolStats = sourceStats.getSymbolStatistics(symbol); - SymbolStatsEstimate innerSymbolStats = baseJoinStats.getSymbolStatistics(symbol); + SymbolStatsEstimate leftSymbolStats = sourceStats.getSymbolStatistics(symbol); + SymbolStatsEstimate innerJoinSymbolStats = innerJoinStats.getSymbolStatistics(symbol); SymbolStatsEstimate joinComplementSymbolStats = joinComplementStats.getSymbolStatistics(symbol); // weighted average - double newNullsFraction = (innerSymbolStats.getNullsFraction() * joinOutputRowCount + joinComplementSymbolStats.getNullsFraction() * joinComplementOutputRowCount) / totalRowCount; - outputStats.addSymbolStatistics(symbol, SymbolStatsEstimate.buildFrom(innerSymbolStats) + double newNullsFraction = (innerJoinSymbolStats.getNullsFraction() * innerJoinRowCount + joinComplementSymbolStats.getNullsFraction() * joinComplementRowCount) / outputRowCount; + outputStats.addSymbolStatistics(symbol, SymbolStatsEstimate.buildFrom(innerJoinSymbolStats) // in outer join low value, high value and NDVs of outer side columns are preserved - .setLowValue(sourceSymbolStats.getLowValue()) - .setHighValue(sourceSymbolStats.getHighValue()) - .setDistinctValuesCount(sourceSymbolStats.getDistinctValuesCount()) + .setLowValue(leftSymbolStats.getLowValue()) + .setHighValue(leftSymbolStats.getHighValue()) + .setDistinctValuesCount(leftSymbolStats.getDistinctValuesCount()) .setNullsFraction(newNullsFraction) .build()); } // add nulls to columns that don't exist in right stats - for (Symbol symbol : difference(baseJoinStats.getSymbolsWithKnownStatistics(), joinComplementStats.getSymbolsWithKnownStatistics())) { - SymbolStatsEstimate innerSymbolStats = baseJoinStats.getSymbolStatistics(symbol); - double newNullsFraction = (innerSymbolStats.getNullsFraction() * joinOutputRowCount + joinComplementOutputRowCount) / totalRowCount; - outputStats.addSymbolStatistics(symbol, innerSymbolStats.mapNullsFraction(nullsFraction -> newNullsFraction)); + for (Symbol symbol : difference(innerJoinStats.getSymbolsWithKnownStatistics(), joinComplementStats.getSymbolsWithKnownStatistics())) { + SymbolStatsEstimate innerJoinSymbolStats = innerJoinStats.getSymbolStatistics(symbol); + double newNullsFraction = (innerJoinSymbolStats.getNullsFraction() * innerJoinRowCount + joinComplementRowCount) / outputRowCount; + outputStats.addSymbolStatistics(symbol, innerJoinSymbolStats.mapNullsFraction(nullsFraction -> newNullsFraction)); } return outputStats.build(); } - private PlanNodeStatsEstimate crossJoinStats(JoinNode node, PlanNodeStatsEstimate leftStats, PlanNodeStatsEstimate rightStats) + private PlanNodeStatsEstimate crossJoinStats(JoinNode node, PlanNodeStatsEstimate leftStats, PlanNodeStatsEstimate rightStats, TypeProvider types) { PlanNodeStatsEstimate.Builder builder = PlanNodeStatsEstimate.builder() .setOutputRowCount(leftStats.getOutputRowCount() * rightStats.getOutputRowCount()); @@ -371,7 +402,7 @@ private PlanNodeStatsEstimate crossJoinStats(JoinNode node, PlanNodeStatsEstimat node.getLeft().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, leftStats.getSymbolStatistics(symbol))); node.getRight().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, rightStats.getSymbolStatistics(symbol))); - return builder.build(); + return normalizer.normalize(builder.build(), types); } private List flippedCriteria(JoinNode node) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeCostEstimate.java b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeCostEstimate.java index f2e540e718d94..cbb97ef5d8750 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeCostEstimate.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeCostEstimate.java @@ -13,6 +13,9 @@ */ package com.facebook.presto.cost; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Objects; import static com.google.common.base.MoreObjects.toStringHelper; @@ -23,9 +26,28 @@ public final class PlanNodeCostEstimate { - public static final PlanNodeCostEstimate INFINITE_COST = new PlanNodeCostEstimate(POSITIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY); - public static final PlanNodeCostEstimate UNKNOWN_COST = new PlanNodeCostEstimate(NaN, NaN, NaN); - public static final PlanNodeCostEstimate ZERO_COST = new PlanNodeCostEstimate(0, 0, 0); + private static final PlanNodeCostEstimate INFINITE = new PlanNodeCostEstimate(POSITIVE_INFINITY, POSITIVE_INFINITY, POSITIVE_INFINITY); + private static final PlanNodeCostEstimate UNKNOWN = new PlanNodeCostEstimate(NaN, NaN, NaN); + private static final PlanNodeCostEstimate ZERO = new PlanNodeCostEstimate(0, 0, 0); + + private final double cpuCost; + private final double memoryCost; + private final double networkCost; + + public static PlanNodeCostEstimate infinite() + { + return INFINITE; + } + + public static PlanNodeCostEstimate unknown() + { + return UNKNOWN; + } + + public static PlanNodeCostEstimate zero() + { + return ZERO; + } public static PlanNodeCostEstimate cpuCost(double cpuCost) { @@ -42,11 +64,11 @@ public static PlanNodeCostEstimate networkCost(double networkCost) return new PlanNodeCostEstimate(0, 0, networkCost); } - private final double cpuCost; - private final double memoryCost; - private final double networkCost; - - public PlanNodeCostEstimate(double cpuCost, double memoryCost, double networkCost) + @JsonCreator + public PlanNodeCostEstimate( + @JsonProperty("cpuCost") double cpuCost, + @JsonProperty("memoryCost") double memoryCost, + @JsonProperty("networkCost") double networkCost) { checkArgument(isNaN(cpuCost) || cpuCost >= 0, "cpuCost cannot be negative"); checkArgument(isNaN(memoryCost) || memoryCost >= 0, "memoryCost cannot be negative"); @@ -59,6 +81,7 @@ public PlanNodeCostEstimate(double cpuCost, double memoryCost, double networkCos /** * Returns CPU component of the cost. Unknown value is represented by {@link Double#NaN} */ + @JsonProperty public double getCpuCost() { return cpuCost; @@ -67,6 +90,7 @@ public double getCpuCost() /** * Returns memory component of the cost. Unknown value is represented by {@link Double#NaN} */ + @JsonProperty public double getMemoryCost() { return memoryCost; @@ -75,6 +99,7 @@ public double getMemoryCost() /** * Returns network component of the cost. Unknown value is represented by {@link Double#NaN} */ + @JsonProperty public double getNetworkCost() { return networkCost; diff --git a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimate.java b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimate.java index cddbfd8e25e2d..449c34a304418 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimate.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimate.java @@ -18,6 +18,9 @@ import com.facebook.presto.spi.type.VariableWidthType; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableMap; import org.pcollections.HashTreePMap; import org.pcollections.PMap; @@ -37,11 +40,24 @@ public class PlanNodeStatsEstimate { private static final double DEFAULT_DATA_SIZE_PER_COLUMN = 50; - public static final PlanNodeStatsEstimate UNKNOWN_STATS = builder().build(); + private static final PlanNodeStatsEstimate UNKNOWN = new PlanNodeStatsEstimate(NaN, ImmutableMap.of()); private final double outputRowCount; private final PMap symbolStatistics; + public static PlanNodeStatsEstimate unknown() + { + return UNKNOWN; + } + + @JsonCreator + public PlanNodeStatsEstimate( + @JsonProperty("outputRowCount") double outputRowCount, + @JsonProperty("symbolStatistics") Map symbolStatistics) + { + this(outputRowCount, HashTreePMap.from(requireNonNull(symbolStatistics, "symbolStatistics is null"))); + } + private PlanNodeStatsEstimate(double outputRowCount, PMap symbolStatistics) { checkArgument(isNaN(outputRowCount) || outputRowCount >= 0, "outputRowCount cannot be negative"); @@ -53,6 +69,7 @@ private PlanNodeStatsEstimate(double outputRowCount, PMap getSymbolStatistics() + { + return symbolStatistics; } public Set getSymbolsWithKnownStatistics() @@ -123,6 +146,11 @@ public Set getSymbolsWithKnownStatistics() return symbolStatistics.keySet(); } + public boolean isOutputRowCountUnknown() + { + return isNaN(outputRowCount); + } + @Override public String toString() { diff --git a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimateMath.java b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimateMath.java index c9265c2238165..e2804fcb40882 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimateMath.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/PlanNodeStatsEstimateMath.java @@ -13,76 +13,135 @@ */ package com.facebook.presto.cost; -import java.util.stream.Stream; +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Double.NaN; +import static java.lang.Double.isNaN; +import static java.lang.Double.max; +import static java.lang.Double.min; +import static java.util.stream.Stream.concat; public class PlanNodeStatsEstimateMath { private PlanNodeStatsEstimateMath() {} - @FunctionalInterface - private interface RangeSubtractionStrategy + /** + * Subtracts subset stats from supersets stats. + * It is assumed that each NDV from subset has a matching NDV in superset. + */ + public static PlanNodeStatsEstimate subtractSubsetStats(PlanNodeStatsEstimate superset, PlanNodeStatsEstimate subset) { - StatisticRange range(StatisticRange leftRange, StatisticRange rightRange); + if (superset.isOutputRowCountUnknown() || subset.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + + double supersetRowCount = superset.getOutputRowCount(); + double subsetRowCount = subset.getOutputRowCount(); + double outputRowCount = max(supersetRowCount - subsetRowCount, 0); + + // everything will be filtered out after applying negation + if (outputRowCount == 0) { + return createZeroStats(superset); + } + + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder(); + result.setOutputRowCount(outputRowCount); + + superset.getSymbolsWithKnownStatistics().forEach(symbol -> { + SymbolStatsEstimate supersetSymbolStats = superset.getSymbolStatistics(symbol); + SymbolStatsEstimate subsetSymbolStats = subset.getSymbolStatistics(symbol); + + SymbolStatsEstimate.Builder newSymbolStats = SymbolStatsEstimate.builder(); + + // for simplicity keep the average row size the same as in the input + // in most cases the average row size doesn't change after applying filters + newSymbolStats.setAverageRowSize(supersetSymbolStats.getAverageRowSize()); + + // nullsCount + double supersetNullsCount = supersetSymbolStats.getNullsFraction() * supersetRowCount; + double subsetNullsCount = subsetSymbolStats.getNullsFraction() * subsetRowCount; + double newNullsCount = max(supersetNullsCount - subsetNullsCount, 0); + newSymbolStats.setNullsFraction(min(newNullsCount, outputRowCount) / outputRowCount); + + // distinctValuesCount + double supersetDistinctValues = supersetSymbolStats.getDistinctValuesCount(); + double subsetDistinctValues = subsetSymbolStats.getDistinctValuesCount(); + double newDistinctValuesCount; + if (isNaN(supersetDistinctValues) || isNaN(subsetDistinctValues)) { + newDistinctValuesCount = NaN; + } + else if (supersetDistinctValues == 0) { + newDistinctValuesCount = 0; + } + else if (subsetDistinctValues == 0) { + newDistinctValuesCount = supersetDistinctValues; + } + else { + double supersetNonNullsCount = supersetRowCount - supersetNullsCount; + double subsetNonNullsCount = subsetRowCount - subsetNullsCount; + double supersetValuesPerDistinctValue = supersetNonNullsCount / supersetDistinctValues; + double subsetValuesPerDistinctValue = subsetNonNullsCount / subsetDistinctValues; + if (supersetValuesPerDistinctValue <= subsetValuesPerDistinctValue) { + newDistinctValuesCount = max(supersetDistinctValues - subsetDistinctValues, 0); + } + else { + newDistinctValuesCount = supersetDistinctValues; + } + } + newSymbolStats.setDistinctValuesCount(newDistinctValuesCount); + + // range + newSymbolStats.setLowValue(supersetSymbolStats.getLowValue()); + newSymbolStats.setHighValue(supersetSymbolStats.getHighValue()); + + result.addSymbolStatistics(symbol, newSymbolStats.build()); + }); + + return result.build(); } - public static PlanNodeStatsEstimate differenceInStats(PlanNodeStatsEstimate left, PlanNodeStatsEstimate right) + public static PlanNodeStatsEstimate capStats(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap) { - return differenceInStatsWithRangeStrategy(left, right, StatisticRange::subtract); - } + if (stats.isOutputRowCountUnknown() || cap.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } - public static PlanNodeStatsEstimate differenceInNonRangeStats(PlanNodeStatsEstimate left, PlanNodeStatsEstimate right) - { - return differenceInStatsWithRangeStrategy(left, right, ((leftRange, rightRange) -> leftRange)); - } + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder(); + double cappedRowCount = min(stats.getOutputRowCount(), cap.getOutputRowCount()); + result.setOutputRowCount(cappedRowCount); - private static PlanNodeStatsEstimate differenceInStatsWithRangeStrategy(PlanNodeStatsEstimate left, PlanNodeStatsEstimate right, RangeSubtractionStrategy strategy) - { - PlanNodeStatsEstimate.Builder statsBuilder = PlanNodeStatsEstimate.builder(); - double newRowCount = left.getOutputRowCount() - right.getOutputRowCount(); + stats.getSymbolsWithKnownStatistics().forEach(symbol -> { + SymbolStatsEstimate symbolStats = stats.getSymbolStatistics(symbol); + SymbolStatsEstimate capSymbolStats = cap.getSymbolStatistics(symbol); - Stream.concat(left.getSymbolsWithKnownStatistics().stream(), right.getSymbolsWithKnownStatistics().stream()) - .forEach(symbol -> { - statsBuilder.addSymbolStatistics( - symbol, - subtractColumnStats( - left.getSymbolStatistics(symbol), - left.getOutputRowCount(), - right.getSymbolStatistics(symbol), - right.getOutputRowCount(), - newRowCount, - strategy)); - }); + SymbolStatsEstimate.Builder newSymbolStats = SymbolStatsEstimate.builder(); - return statsBuilder.setOutputRowCount(newRowCount).build(); - } + // for simplicity keep the average row size the same as in the input + // in most cases the average row size doesn't change after applying filters + newSymbolStats.setAverageRowSize(symbolStats.getAverageRowSize()); - private static SymbolStatsEstimate subtractColumnStats( - SymbolStatsEstimate leftStats, - double leftRowCount, - SymbolStatsEstimate rightStats, - double rightRowCount, - double newRowCount, - RangeSubtractionStrategy strategy) - { - StatisticRange leftRange = StatisticRange.from(leftStats); - StatisticRange rightRange = StatisticRange.from(rightStats); + newSymbolStats.setDistinctValuesCount(min(symbolStats.getDistinctValuesCount(), capSymbolStats.getDistinctValuesCount())); + newSymbolStats.setLowValue(max(symbolStats.getLowValue(), capSymbolStats.getLowValue())); + newSymbolStats.setHighValue(min(symbolStats.getHighValue(), capSymbolStats.getHighValue())); - double nullsCountLeft = leftStats.getNullsFraction() * leftRowCount; - double nullsCountRight = rightStats.getNullsFraction() * rightRowCount; - double totalSizeLeft = (leftRowCount - nullsCountLeft) * leftStats.getAverageRowSize(); - double totalSizeRight = (rightRowCount - nullsCountRight) * rightStats.getAverageRowSize(); - double newNullsFraction = (nullsCountLeft - nullsCountRight) / newRowCount; - double newNonNullsRowCount = newRowCount * (1.0 - newNullsFraction); - StatisticRange range = strategy.range(leftRange, rightRange); + double numberOfNulls = stats.getOutputRowCount() * symbolStats.getNullsFraction(); + double capNumberOfNulls = cap.getOutputRowCount() * capSymbolStats.getNullsFraction(); + double cappedNumberOfNulls = min(numberOfNulls, capNumberOfNulls); + double cappedNullsFraction = cappedRowCount == 0 ? 1 : cappedNumberOfNulls / cappedRowCount; + newSymbolStats.setNullsFraction(cappedNullsFraction); - return SymbolStatsEstimate.builder() - .setDistinctValuesCount(leftStats.getDistinctValuesCount() - rightStats.getDistinctValuesCount()) - .setHighValue(range.getHigh()) - .setLowValue(range.getLow()) - .setAverageRowSize((totalSizeLeft - totalSizeRight) / newNonNullsRowCount) - .setNullsFraction(newNullsFraction) - .build(); + result.addSymbolStatistics(symbol, newSymbolStats.build()); + }); + + return result.build(); + } + + private static PlanNodeStatsEstimate createZeroStats(PlanNodeStatsEstimate stats) + { + PlanNodeStatsEstimate.Builder result = PlanNodeStatsEstimate.builder(); + result.setOutputRowCount(0); + stats.getSymbolsWithKnownStatistics().forEach(symbol -> result.addSymbolStatistics(symbol, SymbolStatsEstimate.zero())); + return result.build(); } @FunctionalInterface @@ -108,20 +167,27 @@ public static PlanNodeStatsEstimate addStatsAndCollapseDistinctValues(PlanNodeSt private static PlanNodeStatsEstimate addStats(PlanNodeStatsEstimate left, PlanNodeStatsEstimate right, RangeAdditionStrategy strategy) { + if (left.isOutputRowCountUnknown() || right.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + PlanNodeStatsEstimate.Builder statsBuilder = PlanNodeStatsEstimate.builder(); double newRowCount = left.getOutputRowCount() + right.getOutputRowCount(); - Stream.concat(left.getSymbolsWithKnownStatistics().stream(), right.getSymbolsWithKnownStatistics().stream()) + concat(left.getSymbolsWithKnownStatistics().stream(), right.getSymbolsWithKnownStatistics().stream()) .distinct() .forEach(symbol -> { - statsBuilder.addSymbolStatistics(symbol, - addColumnStats( - left.getSymbolStatistics(symbol), - left.getOutputRowCount(), - right.getSymbolStatistics(symbol), - right.getOutputRowCount(), - newRowCount, - strategy)); + SymbolStatsEstimate symbolStats = SymbolStatsEstimate.zero(); + if (newRowCount > 0) { + symbolStats = addColumnStats( + left.getSymbolStatistics(symbol), + left.getOutputRowCount(), + right.getSymbolStatistics(symbol), + right.getOutputRowCount(), + newRowCount, + strategy); + } + statsBuilder.addSymbolStatistics(symbol, symbolStats); }); return statsBuilder.setOutputRowCount(newRowCount).build(); @@ -129,6 +195,8 @@ private static PlanNodeStatsEstimate addStats(PlanNodeStatsEstimate left, PlanNo private static SymbolStatsEstimate addColumnStats(SymbolStatsEstimate leftStats, double leftRows, SymbolStatsEstimate rightStats, double rightRows, double newRowCount, RangeAdditionStrategy strategy) { + checkArgument(newRowCount > 0, "newRowCount must be greater than zero"); + StatisticRange leftRange = StatisticRange.from(leftStats); StatisticRange rightRange = StatisticRange.from(rightStats); @@ -140,9 +208,12 @@ private static SymbolStatsEstimate addColumnStats(SymbolStatsEstimate leftStats, double newNullsFraction = (nullsCountLeft + nullsCountRight) / newRowCount; double newNonNullsRowCount = newRowCount * (1.0 - newNullsFraction); + // FIXME, weights to average. left and right should be equal in most cases anyway + double newAverageRowSize = newNonNullsRowCount == 0 ? 0 : ((totalSizeLeft + totalSizeRight) / newNonNullsRowCount); + return SymbolStatsEstimate.builder() .setStatisticsRange(sum) - .setAverageRowSize((totalSizeLeft + totalSizeRight) / newNonNullsRowCount) // FIXME, weights to average. left and right should be equal in most cases anyway + .setAverageRowSize(newAverageRowSize) .setNullsFraction(newNullsFraction) .build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/ProjectStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/ProjectStatsRule.java index 9525fc4470919..cb6ad1e739ba4 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/ProjectStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/ProjectStatsRule.java @@ -54,7 +54,7 @@ protected Optional doCalculate(ProjectNode node, StatsPro .setOutputRowCount(sourceStats.getOutputRowCount()); for (Map.Entry entry : node.getAssignments().entrySet()) { - calculatedStats.addSymbolStatistics(entry.getKey(), scalarStatsCalculator.calculate(entry.getValue(), sourceStats, session)); + calculatedStats.addSymbolStatistics(entry.getKey(), scalarStatsCalculator.calculate(entry.getValue(), sourceStats, session, types)); } return Optional.of(calculatedStats.build()); } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/RowNumberStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/RowNumberStatsRule.java new file mode 100644 index 0000000000000..0c4e10d135dfb --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/cost/RowNumberStatsRule.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cost; + +import com.facebook.presto.Session; +import com.facebook.presto.matching.Pattern; +import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.TypeProvider; +import com.facebook.presto.sql.planner.iterative.Lookup; +import com.facebook.presto.sql.planner.plan.Patterns; +import com.facebook.presto.sql.planner.plan.RowNumberNode; + +import java.util.Optional; + +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static java.lang.Double.isNaN; +import static java.lang.Math.min; + +public class RowNumberStatsRule + extends SimpleStatsRule +{ + private static final Pattern PATTERN = Patterns.rowNumber(); + + public RowNumberStatsRule(StatsNormalizer normalizer) + { + super(normalizer); + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } + + @Override + public Optional doCalculate(RowNumberNode node, StatsProvider statsProvider, Lookup lookup, Session session, TypeProvider types) + { + PlanNodeStatsEstimate sourceStats = statsProvider.getStats(node.getSource()); + if (sourceStats.isOutputRowCountUnknown()) { + return Optional.empty(); + } + double sourceRowsCount = sourceStats.getOutputRowCount(); + + double partitionCount = 1; + for (Symbol groupBySymbol : node.getPartitionBy()) { + SymbolStatsEstimate symbolStatistics = sourceStats.getSymbolStatistics(groupBySymbol); + int nullRow = (symbolStatistics.getNullsFraction() == 0.0) ? 0 : 1; + // assuming no correlation between grouping keys + partitionCount *= symbolStatistics.getDistinctValuesCount() + nullRow; + } + partitionCount = min(sourceRowsCount, partitionCount); + + if (isNaN(partitionCount)) { + return Optional.empty(); + } + + // assuming no skew + double rowsPerPartition = sourceRowsCount / partitionCount; + if (node.getMaxRowCountPerPartition().isPresent()) { + rowsPerPartition = min(rowsPerPartition, node.getMaxRowCountPerPartition().get()); + } + + double outputRowsCount = sourceRowsCount; + if (node.getMaxRowCountPerPartition().isPresent()) { + outputRowsCount = partitionCount * rowsPerPartition; + } + + return Optional.of(PlanNodeStatsEstimate.buildFrom(sourceStats) + .setOutputRowCount(outputRowsCount) + .addSymbolStatistics(node.getRowNumberSymbol(), SymbolStatsEstimate.builder() + // Note: if we assume no skew, we could also estimate highValue + // (as rowsPerPartition), but underestimation of highValue may have + // more severe consequences than underestimation of distinctValuesCount + .setLowValue(1) + .setDistinctValuesCount(rowsPerPartition) + .setNullsFraction(0.0) + .setAverageRowSize(BIGINT.getFixedSize()) + .build()) + .build()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/cost/ScalarStatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/ScalarStatsCalculator.java index 6ad355038551c..7315beca12f42 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/ScalarStatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/ScalarStatsCalculator.java @@ -14,6 +14,7 @@ package com.facebook.presto.cost; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.DecimalType; import com.facebook.presto.spi.type.StandardTypes; @@ -21,30 +22,38 @@ import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.sql.analyzer.ExpressionAnalyzer; import com.facebook.presto.sql.analyzer.Scope; +import com.facebook.presto.sql.planner.ExpressionInterpreter; +import com.facebook.presto.sql.planner.NoOpSymbolResolver; import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.tree.ArithmeticBinaryExpression; import com.facebook.presto.sql.tree.ArithmeticUnaryExpression; import com.facebook.presto.sql.tree.AstVisitor; import com.facebook.presto.sql.tree.Cast; import com.facebook.presto.sql.tree.CoalesceExpression; import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.sql.tree.FunctionCall; import com.facebook.presto.sql.tree.Literal; import com.facebook.presto.sql.tree.Node; +import com.facebook.presto.sql.tree.NodeRef; import com.facebook.presto.sql.tree.NullLiteral; import com.facebook.presto.sql.tree.SymbolReference; import com.google.common.collect.ImmutableList; import javax.inject.Inject; +import java.util.Map; import java.util.OptionalDouble; import static com.facebook.presto.cost.StatsUtil.toStatsRepresentation; import static com.facebook.presto.sql.planner.LiteralInterpreter.evaluate; import static com.facebook.presto.util.MoreMath.max; import static com.facebook.presto.util.MoreMath.min; +import static java.lang.Double.NaN; import static java.lang.Double.isFinite; import static java.lang.Double.isNaN; import static java.lang.Math.abs; +import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; public class ScalarStatsCalculator @@ -57,9 +66,9 @@ public ScalarStatsCalculator(Metadata metadata) this.metadata = requireNonNull(metadata, "metadata can not be null"); } - public SymbolStatsEstimate calculate(Expression scalarExpression, PlanNodeStatsEstimate inputStatistics, Session session) + public SymbolStatsEstimate calculate(Expression scalarExpression, PlanNodeStatsEstimate inputStatistics, Session session, TypeProvider types) { - return new Visitor(inputStatistics, session).process(scalarExpression); + return new Visitor(inputStatistics, session, types).process(scalarExpression); } private class Visitor @@ -67,17 +76,19 @@ private class Visitor { private final PlanNodeStatsEstimate input; private final Session session; + private final TypeProvider types; - Visitor(PlanNodeStatsEstimate input, Session session) + Visitor(PlanNodeStatsEstimate input, Session session, TypeProvider types) { this.input = input; this.session = session; + this.types = types; } @Override protected SymbolStatsEstimate visitNode(Node node, Void context) { - return SymbolStatsEstimate.UNKNOWN_STATS; + return SymbolStatsEstimate.unknown(); } @Override @@ -89,17 +100,14 @@ protected SymbolStatsEstimate visitSymbolReference(SymbolReference node, Void co @Override protected SymbolStatsEstimate visitNullLiteral(NullLiteral node, Void context) { - return SymbolStatsEstimate.builder() - .setDistinctValuesCount(0) - .setNullsFraction(1) - .build(); + return nullStatsEstimate(); } @Override protected SymbolStatsEstimate visitLiteral(Literal node, Void context) { Object value = evaluate(metadata, session.toConnectorSession(), node); - Type type = ExpressionAnalyzer.createConstantAnalyzer(metadata, session, ImmutableList.of()).analyze(node, Scope.create()); + Type type = ExpressionAnalyzer.createConstantAnalyzer(metadata, session, ImmutableList.of(), WarningCollector.NOOP).analyze(node, Scope.create()); OptionalDouble doubleValue = toStatsRepresentation(metadata, session, type, value); SymbolStatsEstimate.Builder estimate = SymbolStatsEstimate.builder() .setNullsFraction(0) @@ -112,6 +120,44 @@ protected SymbolStatsEstimate visitLiteral(Literal node, Void context) return estimate.build(); } + @Override + protected SymbolStatsEstimate visitFunctionCall(FunctionCall node, Void context) + { + Map, Type> expressionTypes = getExpressionTypes(session, node, types); + ExpressionInterpreter interpreter = ExpressionInterpreter.expressionOptimizer(node, metadata, session, expressionTypes); + Object value = interpreter.optimize(NoOpSymbolResolver.INSTANCE); + + if (value == null || value instanceof NullLiteral) { + return nullStatsEstimate(); + } + + if (value instanceof Expression && !(value instanceof Literal)) { + // value is not a constant + return SymbolStatsEstimate.unknown(); + } + + // value is a constant + return SymbolStatsEstimate.builder() + .setNullsFraction(0) + .setDistinctValuesCount(1) + .build(); + } + + private Map, Type> getExpressionTypes(Session session, Expression expression, TypeProvider types) + { + ExpressionAnalyzer expressionAnalyzer = ExpressionAnalyzer.createWithoutSubqueries( + metadata.getFunctionRegistry(), + metadata.getTypeManager(), + session, + types, + emptyList(), + node -> new IllegalStateException("Unexpected node: %s" + node), + WarningCollector.NOOP, + false); + expressionAnalyzer.analyze(expression, Scope.create()); + return expressionAnalyzer.getExpressionTypes(); + } + @Override protected SymbolStatsEstimate visitCast(Cast node, Void context) { @@ -196,7 +242,11 @@ protected SymbolStatsEstimate visitArithmeticBinary(ArithmeticBinaryExpression n double leftHigh = left.getHighValue(); double rightLow = right.getLowValue(); double rightHigh = right.getHighValue(); - if (node.getOperator() == ArithmeticBinaryExpression.Operator.DIVIDE && rightLow < 0 && rightHigh > 0) { + if (isNaN(leftLow) || isNaN(leftHigh) || isNaN(rightLow) || isNaN(rightHigh)) { + result.setLowValue(NaN) + .setHighValue(NaN); + } + else if (node.getOperator() == ArithmeticBinaryExpression.Operator.DIVIDE && rightLow < 0 && rightHigh > 0) { result.setLowValue(Double.NEGATIVE_INFINITY) .setHighValue(Double.POSITIVE_INFINITY); } @@ -287,4 +337,12 @@ else if (left.getNullsFraction() == 1.0) { } } } + + private static SymbolStatsEstimate nullStatsEstimate() + { + return SymbolStatsEstimate.builder() + .setDistinctValuesCount(0) + .setNullsFraction(1) + .build(); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/SemiJoinStatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/SemiJoinStatsCalculator.java index 9dd557bb41721..2d3ce5123e836 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/SemiJoinStatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/SemiJoinStatsCalculator.java @@ -56,17 +56,24 @@ private static PlanNodeStatsEstimate compute( SymbolStatsEstimate filteringSourceJoinSymbolStats = filteringSourceStats.getSymbolStatistics(filteringSourceJoinSymbol); double retainedNdv = retainedNdvProvider.apply(sourceJoinSymbolStats, filteringSourceJoinSymbolStats); - double filterFactor = sourceJoinSymbolStats.getValuesFraction() * retainedNdv / sourceJoinSymbolStats.getDistinctValuesCount(); - SymbolStatsEstimate newSourceJoinSymbolStats = SymbolStatsEstimate.buildFrom(sourceJoinSymbolStats) .setNullsFraction(0) .setDistinctValuesCount(retainedNdv) .build(); - PlanNodeStatsEstimate outputStats = PlanNodeStatsEstimate.buildFrom(sourceStats) + double sourceDistinctValuesCount = sourceJoinSymbolStats.getDistinctValuesCount(); + if (sourceDistinctValuesCount == 0) { + return PlanNodeStatsEstimate.buildFrom(sourceStats) + .addSymbolStatistics(sourceJoinSymbol, newSourceJoinSymbolStats) + .setOutputRowCount(0) + .build(); + } + + double filterFactor = sourceJoinSymbolStats.getValuesFraction() * retainedNdv / sourceDistinctValuesCount; + double outputRowCount = sourceStats.getOutputRowCount() * filterFactor; + return PlanNodeStatsEstimate.buildFrom(sourceStats) .addSymbolStatistics(sourceJoinSymbol, newSourceJoinSymbolStats) - .setOutputRowCount(sourceStats.getOutputRowCount() * filterFactor) + .setOutputRowCount(outputRowCount) .build(); - return outputStats; } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/SimpleFilterProjectSemiJoinStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/SimpleFilterProjectSemiJoinStatsRule.java index 612fca93729d2..d15dce313edb5 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/SimpleFilterProjectSemiJoinStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/SimpleFilterProjectSemiJoinStatsRule.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Optional; +import static com.facebook.presto.cost.FilterStatsCalculator.UNKNOWN_FILTER_COEFFICIENT; import static com.facebook.presto.cost.SemiJoinStatsCalculator.computeAntiJoin; import static com.facebook.presto.cost.SemiJoinStatsCalculator.computeSemiJoin; import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; @@ -108,8 +109,16 @@ private Optional calculate(FilterNode filterNode, SemiJoi semiJoinStats = computeSemiJoin(sourceStats, filteringSourceStats, sourceJoinSymbol, filteringSourceJoinSymbol); } + if (semiJoinStats.isOutputRowCountUnknown()) { + return Optional.of(PlanNodeStatsEstimate.unknown()); + } + // apply remaining predicate - return Optional.of(filterStatsCalculator.filterStats(semiJoinStats, semiJoinOutputFilter.get().getRemainingPredicate(), session, types)); + PlanNodeStatsEstimate filteredStats = filterStatsCalculator.filterStats(semiJoinStats, semiJoinOutputFilter.get().getRemainingPredicate(), session, types); + if (filteredStats.isOutputRowCountUnknown()) { + return Optional.of(semiJoinStats.mapOutputRowCount(rowCount -> rowCount * UNKNOWN_FILTER_COEFFICIENT)); + } + return Optional.of(filteredStats); } private static Optional extractSemiJoinOutputFilter(Expression predicate, Symbol semiJoinOutput) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/SpatialJoinStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/SpatialJoinStatsRule.java new file mode 100644 index 0000000000000..30ebe02cc464d --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/cost/SpatialJoinStatsRule.java @@ -0,0 +1,73 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cost; + +import com.facebook.presto.Session; +import com.facebook.presto.matching.Pattern; +import com.facebook.presto.sql.planner.TypeProvider; +import com.facebook.presto.sql.planner.iterative.Lookup; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; + +import java.util.Optional; + +import static com.facebook.presto.sql.planner.plan.Patterns.spatialJoin; +import static java.util.Objects.requireNonNull; + +public class SpatialJoinStatsRule + extends SimpleStatsRule +{ + private static final Pattern PATTERN = spatialJoin(); + + private final FilterStatsCalculator statsCalculator; + + public SpatialJoinStatsRule(FilterStatsCalculator statsCalculator, StatsNormalizer normalizer) + { + super(normalizer); + this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); + } + + @Override + protected Optional doCalculate(SpatialJoinNode node, StatsProvider sourceStats, Lookup lookup, Session session, TypeProvider types) + { + PlanNodeStatsEstimate leftStats = sourceStats.getStats(node.getLeft()); + PlanNodeStatsEstimate rightStats = sourceStats.getStats(node.getRight()); + PlanNodeStatsEstimate crossJoinStats = crossJoinStats(node, leftStats, rightStats); + + switch (node.getType()) { + case INNER: + return Optional.of(statsCalculator.filterStats(crossJoinStats, node.getFilter(), session, types)); + case LEFT: + return Optional.of(PlanNodeStatsEstimate.unknown()); + default: + throw new IllegalArgumentException("Unknown spatial join type: " + node.getType()); + } + } + + private PlanNodeStatsEstimate crossJoinStats(SpatialJoinNode node, PlanNodeStatsEstimate leftStats, PlanNodeStatsEstimate rightStats) + { + PlanNodeStatsEstimate.Builder builder = PlanNodeStatsEstimate.builder() + .setOutputRowCount(leftStats.getOutputRowCount() * rightStats.getOutputRowCount()); + + node.getLeft().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, leftStats.getSymbolStatistics(symbol))); + node.getRight().getOutputSymbols().forEach(symbol -> builder.addSymbolStatistics(symbol, rightStats.getSymbolStatistics(symbol))); + + return builder.build(); + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/cost/StatisticRange.java b/presto-main/src/main/java/com/facebook/presto/cost/StatisticRange.java index 0c7d5f6f3b754..fd12f36df79ad 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/StatisticRange.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/StatisticRange.java @@ -96,12 +96,12 @@ public double overlapPercentWith(StatisticRange other) { requireNonNull(other, "other is null"); - if (this.equals(other)) { - return 1.0; + if (this.isEmpty() || other.isEmpty() || this.distinctValues == 0 || other.distinctValues == 0) { + return 0.0; // zero is better than NaN as it will behave properly for calculating row count } - if (this.isEmpty() || other.isEmpty()) { - return 0.0; // zero is better than NaN as it will behave properly for calculating row count + if (this.equals(other)) { + return 1.0; } double lengthOfIntersect = min(this.high, other.high) - max(this.low, other.low); @@ -157,7 +157,7 @@ public StatisticRange addAndSumDistinctValues(StatisticRange other) public StatisticRange addAndMaxDistinctValues(StatisticRange other) { - double newDistinctValues = minExcludeNaN(distinctValues, other.distinctValues); + double newDistinctValues = max(distinctValues, other.distinctValues); return new StatisticRange(minExcludeNaN(low, other.low), maxExcludeNaN(high, other.high), newDistinctValues); } @@ -173,25 +173,6 @@ public StatisticRange addAndCollapseDistinctValues(StatisticRange other) return new StatisticRange(minExcludeNaN(low, other.low), maxExcludeNaN(high, other.high), newDistinctValues); } - public StatisticRange subtract(StatisticRange rightRange) - { - StatisticRange intersect = intersect(rightRange); - double newLow = low; - double newHigh = high; - if (intersect.low == low) { - newLow = intersect.high; - } - if (intersect.high == high) { - newHigh = intersect.low; - } - if (newLow > newHigh) { - newLow = NaN; - newHigh = NaN; - } - double newDistinctValues = max(distinctValues, rightRange.distinctValues) - intersect.distinctValues; - return new StatisticRange(newLow, newHigh, newDistinctValues); - } - private static double minExcludeNaN(double v1, double v2) { if (isNaN(v1)) { diff --git a/presto-main/src/main/java/com/facebook/presto/cost/StatsAndCosts.java b/presto-main/src/main/java/com/facebook/presto/cost/StatsAndCosts.java new file mode 100644 index 0000000000000..690a98b8312b4 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/cost/StatsAndCosts.java @@ -0,0 +1,90 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.cost; + +import com.facebook.presto.sql.planner.plan.PlanNode; +import com.facebook.presto.sql.planner.plan.PlanNodeId; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableMap; +import com.google.common.graph.Traverser; + +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +public class StatsAndCosts +{ + private static final StatsAndCosts EMPTY = new StatsAndCosts(ImmutableMap.of(), ImmutableMap.of()); + + private final Map stats; + private final Map costs; + + public static StatsAndCosts empty() + { + return EMPTY; + } + + @JsonCreator + public StatsAndCosts( + @JsonProperty("stats") Map stats, + @JsonProperty("costs") Map costs) + { + this.stats = ImmutableMap.copyOf(requireNonNull(stats, "stats is null")); + this.costs = ImmutableMap.copyOf(requireNonNull(costs, "costs is null")); + } + + @JsonProperty + public Map getStats() + { + return stats; + } + + @JsonProperty + public Map getCosts() + { + return costs; + } + + public StatsAndCosts getForSubplan(PlanNode root) + { + Iterable planIterator = Traverser.forTree(PlanNode::getSources) + .depthFirstPreOrder(root); + ImmutableMap.Builder filteredStats = ImmutableMap.builder(); + ImmutableMap.Builder filteredCosts = ImmutableMap.builder(); + for (PlanNode node : planIterator) { + if (stats.containsKey(node.getId())) { + filteredStats.put(node.getId(), stats.get(node.getId())); + } + if (costs.containsKey(node.getId())) { + filteredCosts.put(node.getId(), costs.get(node.getId())); + } + } + return new StatsAndCosts(filteredStats.build(), filteredCosts.build()); + } + + public static StatsAndCosts create(PlanNode root, StatsProvider statsProvider, CostProvider costProvider) + { + Iterable planIterator = Traverser.forTree(PlanNode::getSources) + .depthFirstPreOrder(root); + ImmutableMap.Builder stats = ImmutableMap.builder(); + ImmutableMap.Builder costs = ImmutableMap.builder(); + for (PlanNode node : planIterator) { + stats.put(node.getId(), statsProvider.getStats(node)); + costs.put(node.getId(), costProvider.getCumulativeCost(node)); + } + return new StatsAndCosts(stats.build(), costs.build()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculator.java b/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculator.java index 6486475f30446..0f58ab0bbf943 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculator.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculator.java @@ -23,7 +23,8 @@ public interface StatsCalculator { /** * Calculate stats for the {@code node}. - * @param node The node to compute stats for. + * + * @param node The node to compute stats for. * @param sourceStats The stats provider for any child nodes' stats, if needed to compute stats for the {@code node} * @param lookup Lookup to be used when resolving source nodes, allowing stats calculation to work within {@link IterativeOptimizer} * @param types diff --git a/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculatorModule.java b/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculatorModule.java index 70a093dc53359..911d30ca09b23 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculatorModule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/StatsCalculatorModule.java @@ -41,17 +41,19 @@ public static StatsCalculator createNewStatsCalculator(Metadata metadata) rules.add(new OutputStatsRule()); rules.add(new TableScanStatsRule(metadata, normalizer)); rules.add(new SimpleFilterProjectSemiJoinStatsRule(normalizer, filterStatsCalculator)); // this must be before FilterStatsRule - rules.add(new FilterStatsRule(filterStatsCalculator)); + rules.add(new FilterStatsRule(normalizer, filterStatsCalculator)); rules.add(new ValuesStatsRule(metadata)); rules.add(new LimitStatsRule(normalizer)); rules.add(new EnforceSingleRowStatsRule(normalizer)); rules.add(new ProjectStatsRule(scalarStatsCalculator, normalizer)); rules.add(new ExchangeStatsRule(normalizer)); rules.add(new JoinStatsRule(filterStatsCalculator, normalizer)); + rules.add(new SpatialJoinStatsRule(filterStatsCalculator, normalizer)); rules.add(new AggregationStatsRule(normalizer)); rules.add(new UnionStatsRule(normalizer)); rules.add(new AssignUniqueIdStatsRule()); rules.add(new SemiJoinStatsRule()); + rules.add(new RowNumberStatsRule(normalizer)); return new ComposableStatsCalculator(rules.build()); } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/StatsNormalizer.java b/presto-main/src/main/java/com/facebook/presto/cost/StatsNormalizer.java index c606ee2de1df7..42d57d6f56caf 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/StatsNormalizer.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/StatsNormalizer.java @@ -30,8 +30,7 @@ import java.util.Optional; import java.util.function.Predicate; -import static com.facebook.presto.cost.SymbolStatsEstimate.UNKNOWN_STATS; -import static com.facebook.presto.cost.SymbolStatsEstimate.ZERO_STATS; +import static com.google.common.base.Preconditions.checkArgument; import static java.lang.Double.NaN; import static java.lang.Double.isNaN; import static java.lang.Math.floor; @@ -55,6 +54,10 @@ public PlanNodeStatsEstimate normalize(PlanNodeStatsEstimate stats, Collection> outputSymbols, TypeProvider types) { + if (stats.isOutputRowCountUnknown()) { + return PlanNodeStatsEstimate.unknown(); + } + PlanNodeStatsEstimate.Builder normalized = PlanNodeStatsEstimate.buildFrom(stats); Predicate symbolFilter = outputSymbols @@ -69,8 +72,8 @@ private PlanNodeStatsEstimate normalize(PlanNodeStatsEstimate stats, Optional 0, "outputRowCount must be greater than zero: %s", outputRowCount); double distinctValuesCount = symbolStats.getDistinctValuesCount(); double nullsFraction = symbolStats.getNullsFraction(); if (!isNaN(distinctValuesCount)) { - Type type = requireNonNull(types.get(symbol), () -> "No stats for symbol " + symbol); + Type type = requireNonNull(types.get(symbol), () -> "type is missing for symbol " + symbol); double maxDistinctValuesByLowHigh = maxDistinctValuesByLowHigh(symbolStats, type); if (distinctValuesCount > maxDistinctValuesByLowHigh) { distinctValuesCount = maxDistinctValuesByLowHigh; @@ -116,7 +120,7 @@ private SymbolStatsEstimate normalizeSymbolStats(Symbol symbol, SymbolStatsEstim } if (distinctValuesCount == 0.0) { - return ZERO_STATS; + return SymbolStatsEstimate.zero(); } return SymbolStatsEstimate.buildFrom(symbolStats) diff --git a/presto-main/src/main/java/com/facebook/presto/cost/SymbolStatsEstimate.java b/presto-main/src/main/java/com/facebook/presto/cost/SymbolStatsEstimate.java index d9d829ab8b084..900dbdf11ef52 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/SymbolStatsEstimate.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/SymbolStatsEstimate.java @@ -13,26 +13,25 @@ */ package com.facebook.presto.cost; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.Objects; import java.util.function.Function; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; +import static java.lang.Double.POSITIVE_INFINITY; +import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; import static java.lang.String.format; public class SymbolStatsEstimate { - public static final SymbolStatsEstimate UNKNOWN_STATS = SymbolStatsEstimate.builder().build(); - - public static final SymbolStatsEstimate ZERO_STATS = SymbolStatsEstimate.builder() - .setLowValue(NaN) - .setHighValue(NaN) - .setDistinctValuesCount(0) - .setNullsFraction(1) - .setAverageRowSize(0) - .build(); + private static final SymbolStatsEstimate UNKNOWN = new SymbolStatsEstimate(NEGATIVE_INFINITY, POSITIVE_INFINITY, NaN, NaN, NaN); + private static final SymbolStatsEstimate ZERO = new SymbolStatsEstimate(NaN, NaN, 1.0, 0.0, 0.0); // for now we support only types which map to real domain naturally and keep low/high value as double in stats. private final double lowValue; @@ -41,7 +40,23 @@ public class SymbolStatsEstimate private final double averageRowSize; private final double distinctValuesCount; - public SymbolStatsEstimate(double lowValue, double highValue, double nullsFraction, double averageRowSize, double distinctValuesCount) + public static SymbolStatsEstimate unknown() + { + return UNKNOWN; + } + + public static SymbolStatsEstimate zero() + { + return ZERO; + } + + @JsonCreator + public SymbolStatsEstimate( + @JsonProperty("lowValue") double lowValue, + @JsonProperty("highValue") double highValue, + @JsonProperty("nullsFraction") double nullsFraction, + @JsonProperty("averageRowSize") double averageRowSize, + @JsonProperty("distinctValuesCount") double distinctValuesCount) { checkArgument( lowValue <= highValue || (isNaN(lowValue) && isNaN(highValue)), @@ -66,21 +81,19 @@ public SymbolStatsEstimate(double lowValue, double highValue, double nullsFracti this.distinctValuesCount = distinctValuesCount; } + @JsonProperty public double getLowValue() { return lowValue; } + @JsonProperty public double getHighValue() { return highValue; } - public boolean isRangeEmpty() - { - return isNaN(lowValue) && isNaN(highValue); - } - + @JsonProperty public double getNullsFraction() { return nullsFraction; @@ -96,34 +109,38 @@ public double getValuesFraction() return 1.0 - nullsFraction; } + @JsonProperty public double getAverageRowSize() { return averageRowSize; } + @JsonProperty public double getDistinctValuesCount() { return distinctValuesCount; } - public SymbolStatsEstimate mapLowValue(Function mappingFunction) + public SymbolStatsEstimate mapNullsFraction(Function mappingFunction) { - return buildFrom(this).setLowValue(mappingFunction.apply(lowValue)).build(); + return buildFrom(this).setNullsFraction(mappingFunction.apply(nullsFraction)).build(); } - public SymbolStatsEstimate mapHighValue(Function mappingFunction) + public SymbolStatsEstimate mapDistinctValuesCount(Function mappingFunction) { - return buildFrom(this).setHighValue(mappingFunction.apply(highValue)).build(); + return buildFrom(this).setDistinctValuesCount(mappingFunction.apply(distinctValuesCount)).build(); } - public SymbolStatsEstimate mapNullsFraction(Function mappingFunction) + public boolean isUnknown() { - return buildFrom(this).setNullsFraction(mappingFunction.apply(nullsFraction)).build(); + return this.equals(UNKNOWN); } - public SymbolStatsEstimate mapDistinctValuesCount(Function mappingFunction) + public boolean isSingleValue() { - return buildFrom(this).setDistinctValuesCount(mappingFunction.apply(distinctValuesCount)).build(); + return distinctValuesCount == 1.0 + && Double.compare(lowValue, highValue) == 0 + && !isInfinite(lowValue); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/cost/TableScanStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/TableScanStatsRule.java index 7460261b0a0cf..3913cecf3dca5 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/TableScanStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/TableScanStatsRule.java @@ -20,7 +20,6 @@ import com.facebook.presto.spi.Constraint; import com.facebook.presto.spi.statistics.ColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; -import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.Lookup; @@ -29,13 +28,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.OptionalDouble; -import static com.facebook.presto.cost.StatsUtil.toStatsRepresentation; -import static com.facebook.presto.cost.SymbolStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.sql.planner.plan.Patterns.tableScan; -import static java.lang.Double.NEGATIVE_INFINITY; -import static java.lang.Double.POSITIVE_INFINITY; import static java.util.Objects.requireNonNull; public class TableScanStatsRule @@ -68,9 +62,8 @@ protected Optional doCalculate(TableScanNode node, StatsP for (Map.Entry entry : node.getAssignments().entrySet()) { Symbol symbol = entry.getKey(); - Type symbolType = types.get(symbol); Optional columnStatistics = Optional.ofNullable(tableStatistics.getColumnStatistics().get(entry.getValue())); - outputSymbolStats.put(symbol, columnStatistics.map(statistics -> toSymbolStatistics(tableStatistics, statistics, session, symbolType)).orElse(UNKNOWN_STATS)); + outputSymbolStats.put(symbol, columnStatistics.map(statistics -> toSymbolStatistics(tableStatistics, statistics)).orElse(SymbolStatsEstimate.unknown())); } return Optional.of(PlanNodeStatsEstimate.builder() @@ -79,23 +72,19 @@ protected Optional doCalculate(TableScanNode node, StatsP .build()); } - private SymbolStatsEstimate toSymbolStatistics(TableStatistics tableStatistics, ColumnStatistics columnStatistics, Session session, Type type) + private SymbolStatsEstimate toSymbolStatistics(TableStatistics tableStatistics, ColumnStatistics columnStatistics) { double nullsFraction = columnStatistics.getNullsFraction().getValue(); double nonNullRowsCount = tableStatistics.getRowCount().getValue() * (1.0 - nullsFraction); - return SymbolStatsEstimate.builder() - .setLowValue(asDouble(session, type, columnStatistics.getOnlyRangeColumnStatistics().getLowValue()).orElse(NEGATIVE_INFINITY)) - .setHighValue(asDouble(session, type, columnStatistics.getOnlyRangeColumnStatistics().getHighValue()).orElse(POSITIVE_INFINITY)) - .setNullsFraction(nullsFraction) - .setDistinctValuesCount(columnStatistics.getOnlyRangeColumnStatistics().getDistinctValuesCount().getValue()) - .setAverageRowSize(columnStatistics.getOnlyRangeColumnStatistics().getDataSize().getValue() / nonNullRowsCount) - .build(); - } - - private OptionalDouble asDouble(Session session, Type type, Optional optionalValue) - { - return optionalValue - .map(value -> toStatsRepresentation(metadata, session, type, value)) - .orElseGet(OptionalDouble::empty); + double averageRowSize = nonNullRowsCount == 0 ? 0 : columnStatistics.getDataSize().getValue() / nonNullRowsCount; + SymbolStatsEstimate.Builder result = SymbolStatsEstimate.builder(); + result.setNullsFraction(nullsFraction); + result.setDistinctValuesCount(columnStatistics.getDistinctValuesCount().getValue()); + result.setAverageRowSize(averageRowSize); + columnStatistics.getRange().ifPresent(range -> { + result.setLowValue(range.getMin()); + result.setHighValue(range.getMax()); + }); + return result.build(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/cost/TaskCountEstimator.java b/presto-main/src/main/java/com/facebook/presto/cost/TaskCountEstimator.java new file mode 100644 index 0000000000000..9e4eb75851ec2 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/cost/TaskCountEstimator.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cost; + +import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.metadata.InternalNodeManager; +import com.facebook.presto.spi.Node; + +import javax.inject.Inject; + +import java.util.Set; +import java.util.function.IntSupplier; + +import static java.lang.Math.toIntExact; +import static java.util.Objects.requireNonNull; + +public class TaskCountEstimator +{ + private final IntSupplier numberOfNodes; + + @Inject + public TaskCountEstimator(NodeSchedulerConfig nodeSchedulerConfig, InternalNodeManager nodeManager) + { + requireNonNull(nodeSchedulerConfig, "nodeSchedulerConfig is null"); + requireNonNull(nodeManager, "nodeManager is null"); + this.numberOfNodes = () -> { + Set activeNodes = nodeManager.getAllNodes().getActiveNodes(); + if (nodeSchedulerConfig.isIncludeCoordinator()) { + return activeNodes.size(); + } + return toIntExact(activeNodes.stream() + .filter(node -> !node.isCoordinator()) + .count()); + }; + } + + public TaskCountEstimator(IntSupplier numberOfNodes) + { + this.numberOfNodes = requireNonNull(numberOfNodes, "numberOfNodes is null"); + } + + public int estimateSourceDistributedTaskCount() + { + return numberOfNodes.getAsInt(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/cost/ValuesStatsRule.java b/presto-main/src/main/java/com/facebook/presto/cost/ValuesStatsRule.java index c09e38ac25b77..bcf1fe16e26e5 100644 --- a/presto-main/src/main/java/com/facebook/presto/cost/ValuesStatsRule.java +++ b/presto-main/src/main/java/com/facebook/presto/cost/ValuesStatsRule.java @@ -92,7 +92,7 @@ private SymbolStatsEstimate buildSymbolStatistics(List values, Session s .collect(toImmutableList()); if (nonNullValues.isEmpty()) { - return SymbolStatsEstimate.ZERO_STATS; + return SymbolStatsEstimate.zero(); } double[] valuesAsDoubles = nonNullValues.stream() diff --git a/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java new file mode 100644 index 0000000000000..0aed58742b6a0 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java @@ -0,0 +1,511 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.event; + +import com.facebook.presto.SessionRepresentation; +import com.facebook.presto.client.NodeVersion; +import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.eventlistener.EventListenerManager; +import com.facebook.presto.execution.Column; +import com.facebook.presto.execution.ExecutionFailureInfo; +import com.facebook.presto.execution.Input; +import com.facebook.presto.execution.QueryInfo; +import com.facebook.presto.execution.QueryStats; +import com.facebook.presto.execution.StageInfo; +import com.facebook.presto.execution.TaskInfo; +import com.facebook.presto.execution.TaskState; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.metadata.SessionPropertyManager; +import com.facebook.presto.operator.OperatorStats; +import com.facebook.presto.operator.TableFinishInfo; +import com.facebook.presto.operator.TaskStats; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; +import com.facebook.presto.spi.eventlistener.QueryContext; +import com.facebook.presto.spi.eventlistener.QueryCreatedEvent; +import com.facebook.presto.spi.eventlistener.QueryFailureInfo; +import com.facebook.presto.spi.eventlistener.QueryIOMetadata; +import com.facebook.presto.spi.eventlistener.QueryInputMetadata; +import com.facebook.presto.spi.eventlistener.QueryMetadata; +import com.facebook.presto.spi.eventlistener.QueryOutputMetadata; +import com.facebook.presto.spi.eventlistener.QueryStatistics; +import com.facebook.presto.spi.eventlistener.StageCpuDistribution; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.transaction.TransactionId; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.airlift.json.JsonCodec; +import io.airlift.log.Logger; +import io.airlift.node.NodeInfo; +import io.airlift.stats.Distribution; +import io.airlift.stats.Distribution.DistributionSnapshot; +import org.joda.time.DateTime; + +import javax.inject.Inject; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.facebook.presto.execution.QueryState.QUEUED; +import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textDistributedPlan; +import static java.lang.Math.max; +import static java.lang.Math.toIntExact; +import static java.time.Duration.ofMillis; +import static java.time.Instant.ofEpochMilli; +import static java.util.Objects.requireNonNull; + +public class QueryMonitor +{ + private static final Logger log = Logger.get(QueryMonitor.class); + + private final JsonCodec stageInfoCodec; + private final JsonCodec operatorStatsCodec; + private final JsonCodec executionFailureInfoCodec; + private final EventListenerManager eventListenerManager; + private final String serverVersion; + private final String serverAddress; + private final String environment; + private final SessionPropertyManager sessionPropertyManager; + private final FunctionRegistry functionRegistry; + private final int maxJsonLimit; + + @Inject + public QueryMonitor( + JsonCodec stageInfoCodec, + JsonCodec operatorStatsCodec, + JsonCodec executionFailureInfoCodec, + EventListenerManager eventListenerManager, + NodeInfo nodeInfo, + NodeVersion nodeVersion, + SessionPropertyManager sessionPropertyManager, + Metadata metadata, + QueryMonitorConfig config) + { + this.eventListenerManager = requireNonNull(eventListenerManager, "eventListenerManager is null"); + this.stageInfoCodec = requireNonNull(stageInfoCodec, "stageInfoCodec is null"); + this.operatorStatsCodec = requireNonNull(operatorStatsCodec, "operatorStatsCodec is null"); + this.executionFailureInfoCodec = requireNonNull(executionFailureInfoCodec, "executionFailureInfoCodec is null"); + this.serverVersion = requireNonNull(nodeVersion, "nodeVersion is null").toString(); + this.serverAddress = requireNonNull(nodeInfo, "nodeInfo is null").getExternalAddress(); + this.environment = requireNonNull(nodeInfo, "nodeInfo is null").getEnvironment(); + this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); + this.functionRegistry = requireNonNull(metadata, "metadata is null").getFunctionRegistry(); + this.maxJsonLimit = toIntExact(requireNonNull(config, "config is null").getMaxOutputStageJsonSize().toBytes()); + } + + public void queryCreatedEvent(BasicQueryInfo queryInfo) + { + eventListenerManager.queryCreated( + new QueryCreatedEvent( + queryInfo.getQueryStats().getCreateTime().toDate().toInstant(), + createQueryContext(queryInfo.getSession(), queryInfo.getResourceGroupId()), + new QueryMetadata( + queryInfo.getQueryId().toString(), + queryInfo.getSession().getTransactionId().map(TransactionId::toString), + queryInfo.getQuery(), + QUEUED.toString(), + queryInfo.getSelf(), + Optional.empty(), + Optional.empty()))); + } + + public void queryImmediateFailureEvent(BasicQueryInfo queryInfo, ExecutionFailureInfo failure) + { + eventListenerManager.queryCompleted(new QueryCompletedEvent( + new QueryMetadata( + queryInfo.getQueryId().toString(), + queryInfo.getSession().getTransactionId().map(TransactionId::toString), + queryInfo.getQuery(), + queryInfo.getState().toString(), + queryInfo.getSelf(), + Optional.empty(), + Optional.empty()), + new QueryStatistics( + ofMillis(0), + ofMillis(0), + ofMillis(0), + Optional.empty(), + Optional.empty(), + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ImmutableList.of(), + 0, + true, + ImmutableList.of(), + ImmutableList.of()), + createQueryContext(queryInfo.getSession(), queryInfo.getResourceGroupId()), + new QueryIOMetadata(ImmutableList.of(), Optional.empty()), + createQueryFailureInfo(failure, Optional.empty()), + ImmutableList.of(), + ofEpochMilli(queryInfo.getQueryStats().getCreateTime().getMillis()), + ofEpochMilli(queryInfo.getQueryStats().getCreateTime().getMillis()), + ofEpochMilli(queryInfo.getQueryStats().getEndTime().getMillis()))); + + logQueryTimeline(queryInfo); + } + + public void queryCompletedEvent(QueryInfo queryInfo) + { + QueryStats queryStats = queryInfo.getQueryStats(); + eventListenerManager.queryCompleted( + new QueryCompletedEvent( + createQueryMetadata(queryInfo), + createQueryStatistics(queryInfo), + createQueryContext(queryInfo.getSession(), queryInfo.getResourceGroupId()), + getQueryIOMetadata(queryInfo), + createQueryFailureInfo(queryInfo.getFailureInfo(), queryInfo.getOutputStage()), + queryInfo.getWarnings(), + ofEpochMilli(queryStats.getCreateTime().getMillis()), + ofEpochMilli(queryStats.getExecutionStartTime().getMillis()), + ofEpochMilli(queryStats.getEndTime() != null ? queryStats.getEndTime().getMillis() : 0))); + + logQueryTimeline(queryInfo); + } + + private QueryMetadata createQueryMetadata(QueryInfo queryInfo) + { + return new QueryMetadata( + queryInfo.getQueryId().toString(), + queryInfo.getSession().getTransactionId().map(TransactionId::toString), + queryInfo.getQuery(), + queryInfo.getState().toString(), + queryInfo.getSelf(), + createTextQueryPlan(queryInfo), + queryInfo.getOutputStage().flatMap(stage -> stageInfoCodec.toJsonWithLengthLimit(stage, maxJsonLimit))); + } + + private QueryStatistics createQueryStatistics(QueryInfo queryInfo) + { + ImmutableList.Builder operatorSummaries = ImmutableList.builder(); + for (OperatorStats summary : queryInfo.getQueryStats().getOperatorSummaries()) { + operatorSummaries.add(operatorStatsCodec.toJson(summary)); + } + + QueryStats queryStats = queryInfo.getQueryStats(); + return new QueryStatistics( + ofMillis(queryStats.getTotalCpuTime().toMillis()), + ofMillis(queryStats.getTotalScheduledTime().toMillis()), + ofMillis(queryStats.getQueuedTime().toMillis()), + Optional.of(ofMillis(queryStats.getAnalysisTime().toMillis())), + Optional.of(ofMillis(queryStats.getDistributedPlanningTime().toMillis())), + queryStats.getPeakUserMemoryReservation().toBytes(), + queryStats.getPeakTotalMemoryReservation().toBytes(), + queryStats.getPeakTaskTotalMemory().toBytes(), + queryStats.getRawInputDataSize().toBytes(), + queryStats.getRawInputPositions(), + queryStats.getOutputDataSize().toBytes(), + queryStats.getOutputPositions(), + queryStats.getLogicalWrittenDataSize().toBytes(), + queryStats.getWrittenPositions(), + queryStats.getCumulativeUserMemory(), + queryStats.getStageGcStatistics(), + queryStats.getCompletedDrivers(), + queryInfo.isCompleteInfo(), + getCpuDistributions(queryInfo), + operatorSummaries.build()); + } + + private QueryContext createQueryContext(SessionRepresentation session, Optional resourceGroup) + { + return new QueryContext( + session.getUser(), + session.getPrincipal(), + session.getRemoteUserAddress(), + session.getUserAgent(), + session.getClientInfo(), + session.getClientTags(), + session.getClientCapabilities(), + session.getSource(), + session.getCatalog(), + session.getSchema(), + resourceGroup, + mergeSessionAndCatalogProperties(session), + session.getResourceEstimates(), + serverAddress, + serverVersion, + environment); + } + + private Optional createTextQueryPlan(QueryInfo queryInfo) + { + try { + if (queryInfo.getOutputStage().isPresent()) { + return Optional.of(textDistributedPlan( + queryInfo.getOutputStage().get(), + functionRegistry, + queryInfo.getSession().toSession(sessionPropertyManager), + false)); + } + } + catch (Exception e) { + // Sometimes it is expected to fail. For example if generated plan is too long. + // Don't fail to create event if the plan can not be created. + log.warn(e, "Error creating explain plan for query %s", queryInfo.getQueryId()); + } + return Optional.empty(); + } + + private static QueryIOMetadata getQueryIOMetadata(QueryInfo queryInfo) + { + ImmutableList.Builder inputs = ImmutableList.builder(); + for (Input input : queryInfo.getInputs()) { + inputs.add(new QueryInputMetadata( + input.getConnectorId().getCatalogName(), + input.getSchema(), + input.getTable(), + input.getColumns().stream() + .map(Column::getName).collect(Collectors.toList()), + input.getConnectorInfo())); + } + + Optional output = Optional.empty(); + if (queryInfo.getOutput().isPresent()) { + Optional tableFinishInfo = queryInfo.getQueryStats().getOperatorSummaries().stream() + .map(OperatorStats::getInfo) + .filter(TableFinishInfo.class::isInstance) + .map(TableFinishInfo.class::cast) + .findFirst(); + + output = Optional.of( + new QueryOutputMetadata( + queryInfo.getOutput().get().getConnectorId().getCatalogName(), + queryInfo.getOutput().get().getSchema(), + queryInfo.getOutput().get().getTable(), + tableFinishInfo.map(TableFinishInfo::getConnectorOutputMetadata), + tableFinishInfo.map(TableFinishInfo::isJsonLengthLimitExceeded))); + } + return new QueryIOMetadata(inputs.build(), output); + } + + private Optional createQueryFailureInfo(ExecutionFailureInfo failureInfo, Optional outputStage) + { + if (failureInfo == null) { + return Optional.empty(); + } + + Optional failedTask = outputStage.flatMap(QueryMonitor::findFailedTask); + + return Optional.of(new QueryFailureInfo( + failureInfo.getErrorCode(), + Optional.ofNullable(failureInfo.getType()), + Optional.ofNullable(failureInfo.getMessage()), + failedTask.map(task -> task.getTaskStatus().getTaskId().toString()), + failedTask.map(task -> task.getTaskStatus().getSelf().getHost()), + executionFailureInfoCodec.toJson(failureInfo))); + } + + private static Optional findFailedTask(StageInfo stageInfo) + { + for (StageInfo subStage : stageInfo.getSubStages()) { + Optional task = findFailedTask(subStage); + if (task.isPresent()) { + return task; + } + } + return stageInfo.getTasks().stream() + .filter(taskInfo -> taskInfo.getTaskStatus().getState() == TaskState.FAILED) + .findFirst(); + } + + private static Map mergeSessionAndCatalogProperties(SessionRepresentation session) + { + Map mergedProperties = new LinkedHashMap<>(session.getSystemProperties()); + + // Either processed or unprocessed catalog properties, but not both. Instead of trying to enforces this while + // firing events, allow both to be set and if there is a duplicate favor the processed properties. + for (Map.Entry> catalogEntry : session.getUnprocessedCatalogProperties().entrySet()) { + for (Map.Entry entry : catalogEntry.getValue().entrySet()) { + mergedProperties.put(catalogEntry.getKey() + "." + entry.getKey(), entry.getValue()); + } + } + for (Map.Entry> catalogEntry : session.getCatalogProperties().entrySet()) { + for (Map.Entry entry : catalogEntry.getValue().entrySet()) { + mergedProperties.put(catalogEntry.getKey().getCatalogName() + "." + entry.getKey(), entry.getValue()); + } + } + return ImmutableMap.copyOf(mergedProperties); + } + + private static void logQueryTimeline(QueryInfo queryInfo) + { + try { + QueryStats queryStats = queryInfo.getQueryStats(); + DateTime queryStartTime = queryStats.getCreateTime(); + DateTime queryEndTime = queryStats.getEndTime(); + + // query didn't finish cleanly + if (queryStartTime == null || queryEndTime == null) { + return; + } + + // planning duration -- start to end of planning + long planning = queryStats.getTotalPlanningTime().toMillis(); + + List stages = StageInfo.getAllStages(queryInfo.getOutputStage()); + // long lastSchedulingCompletion = 0; + long firstTaskStartTime = queryEndTime.getMillis(); + long lastTaskStartTime = queryStartTime.getMillis() + planning; + long lastTaskEndTime = queryStartTime.getMillis() + planning; + for (StageInfo stage : stages) { + // only consider leaf stages + if (!stage.getSubStages().isEmpty()) { + continue; + } + + for (TaskInfo taskInfo : stage.getTasks()) { + TaskStats taskStats = taskInfo.getStats(); + + DateTime firstStartTime = taskStats.getFirstStartTime(); + if (firstStartTime != null) { + firstTaskStartTime = Math.min(firstStartTime.getMillis(), firstTaskStartTime); + } + + DateTime lastStartTime = taskStats.getLastStartTime(); + if (lastStartTime != null) { + lastTaskStartTime = max(lastStartTime.getMillis(), lastTaskStartTime); + } + + DateTime endTime = taskStats.getEndTime(); + if (endTime != null) { + lastTaskEndTime = max(endTime.getMillis(), lastTaskEndTime); + } + } + } + + long elapsed = max(queryEndTime.getMillis() - queryStartTime.getMillis(), 0); + long scheduling = max(firstTaskStartTime - queryStartTime.getMillis() - planning, 0); + long running = max(lastTaskEndTime - firstTaskStartTime, 0); + long finishing = max(queryEndTime.getMillis() - lastTaskEndTime, 0); + + logQueryTimeline( + queryInfo.getQueryId(), + queryInfo.getSession().getTransactionId().map(TransactionId::toString).orElse(""), + elapsed, + planning, + scheduling, + running, + finishing, + queryStartTime, + queryEndTime); + } + catch (Exception e) { + log.error(e, "Error logging query timeline"); + } + } + + private static void logQueryTimeline(BasicQueryInfo queryInfo) + { + DateTime queryStartTime = queryInfo.getQueryStats().getCreateTime(); + DateTime queryEndTime = queryInfo.getQueryStats().getEndTime(); + + // query didn't finish cleanly + if (queryStartTime == null || queryEndTime == null) { + return; + } + + long elapsed = max(queryEndTime.getMillis() - queryStartTime.getMillis(), 0); + + logQueryTimeline( + queryInfo.getQueryId(), + queryInfo.getSession().getTransactionId().map(TransactionId::toString).orElse(""), + elapsed, + elapsed, + 0, + 0, + 0, + queryStartTime, + queryEndTime); + } + + private static void logQueryTimeline( + QueryId queryId, + String transactionId, + long elapsedMillis, + long planningMillis, + long schedulingMillis, + long runningMillis, + long finishingMillis, + DateTime queryStartTime, + DateTime queryEndTime) + { + log.info("TIMELINE: Query %s :: Transaction:[%s] :: elapsed %sms :: planning %sms :: scheduling %sms :: running %sms :: finishing %sms :: begin %s :: end %s", + queryId, + transactionId, + elapsedMillis, + planningMillis, + schedulingMillis, + runningMillis, + finishingMillis, + queryStartTime, + queryEndTime); + } + + private static List getCpuDistributions(QueryInfo queryInfo) + { + if (!queryInfo.getOutputStage().isPresent()) { + return ImmutableList.of(); + } + + ImmutableList.Builder builder = ImmutableList.builder(); + populateDistribution(queryInfo.getOutputStage().get(), builder); + + return builder.build(); + } + + private static void populateDistribution(StageInfo stageInfo, ImmutableList.Builder distributions) + { + distributions.add(computeCpuDistribution(stageInfo)); + for (StageInfo subStage : stageInfo.getSubStages()) { + populateDistribution(subStage, distributions); + } + } + + private static StageCpuDistribution computeCpuDistribution(StageInfo stageInfo) + { + Distribution cpuDistribution = new Distribution(); + + for (TaskInfo taskInfo : stageInfo.getTasks()) { + cpuDistribution.add(taskInfo.getStats().getTotalCpuTime().toMillis()); + } + + DistributionSnapshot snapshot = cpuDistribution.snapshot(); + + return new StageCpuDistribution( + stageInfo.getStageId().getId(), + stageInfo.getTasks().size(), + snapshot.getP25(), + snapshot.getP50(), + snapshot.getP75(), + snapshot.getP90(), + snapshot.getP95(), + snapshot.getP99(), + snapshot.getMin(), + snapshot.getMax(), + (long) snapshot.getTotal(), + snapshot.getTotal() / snapshot.getCount()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitorConfig.java b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitorConfig.java similarity index 96% rename from presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitorConfig.java rename to presto-main/src/main/java/com/facebook/presto/event/QueryMonitorConfig.java index bc32e61e9a25d..371121c31a52b 100644 --- a/presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitorConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitorConfig.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.event.query; +package com.facebook.presto.event; import io.airlift.configuration.Config; import io.airlift.units.DataSize; diff --git a/presto-main/src/main/java/com/facebook/presto/event/SplitMonitor.java b/presto-main/src/main/java/com/facebook/presto/event/SplitMonitor.java new file mode 100644 index 0000000000000..13c73c47935ab --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/event/SplitMonitor.java @@ -0,0 +1,101 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.event; + +import com.facebook.presto.eventlistener.EventListenerManager; +import com.facebook.presto.execution.TaskId; +import com.facebook.presto.operator.DriverStats; +import com.facebook.presto.spi.eventlistener.SplitCompletedEvent; +import com.facebook.presto.spi.eventlistener.SplitFailureInfo; +import com.facebook.presto.spi.eventlistener.SplitStatistics; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.airlift.log.Logger; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import java.time.Duration; +import java.util.Optional; + +import static java.time.Duration.ofMillis; +import static java.util.Objects.requireNonNull; + +public class SplitMonitor +{ + private static final Logger log = Logger.get(SplitMonitor.class); + + private final ObjectMapper objectMapper; + private final EventListenerManager eventListenerManager; + + @Inject + public SplitMonitor(EventListenerManager eventListenerManager, ObjectMapper objectMapper) + { + this.eventListenerManager = requireNonNull(eventListenerManager, "eventListenerManager is null"); + this.objectMapper = requireNonNull(objectMapper, "objectMapper is null"); + } + + public void splitCompletedEvent(TaskId taskId, DriverStats driverStats) + { + splitCompletedEvent(taskId, driverStats, null, null); + } + + public void splitFailedEvent(TaskId taskId, DriverStats driverStats, Throwable cause) + { + splitCompletedEvent(taskId, driverStats, cause.getClass().getName(), cause.getMessage()); + } + + private void splitCompletedEvent(TaskId taskId, DriverStats driverStats, @Nullable String failureType, @Nullable String failureMessage) + { + Optional timeToStart = Optional.empty(); + if (driverStats.getStartTime() != null) { + timeToStart = Optional.of(ofMillis(driverStats.getStartTime().getMillis() - driverStats.getCreateTime().getMillis())); + } + + Optional timeToEnd = Optional.empty(); + if (driverStats.getEndTime() != null) { + timeToEnd = Optional.of(ofMillis(driverStats.getEndTime().getMillis() - driverStats.getCreateTime().getMillis())); + } + + Optional splitFailureMetadata = Optional.empty(); + if (failureType != null) { + splitFailureMetadata = Optional.of(new SplitFailureInfo(failureType, failureMessage != null ? failureMessage : "")); + } + + try { + eventListenerManager.splitCompleted( + new SplitCompletedEvent( + taskId.getQueryId().toString(), + taskId.getStageId().toString(), + Integer.toString(taskId.getId()), + driverStats.getCreateTime().toDate().toInstant(), + Optional.ofNullable(driverStats.getStartTime()).map(startTime -> startTime.toDate().toInstant()), + Optional.ofNullable(driverStats.getEndTime()).map(endTime -> endTime.toDate().toInstant()), + new SplitStatistics( + ofMillis(driverStats.getTotalCpuTime().toMillis()), + ofMillis(driverStats.getElapsedTime().toMillis()), + ofMillis(driverStats.getQueuedTime().toMillis()), + ofMillis(driverStats.getRawInputReadTime().toMillis()), + driverStats.getRawInputPositions(), + driverStats.getRawInputDataSize().toBytes(), + timeToStart, + timeToEnd), + splitFailureMetadata, + objectMapper.writeValueAsString(driverStats))); + } + catch (JsonProcessingException e) { + log.error(e, "Error processing split completion event for task %s", taskId); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitor.java b/presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitor.java deleted file mode 100644 index 2f983a180524d..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/event/query/QueryMonitor.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.event.query; - -import com.facebook.presto.client.FailureInfo; -import com.facebook.presto.client.NodeVersion; -import com.facebook.presto.connector.ConnectorId; -import com.facebook.presto.eventlistener.EventListenerManager; -import com.facebook.presto.execution.Column; -import com.facebook.presto.execution.Input; -import com.facebook.presto.execution.QueryInfo; -import com.facebook.presto.execution.QueryStats; -import com.facebook.presto.execution.StageInfo; -import com.facebook.presto.execution.TaskId; -import com.facebook.presto.execution.TaskInfo; -import com.facebook.presto.execution.TaskState; -import com.facebook.presto.metadata.FunctionRegistry; -import com.facebook.presto.metadata.Metadata; -import com.facebook.presto.metadata.SessionPropertyManager; -import com.facebook.presto.operator.DriverStats; -import com.facebook.presto.operator.OperatorStats; -import com.facebook.presto.operator.TableFinishInfo; -import com.facebook.presto.operator.TaskStats; -import com.facebook.presto.spi.eventlistener.QueryCompletedEvent; -import com.facebook.presto.spi.eventlistener.QueryContext; -import com.facebook.presto.spi.eventlistener.QueryCreatedEvent; -import com.facebook.presto.spi.eventlistener.QueryFailureInfo; -import com.facebook.presto.spi.eventlistener.QueryIOMetadata; -import com.facebook.presto.spi.eventlistener.QueryInputMetadata; -import com.facebook.presto.spi.eventlistener.QueryMetadata; -import com.facebook.presto.spi.eventlistener.QueryOutputMetadata; -import com.facebook.presto.spi.eventlistener.QueryStatistics; -import com.facebook.presto.spi.eventlistener.SplitCompletedEvent; -import com.facebook.presto.spi.eventlistener.SplitFailureInfo; -import com.facebook.presto.spi.eventlistener.SplitStatistics; -import com.facebook.presto.spi.eventlistener.StageCpuDistribution; -import com.facebook.presto.transaction.TransactionId; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import io.airlift.json.JsonCodec; -import io.airlift.log.Logger; -import io.airlift.node.NodeInfo; -import io.airlift.stats.Distribution; -import io.airlift.stats.Distribution.DistributionSnapshot; -import org.joda.time.DateTime; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; -import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textDistributedPlan; -import static java.lang.Math.max; -import static java.lang.Math.toIntExact; -import static java.time.Duration.ofMillis; -import static java.time.Instant.ofEpochMilli; -import static java.util.Objects.requireNonNull; - -public class QueryMonitor -{ - private static final Logger log = Logger.get(QueryMonitor.class); - - private final JsonCodec stageInfoCodec; - private final ObjectMapper objectMapper; - private final EventListenerManager eventListenerManager; - private final String serverVersion; - private final String serverAddress; - private final String environment; - private final SessionPropertyManager sessionPropertyManager; - private final FunctionRegistry functionRegistry; - private final int maxJsonLimit; - - @Inject - public QueryMonitor( - ObjectMapper objectMapper, - JsonCodec stageInfoCodec, - EventListenerManager eventListenerManager, - NodeInfo nodeInfo, - NodeVersion nodeVersion, - SessionPropertyManager sessionPropertyManager, - Metadata metadata, - QueryMonitorConfig config) - { - this.eventListenerManager = requireNonNull(eventListenerManager, "eventListenerManager is null"); - this.stageInfoCodec = requireNonNull(stageInfoCodec, "stageInfoCodec is null"); - this.objectMapper = requireNonNull(objectMapper, "objectMapper is null"); - this.serverVersion = requireNonNull(nodeVersion, "nodeVersion is null").toString(); - this.serverAddress = requireNonNull(nodeInfo, "nodeInfo is null").getExternalAddress(); - this.environment = requireNonNull(nodeInfo, "nodeInfo is null").getEnvironment(); - this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); - this.functionRegistry = requireNonNull(metadata, "metadata is null").getFunctionRegistry(); - this.maxJsonLimit = toIntExact(requireNonNull(config, "config is null").getMaxOutputStageJsonSize().toBytes()); - } - - public void queryCreatedEvent(QueryInfo queryInfo) - { - eventListenerManager.queryCreated( - new QueryCreatedEvent( - queryInfo.getQueryStats().getCreateTime().toDate().toInstant(), - new QueryContext( - queryInfo.getSession().getUser(), - queryInfo.getSession().getPrincipal(), - queryInfo.getSession().getRemoteUserAddress(), - queryInfo.getSession().getUserAgent(), - queryInfo.getSession().getClientInfo(), - queryInfo.getSession().getClientTags(), - queryInfo.getSession().getClientCapabilities(), - queryInfo.getSession().getSource(), - queryInfo.getSession().getCatalog(), - queryInfo.getSession().getSchema(), - queryInfo.getResourceGroupId(), - mergeSessionAndCatalogProperties(queryInfo), - queryInfo.getSession().getResourceEstimates(), - serverAddress, - serverVersion, - environment), - new QueryMetadata( - queryInfo.getQueryId().toString(), - queryInfo.getSession().getTransactionId().map(TransactionId::toString), - queryInfo.getQuery(), - queryInfo.getState().toString(), - queryInfo.getSelf(), - Optional.empty(), - Optional.empty()))); - } - - public void queryCompletedEvent(QueryInfo queryInfo) - { - try { - Optional queryFailureInfo = Optional.empty(); - - if (queryInfo.getFailureInfo() != null) { - FailureInfo failureInfo = queryInfo.getFailureInfo(); - Optional failedTask = queryInfo.getOutputStage().flatMap(QueryMonitor::findFailedTask); - - queryFailureInfo = Optional.of(new QueryFailureInfo( - queryInfo.getErrorCode(), - Optional.ofNullable(failureInfo.getType()), - Optional.ofNullable(failureInfo.getMessage()), - failedTask.map(task -> task.getTaskStatus().getTaskId().toString()), - failedTask.map(task -> task.getTaskStatus().getSelf().getHost()), - objectMapper.writeValueAsString(queryInfo.getFailureInfo()))); - } - - ImmutableList.Builder inputs = ImmutableList.builder(); - for (Input input : queryInfo.getInputs()) { - inputs.add(new QueryInputMetadata( - input.getConnectorId().getCatalogName(), - input.getSchema(), - input.getTable(), - input.getColumns().stream() - .map(Column::getName).collect(Collectors.toList()), - input.getConnectorInfo())); - } - - QueryStats queryStats = queryInfo.getQueryStats(); - - Optional output = Optional.empty(); - if (queryInfo.getOutput().isPresent()) { - Optional tableFinishInfo = queryStats.getOperatorSummaries().stream() - .map(OperatorStats::getInfo) - .filter(TableFinishInfo.class::isInstance) - .map(TableFinishInfo.class::cast) - .findFirst(); - - output = Optional.of( - new QueryOutputMetadata( - queryInfo.getOutput().get().getConnectorId().getCatalogName(), - queryInfo.getOutput().get().getSchema(), - queryInfo.getOutput().get().getTable(), - tableFinishInfo.map(TableFinishInfo::getConnectorOutputMetadata), - tableFinishInfo.map(TableFinishInfo::isJsonLengthLimitExceeded))); - } - - ImmutableList.Builder operatorSummaries = ImmutableList.builder(); - for (OperatorStats summary : queryInfo.getQueryStats().getOperatorSummaries()) { - operatorSummaries.add(objectMapper.writeValueAsString(summary)); - } - - Optional plan = Optional.empty(); - try { - if (queryInfo.getOutputStage().isPresent()) { - // Stats and costs are suppress, since transaction is already completed - plan = Optional.of(textDistributedPlan( - queryInfo.getOutputStage().get(), - functionRegistry, - (node, sourceStats, lookup, session, types) -> UNKNOWN_STATS, - (node, stats, lookup, session, types) -> UNKNOWN_COST, - queryInfo.getSession().toSession(sessionPropertyManager), - false)); - } - } - catch (Exception e) { - // don't fail to create event if the plan can not be created - log.debug(e, "Error creating explain plan"); - } - - eventListenerManager.queryCompleted( - new QueryCompletedEvent( - new QueryMetadata( - queryInfo.getQueryId().toString(), - queryInfo.getSession().getTransactionId().map(TransactionId::toString), - queryInfo.getQuery(), - queryInfo.getState().toString(), - queryInfo.getSelf(), - plan, - queryInfo.getOutputStage().flatMap(stage -> stageInfoCodec.toJsonWithLengthLimit(stage, maxJsonLimit))), - new QueryStatistics( - ofMillis(queryStats.getTotalCpuTime().toMillis()), - ofMillis(queryStats.getTotalScheduledTime().toMillis()), - ofMillis(queryStats.getQueuedTime().toMillis()), - Optional.ofNullable(queryStats.getAnalysisTime()).map(duration -> ofMillis(duration.toMillis())), - Optional.ofNullable(queryStats.getDistributedPlanningTime()).map(duration -> ofMillis(duration.toMillis())), - queryStats.getPeakUserMemoryReservation().toBytes(), - queryStats.getPeakTotalMemoryReservation().toBytes(), - queryStats.getPeakTaskTotalMemory().toBytes(), - queryStats.getRawInputDataSize().toBytes(), - queryStats.getRawInputPositions(), - queryStats.getOutputDataSize().toBytes(), - queryStats.getOutputPositions(), - queryStats.getLogicalWrittenDataSize().toBytes(), - queryStats.getWrittenPositions(), - queryStats.getCumulativeUserMemory(), - queryStats.getStageGcStatistics(), - queryStats.getCompletedDrivers(), - queryInfo.isCompleteInfo(), - getCpuDistributions(queryInfo), - operatorSummaries.build()), - new QueryContext( - queryInfo.getSession().getUser(), - queryInfo.getSession().getPrincipal(), - queryInfo.getSession().getRemoteUserAddress(), - queryInfo.getSession().getUserAgent(), - queryInfo.getSession().getClientInfo(), - queryInfo.getSession().getClientTags(), - queryInfo.getSession().getClientCapabilities(), - queryInfo.getSession().getSource(), - queryInfo.getSession().getCatalog(), - queryInfo.getSession().getSchema(), - queryInfo.getResourceGroupId(), - mergeSessionAndCatalogProperties(queryInfo), - queryInfo.getSession().getResourceEstimates(), - serverAddress, - serverVersion, - environment), - new QueryIOMetadata(inputs.build(), output), - queryFailureInfo, - ofEpochMilli(queryStats.getCreateTime().getMillis()), - ofEpochMilli(queryStats.getExecutionStartTime().getMillis()), - ofEpochMilli(queryStats.getEndTime().getMillis()))); - - logQueryTimeline(queryInfo); - } - catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } - - private static Optional findFailedTask(StageInfo stageInfo) - { - for (StageInfo subStage : stageInfo.getSubStages()) { - Optional task = findFailedTask(subStage); - if (task.isPresent()) { - return task; - } - } - return stageInfo.getTasks().stream() - .filter(taskInfo -> taskInfo.getTaskStatus().getState() == TaskState.FAILED) - .findFirst(); - } - - private static Map mergeSessionAndCatalogProperties(QueryInfo queryInfo) - { - ImmutableMap.Builder mergedProperties = ImmutableMap.builder(); - mergedProperties.putAll(queryInfo.getSession().getSystemProperties()); - for (Map.Entry> catalogEntry : queryInfo.getSession().getCatalogProperties().entrySet()) { - for (Map.Entry entry : catalogEntry.getValue().entrySet()) { - mergedProperties.put(catalogEntry.getKey().getCatalogName() + "." + entry.getKey(), entry.getValue()); - } - } - return mergedProperties.build(); - } - - private static void logQueryTimeline(QueryInfo queryInfo) - { - try { - QueryStats queryStats = queryInfo.getQueryStats(); - DateTime queryStartTime = queryStats.getCreateTime(); - DateTime queryEndTime = queryStats.getEndTime(); - - // query didn't finish cleanly - if (queryStartTime == null || queryEndTime == null) { - return; - } - - // planning duration -- start to end of planning - long planning = queryStats.getTotalPlanningTime() == null ? 0 : queryStats.getTotalPlanningTime().toMillis(); - - List stages = StageInfo.getAllStages(queryInfo.getOutputStage()); - // long lastSchedulingCompletion = 0; - long firstTaskStartTime = queryEndTime.getMillis(); - long lastTaskStartTime = queryStartTime.getMillis() + planning; - long lastTaskEndTime = queryStartTime.getMillis() + planning; - for (StageInfo stage : stages) { - // only consider leaf stages - if (!stage.getSubStages().isEmpty()) { - continue; - } - - for (TaskInfo taskInfo : stage.getTasks()) { - TaskStats taskStats = taskInfo.getStats(); - - DateTime firstStartTime = taskStats.getFirstStartTime(); - if (firstStartTime != null) { - firstTaskStartTime = Math.min(firstStartTime.getMillis(), firstTaskStartTime); - } - - DateTime lastStartTime = taskStats.getLastStartTime(); - if (lastStartTime != null) { - lastTaskStartTime = max(lastStartTime.getMillis(), lastTaskStartTime); - } - - DateTime endTime = taskStats.getEndTime(); - if (endTime != null) { - lastTaskEndTime = max(endTime.getMillis(), lastTaskEndTime); - } - } - } - - long elapsed = queryEndTime.getMillis() - queryStartTime.getMillis(); - long scheduling = firstTaskStartTime - queryStartTime.getMillis() - planning; - long running = lastTaskEndTime - firstTaskStartTime; - long finishing = queryEndTime.getMillis() - lastTaskEndTime; - - log.info("TIMELINE: Query %s :: Transaction:[%s] :: elapsed %sms :: planning %sms :: scheduling %sms :: running %sms :: finishing %sms :: begin %s :: end %s", - queryInfo.getQueryId(), - queryInfo.getSession().getTransactionId().map(TransactionId::toString).orElse(""), - max(elapsed, 0), - max(planning, 0), - max(scheduling, 0), - max(running, 0), - max(finishing, 0), - queryStartTime, - queryEndTime); - } - catch (Exception e) { - log.error(e, "Error logging query timeline"); - } - } - - public void splitCompletedEvent(TaskId taskId, DriverStats driverStats) - { - splitCompletedEvent(taskId, driverStats, null, null); - } - - public void splitFailedEvent(TaskId taskId, DriverStats driverStats, Throwable cause) - { - splitCompletedEvent(taskId, driverStats, cause.getClass().getName(), cause.getMessage()); - } - - private void splitCompletedEvent(TaskId taskId, DriverStats driverStats, @Nullable String failureType, @Nullable String failureMessage) - { - Optional timeToStart = Optional.empty(); - if (driverStats.getStartTime() != null) { - timeToStart = Optional.of(ofMillis(driverStats.getStartTime().getMillis() - driverStats.getCreateTime().getMillis())); - } - - Optional timeToEnd = Optional.empty(); - if (driverStats.getEndTime() != null) { - timeToEnd = Optional.of(ofMillis(driverStats.getEndTime().getMillis() - driverStats.getCreateTime().getMillis())); - } - - Optional splitFailureMetadata = Optional.empty(); - if (failureType != null) { - splitFailureMetadata = Optional.of(new SplitFailureInfo(failureType, failureMessage != null ? failureMessage : "")); - } - - try { - eventListenerManager.splitCompleted( - new SplitCompletedEvent( - taskId.getQueryId().toString(), - taskId.getStageId().toString(), - Integer.toString(taskId.getId()), - driverStats.getCreateTime().toDate().toInstant(), - Optional.ofNullable(driverStats.getStartTime()).map(startTime -> startTime.toDate().toInstant()), - Optional.ofNullable(driverStats.getEndTime()).map(endTime -> endTime.toDate().toInstant()), - new SplitStatistics( - ofMillis(driverStats.getTotalCpuTime().toMillis()), - ofMillis(driverStats.getElapsedTime().toMillis()), - ofMillis(driverStats.getQueuedTime().toMillis()), - ofMillis(driverStats.getTotalUserTime().toMillis()), - ofMillis(driverStats.getRawInputReadTime().toMillis()), - driverStats.getRawInputPositions(), - driverStats.getRawInputDataSize().toBytes(), - timeToStart, - timeToEnd), - splitFailureMetadata, - objectMapper.writeValueAsString(driverStats))); - } - catch (JsonProcessingException e) { - log.error(e, "Error processing split completion event for task %s", taskId); - } - } - - private static List getCpuDistributions(QueryInfo queryInfo) - { - if (!queryInfo.getOutputStage().isPresent()) { - return ImmutableList.of(); - } - - ImmutableList.Builder builder = ImmutableList.builder(); - populateDistribution(queryInfo.getOutputStage().get(), builder); - - return builder.build(); - } - - private static void populateDistribution(StageInfo stageInfo, ImmutableList.Builder distributions) - { - distributions.add(computeCpuDistribution(stageInfo)); - for (StageInfo subStage : stageInfo.getSubStages()) { - populateDistribution(subStage, distributions); - } - } - - private static StageCpuDistribution computeCpuDistribution(StageInfo stageInfo) - { - Distribution cpuDistribution = new Distribution(); - - for (TaskInfo taskInfo : stageInfo.getTasks()) { - cpuDistribution.add(taskInfo.getStats().getTotalCpuTime().toMillis()); - } - - DistributionSnapshot snapshot = cpuDistribution.snapshot(); - - return new StageCpuDistribution( - stageInfo.getStageId().getId(), - stageInfo.getTasks().size(), - snapshot.getP25(), - snapshot.getP50(), - snapshot.getP75(), - snapshot.getP90(), - snapshot.getP95(), - snapshot.getP99(), - snapshot.getMin(), - snapshot.getMax(), - (long) snapshot.getTotal(), - snapshot.getTotal() / snapshot.getCount()); - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/BasicStageStats.java b/presto-main/src/main/java/com/facebook/presto/execution/BasicStageStats.java new file mode 100644 index 0000000000000..89af5a23e11e3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/BasicStageStats.java @@ -0,0 +1,259 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.operator.BlockedReason; +import com.google.common.collect.ImmutableSet; +import io.airlift.units.DataSize; +import io.airlift.units.Duration; + +import java.util.HashSet; +import java.util.OptionalDouble; +import java.util.Set; + +import static io.airlift.units.DataSize.Unit.BYTE; +import static io.airlift.units.DataSize.succinctBytes; +import static java.lang.Math.min; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public class BasicStageStats +{ + public static final BasicStageStats EMPTY_STAGE_STATS = new BasicStageStats( + false, + + 0, + 0, + 0, + 0, + + new DataSize(0, BYTE), + 0, + + 0, + new DataSize(0, BYTE), + new DataSize(0, BYTE), + + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + + false, + ImmutableSet.of(), + + OptionalDouble.empty()); + + private final boolean isScheduled; + private final int totalDrivers; + private final int queuedDrivers; + private final int runningDrivers; + private final int completedDrivers; + private final DataSize rawInputDataSize; + private final long rawInputPositions; + private final long cumulativeUserMemory; + private final DataSize userMemoryReservation; + private final DataSize totalMemoryReservation; + private final Duration totalCpuTime; + private final Duration totalScheduledTime; + private final boolean fullyBlocked; + private final Set blockedReasons; + private final OptionalDouble progressPercentage; + + public BasicStageStats( + boolean isScheduled, + + int totalDrivers, + int queuedDrivers, + int runningDrivers, + int completedDrivers, + + DataSize rawInputDataSize, + long rawInputPositions, + + long cumulativeUserMemory, + DataSize userMemoryReservation, + DataSize totalMemoryReservation, + + Duration totalCpuTime, + Duration totalScheduledTime, + + boolean fullyBlocked, + Set blockedReasons, + + OptionalDouble progressPercentage) + { + this.isScheduled = isScheduled; + this.totalDrivers = totalDrivers; + this.queuedDrivers = queuedDrivers; + this.runningDrivers = runningDrivers; + this.completedDrivers = completedDrivers; + this.rawInputDataSize = requireNonNull(rawInputDataSize, "rawInputDataSize is null"); + this.rawInputPositions = rawInputPositions; + this.cumulativeUserMemory = cumulativeUserMemory; + this.userMemoryReservation = requireNonNull(userMemoryReservation, "userMemoryReservation is null"); + this.totalMemoryReservation = requireNonNull(totalMemoryReservation, "totalMemoryReservation is null"); + this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); + this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); + this.fullyBlocked = fullyBlocked; + this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); + this.progressPercentage = requireNonNull(progressPercentage, "progressPercentage is null"); + } + + public boolean isScheduled() + { + return isScheduled; + } + + public int getTotalDrivers() + { + return totalDrivers; + } + + public int getQueuedDrivers() + { + return queuedDrivers; + } + + public int getRunningDrivers() + { + return runningDrivers; + } + + public int getCompletedDrivers() + { + return completedDrivers; + } + + public DataSize getRawInputDataSize() + { + return rawInputDataSize; + } + + public long getRawInputPositions() + { + return rawInputPositions; + } + + public long getCumulativeUserMemory() + { + return cumulativeUserMemory; + } + + public DataSize getUserMemoryReservation() + { + return userMemoryReservation; + } + + public DataSize getTotalMemoryReservation() + { + return totalMemoryReservation; + } + + public Duration getTotalCpuTime() + { + return totalCpuTime; + } + + public Duration getTotalScheduledTime() + { + return totalScheduledTime; + } + + public boolean isFullyBlocked() + { + return fullyBlocked; + } + + public Set getBlockedReasons() + { + return blockedReasons; + } + + public OptionalDouble getProgressPercentage() + { + return progressPercentage; + } + + public static BasicStageStats aggregateBasicStageStats(Iterable stages) + { + int totalDrivers = 0; + int queuedDrivers = 0; + int runningDrivers = 0; + int completedDrivers = 0; + + long cumulativeUserMemory = 0; + long userMemoryReservation = 0; + long totalMemoryReservation = 0; + + long totalScheduledTimeMillis = 0; + long totalCpuTime = 0; + + long rawInputDataSize = 0; + long rawInputPositions = 0; + + boolean isScheduled = true; + + boolean fullyBlocked = true; + Set blockedReasons = new HashSet<>(); + + for (BasicStageStats stageStats : stages) { + totalDrivers += stageStats.getTotalDrivers(); + queuedDrivers += stageStats.getQueuedDrivers(); + runningDrivers += stageStats.getRunningDrivers(); + completedDrivers += stageStats.getCompletedDrivers(); + + cumulativeUserMemory += stageStats.getCumulativeUserMemory(); + userMemoryReservation += stageStats.getUserMemoryReservation().toBytes(); + totalMemoryReservation += stageStats.getTotalMemoryReservation().toBytes(); + + totalScheduledTimeMillis += stageStats.getTotalScheduledTime().roundTo(MILLISECONDS); + totalCpuTime += stageStats.getTotalCpuTime().roundTo(MILLISECONDS); + + isScheduled &= stageStats.isScheduled(); + + fullyBlocked &= stageStats.isFullyBlocked(); + blockedReasons.addAll(stageStats.getBlockedReasons()); + + rawInputDataSize += stageStats.getRawInputDataSize().toBytes(); + rawInputPositions += stageStats.getRawInputPositions(); + } + + OptionalDouble progressPercentage = OptionalDouble.empty(); + if (isScheduled && totalDrivers != 0) { + progressPercentage = OptionalDouble.of(min(100, (completedDrivers * 100.0) / totalDrivers)); + } + + return new BasicStageStats( + isScheduled, + + totalDrivers, + queuedDrivers, + runningDrivers, + completedDrivers, + + succinctBytes(rawInputDataSize), + rawInputPositions, + + cumulativeUserMemory, + succinctBytes(userMemoryReservation), + succinctBytes(totalMemoryReservation), + + new Duration(totalCpuTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), + new Duration(totalScheduledTimeMillis, MILLISECONDS).convertToMostSuccinctTimeUnit(), + + fullyBlocked, + blockedReasons, + + progressPercentage); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/CallTask.java b/presto-main/src/main/java/com/facebook/presto/execution/CallTask.java index b2ddf7ef3c862..4ba409d0452ff 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/CallTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/CallTask.java @@ -69,7 +69,7 @@ public String getName() @Override public ListenableFuture execute(Call call, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) { - if (!stateMachine.isAutoCommit()) { + if (!transactionManager.isAutoCommit(stateMachine.getSession().getRequiredTransactionId())) { throw new PrestoException(NOT_SUPPORTED, "Procedures cannot be called within a transaction (use autocommit mode)"); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java b/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java new file mode 100644 index 0000000000000..a2d75e51c6368 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java @@ -0,0 +1,176 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.metadata.AllNodes; +import com.facebook.presto.metadata.InternalNodeManager; +import com.facebook.presto.spi.PrestoException; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import io.airlift.units.Duration; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.function.Consumer; + +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INSUFFICIENT_RESOURCES; +import static com.facebook.presto.spi.StandardErrorCode.SERVER_STARTING_UP; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static io.airlift.concurrent.Threads.threadsNamed; +import static io.airlift.units.Duration.nanosSince; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public class ClusterSizeMonitor +{ + private final InternalNodeManager nodeManager; + private final boolean includeCoordinator; + private final int initializationMinCount; + private final Duration initializationMaxWait; + private final int executionMinCount; + private final Duration executionMaxWait; + private final ScheduledExecutorService executor; + + private final long createNanos = System.nanoTime(); + + private final Consumer listener = this::updateAllNodes; + + @GuardedBy("this") + private int currentCount; + + @GuardedBy("this") + private final List> futures = new ArrayList<>(); + + @GuardedBy("this") + private boolean minimumWorkerRequirementMet; + + @Inject + public ClusterSizeMonitor(InternalNodeManager nodeManager, NodeSchedulerConfig nodeSchedulerConfig, QueryManagerConfig queryManagerConfig) + { + this( + nodeManager, + requireNonNull(nodeSchedulerConfig, "nodeSchedulerConfig is null").isIncludeCoordinator(), + requireNonNull(queryManagerConfig, "queryManagerConfig is null").getInitializationRequiredWorkers(), + queryManagerConfig.getInitializationTimeout(), + queryManagerConfig.getRequiredWorkers(), + queryManagerConfig.getRequiredWorkersMaxWait()); + } + + public ClusterSizeMonitor( + InternalNodeManager nodeManager, + boolean includeCoordinator, + int initializationMinCount, + Duration initializationMaxWait, + int executionMinCount, + Duration executionMaxWait) + { + this.nodeManager = requireNonNull(nodeManager, "nodeManager is null"); + this.includeCoordinator = includeCoordinator; + checkArgument(initializationMinCount >= 0, "initializationMinCount is negative"); + this.initializationMinCount = initializationMinCount; + this.initializationMaxWait = requireNonNull(initializationMaxWait, "initializationMaxWait is null"); + checkArgument(executionMinCount >= 0, "executionMinCount is negative"); + this.executionMinCount = executionMinCount; + this.executionMaxWait = requireNonNull(executionMaxWait, "executionMaxWait is null"); + this.executor = newSingleThreadScheduledExecutor(threadsNamed("node-monitor-%s")); + } + + @PostConstruct + public void start() + { + nodeManager.addNodeChangeListener(listener); + updateAllNodes(nodeManager.getAllNodes()); + } + + @PreDestroy + public void stop() + { + nodeManager.removeNodeChangeListener(listener); + } + + public synchronized void verifyInitialMinimumWorkersRequirement() + { + if (minimumWorkerRequirementMet) { + return; + } + + if (currentCount < initializationMinCount && nanosSince(createNanos).compareTo(initializationMaxWait) < 0) { + throw new PrestoException(SERVER_STARTING_UP, format("Cluster is still initializing, there are insufficient active worker nodes (%s) to run query", currentCount)); + } + minimumWorkerRequirementMet = true; + } + + public synchronized ListenableFuture waitForMinimumWorkers() + { + if (currentCount >= executionMinCount) { + return immediateFuture(null); + } + + SettableFuture future = SettableFuture.create(); + futures.add(future); + + // if future does not finish in wait period, complete with an exception + ScheduledFuture timeoutTask = executor.schedule( + () -> { + synchronized (this) { + future.setException(new PrestoException( + GENERIC_INSUFFICIENT_RESOURCES, + format("Insufficient active worker nodes. Waited %s for at least %s workers, but only %s workers are active", executionMaxWait, executionMinCount, currentCount))); + } + }, + executionMaxWait.toMillis(), + MILLISECONDS); + + // remove future if finished (e.g., canceled, timed out) + future.addListener(() -> { + timeoutTask.cancel(true); + removeFuture(future); + }, executor); + + return future; + } + + private synchronized void removeFuture(SettableFuture future) + { + futures.remove(future); + } + + private synchronized void updateAllNodes(AllNodes allNodes) + { + if (includeCoordinator) { + currentCount = allNodes.getActiveNodes().size(); + } + else { + currentCount = Sets.difference(allNodes.getActiveNodes(), allNodes.getActiveCoordinators()).size(); + } + if (currentCount >= executionMinCount) { + ImmutableList> listeners = ImmutableList.copyOf(futures); + futures.clear(); + executor.submit(() -> listeners.forEach(listener -> listener.set(null))); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/CreateViewTask.java b/presto-main/src/main/java/com/facebook/presto/execution/CreateViewTask.java index a23bcaf40039d..224a1ef8b7ef1 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/CreateViewTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/CreateViewTask.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.metadata.ViewDefinition; @@ -80,7 +81,7 @@ public ListenableFuture execute(CreateView statement, TransactionManager tran String sql = getFormattedSql(statement.getQuery(), sqlParser, Optional.of(parameters)); - Analysis analysis = analyzeStatement(statement, session, metadata, accessControl, parameters); + Analysis analysis = analyzeStatement(statement, session, metadata, accessControl, parameters, stateMachine.getWarningCollector()); List columns = analysis.getOutputDescriptor(statement.getQuery()) .getVisibleFields().stream() @@ -94,9 +95,9 @@ public ListenableFuture execute(CreateView statement, TransactionManager tran return immediateFuture(null); } - private Analysis analyzeStatement(Statement statement, Session session, Metadata metadata, AccessControl accessControl, List parameters) + private Analysis analyzeStatement(Statement statement, Session session, Metadata metadata, AccessControl accessControl, List parameters, WarningCollector warningCollector) { - Analyzer analyzer = new Analyzer(session, metadata, sqlParser, accessControl, Optional.empty(), parameters); + Analyzer analyzer = new Analyzer(session, metadata, sqlParser, accessControl, Optional.empty(), parameters, warningCollector); return analyzer.analyze(statement); } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java index 5a14f93f21b28..90e1121c60c52 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java @@ -14,11 +14,15 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.VersionedMemoryPoolId; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.security.AccessControl; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.planner.Plan; @@ -28,23 +32,25 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.units.DataSize; import io.airlift.units.Duration; +import org.joda.time.DateTime; import javax.annotation.Nullable; import javax.inject.Inject; -import java.net.URI; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static io.airlift.units.DataSize.Unit.BYTE; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; public class DataDefinitionExecution implements QueryExecution @@ -88,27 +94,65 @@ public void setMemoryPool(VersionedMemoryPoolId poolId) } @Override - public long getUserMemoryReservation() + public Session getSession() + { + return stateMachine.getSession(); + } + + @Override + public Optional getErrorCode() + { + return stateMachine.getFailureInfo().map(ExecutionFailureInfo::getErrorCode); + } + + @Override + public DataSize getUserMemoryReservation() + { + return new DataSize(0, BYTE); + } + + @Override + public DataSize getTotalMemoryReservation() + { + return new DataSize(0, BYTE); + } + + @Override + public DateTime getCreateTime() + { + return stateMachine.getCreateTime(); + } + + @Override + public Optional getExecutionStartTime() + { + return stateMachine.getExecutionStartTime(); + } + + @Override + public DateTime getLastHeartbeat() { - return 0; + return stateMachine.getLastHeartbeat(); } @Override - public long getTotalMemoryReservation() + public Optional getEndTime() { - return 0; + return stateMachine.getEndTime(); } @Override public Duration getTotalCpuTime() { - return new Duration(0, TimeUnit.SECONDS); + return new Duration(0, NANOSECONDS); } @Override - public Session getSession() + public BasicQueryInfo getBasicQueryInfo() { - return stateMachine.getSession(); + return stateMachine.getFinalQueryInfo() + .map(BasicQueryInfo::new) + .orElseGet(() -> stateMachine.getBasicQueryInfo(Optional.empty())); } @Override @@ -173,6 +217,12 @@ public void fail(Throwable cause) stateMachine.transitionToFailed(cause); } + @Override + public boolean isDone() + { + return getState().isDone(); + } + @Override public void cancelQuery() { @@ -206,11 +256,7 @@ public QueryId getQueryId() @Override public QueryInfo getQueryInfo() { - Optional finalQueryInfo = stateMachine.getFinalQueryInfo(); - if (finalQueryInfo.isPresent()) { - return finalQueryInfo.get(); - } - return stateMachine.updateQueryInfo(Optional.empty()); + return stateMachine.getFinalQueryInfo().orElseGet(() -> stateMachine.updateQueryInfo(Optional.empty())); } @Override @@ -225,18 +271,6 @@ public QueryState getState() return stateMachine.getQueryState(); } - @Override - public Optional getResourceGroup() - { - return stateMachine.getResourceGroup(); - } - - @Override - public void setResourceGroup(ResourceGroupId resourceGroupId) - { - stateMachine.setResourceGroup(resourceGroupId); - } - public List getParameters() { return parameters; @@ -269,35 +303,42 @@ public DataDefinitionExecutionFactory( this.tasks = requireNonNull(tasks, "tasks is null"); } - public String explain(Statement statement, List parameters) - { - DataDefinitionTask task = getTask(statement); - checkArgument(task != null, "no task for statement: %s", statement.getClass().getSimpleName()); - return task.explain(statement, parameters); - } - @Override public DataDefinitionExecution createQueryExecution( - QueryId queryId, String query, Session session, - Statement statement, - List parameters) + PreparedQuery preparedQuery, + ResourceGroupId resourceGroup, + WarningCollector warningCollector) { - URI self = locationFactory.createQueryLocation(queryId); + return createDataDefinitionExecution(query, session, resourceGroup, preparedQuery.getStatement(), preparedQuery.getParameters(), warningCollector); + } - DataDefinitionTask task = getTask(statement); + private DataDefinitionExecution createDataDefinitionExecution( + String query, + Session session, + ResourceGroupId resourceGroup, + T statement, + List parameters, + WarningCollector warningCollector) + { + @SuppressWarnings("unchecked") + DataDefinitionTask task = (DataDefinitionTask) tasks.get(statement.getClass()); checkArgument(task != null, "no task for statement: %s", statement.getClass().getSimpleName()); - QueryStateMachine stateMachine = QueryStateMachine.begin(queryId, query, session, self, task.isTransactionControl(), transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = QueryStateMachine.begin( + query, + session, + locationFactory.createQueryLocation(session.getQueryId()), + resourceGroup, + task.isTransactionControl(), + transactionManager, + accessControl, + executor, + metadata, + warningCollector); stateMachine.setUpdateType(task.getName()); return new DataDefinitionExecution<>(task, statement, transactionManager, metadata, accessControl, stateMachine, parameters); } - - @SuppressWarnings("unchecked") - private DataDefinitionTask getTask(T statement) - { - return (DataDefinitionTask) tasks.get(statement.getClass()); - } } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ExecutionFailureInfo.java b/presto-main/src/main/java/com/facebook/presto/execution/ExecutionFailureInfo.java index 37d18dec98604..1b111215129b6 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/ExecutionFailureInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/ExecutionFailureInfo.java @@ -23,7 +23,6 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import javax.validation.constraints.NotNull; import java.util.List; import java.util.regex.Matcher; @@ -72,7 +71,6 @@ public ExecutionFailureInfo( this.remoteHost = remoteHost; } - @NotNull @JsonProperty public String getType() { @@ -93,14 +91,12 @@ public ExecutionFailureInfo getCause() return cause; } - @NotNull @JsonProperty public List getSuppressed() { return suppressed; } - @NotNull @JsonProperty public List getStack() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ExplainAnalyzeContext.java b/presto-main/src/main/java/com/facebook/presto/execution/ExplainAnalyzeContext.java index 2d780090eef64..bdc1540f8c9f8 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/ExplainAnalyzeContext.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/ExplainAnalyzeContext.java @@ -13,9 +13,6 @@ */ package com.facebook.presto.execution; -import com.facebook.presto.cost.CostCalculator; -import com.facebook.presto.cost.StatsCalculator; - import javax.inject.Inject; import static java.util.Objects.requireNonNull; @@ -23,32 +20,15 @@ public class ExplainAnalyzeContext { private final QueryPerformanceFetcher queryPerformanceFetcher; - private final StatsCalculator statsCalculator; - private final CostCalculator costCalculator; @Inject - public ExplainAnalyzeContext( - QueryPerformanceFetcher queryPerformanceFetcher, - StatsCalculator statsCalculator, - CostCalculator costCalculator) + public ExplainAnalyzeContext(QueryPerformanceFetcher queryPerformanceFetcher) { this.queryPerformanceFetcher = requireNonNull(queryPerformanceFetcher, "queryPerformanceFetcher is null"); - this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); - this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); } public QueryPerformanceFetcher getQueryPerformanceFetcher() { return queryPerformanceFetcher; } - - public StatsCalculator getStatsCalculator() - { - return statsCalculator; - } - - public CostCalculator getCostCalculator() - { - return costCalculator; - } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java index 1e34be52c2892..07c9f25b7e989 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java @@ -16,23 +16,28 @@ import com.facebook.presto.Session; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.memory.VersionedMemoryPoolId; -import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.planner.Plan; -import com.facebook.presto.transaction.TransactionManager; import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.units.DataSize; import io.airlift.units.Duration; +import org.joda.time.DateTime; import java.net.URI; import java.util.Optional; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import static com.facebook.presto.execution.QueryInfo.immediateFailureQueryInfo; +import static com.facebook.presto.execution.QueryState.FAILED; import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; import static com.google.common.util.concurrent.Futures.immediateFuture; +import static io.airlift.units.DataSize.Unit.BYTE; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; public class FailedQueryExecution implements QueryExecution @@ -40,16 +45,13 @@ public class FailedQueryExecution private final QueryInfo queryInfo; private final Session session; private final Executor executor; - private final Optional resourceGroup; - public FailedQueryExecution(QueryId queryId, String query, Optional resourceGroup, Session session, URI self, TransactionManager transactionManager, Executor executor, Metadata metadata, Throwable cause) + public FailedQueryExecution(Session session, String query, URI self, Optional resourceGroup, Executor executor, Throwable cause) { requireNonNull(cause, "cause is null"); this.session = requireNonNull(session, "session is null"); this.executor = requireNonNull(executor, "executor is null"); - QueryStateMachine queryStateMachine = QueryStateMachine.failed(queryId, query, session, self, transactionManager, executor, metadata, cause); - queryInfo = queryStateMachine.updateQueryInfo(Optional.empty()); - this.resourceGroup = requireNonNull(resourceGroup, "resourceGroup is null"); + this.queryInfo = immediateFailureQueryInfo(session, query, self, resourceGroup, cause); } @Override @@ -89,21 +91,21 @@ public void setMemoryPool(VersionedMemoryPoolId poolId) } @Override - public long getUserMemoryReservation() + public DataSize getUserMemoryReservation() { - return 0; + return new DataSize(0, BYTE); } @Override - public long getTotalMemoryReservation() + public DataSize getTotalMemoryReservation() { - return 0; + return new DataSize(0, BYTE); } @Override public Duration getTotalCpuTime() { - return new Duration(0, TimeUnit.SECONDS); + return new Duration(0, NANOSECONDS); } @Override @@ -112,6 +114,42 @@ public Session getSession() return session; } + @Override + public DateTime getCreateTime() + { + return queryInfo.getQueryStats().getCreateTime(); + } + + @Override + public Optional getExecutionStartTime() + { + return Optional.ofNullable(queryInfo.getQueryStats().getExecutionStartTime()); + } + + @Override + public DateTime getLastHeartbeat() + { + return queryInfo.getQueryStats().getLastHeartbeat(); + } + + @Override + public Optional getEndTime() + { + return Optional.ofNullable(queryInfo.getQueryStats().getEndTime()); + } + + @Override + public Optional getErrorCode() + { + return Optional.ofNullable(getQueryInfo().getFailureInfo()).map(ExecutionFailureInfo::getErrorCode); + } + + @Override + public BasicQueryInfo getBasicQueryInfo() + { + return new BasicQueryInfo(getQueryInfo()); + } + @Override public void start() { @@ -133,7 +171,7 @@ public ListenableFuture getStateChange(QueryState currentState) @Override public void addStateChangeListener(StateChangeListener stateChangeListener) { - executor.execute(() -> stateChangeListener.stateChanged(QueryState.FAILED)); + executor.execute(() -> stateChangeListener.stateChanged(FAILED)); } @Override @@ -148,6 +186,12 @@ public void fail(Throwable cause) // no-op } + @Override + public boolean isDone() + { + return getState().isDone(); + } + @Override public void cancelQuery() { @@ -171,16 +215,4 @@ public void pruneInfo() { // no-op } - - @Override - public Optional getResourceGroup() - { - return resourceGroup; - } - - @Override - public void setResourceGroup(ResourceGroupId resourceGroupId) - { - throw new UnsupportedOperationException("setResouceGroup is not supported for FailedQueryExecution"); - } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/Failure.java b/presto-main/src/main/java/com/facebook/presto/execution/Failure.java index f497f56b60f3f..8f57127407b99 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/Failure.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/Failure.java @@ -27,7 +27,7 @@ public class Failure Failure(String type, String message, @Nullable ErrorCode errorCode, Failure cause) { - super(message, cause, true, true); + super(message, cause); this.type = requireNonNull(type, "type is null"); this.errorCode = errorCode; } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java new file mode 100644 index 0000000000000..c116110e39c3c --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java @@ -0,0 +1,54 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; +import io.airlift.units.DataSize; +import io.airlift.units.Duration; + +import java.util.Optional; + +public interface ManagedQueryExecution +{ + void start(); + + void fail(Throwable cause); + + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ + void addStateChangeListener(StateChangeListener stateChangeListener); + + Session getSession(); + + DataSize getUserMemoryReservation(); + + DataSize getTotalMemoryReservation(); + + Duration getTotalCpuTime(); + + BasicQueryInfo getBasicQueryInfo(); + + boolean isDone(); + + /** + * @return Returns non-empty value iff error has occurred and query failed state is visible. + */ + Optional getErrorCode(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/MemoryAwareQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/MemoryAwareQueryExecution.java deleted file mode 100644 index 1b5957a28cbee..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/execution/MemoryAwareQueryExecution.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.execution; - -import com.facebook.presto.Session; -import com.facebook.presto.memory.ClusterMemoryManager; -import com.facebook.presto.memory.VersionedMemoryPoolId; -import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.sql.planner.Plan; -import com.google.common.util.concurrent.ListenableFuture; -import io.airlift.concurrent.SetThreadName; -import io.airlift.units.DataSize; -import io.airlift.units.Duration; - -import javax.annotation.concurrent.GuardedBy; - -import java.util.Optional; -import java.util.function.Consumer; - -import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; -import static com.facebook.presto.memory.LocalMemoryManager.RESERVED_POOL; -import static com.google.common.base.Throwables.throwIfInstanceOf; - -public class MemoryAwareQueryExecution - implements QueryExecution -{ - private final ClusterMemoryManager memoryManager; - private final SqlQueryExecution delegate; - private final long peakMemoryEstimate; - - @GuardedBy("this") - private boolean startedWaiting; - - public MemoryAwareQueryExecution(ClusterMemoryManager memoryManager, SqlQueryExecution delegate) - { - this.memoryManager = memoryManager; - this.delegate = delegate; - this.peakMemoryEstimate = delegate.getSession().getResourceEstimates().getPeakMemory().map(DataSize::toBytes).orElse(0L); - } - - @Override - public QueryId getQueryId() - { - return delegate.getQueryId(); - } - - @Override - public QueryInfo getQueryInfo() - { - return delegate.getQueryInfo(); - } - - @Override - public QueryState getState() - { - return delegate.getState(); - } - - @Override - public ListenableFuture getStateChange(QueryState currentState) - { - return delegate.getStateChange(currentState); - } - - @Override - public void addOutputInfoListener(Consumer listener) - { - delegate.addOutputInfoListener(listener); - } - - @Override - public Optional getResourceGroup() - { - return delegate.getResourceGroup(); - } - - @Override - public void setResourceGroup(ResourceGroupId resourceGroupId) - { - delegate.setResourceGroup(resourceGroupId); - } - - @Override - public Plan getQueryPlan() - { - return delegate.getQueryPlan(); - } - - @Override - public VersionedMemoryPoolId getMemoryPool() - { - return delegate.getMemoryPool(); - } - - @Override - public void setMemoryPool(VersionedMemoryPoolId poolId) - { - delegate.setMemoryPool(poolId); - } - - @Override - public long getUserMemoryReservation() - { - return delegate.getUserMemoryReservation(); - } - - @Override - public long getTotalMemoryReservation() - { - return delegate.getTotalMemoryReservation(); - } - - @Override - public Duration getTotalCpuTime() - { - return delegate.getTotalCpuTime(); - } - - @Override - public Session getSession() - { - return delegate.getSession(); - } - - @Override - public synchronized void start() - { - try (SetThreadName ignored = new SetThreadName("Query-%s", delegate.getQueryId())) { - try { - if (memoryManager.preAllocateQueryMemory(delegate.getQueryId(), peakMemoryEstimate)) { - delegate.addStateChangeListener(state -> { - if (state.isDone()) { - memoryManager.removePreAllocation(delegate.getQueryId()); - } - }); - delegate.start(); - return; - } - - if (!startedWaiting) { - // This may cause starvation, since requests may not be granted in the order they arrive - startedWaiting = true; - delegate.startWaitingForResources(); - memoryManager.addChangeListener(GENERAL_POOL, none -> start()); - memoryManager.addChangeListener(RESERVED_POOL, none -> start()); - } - } - catch (Throwable e) { - fail(e); - throwIfInstanceOf(e, Error.class); - } - } - } - - @Override - public void fail(Throwable cause) - { - delegate.fail(cause); - } - - @Override - public void cancelQuery() - { - delegate.cancelQuery(); - } - - @Override - public void cancelStage(StageId stageId) - { - delegate.cancelStage(stageId); - } - - @Override - public void recordHeartbeat() - { - delegate.recordHeartbeat(); - } - - @Override - public void pruneInfo() - { - delegate.pruneInfo(); - } - - @Override - public void addStateChangeListener(StateMachine.StateChangeListener stateChangeListener) - { - delegate.addStateChangeListener(stateChangeListener); - } - - @Override - public void addFinalQueryInfoListener(StateMachine.StateChangeListener stateChangeListener) - { - delegate.addFinalQueryInfoListener(stateChangeListener); - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/MemoryRevokingScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/MemoryRevokingScheduler.java index 38852bead8b85..cca4bf60287c1 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/MemoryRevokingScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/MemoryRevokingScheduler.java @@ -105,7 +105,10 @@ private static double checkFraction(double value, String valueName) private static List getMemoryPools(LocalMemoryManager localMemoryManager) { requireNonNull(localMemoryManager, "localMemoryManager can not be null"); - return ImmutableList.of(localMemoryManager.getPool(LocalMemoryManager.GENERAL_POOL), localMemoryManager.getPool(LocalMemoryManager.RESERVED_POOL)); + ImmutableList.Builder builder = new ImmutableList.Builder<>(); + builder.add(localMemoryManager.getGeneralPool()); + localMemoryManager.getReservedPool().ifPresent(builder::add); + return builder.build(); } @PostConstruct diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java index 0cae3b9e1e7ab..1b0d368611e15 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java @@ -14,80 +14,58 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; +import com.facebook.presto.execution.QueryTracker.TrackedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.VersionedMemoryPoolId; -import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.Plan; -import com.facebook.presto.sql.tree.Expression; -import com.facebook.presto.sql.tree.Statement; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; -import io.airlift.units.Duration; import java.net.URI; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import static java.util.Objects.requireNonNull; public interface QueryExecution + extends ManagedQueryExecution, TrackedQuery { - QueryId getQueryId(); - - QueryInfo getQueryInfo(); - QueryState getState(); ListenableFuture getStateChange(QueryState currentState); void addOutputInfoListener(Consumer listener); - Optional getResourceGroup(); - - void setResourceGroup(ResourceGroupId resourceGroupId); - Plan getQueryPlan(); + QueryInfo getQueryInfo(); + VersionedMemoryPoolId getMemoryPool(); void setMemoryPool(VersionedMemoryPoolId poolId); - long getUserMemoryReservation(); - - /** - * @return the user + system memory reservation - */ - long getTotalMemoryReservation(); - - Duration getTotalCpuTime(); - - Session getSession(); - - void start(); - - void fail(Throwable cause); - void cancelQuery(); void cancelStage(StageId stageId); void recordHeartbeat(); - // XXX: This should be removed when the client protocol is improved, so that we don't need to hold onto so much query history - void pruneInfo(); - - void addStateChangeListener(StateChangeListener stateChangeListener); - + /** + * Add a listener for the final query info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. + */ void addFinalQueryInfoListener(StateChangeListener stateChangeListener); interface QueryExecutionFactory { - T createQueryExecution(QueryId queryId, String query, Session session, Statement statement, List parameters); + T createQueryExecution(String query, Session session, PreparedQuery preparedQuery, ResourceGroupId resourceGroup, WarningCollector warningCollector); } /** diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java index b6b2e80e18f9c..0ed3c0acdb64b 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java @@ -13,10 +13,11 @@ */ package com.facebook.presto.execution; +import com.facebook.presto.Session; import com.facebook.presto.SessionRepresentation; -import com.facebook.presto.client.FailureInfo; import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.ErrorType; +import com.facebook.presto.spi.PrestoWarning; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.memory.MemoryPoolId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -36,7 +37,11 @@ import java.util.Optional; import java.util.Set; +import static com.facebook.presto.execution.QueryState.FAILED; +import static com.facebook.presto.execution.QueryStats.immediateFailureQueryStats; import static com.facebook.presto.execution.StageInfo.getAllStages; +import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; +import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -63,9 +68,10 @@ public class QueryInfo private final boolean clearTransactionId; private final String updateType; private final Optional outputStage; - private final FailureInfo failureInfo; + private final ExecutionFailureInfo failureInfo; private final ErrorType errorType; private final ErrorCode errorCode; + private final List warnings; private final Set inputs; private final Optional output; private final boolean completeInfo; @@ -93,8 +99,9 @@ public QueryInfo( @JsonProperty("clearTransactionId") boolean clearTransactionId, @JsonProperty("updateType") String updateType, @JsonProperty("outputStage") Optional outputStage, - @JsonProperty("failureInfo") FailureInfo failureInfo, + @JsonProperty("failureInfo") ExecutionFailureInfo failureInfo, @JsonProperty("errorCode") ErrorCode errorCode, + @JsonProperty("warnings") List warnings, @JsonProperty("inputs") Set inputs, @JsonProperty("output") Optional output, @JsonProperty("completeInfo") boolean completeInfo, @@ -119,6 +126,7 @@ public QueryInfo( requireNonNull(inputs, "inputs is null"); requireNonNull(output, "output is null"); requireNonNull(resourceGroupId, "resourceGroupId is null"); + requireNonNull(warnings, "warnings is null"); this.queryId = queryId; this.session = session; @@ -143,12 +151,48 @@ public QueryInfo( this.failureInfo = failureInfo; this.errorType = errorCode == null ? null : errorCode.getType(); this.errorCode = errorCode; + this.warnings = ImmutableList.copyOf(warnings); this.inputs = ImmutableSet.copyOf(inputs); this.output = output; this.completeInfo = completeInfo; this.resourceGroupId = resourceGroupId; } + public static QueryInfo immediateFailureQueryInfo(Session session, String query, URI self, Optional resourceGroupId, Throwable throwable) + { + ExecutionFailureInfo failureCause = toFailure(throwable); + QueryInfo queryInfo = new QueryInfo( + session.getQueryId(), + session.toSessionRepresentation(), + FAILED, + GENERAL_POOL, + false, + self, + ImmutableList.of(), + query, + immediateFailureQueryStats(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + ImmutableMap.of(), + ImmutableSet.of(), + ImmutableMap.of(), + ImmutableSet.of(), + Optional.empty(), + false, + null, + Optional.empty(), + failureCause, + failureCause.getErrorCode(), + ImmutableList.of(), + ImmutableSet.of(), + Optional.empty(), + true, + resourceGroupId); + + return queryInfo; + } + @JsonProperty public QueryId getQueryId() { @@ -272,7 +316,7 @@ public Optional getOutputStage() @Nullable @JsonProperty - public FailureInfo getFailureInfo() + public ExecutionFailureInfo getFailureInfo() { return failureInfo; } @@ -291,6 +335,12 @@ public ErrorCode getErrorCode() return errorCode; } + @JsonProperty + public List getWarnings() + { + return warnings; + } + @JsonProperty public boolean isFinalQueryInfo() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java index fb0a0dfd6fa58..c498ac0638b6e 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java @@ -15,44 +15,93 @@ import com.facebook.presto.execution.QueryExecution.QueryOutputInfo; import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.sql.planner.Plan; import com.google.common.util.concurrent.ListenableFuture; import java.util.List; -import java.util.Optional; +import java.util.NoSuchElementException; import java.util.function.Consumer; public interface QueryManager { - List getAllQueryInfo(); - - void addOutputInfoListener(QueryId queryId, Consumer listener); - - void addStateChangeListener(QueryId queryId, StateChangeListener listener); - + List getQueries(); + + /** + * Add a listener that fires once the query output locations are known. + * + * @throws NoSuchElementException if query does not exist + */ + void addOutputInfoListener(QueryId queryId, Consumer listener) + throws NoSuchElementException; + + /** + * Add a listener that fires each time the query state changes. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + * + * @throws NoSuchElementException if query does not exist + */ + void addStateChangeListener(QueryId queryId, StateChangeListener listener) + throws NoSuchElementException; + + /** + * Gets a future that completes when the query changes from the specified current state + * or immediately if the query is already in a final state. If the query does not exist, + * the future will contain a {@link NoSuchElementException} + */ ListenableFuture getStateChange(QueryId queryId, QueryState currentState); - QueryInfo getQueryInfo(QueryId queryId); - - Optional getQueryResourceGroup(QueryId queryId); - - Plan getQueryPlan(QueryId queryId); - - Optional getQueryState(QueryId queryId); - + /** + * @throws NoSuchElementException if query does not exist + */ + BasicQueryInfo getQueryInfo(QueryId queryId) + throws NoSuchElementException; + + /** + * @throws NoSuchElementException if query does not exist + */ + QueryInfo getFullQueryInfo(QueryId queryId) + throws NoSuchElementException; + + /** + * @throws NoSuchElementException if query does not exist + */ + QueryState getQueryState(QueryId queryId) + throws NoSuchElementException; + + /** + * Updates the client heartbeat time, to prevent the query from be automatically purged. + * If the query does not exist, the call is ignored. + */ void recordHeartbeat(QueryId queryId); QueryId createQueryId(); + /** + * Creates a new query. This method may be called multiple times for the same query id. The + * the first call will be accepted, and the other calls will be ignored. + */ ListenableFuture createQuery(QueryId queryId, SessionContext sessionContext, String query); + /** + * Attempts to fail the query for the specified reason. If the query is already in a final + * state, the call is ignored. If the query does not exist, the call is ignored. + */ void failQuery(QueryId queryId, Throwable cause); + /** + * Attempts to fail the query due to a user cancellation. If the query is already in a final + * state, the call is ignored. If the query does not exist, the call is ignored. + */ void cancelQuery(QueryId queryId); + /** + * Attempts to cancel the stage and continue the query. If the stage is already in a final + * state, the call is ignored. If the query does not exist, the call is ignored. + */ void cancelStage(StageId stageId); SqlQueryManagerStats getStats(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java index 2a5f6503cb675..a47a3101ad58a 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java @@ -60,6 +60,9 @@ public class QueryManagerConfig private int initializationRequiredWorkers = 1; private Duration initializationTimeout = new Duration(5, TimeUnit.MINUTES); + private int requiredWorkers = 1; + private Duration requiredWorkersMaxWait = new Duration(5, TimeUnit.MINUTES); + @Min(1) public int getScheduleSplitBatchSize() { @@ -330,4 +333,32 @@ public QueryManagerConfig setInitializationTimeout(Duration initializationTimeou this.initializationTimeout = initializationTimeout; return this; } + + @Min(1) + public int getRequiredWorkers() + { + return requiredWorkers; + } + + @Config("query-manager.required-workers") + @ConfigDescription("Minimum number of active workers that must be available before a query will start") + public QueryManagerConfig setRequiredWorkers(int requiredWorkers) + { + this.requiredWorkers = requiredWorkers; + return this; + } + + @NotNull + public Duration getRequiredWorkersMaxWait() + { + return requiredWorkersMaxWait; + } + + @Config("query-manager.required-workers-max-wait") + @ConfigDescription("Maximum time to wait for minimum number of workers before the query is failed") + public QueryManagerConfig setRequiredWorkersMaxWait(Duration requiredWorkersMaxWait) + { + this.requiredWorkersMaxWait = requiredWorkersMaxWait; + return this; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryPreparer.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryPreparer.java new file mode 100644 index 0000000000000..47d084ee1cbdf --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryPreparer.java @@ -0,0 +1,120 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.resourceGroups.QueryType; +import com.facebook.presto.sql.analyzer.SemanticException; +import com.facebook.presto.sql.parser.ParsingException; +import com.facebook.presto.sql.parser.SqlParser; +import com.facebook.presto.sql.tree.Execute; +import com.facebook.presto.sql.tree.Explain; +import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.sql.tree.Statement; +import com.facebook.presto.util.StatementUtils; +import com.google.common.collect.ImmutableList; + +import javax.inject.Inject; + +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.execution.ParameterExtractor.getParameterCount; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static com.facebook.presto.sql.ParsingUtil.createParsingOptions; +import static com.facebook.presto.sql.analyzer.ConstantExpressionVerifier.verifyExpressionIsConstant; +import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_PARAMETER_USAGE; +import static java.util.Collections.emptySet; +import static java.util.Objects.requireNonNull; + +public class QueryPreparer +{ + private final SqlParser sqlParser; + + @Inject + public QueryPreparer(SqlParser sqlParser) + { + this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); + } + + public PreparedQuery prepareQuery(Session session, String query) + throws ParsingException, PrestoException, SemanticException + { + Statement wrappedStatement = sqlParser.createStatement(query, createParsingOptions(session)); + return prepareQuery(session, wrappedStatement); + } + + public PreparedQuery prepareQuery(Session session, Statement wrappedStatement) + throws ParsingException, PrestoException, SemanticException + { + Statement statement = unwrapExecuteStatement(wrappedStatement, sqlParser, session); + if (statement instanceof Explain && ((Explain) statement).isAnalyze()) { + Statement innerStatement = ((Explain) statement).getStatement(); + Optional innerQueryType = StatementUtils.getQueryType(innerStatement.getClass()); + if (!innerQueryType.isPresent() || innerQueryType.get() == QueryType.DATA_DEFINITION) { + throw new PrestoException(NOT_SUPPORTED, "EXPLAIN ANALYZE doesn't support statement type: " + innerStatement.getClass().getSimpleName()); + } + } + List parameters = ImmutableList.of(); + if (wrappedStatement instanceof Execute) { + parameters = ((Execute) wrappedStatement).getParameters(); + } + validateParameters(statement, parameters); + return new PreparedQuery(statement, parameters); + } + + private static Statement unwrapExecuteStatement(Statement statement, SqlParser sqlParser, Session session) + { + if (!(statement instanceof Execute)) { + return statement; + } + + String sql = session.getPreparedStatementFromExecute((Execute) statement); + return sqlParser.createStatement(sql, createParsingOptions(session)); + } + + private static void validateParameters(Statement node, List parameterValues) + { + int parameterCount = getParameterCount(node); + if (parameterValues.size() != parameterCount) { + throw new SemanticException(INVALID_PARAMETER_USAGE, node, "Incorrect number of parameters: expected %s but found %s", parameterCount, parameterValues.size()); + } + for (Expression expression : parameterValues) { + verifyExpressionIsConstant(emptySet(), expression); + } + } + + public static class PreparedQuery + { + private final Statement statement; + private final List parameters; + + public PreparedQuery(Statement statement, List parameters) + { + this.statement = requireNonNull(statement, "statement is null"); + this.parameters = ImmutableList.copyOf(requireNonNull(parameters, "parameters is null")); + } + + public Statement getStatement() + { + return statement; + } + + public List getParameters() + { + return parameters; + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java index 00b8f1268524d..5f716b2d622a5 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java @@ -14,14 +14,16 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; -import com.facebook.presto.client.FailureInfo; import com.facebook.presto.execution.QueryExecution.QueryOutputInfo; import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.VersionedMemoryPoolId; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.operator.BlockedReason; import com.facebook.presto.operator.OperatorStats; import com.facebook.presto.security.AccessControl; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.server.BasicQueryStats; import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; @@ -32,6 +34,7 @@ import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.transaction.TransactionId; import com.facebook.presto.transaction.TransactionManager; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Ticker; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -62,6 +65,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import static com.facebook.presto.execution.BasicStageStats.EMPTY_STAGE_STATS; import static com.facebook.presto.execution.QueryState.FAILED; import static com.facebook.presto.execution.QueryState.FINISHED; import static com.facebook.presto.execution.QueryState.FINISHING; @@ -80,27 +84,20 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.succinctBytes; -import static io.airlift.units.Duration.succinctNanos; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.NANOSECONDS; @ThreadSafe public class QueryStateMachine { - private static final Logger log = Logger.get(QueryStateMachine.class); - - private final DateTime createTime = DateTime.now(); - private final long createNanos; - private final AtomicLong endNanos = new AtomicLong(); + public static final Logger QUERY_STATE_LOG = Logger.get(QueryStateMachine.class); private final QueryId queryId; private final String query; private final Session session; private final URI self; - private final boolean autoCommit; + private final Optional resourceGroup; private final TransactionManager transactionManager; - private final Ticker ticker; private final Metadata metadata; private final QueryOutputManager outputManager; @@ -115,22 +112,7 @@ public class QueryStateMachine private final AtomicLong peakTaskTotalMemory = new AtomicLong(); - private final AtomicReference lastHeartbeat = new AtomicReference<>(DateTime.now()); - private final AtomicReference executionStartTime = new AtomicReference<>(); - private final AtomicReference endTime = new AtomicReference<>(); - - private final AtomicReference queuedTime = new AtomicReference<>(); - private final AtomicReference analysisTime = new AtomicReference<>(); - private final AtomicReference distributedPlanningTime = new AtomicReference<>(); - - private final AtomicReference finishingStartNanos = new AtomicReference<>(); - private final AtomicReference finishingTime = new AtomicReference<>(); - - private final AtomicReference totalPlanningStartNanos = new AtomicReference<>(); - private final AtomicReference totalPlanningTime = new AtomicReference<>(); - - private final AtomicReference resourceWaitingStartNanos = new AtomicReference<>(); - private final AtomicReference resourceWaitingTime = new AtomicReference<>(); + private final QueryStateTimer queryStateTimer; private final StateMachine queryState; @@ -155,99 +137,95 @@ public class QueryStateMachine private final AtomicReference> output = new AtomicReference<>(Optional.empty()); private final StateMachine> finalQueryInfo; - private final AtomicReference resourceGroup = new AtomicReference<>(); + private final WarningCollector warningCollector; - private QueryStateMachine(QueryId queryId, String query, Session session, URI self, boolean autoCommit, TransactionManager transactionManager, Executor executor, Ticker ticker, Metadata metadata) + private QueryStateMachine( + String query, + Session session, + URI self, + Optional resourceGroup, + TransactionManager transactionManager, + Executor executor, + Ticker ticker, + Metadata metadata, + WarningCollector warningCollector) { - this.queryId = requireNonNull(queryId, "queryId is null"); this.query = requireNonNull(query, "query is null"); this.session = requireNonNull(session, "session is null"); + this.queryId = session.getQueryId(); this.self = requireNonNull(self, "self is null"); - this.autoCommit = autoCommit; + this.resourceGroup = requireNonNull(resourceGroup, "resourceGroup is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); - this.ticker = ticker; + this.queryStateTimer = new QueryStateTimer(ticker); this.metadata = requireNonNull(metadata, "metadata is null"); - this.createNanos = tickerNanos(); this.queryState = new StateMachine<>("query " + query, executor, QUEUED, TERMINAL_QUERY_STATES); this.finalQueryInfo = new StateMachine<>("finalQueryInfo-" + queryId, executor, Optional.empty()); this.outputManager = new QueryOutputManager(executor); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } /** * Created QueryStateMachines must be transitioned to terminal states to clean up resources. */ public static QueryStateMachine begin( - QueryId queryId, String query, Session session, URI self, + ResourceGroupId resourceGroup, boolean transactionControl, TransactionManager transactionManager, AccessControl accessControl, Executor executor, - Metadata metadata) + Metadata metadata, + WarningCollector warningCollector) { - return beginWithTicker(queryId, query, session, self, transactionControl, transactionManager, accessControl, executor, Ticker.systemTicker(), metadata); + return beginWithTicker( + query, + session, + self, + resourceGroup, + transactionControl, + transactionManager, + accessControl, + executor, + Ticker.systemTicker(), + metadata, + warningCollector); } static QueryStateMachine beginWithTicker( - QueryId queryId, String query, Session session, URI self, + ResourceGroupId resourceGroup, boolean transactionControl, TransactionManager transactionManager, AccessControl accessControl, Executor executor, Ticker ticker, - Metadata metadata) + Metadata metadata, + WarningCollector warningCollector) { - session.getTransactionId().ifPresent(transactionControl ? transactionManager::trySetActive : transactionManager::checkAndSetActive); - - Session querySession; - boolean autoCommit = !session.getTransactionId().isPresent() && !transactionControl; - if (autoCommit) { + // If there is not an existing transaction, begin an auto commit transaction + if (!session.getTransactionId().isPresent() && !transactionControl) { // TODO: make autocommit isolation level a session parameter TransactionId transactionId = transactionManager.beginTransaction(true); - querySession = session.beginTransactionId(transactionId, transactionManager, accessControl); - } - else { - querySession = session; + session = session.beginTransactionId(transactionId, transactionManager, accessControl); } - QueryStateMachine queryStateMachine = new QueryStateMachine(queryId, query, querySession, self, autoCommit, transactionManager, executor, ticker, metadata); - queryStateMachine.addStateChangeListener(newState -> { - log.debug("Query %s is %s", queryId, newState); - if (newState.isDone()) { - session.getTransactionId().ifPresent(transactionManager::trySetInactive); - } - }); - - return queryStateMachine; - } - - /** - * Create a QueryStateMachine that is already in a failed state. - */ - public static QueryStateMachine failed(QueryId queryId, String query, Session session, URI self, TransactionManager transactionManager, Executor executor, Metadata metadata, Throwable throwable) - { - return failedWithTicker(queryId, query, session, self, transactionManager, executor, Ticker.systemTicker(), metadata, throwable); - } + QueryStateMachine queryStateMachine = new QueryStateMachine( + query, + session, + self, + Optional.of(resourceGroup), + transactionManager, + executor, + ticker, + metadata, + warningCollector); + queryStateMachine.addStateChangeListener(newState -> QUERY_STATE_LOG.debug("Query %s is %s", queryStateMachine.getQueryId(), newState)); - static QueryStateMachine failedWithTicker( - QueryId queryId, - String query, - Session session, - URI self, - TransactionManager transactionManager, - Executor executor, - Ticker ticker, - Metadata metadata, - Throwable throwable) - { - QueryStateMachine queryStateMachine = new QueryStateMachine(queryId, query, session, self, false, transactionManager, executor, ticker, metadata); - queryStateMachine.transitionToFailed(throwable); return queryStateMachine; } @@ -261,11 +239,6 @@ public Session getSession() return session; } - public boolean isAutoCommit() - { - return autoCommit; - } - public long getPeakUserMemoryInBytes() { return peakUserMemory.get(); @@ -281,6 +254,11 @@ public long getPeakTaskTotalMemory() return peakTaskTotalMemory.get(); } + public WarningCollector getWarningCollector() + { + return warningCollector; + } + public void updateMemoryUsage(long deltaUserMemoryInBytes, long deltaTotalMemoryInBytes, long taskTotalMemoryInBytes) { currentUserMemory.addAndGet(deltaUserMemoryInBytes); @@ -290,23 +268,66 @@ public void updateMemoryUsage(long deltaUserMemoryInBytes, long deltaTotalMemory peakTaskTotalMemory.accumulateAndGet(taskTotalMemoryInBytes, Math::max); } - public void setResourceGroup(ResourceGroupId group) + public BasicQueryInfo getBasicQueryInfo(Optional rootStage) { - requireNonNull(group, "group is null"); - resourceGroup.compareAndSet(null, group); - } + // Query state must be captured first in order to provide a + // correct view of the query. For example, building this + // information, the query could finish, and the task states would + // never be visible. + QueryState state = queryState.get(); - public Optional getResourceGroup() - { - return Optional.ofNullable(resourceGroup.get()); - } + ErrorCode errorCode = null; + if (state == FAILED) { + ExecutionFailureInfo failureCause = this.failureCause.get(); + if (failureCause != null) { + errorCode = failureCause.getErrorCode(); + } + } - public QueryInfo getQueryInfoWithoutDetails() - { - return getQueryInfo(Optional.empty()); + BasicStageStats stageStats = rootStage.orElse(EMPTY_STAGE_STATS); + BasicQueryStats queryStats = new BasicQueryStats( + queryStateTimer.getCreateTime(), + getEndTime().orElse(null), + queryStateTimer.getQueuedTime(), + queryStateTimer.getElapsedTime(), + queryStateTimer.getExecutionTime(), + + stageStats.getTotalDrivers(), + stageStats.getQueuedDrivers(), + stageStats.getRunningDrivers(), + stageStats.getCompletedDrivers(), + + stageStats.getRawInputDataSize(), + stageStats.getRawInputPositions(), + + stageStats.getCumulativeUserMemory(), + stageStats.getUserMemoryReservation(), + stageStats.getTotalMemoryReservation(), + succinctBytes(getPeakUserMemoryInBytes()), + + stageStats.getTotalCpuTime(), + stageStats.getTotalScheduledTime(), + + stageStats.isFullyBlocked(), + stageStats.getBlockedReasons(), + stageStats.getProgressPercentage()); + + return new BasicQueryInfo( + queryId, + session.toSessionRepresentation(), + resourceGroup, + state, + memoryPool.get().getId(), + stageStats.isScheduled(), + self, + query, + queryStats, + errorCode == null ? null : errorCode.getType(), + errorCode); } - public QueryInfo getQueryInfo(Optional rootStage) + @VisibleForTesting + QueryInfo getQueryInfo(Optional rootStage) { // Query state must be captured first in order to provide a // correct view of the query. For example, building this @@ -314,25 +335,50 @@ public QueryInfo getQueryInfo(Optional rootStage) // never be visible. QueryState state = queryState.get(); - Duration elapsedTime; - if (endNanos.get() != 0) { - elapsedTime = new Duration(endNanos.get() - createNanos, NANOSECONDS); - } - else { - elapsedTime = nanosSince(createNanos); - } - - // don't report failure info is query is marked as success - FailureInfo failureInfo = null; + ExecutionFailureInfo failureCause = null; ErrorCode errorCode = null; if (state == FAILED) { - ExecutionFailureInfo failureCause = this.failureCause.get(); + failureCause = this.failureCause.get(); if (failureCause != null) { - failureInfo = failureCause.toFailureInfo(); errorCode = failureCause.getErrorCode(); } } + boolean completeInfo = getAllStages(rootStage).stream().allMatch(StageInfo::isCompleteInfo); + boolean isScheduled = isScheduled(rootStage); + + return new QueryInfo( + queryId, + session.toSessionRepresentation(), + state, + memoryPool.get().getId(), + isScheduled, + self, + outputManager.getQueryOutputInfo().map(QueryOutputInfo::getColumnNames).orElse(ImmutableList.of()), + query, + getQueryStats(rootStage), + Optional.ofNullable(setCatalog.get()), + Optional.ofNullable(setSchema.get()), + Optional.ofNullable(setPath.get()), + setSessionProperties, + resetSessionProperties, + addedPreparedStatements, + deallocatedPreparedStatements, + Optional.ofNullable(startedTransactionId.get()), + clearTransactionId.get(), + updateType.get(), + rootStage, + failureCause, + errorCode, + warningCollector.getWarnings(), + inputs.get(), + output.get(), + completeInfo, + resourceGroup); + } + + private QueryStats getQueryStats(Optional rootStage) + { int totalTasks = 0; int runningTasks = 0; int completedTasks = 0; @@ -349,7 +395,6 @@ public QueryInfo getQueryInfo(Optional rootStage) long totalScheduledTime = 0; long totalCpuTime = 0; - long totalUserTime = 0; long totalBlockedTime = 0; long rawInputDataSize = 0; @@ -387,7 +432,6 @@ public QueryInfo getQueryInfo(Optional rootStage) totalMemoryReservation += stageStats.getTotalMemoryReservation().toBytes(); totalScheduledTime += stageStats.getTotalScheduledTime().roundTo(MILLISECONDS); totalCpuTime += stageStats.getTotalCpuTime().roundTo(MILLISECONDS); - totalUserTime += stageStats.getTotalUserTime().roundTo(MILLISECONDS); totalBlockedTime += stageStats.getTotalBlockedTime().roundTo(MILLISECONDS); if (!stageInfo.getState().isDone()) { fullyBlocked &= stageStats.isFullyBlocked(); @@ -419,19 +463,20 @@ public QueryInfo getQueryInfo(Optional rootStage) boolean isScheduled = isScheduled(rootStage); - QueryStats queryStats = new QueryStats( - createTime, - executionStartTime.get(), - lastHeartbeat.get(), - endTime.get(), - - elapsedTime.convertToMostSuccinctTimeUnit(), - queuedTime.get(), - resourceWaitingTime.get(), - analysisTime.get(), - distributedPlanningTime.get(), - totalPlanningTime.get(), - finishingTime.get(), + return new QueryStats( + queryStateTimer.getCreateTime(), + getExecutionStartTime().orElse(null), + getLastHeartbeat(), + getEndTime().orElse(null), + + queryStateTimer.getElapsedTime(), + queryStateTimer.getQueuedTime(), + queryStateTimer.getResourceWaitingTime(), + queryStateTimer.getExecutionTime(), + queryStateTimer.getAnalysisTime(), + queryStateTimer.getDistributedPlanningTime(), + queryStateTimer.getPlanningTime(), + queryStateTimer.getFinishingTime(), totalTasks, runningTasks, @@ -454,7 +499,6 @@ public QueryInfo getQueryInfo(Optional rootStage) new Duration(totalScheduledTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalCpuTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), - new Duration(totalUserTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalBlockedTime, MILLISECONDS).convertToMostSuccinctTimeUnit(), fullyBlocked, blockedReasons, @@ -471,33 +515,6 @@ public QueryInfo getQueryInfo(Optional rootStage) stageGcStatistics.build(), operatorStatsSummary.build()); - - return new QueryInfo(queryId, - session.toSessionRepresentation(), - state, - memoryPool.get().getId(), - isScheduled, - self, - outputManager.getQueryOutputInfo().map(QueryOutputInfo::getColumnNames).orElse(ImmutableList.of()), - query, - queryStats, - Optional.ofNullable(setCatalog.get()), - Optional.ofNullable(setSchema.get()), - Optional.ofNullable(setPath.get()), - setSessionProperties, - resetSessionProperties, - addedPreparedStatements, - deallocatedPreparedStatements, - Optional.ofNullable(startedTransactionId.get()), - clearTransactionId.get(), - updateType.get(), - rootStage, - failureInfo, - errorCode, - inputs.get(), - output.get(), - completeInfo, - getResourceGroup()); } public VersionedMemoryPoolId getMemoryPool() @@ -635,60 +652,39 @@ public boolean isDone() public boolean transitionToWaitingForResources() { - queuedTime.compareAndSet(null, nanosSince(createNanos).convertToMostSuccinctTimeUnit()); - resourceWaitingStartNanos.compareAndSet(null, tickerNanos()); - return queryState.compareAndSet(QUEUED, WAITING_FOR_RESOURCES); + queryStateTimer.beginWaitingForResources(); + return queryState.setIf(WAITING_FOR_RESOURCES, currentState -> currentState.ordinal() < WAITING_FOR_RESOURCES.ordinal()); } public boolean transitionToPlanning() { - queuedTime.compareAndSet(null, nanosSince(createNanos).convertToMostSuccinctTimeUnit()); - resourceWaitingStartNanos.compareAndSet(null, tickerNanos()); - resourceWaitingTime.compareAndSet(null, nanosSince(resourceWaitingStartNanos.get()).convertToMostSuccinctTimeUnit()); - totalPlanningStartNanos.compareAndSet(null, tickerNanos()); - return queryState.setIf(PLANNING, currentState -> currentState == QUEUED || currentState == WAITING_FOR_RESOURCES); + queryStateTimer.beginPlanning(); + return queryState.setIf(PLANNING, currentState -> currentState.ordinal() < PLANNING.ordinal()); } public boolean transitionToStarting() { - queuedTime.compareAndSet(null, nanosSince(createNanos).convertToMostSuccinctTimeUnit()); - resourceWaitingStartNanos.compareAndSet(null, tickerNanos()); - resourceWaitingTime.compareAndSet(null, nanosSince(resourceWaitingStartNanos.get()).convertToMostSuccinctTimeUnit()); - totalPlanningStartNanos.compareAndSet(null, tickerNanos()); - totalPlanningTime.compareAndSet(null, nanosSince(totalPlanningStartNanos.get())); - - return queryState.setIf(STARTING, currentState -> currentState == QUEUED || currentState == PLANNING); + queryStateTimer.beginStarting(); + return queryState.setIf(STARTING, currentState -> currentState.ordinal() < STARTING.ordinal()); } public boolean transitionToRunning() { - queuedTime.compareAndSet(null, nanosSince(createNanos).convertToMostSuccinctTimeUnit()); - resourceWaitingStartNanos.compareAndSet(null, tickerNanos()); - resourceWaitingTime.compareAndSet(null, nanosSince(resourceWaitingStartNanos.get()).convertToMostSuccinctTimeUnit()); - totalPlanningStartNanos.compareAndSet(null, tickerNanos()); - totalPlanningTime.compareAndSet(null, nanosSince(totalPlanningStartNanos.get())); - executionStartTime.compareAndSet(null, DateTime.now()); - - return queryState.setIf(RUNNING, currentState -> currentState != RUNNING && currentState != FINISHING && !currentState.isDone()); + queryStateTimer.beginRunning(); + return queryState.setIf(RUNNING, currentState -> currentState.ordinal() < RUNNING.ordinal()); } public boolean transitionToFinishing() { - queuedTime.compareAndSet(null, nanosSince(createNanos).convertToMostSuccinctTimeUnit()); - resourceWaitingStartNanos.compareAndSet(null, tickerNanos()); - resourceWaitingTime.compareAndSet(null, nanosSince(resourceWaitingStartNanos.get()).convertToMostSuccinctTimeUnit()); - totalPlanningStartNanos.compareAndSet(null, tickerNanos()); - totalPlanningTime.compareAndSet(null, nanosSince(totalPlanningStartNanos.get())); - DateTime now = DateTime.now(); - executionStartTime.compareAndSet(null, now); - finishingStartNanos.compareAndSet(null, tickerNanos()); + queryStateTimer.beginFinishing(); if (!queryState.setIf(FINISHING, currentState -> currentState != FINISHING && !currentState.isDone())) { return false; } - if (autoCommit) { - ListenableFuture commitFuture = transactionManager.asyncCommit(session.getTransactionId().get()); + Optional transactionId = session.getTransactionId(); + if (transactionId.isPresent() && transactionManager.transactionExists(transactionId.get()) && transactionManager.isAutoCommit(transactionId.get())) { + ListenableFuture commitFuture = transactionManager.asyncCommit(transactionId.get()); Futures.addCallback(commitFuture, new FutureCallback() { @Override @@ -710,18 +706,18 @@ public void onFailure(Throwable throwable) return true; } - private boolean transitionToFinished() + private void transitionToFinished() { cleanupQueryQuietly(); - recordDoneStats(); + queryStateTimer.endQuery(); - return queryState.setIf(FINISHED, currentState -> !currentState.isDone()); + queryState.setIf(FINISHED, currentState -> !currentState.isDone()); } public boolean transitionToFailed(Throwable throwable) { cleanupQueryQuietly(); - recordDoneStats(); + queryStateTimer.endQuery(); // NOTE: The failure cause must be set before triggering the state change, so // listeners can observe the exception. This is safe because the failure cause @@ -731,11 +727,18 @@ public boolean transitionToFailed(Throwable throwable) boolean failed = queryState.setIf(FAILED, currentState -> !currentState.isDone()); if (failed) { - log.debug(throwable, "Query %s failed", queryId); - session.getTransactionId().ifPresent(autoCommit ? transactionManager::asyncAbort : transactionManager::fail); + QUERY_STATE_LOG.debug(throwable, "Query %s failed", queryId); + session.getTransactionId().ifPresent(transactionId -> { + if (transactionManager.isAutoCommit(transactionId)) { + transactionManager.asyncAbort(transactionId); + } + else { + transactionManager.fail(transactionId); + } + }); } else { - log.debug(throwable, "Failure after query %s finished", queryId); + QUERY_STATE_LOG.debug(throwable, "Failure after query %s finished", queryId); } return failed; @@ -744,7 +747,7 @@ public boolean transitionToFailed(Throwable throwable) public boolean transitionToCanceled() { cleanupQueryQuietly(); - recordDoneStats(); + queryStateTimer.endQuery(); // NOTE: The failure cause must be set before triggering the state change, so // listeners can observe the exception. This is safe because the failure cause @@ -753,7 +756,14 @@ public boolean transitionToCanceled() boolean canceled = queryState.setIf(FAILED, currentState -> !currentState.isDone()); if (canceled) { - session.getTransactionId().ifPresent(autoCommit ? transactionManager::asyncAbort : transactionManager::fail); + session.getTransactionId().ifPresent(transactionId -> { + if (transactionManager.isAutoCommit(transactionId)) { + transactionManager.asyncAbort(transactionId); + } + else { + transactionManager.fail(transactionId); + } + }); } return canceled; @@ -765,30 +775,25 @@ private void cleanupQueryQuietly() metadata.cleanupQuery(session); } catch (Throwable t) { - log.error("Error cleaning up query: %s", t); + QUERY_STATE_LOG.error("Error cleaning up query: %s", t); } } - private void recordDoneStats() - { - Duration durationSinceCreation = nanosSince(createNanos).convertToMostSuccinctTimeUnit(); - queuedTime.compareAndSet(null, durationSinceCreation); - resourceWaitingTime.compareAndSet(null, succinctNanos(0)); - totalPlanningStartNanos.compareAndSet(null, tickerNanos()); - totalPlanningTime.compareAndSet(null, nanosSince(totalPlanningStartNanos.get())); - DateTime now = DateTime.now(); - executionStartTime.compareAndSet(null, now); - finishingStartNanos.compareAndSet(null, tickerNanos()); - finishingTime.compareAndSet(null, nanosSince(finishingStartNanos.get())); - endTime.compareAndSet(null, now); - endNanos.compareAndSet(0, tickerNanos()); - } - + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ public void addStateChangeListener(StateChangeListener stateChangeListener) { queryState.addStateChangeListener(stateChangeListener); } + /** + * Add a listener for the final query info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. + */ public void addQueryInfoStateChangeListener(StateChangeListener stateChangeListener) { AtomicBoolean done = new AtomicBoolean(); @@ -798,7 +803,6 @@ public void addQueryInfoStateChangeListener(StateChangeListener state } }; finalQueryInfo.addStateChangeListener(fireOnceStateChangeListener); - fireOnceStateChangeListener.stateChanged(finalQueryInfo.get()); } public ListenableFuture getStateChange(QueryState currentState) @@ -808,17 +812,47 @@ public ListenableFuture getStateChange(QueryState currentState) public void recordHeartbeat() { - this.lastHeartbeat.set(DateTime.now()); + queryStateTimer.recordHeartbeat(); + } + + public void beginAnalysis() + { + queryStateTimer.beginAnalyzing(); + } + + public void endAnalysis() + { + queryStateTimer.endAnalysis(); + } + + public void beginDistributedPlanning() + { + queryStateTimer.beginDistributedPlanning(); + } + + public void endDistributedPlanning() + { + queryStateTimer.endDistributedPlanning(); + } + + public DateTime getCreateTime() + { + return queryStateTimer.getCreateTime(); + } + + public Optional getExecutionStartTime() + { + return queryStateTimer.getExecutionStartTime(); } - public void recordAnalysisTime(long analysisStart) + public DateTime getLastHeartbeat() { - analysisTime.compareAndSet(null, nanosSince(analysisStart).convertToMostSuccinctTimeUnit()); + return queryStateTimer.getLastHeartbeat(); } - public void recordDistributedPlanningTime(long distributedPlanningStart) + public Optional getEndTime() { - distributedPlanningTime.compareAndSet(null, nanosSince(distributedPlanningStart).convertToMostSuccinctTimeUnit()); + return queryStateTimer.getEndTime(); } private static boolean isScheduled(Optional rootStage) @@ -831,6 +865,14 @@ private static boolean isScheduled(Optional rootStage) .allMatch(state -> (state == StageState.RUNNING) || state.isDone()); } + public Optional getFailureInfo() + { + if (queryState.get() != FAILED) { + return Optional.empty(); + } + return Optional.ofNullable(this.failureCause.get()); + } + public Optional getFinalQueryInfo() { return finalQueryInfo.get(); @@ -853,8 +895,7 @@ public void pruneQueryInfo() } QueryInfo queryInfo = finalInfo.get(); - StageInfo outputStage = queryInfo.getOutputStage().get(); - StageInfo prunedOutputStage = new StageInfo( + Optional prunedOutputStage = queryInfo.getOutputStage().map(outputStage -> new StageInfo( outputStage.getStageId(), outputStage.getState(), outputStage.getSelf(), @@ -863,7 +904,7 @@ public void pruneQueryInfo() outputStage.getStageStats(), ImmutableList.of(), // Remove the tasks ImmutableList.of(), // Remove the substages - outputStage.getFailureCause()); + outputStage.getFailureCause())); QueryInfo prunedQueryInfo = new QueryInfo( queryInfo.getQueryId(), @@ -885,9 +926,10 @@ public void pruneQueryInfo() queryInfo.getStartedTransactionId(), queryInfo.isClearTransactionId(), queryInfo.getUpdateType(), - Optional.of(prunedOutputStage), + prunedOutputStage, queryInfo.getFailureInfo(), queryInfo.getErrorCode(), + queryInfo.getWarnings(), queryInfo.getInputs(), queryInfo.getOutput(), queryInfo.isCompleteInfo(), @@ -895,7 +937,7 @@ public void pruneQueryInfo() finalQueryInfo.compareAndSet(finalInfo, Optional.of(prunedQueryInfo)); } - private QueryStats pruneQueryStats(QueryStats queryStats) + private static QueryStats pruneQueryStats(QueryStats queryStats) { return new QueryStats( queryStats.getCreateTime(), @@ -905,6 +947,7 @@ private QueryStats pruneQueryStats(QueryStats queryStats) queryStats.getElapsedTime(), queryStats.getQueuedTime(), queryStats.getResourceWaitingTime(), + queryStats.getExecutionTime(), queryStats.getAnalysisTime(), queryStats.getDistributedPlanningTime(), queryStats.getTotalPlanningTime(), @@ -926,7 +969,6 @@ private QueryStats pruneQueryStats(QueryStats queryStats) queryStats.isScheduled(), queryStats.getTotalScheduledTime(), queryStats.getTotalCpuTime(), - queryStats.getTotalUserTime(), queryStats.getTotalBlockedTime(), queryStats.isFullyBlocked(), queryStats.getBlockedReasons(), @@ -941,16 +983,6 @@ private QueryStats pruneQueryStats(QueryStats queryStats) ImmutableList.of()); // Remove the operator summaries as OperatorInfo (especially ExchangeClientStatus) can hold onto a large amount of memory } - private long tickerNanos() - { - return ticker.read(); - } - - private Duration nanosSince(long start) - { - return succinctNanos(tickerNanos() - start); - } - public static class QueryOutputManager { private final Executor executor; diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java new file mode 100644 index 0000000000000..b974e1a2bfd43 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java @@ -0,0 +1,285 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.google.common.base.Ticker; +import io.airlift.units.Duration; +import org.joda.time.DateTime; + +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; + +import static io.airlift.units.Duration.succinctNanos; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +class QueryStateTimer +{ + private final Ticker ticker; + + private final DateTime createTime = DateTime.now(); + + private final long createNanos; + private final AtomicReference beginResourceWaitingNanos = new AtomicReference<>(); + private final AtomicReference beginPlanningNanos = new AtomicReference<>(); + private final AtomicReference beginFinishingNanos = new AtomicReference<>(); + private final AtomicReference endNanos = new AtomicReference<>(); + + private final AtomicReference queuedTime = new AtomicReference<>(); + private final AtomicReference resourceWaitingTime = new AtomicReference<>(); + private final AtomicReference executionTime = new AtomicReference<>(); + private final AtomicReference planningTime = new AtomicReference<>(); + private final AtomicReference finishingTime = new AtomicReference<>(); + + private final AtomicReference beginAnalysisNanos = new AtomicReference<>(); + private final AtomicReference analysisTime = new AtomicReference<>(); + + private final AtomicReference beginDistributedPlanningNanos = new AtomicReference<>(); + private final AtomicReference distributedPlanningTime = new AtomicReference<>(); + + private final AtomicReference lastHeartbeatNanos; + + public QueryStateTimer(Ticker ticker) + { + this.ticker = requireNonNull(ticker, "ticker is null"); + this.createNanos = tickerNanos(); + this.lastHeartbeatNanos = new AtomicReference<>(createNanos); + } + + // + // State transitions + // + + public void beginWaitingForResources() + { + beginWaitingForResources(tickerNanos()); + } + + private void beginWaitingForResources(long now) + { + queuedTime.compareAndSet(null, nanosSince(createNanos, now)); + beginResourceWaitingNanos.compareAndSet(null, now); + } + + public void beginPlanning() + { + beginPlanning(tickerNanos()); + } + + private void beginPlanning(long now) + { + beginWaitingForResources(now); + resourceWaitingTime.compareAndSet(null, nanosSince(beginResourceWaitingNanos, now)); + beginPlanningNanos.compareAndSet(null, now); + } + + public void beginStarting() + { + beginStarting(tickerNanos()); + } + + private void beginStarting(long now) + { + beginPlanning(now); + planningTime.compareAndSet(null, nanosSince(beginPlanningNanos, now)); + } + + public void beginRunning() + { + beginRunning(tickerNanos()); + } + + private void beginRunning(long now) + { + beginStarting(now); + } + + public void beginFinishing() + { + beginFinishing(tickerNanos()); + } + + private void beginFinishing(long now) + { + beginRunning(now); + beginFinishingNanos.compareAndSet(null, now); + } + + public void endQuery() + { + endQuery(tickerNanos()); + } + + private void endQuery(long now) + { + beginFinishing(now); + finishingTime.compareAndSet(null, nanosSince(beginFinishingNanos, now)); + executionTime.compareAndSet(null, nanosSince(beginPlanningNanos, now)); + endNanos.compareAndSet(null, now); + } + + // + // Additional timings + // + + public void beginAnalyzing() + { + beginAnalysisNanos.compareAndSet(null, tickerNanos()); + } + + public void endAnalysis() + { + analysisTime.compareAndSet(null, nanosSince(beginAnalysisNanos, tickerNanos())); + } + + public void beginDistributedPlanning() + { + beginDistributedPlanningNanos.compareAndSet(null, tickerNanos()); + } + + public void endDistributedPlanning() + { + distributedPlanningTime.compareAndSet(null, nanosSince(beginDistributedPlanningNanos, tickerNanos())); + } + + public void recordHeartbeat() + { + lastHeartbeatNanos.set(tickerNanos()); + } + + // + // Stats + // + + public DateTime getCreateTime() + { + return createTime; + } + + public Optional getExecutionStartTime() + { + return toDateTime(beginPlanningNanos); + } + + public Duration getElapsedTime() + { + if (endNanos.get() != null) { + return succinctNanos(endNanos.get() - createNanos); + } + return nanosSince(createNanos, tickerNanos()); + } + + public Duration getQueuedTime() + { + Duration queuedTime = this.queuedTime.get(); + if (queuedTime != null) { + return queuedTime; + } + + // if queue time is not set, the query is still queued + return getElapsedTime(); + } + + public Duration getResourceWaitingTime() + { + return getDuration(resourceWaitingTime, beginResourceWaitingNanos); + } + + public Duration getPlanningTime() + { + return getDuration(planningTime, beginPlanningNanos); + } + + public Duration getFinishingTime() + { + return getDuration(finishingTime, beginFinishingNanos); + } + + public Duration getExecutionTime() + { + return getDuration(executionTime, beginPlanningNanos); + } + + public Optional getEndTime() + { + return toDateTime(endNanos); + } + + public Duration getAnalysisTime() + { + return getDuration(analysisTime, beginAnalysisNanos); + } + + public Duration getDistributedPlanningTime() + { + return getDuration(distributedPlanningTime, beginDistributedPlanningNanos); + } + + public DateTime getLastHeartbeat() + { + return toDateTime(lastHeartbeatNanos.get()); + } + + // + // Helper methods + // + + private long tickerNanos() + { + return ticker.read(); + } + + private static Duration nanosSince(AtomicReference start, long end) + { + Long startNanos = start.get(); + if (startNanos == null) { + throw new IllegalStateException("Start time not set"); + } + return nanosSince(startNanos, end); + } + + private static Duration nanosSince(long start, long now) + { + return succinctNanos(now - start); + } + + private Duration getDuration(AtomicReference finalDuration, AtomicReference start) + { + Duration duration = finalDuration.get(); + if (duration != null) { + return duration; + } + Long startNanos = start.get(); + if (startNanos != null) { + return nanosSince(startNanos, tickerNanos()); + } + return new Duration(0, MILLISECONDS); + } + + private Optional toDateTime(AtomicReference instantNanos) + { + Long nanos = instantNanos.get(); + if (nanos == null) { + return Optional.empty(); + } + return Optional.of(toDateTime(nanos)); + } + + private DateTime toDateTime(long instantNanos) + { + long millisSinceCreate = NANOSECONDS.toMillis(instantNanos - createNanos); + return new DateTime(createTime.getMillis() + millisSinceCreate); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java index b067d68b32b2e..b47511267a48f 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java @@ -19,7 +19,6 @@ import com.facebook.presto.spi.eventlistener.StageGcStatistics; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import io.airlift.units.DataSize; @@ -33,11 +32,11 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; +import static io.airlift.units.DataSize.Unit.BYTE; import static io.airlift.units.DataSize.succinctBytes; -import static io.airlift.units.Duration.succinctNanos; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; public class QueryStats { @@ -49,6 +48,7 @@ public class QueryStats private final Duration elapsedTime; private final Duration queuedTime; + private final Duration executionTime; private final Duration resourceWaitingTime; private final Duration analysisTime; private final Duration distributedPlanningTime; @@ -75,7 +75,6 @@ public class QueryStats private final boolean scheduled; private final Duration totalScheduledTime; private final Duration totalCpuTime; - private final Duration totalUserTime; private final Duration totalBlockedTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -95,52 +94,6 @@ public class QueryStats private final List operatorSummaries; - @VisibleForTesting - public QueryStats() - { - this.createTime = null; - this.executionStartTime = null; - this.lastHeartbeat = null; - this.endTime = null; - this.elapsedTime = null; - this.queuedTime = null; - this.resourceWaitingTime = null; - this.analysisTime = null; - this.distributedPlanningTime = null; - this.totalPlanningTime = null; - this.finishingTime = null; - this.totalTasks = 0; - this.runningTasks = 0; - this.blockedDrivers = 0; - this.completedTasks = 0; - this.totalDrivers = 0; - this.queuedDrivers = 0; - this.runningDrivers = 0; - this.completedDrivers = 0; - this.cumulativeUserMemory = 0.0; - this.userMemoryReservation = null; - this.totalMemoryReservation = null; - this.peakUserMemoryReservation = null; - this.peakTotalMemoryReservation = null; - this.peakTaskTotalMemory = null; - this.scheduled = false; - this.totalScheduledTime = null; - this.totalCpuTime = null; - this.totalUserTime = null; - this.totalBlockedTime = null; - this.fullyBlocked = false; - this.blockedReasons = ImmutableSet.of(); - this.rawInputDataSize = null; - this.rawInputPositions = 0; - this.processedInputDataSize = null; - this.processedInputPositions = 0; - this.outputDataSize = null; - this.outputPositions = 0; - this.physicalWrittenDataSize = null; - this.stageGcStatistics = null; - this.operatorSummaries = null; - } - @JsonCreator public QueryStats( @JsonProperty("createTime") DateTime createTime, @@ -151,6 +104,7 @@ public QueryStats( @JsonProperty("elapsedTime") Duration elapsedTime, @JsonProperty("queuedTime") Duration queuedTime, @JsonProperty("resourceWaitingTime") Duration resourceWaitingTime, + @JsonProperty("executionTime") Duration executionTime, @JsonProperty("analysisTime") Duration analysisTime, @JsonProperty("distributedPlanningTime") Duration distributedPlanningTime, @JsonProperty("totalPlanningTime") Duration totalPlanningTime, @@ -176,7 +130,6 @@ public QueryStats( @JsonProperty("scheduled") boolean scheduled, @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("totalCpuTime") Duration totalCpuTime, - @JsonProperty("totalUserTime") Duration totalUserTime, @JsonProperty("totalBlockedTime") Duration totalBlockedTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @@ -201,13 +154,14 @@ public QueryStats( this.lastHeartbeat = requireNonNull(lastHeartbeat, "lastHeartbeat is null"); this.endTime = endTime; - this.elapsedTime = elapsedTime; - this.queuedTime = queuedTime; - this.resourceWaitingTime = resourceWaitingTime; - this.analysisTime = analysisTime; - this.distributedPlanningTime = distributedPlanningTime; - this.totalPlanningTime = totalPlanningTime; - this.finishingTime = finishingTime; + this.elapsedTime = requireNonNull(elapsedTime, "elapsedTime is null"); + this.queuedTime = requireNonNull(queuedTime, "queuedTime is null"); + this.resourceWaitingTime = requireNonNull(resourceWaitingTime, "resourceWaitingTime is null"); + this.executionTime = requireNonNull(executionTime, "executionTime is null"); + this.analysisTime = requireNonNull(analysisTime, "analysisTime is null"); + this.distributedPlanningTime = requireNonNull(distributedPlanningTime, "distributedPlanningTime is null"); + this.totalPlanningTime = requireNonNull(totalPlanningTime, "totalPlanningTime is null"); + this.finishingTime = requireNonNull(finishingTime, "finishingTime is null"); checkArgument(totalTasks >= 0, "totalTasks is negative"); this.totalTasks = totalTasks; @@ -236,7 +190,6 @@ public QueryStats( this.scheduled = scheduled; this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); - this.totalUserTime = requireNonNull(totalUserTime, "totalUserTime is null"); this.totalBlockedTime = requireNonNull(totalBlockedTime, "totalBlockedTime is null"); this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -260,6 +213,53 @@ public QueryStats( this.operatorSummaries = ImmutableList.copyOf(requireNonNull(operatorSummaries, "operatorSummaries is null")); } + public static QueryStats immediateFailureQueryStats() + { + DateTime now = DateTime.now(); + return new QueryStats( + now, + now, + now, + now, + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + new DataSize(0, BYTE), + new DataSize(0, BYTE), + new DataSize(0, BYTE), + new DataSize(0, BYTE), + new DataSize(0, BYTE), + false, + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + false, + ImmutableSet.of(), + new DataSize(0, BYTE), + 0, + new DataSize(0, BYTE), + 0, + new DataSize(0, BYTE), + 0, + new DataSize(0, BYTE), + ImmutableList.of(), + ImmutableList.of()); + } + @JsonProperty public DateTime getCreateTime() { @@ -300,22 +300,13 @@ public Duration getResourceWaitingTime() @JsonProperty public Duration getQueuedTime() { - if (queuedTime == null) { - // counter-intuitively, this means that the query is still queued - return elapsedTime; - } return queuedTime; } @JsonProperty public Duration getExecutionTime() { - if (queuedTime == null) { - // counter-intuitively, this means that the query is still queued - return new Duration(0, NANOSECONDS); - } - long executionNanos = (long) elapsedTime.getValue(NANOSECONDS) - (long) queuedTime.getValue(NANOSECONDS); - return succinctNanos(Math.max(0, executionNanos)); + return executionTime; } @JsonProperty @@ -444,12 +435,6 @@ public Duration getTotalCpuTime() return totalCpuTime; } - @JsonProperty - public Duration getTotalUserTime() - { - return totalUserTime; - } - @JsonProperty public Duration getTotalBlockedTime() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java new file mode 100644 index 0000000000000..9ef467d8f78ad --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java @@ -0,0 +1,300 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryTracker.TrackedQuery; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.QueryId; +import com.google.common.collect.ImmutableList; +import io.airlift.log.Logger; +import io.airlift.units.Duration; +import org.joda.time.DateTime; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import java.util.Collection; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.SystemSessionProperties.getQueryMaxExecutionTime; +import static com.facebook.presto.SystemSessionProperties.getQueryMaxRunTime; +import static com.facebook.presto.spi.StandardErrorCode.ABANDONED_QUERY; +import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_TIME_LIMIT; +import static com.facebook.presto.spi.StandardErrorCode.SERVER_SHUTTING_DOWN; +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +@ThreadSafe +public class QueryTracker +{ + private static final Logger log = Logger.get(QueryTracker.class); + + private final int maxQueryHistory; + private final Duration minQueryExpireAge; + + private final ConcurrentMap queries = new ConcurrentHashMap<>(); + private final Queue expirationQueue = new LinkedBlockingQueue<>(); + + private final Duration clientTimeout; + + private final ScheduledExecutorService queryManagementExecutor; + + @GuardedBy("this") + private ScheduledFuture backgroundTask; + + public QueryTracker(QueryManagerConfig queryManagerConfig, ScheduledExecutorService queryManagementExecutor) + { + requireNonNull(queryManagerConfig, "queryManagerConfig is null"); + this.minQueryExpireAge = queryManagerConfig.getMinQueryExpireAge(); + this.maxQueryHistory = queryManagerConfig.getMaxQueryHistory(); + this.clientTimeout = queryManagerConfig.getClientTimeout(); + + this.queryManagementExecutor = requireNonNull(queryManagementExecutor, "queryManagementExecutor is null"); + } + + public synchronized void start() + { + checkState(backgroundTask == null, "QueryTracker already started"); + backgroundTask = queryManagementExecutor.scheduleWithFixedDelay(() -> { + try { + failAbandonedQueries(); + } + catch (Throwable e) { + log.error(e, "Error cancelling abandoned queries"); + } + + try { + enforceTimeLimits(); + } + catch (Throwable e) { + log.error(e, "Error enforcing query timeout limits"); + } + + try { + removeExpiredQueries(); + } + catch (Throwable e) { + log.error(e, "Error removing expired queries"); + } + + try { + pruneExpiredQueries(); + } + catch (Throwable e) { + log.error(e, "Error pruning expired queries"); + } + }, 1, 1, TimeUnit.SECONDS); + } + + public void stop() + { + synchronized (this) { + if (backgroundTask != null) { + backgroundTask.cancel(true); + } + } + + boolean queryCancelled = false; + for (T trackedQuery : queries.values()) { + if (trackedQuery.isDone()) { + continue; + } + + log.info("Server shutting down. Query %s has been cancelled", trackedQuery.getQueryId()); + trackedQuery.fail(new PrestoException(SERVER_SHUTTING_DOWN, "Server is shutting down. Query " + trackedQuery.getQueryId() + " has been cancelled")); + queryCancelled = true; + } + if (queryCancelled) { + try { + TimeUnit.SECONDS.sleep(5); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public Collection getAllQueries() + { + return ImmutableList.copyOf(queries.values()); + } + + public T getQuery(QueryId queryId) + throws NoSuchElementException + { + return tryGetQuery(queryId) + .orElseThrow(NoSuchElementException::new); + } + + public Optional tryGetQuery(QueryId queryId) + { + requireNonNull(queryId, "queryId is null"); + return Optional.ofNullable(queries.get(queryId)); + } + + public boolean addQuery(T execution) + { + return queries.putIfAbsent(execution.getQueryId(), execution) == null; + } + + /** + * Query is finished and expiration should begin. + */ + public void expireQuery(QueryId queryId) + { + tryGetQuery(queryId) + .ifPresent(expirationQueue::add); + } + + /** + * Enforce query max runtime/execution time limits + */ + private void enforceTimeLimits() + { + for (T query : queries.values()) { + if (query.isDone()) { + continue; + } + Duration queryMaxRunTime = getQueryMaxRunTime(query.getSession()); + Duration queryMaxExecutionTime = getQueryMaxExecutionTime(query.getSession()); + Optional executionStartTime = query.getExecutionStartTime(); + DateTime createTime = query.getCreateTime(); + if (executionStartTime.isPresent() && executionStartTime.get().plus(queryMaxExecutionTime.toMillis()).isBeforeNow()) { + query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "Query exceeded the maximum execution time limit of " + queryMaxExecutionTime)); + } + if (createTime.plus(queryMaxRunTime.toMillis()).isBeforeNow()) { + query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "Query exceeded maximum time limit of " + queryMaxRunTime)); + } + } + } + + /** + * Prune extraneous info from old queries + */ + private void pruneExpiredQueries() + { + if (expirationQueue.size() <= maxQueryHistory) { + return; + } + + int count = 0; + // we're willing to keep full info for up to maxQueryHistory queries + for (T query : expirationQueue) { + if (expirationQueue.size() - count <= maxQueryHistory) { + break; + } + query.pruneInfo(); + count++; + } + } + + /** + * Remove completed queries after a waiting period + */ + private void removeExpiredQueries() + { + DateTime timeHorizon = DateTime.now().minus(minQueryExpireAge.toMillis()); + + // we're willing to keep queries beyond timeHorizon as long as we have fewer than maxQueryHistory + while (expirationQueue.size() > maxQueryHistory) { + T query = expirationQueue.peek(); + if (query == null) { + return; + } + + // expirationQueue is FIFO based on query end time. Stop when we see the + // first query that's too young to expire + Optional endTime = query.getEndTime(); + if (!endTime.isPresent()) { + // this shouldn't happen but it is better to be safe here + continue; + } + if (endTime.get().isAfter(timeHorizon)) { + return; + } + + // only expire them if they are older than minQueryExpireAge. We need to keep them + // around for a while in case clients come back asking for status + QueryId queryId = query.getQueryId(); + + log.debug("Remove query %s", queryId); + queries.remove(queryId); + expirationQueue.remove(query); + } + } + + private void failAbandonedQueries() + { + for (T query : queries.values()) { + try { + if (query.isDone()) { + continue; + } + + if (isAbandoned(query)) { + log.info("Failing abandoned query %s", query.getQueryId()); + query.fail(new PrestoException( + ABANDONED_QUERY, + format("Query %s has not been accessed since %s: currentTime %s", + query.getQueryId(), + query.getLastHeartbeat(), + DateTime.now()))); + } + } + catch (RuntimeException e) { + log.error(e, "Exception failing abandoned query %s", query.getQueryId()); + } + } + } + + private boolean isAbandoned(T query) + { + DateTime oldestAllowedHeartbeat = DateTime.now().minus(clientTimeout.toMillis()); + DateTime lastHeartbeat = query.getLastHeartbeat(); + + return lastHeartbeat != null && lastHeartbeat.isBefore(oldestAllowedHeartbeat); + } + + public interface TrackedQuery + { + QueryId getQueryId(); + + boolean isDone(); + + Session getSession(); + + DateTime getCreateTime(); + + Optional getExecutionStartTime(); + + DateTime getLastHeartbeat(); + + Optional getEndTime(); + + void fail(Throwable cause); + + // XXX: This should be removed when the client protocol is improved, so that we don't need to hold onto so much query history + void pruneInfo(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/RemoteTask.java b/presto-main/src/main/java/com/facebook/presto/execution/RemoteTask.java index 9671c93683814..26066a3247b1f 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/RemoteTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/RemoteTask.java @@ -40,8 +40,21 @@ public interface RemoteTask void setOutputBuffers(OutputBuffers outputBuffers); + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ void addStateChangeListener(StateChangeListener stateChangeListener); + /** + * Add a listener for the final task info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ + void addFinalTaskInfoListener(StateChangeListener stateChangeListener); + ListenableFuture whenSplitQueueHasSpace(int threshold); void cancel(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SetSessionTask.java b/presto-main/src/main/java/com/facebook/presto/execution/SetSessionTask.java index 1809a9d85b5eb..6f744b30eefde 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SetSessionTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SetSessionTask.java @@ -85,6 +85,8 @@ public ListenableFuture execute(SetSession statement, TransactionManager tran String value = serializeSessionProperty(type, objectValue); // verify the SQL value can be decoded by the property + propertyMetadata.decode(objectValue); + stateMachine.addSetSessionProperties(propertyName.toString(), value); return immediateFuture(null); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java index 491dbf20ec385..755699e35c85c 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java @@ -18,18 +18,23 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.CostCalculator; +import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.scheduler.ExecutionPolicy; import com.facebook.presto.execution.scheduler.NodeScheduler; import com.facebook.presto.execution.scheduler.SplitSchedulerStats; import com.facebook.presto.execution.scheduler.SqlQueryScheduler; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.failureDetector.FailureDetector; -import com.facebook.presto.memory.ClusterMemoryManager; import com.facebook.presto.memory.VersionedMemoryPoolId; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.operator.ForScheduler; import com.facebook.presto.security.AccessControl; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -37,7 +42,6 @@ import com.facebook.presto.split.SplitSource; import com.facebook.presto.sql.analyzer.Analysis; import com.facebook.presto.sql.analyzer.Analyzer; -import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.analyzer.QueryExplainer; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.DistributedExecutionPlanner; @@ -54,8 +58,6 @@ import com.facebook.presto.sql.planner.SubPlan; import com.facebook.presto.sql.planner.optimizations.PlanOptimizer; import com.facebook.presto.sql.tree.Explain; -import com.facebook.presto.sql.tree.Expression; -import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.transaction.TransactionManager; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; @@ -63,6 +65,7 @@ import io.airlift.log.Logger; import io.airlift.units.DataSize; import io.airlift.units.Duration; +import org.joda.time.DateTime; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; @@ -79,9 +82,14 @@ import static com.facebook.presto.OutputBuffers.BROADCAST_PARTITION_ID; import static com.facebook.presto.OutputBuffers.createInitialEmptyOutputBuffers; +import static com.facebook.presto.execution.scheduler.SqlQueryScheduler.createSqlQueryScheduler; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfInstanceOf; +import static io.airlift.concurrent.MoreFutures.addExceptionCallback; +import static io.airlift.concurrent.MoreFutures.addSuccessCallback; +import static io.airlift.units.DataSize.Unit.BYTE; +import static io.airlift.units.DataSize.succinctBytes; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; @@ -95,6 +103,7 @@ public class SqlQueryExecution private final QueryStateMachine stateMachine; + private final ClusterSizeMonitor clusterSizeMonitor; private final Metadata metadata; private final SqlParser sqlParser; private final SplitManager splitManager; @@ -115,12 +124,16 @@ public class SqlQueryExecution private final ExecutionPolicy executionPolicy; private final SplitSchedulerStats schedulerStats; private final Analysis analysis; + private final StatsCalculator statsCalculator; + private final CostCalculator costCalculator; - public SqlQueryExecution(QueryId queryId, + private SqlQueryExecution( String query, Session session, URI self, - Statement statement, + ResourceGroupId resourceGroup, + PreparedQuery preparedQuery, + ClusterSizeMonitor clusterSizeMonitor, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, @@ -139,10 +152,13 @@ public SqlQueryExecution(QueryId queryId, NodeTaskMap nodeTaskMap, QueryExplainer queryExplainer, ExecutionPolicy executionPolicy, - List parameters, - SplitSchedulerStats schedulerStats) + SplitSchedulerStats schedulerStats, + StatsCalculator statsCalculator, + CostCalculator costCalculator, + WarningCollector warningCollector) { - try (SetThreadName ignored = new SetThreadName("Query-%s", queryId)) { + try (SetThreadName ignored = new SetThreadName("Query-%s", session.getQueryId())) { + this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.splitManager = requireNonNull(splitManager, "splitManager is null"); @@ -157,23 +173,43 @@ public SqlQueryExecution(QueryId queryId, this.nodeTaskMap = requireNonNull(nodeTaskMap, "nodeTaskMap is null"); this.executionPolicy = requireNonNull(executionPolicy, "executionPolicy is null"); this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); + this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); + this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); checkArgument(scheduleSplitBatchSize > 0, "scheduleSplitBatchSize must be greater than 0"); this.scheduleSplitBatchSize = scheduleSplitBatchSize; - requireNonNull(queryId, "queryId is null"); requireNonNull(query, "query is null"); requireNonNull(session, "session is null"); requireNonNull(self, "self is null"); - this.stateMachine = QueryStateMachine.begin(queryId, query, session, self, false, transactionManager, accessControl, queryExecutor, metadata); + this.stateMachine = QueryStateMachine.begin( + query, + session, + self, + resourceGroup, + false, + transactionManager, + accessControl, + queryExecutor, + metadata, + warningCollector); // analyze query - Analyzer analyzer = new Analyzer(stateMachine.getSession(), metadata, sqlParser, accessControl, Optional.of(queryExplainer), parameters); - this.analysis = analyzer.analyze(statement); + requireNonNull(preparedQuery, "preparedQuery is null"); + Analyzer analyzer = new Analyzer( + stateMachine.getSession(), + metadata, + sqlParser, + accessControl, + Optional.of(queryExplainer), + preparedQuery.getParameters(), + warningCollector); + this.analysis = analyzer.analyze(preparedQuery.getStatement()); stateMachine.setUpdateType(analysis.getUpdateType()); // when the query finishes cache the final query info, and clear the reference to the output stage + AtomicReference queryScheduler = this.queryScheduler; stateMachine.addStateChangeListener(state -> { if (!state.isDone()) { return; @@ -203,7 +239,7 @@ public void setMemoryPool(VersionedMemoryPoolId poolId) } @Override - public long getUserMemoryReservation() + public DataSize getUserMemoryReservation() { // acquire reference to scheduler before checking finalQueryInfo, because // state change listener sets finalQueryInfo and then clears scheduler when @@ -211,16 +247,16 @@ public long getUserMemoryReservation() SqlQueryScheduler scheduler = queryScheduler.get(); Optional finalQueryInfo = stateMachine.getFinalQueryInfo(); if (finalQueryInfo.isPresent()) { - return finalQueryInfo.get().getQueryStats().getUserMemoryReservation().toBytes(); + return finalQueryInfo.get().getQueryStats().getUserMemoryReservation(); } if (scheduler == null) { - return 0; + return new DataSize(0, BYTE); } - return scheduler.getUserMemoryReservation(); + return succinctBytes(scheduler.getUserMemoryReservation()); } @Override - public long getTotalMemoryReservation() + public DataSize getTotalMemoryReservation() { // acquire reference to scheduler before checking finalQueryInfo, because // state change listener sets finalQueryInfo and then clears scheduler when @@ -228,12 +264,36 @@ public long getTotalMemoryReservation() SqlQueryScheduler scheduler = queryScheduler.get(); Optional finalQueryInfo = stateMachine.getFinalQueryInfo(); if (finalQueryInfo.isPresent()) { - return finalQueryInfo.get().getQueryStats().getTotalMemoryReservation().toBytes(); + return finalQueryInfo.get().getQueryStats().getTotalMemoryReservation(); } if (scheduler == null) { - return 0; + return new DataSize(0, BYTE); } - return scheduler.getTotalMemoryReservation(); + return succinctBytes(scheduler.getTotalMemoryReservation()); + } + + @Override + public DateTime getCreateTime() + { + return stateMachine.getCreateTime(); + } + + @Override + public Optional getExecutionStartTime() + { + return stateMachine.getExecutionStartTime(); + } + + @Override + public DateTime getLastHeartbeat() + { + return stateMachine.getLastHeartbeat(); + } + + @Override + public Optional getEndTime() + { + return stateMachine.getEndTime(); } @Override @@ -251,27 +311,29 @@ public Duration getTotalCpuTime() } @Override - public Session getSession() + public BasicQueryInfo getBasicQueryInfo() { - return stateMachine.getSession(); + return stateMachine.getFinalQueryInfo() + .map(BasicQueryInfo::new) + .orElseGet(() -> stateMachine.getBasicQueryInfo(Optional.ofNullable(queryScheduler.get()).map(SqlQueryScheduler::getBasicStageStats))); } - public void startWaitingForResources() + @Override + public void start() { - try (SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())) { - try { - // transition to waiting for resources - stateMachine.transitionToWaitingForResources(); - } - catch (Throwable e) { - fail(e); - throwIfInstanceOf(e, Error.class); - } + if (stateMachine.transitionToWaitingForResources()) { + waitForMinimumWorkers(); } } - @Override - public void start() + private void waitForMinimumWorkers() + { + ListenableFuture minimumWorkerFuture = clusterSizeMonitor.waitForMinimumWorkers(); + addSuccessCallback(minimumWorkerFuture, this::startExecution); + addExceptionCallback(minimumWorkerFuture, stateMachine::transitionToFailed); + } + + private void startExecution() { try (SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())) { try { @@ -317,6 +379,18 @@ public void addStateChangeListener(StateChangeListener stateChangeLi } } + @Override + public Session getSession() + { + return stateMachine.getSession(); + } + + @Override + public Optional getErrorCode() + { + return stateMachine.getFailureInfo().map(ExecutionFailureInfo::getErrorCode); + } + @Override public void addFinalQueryInfoListener(StateChangeListener stateChangeListener) { @@ -336,11 +410,11 @@ private PlanRoot analyzeQuery() private PlanRoot doAnalyzeQuery() { // time analysis phase - long analysisStart = System.nanoTime(); + stateMachine.beginAnalysis(); // plan query PlanNodeIdAllocator idAllocator = new PlanNodeIdAllocator(); - LogicalPlanner logicalPlanner = new LogicalPlanner(stateMachine.getSession(), planOptimizers, idAllocator, metadata, sqlParser); + LogicalPlanner logicalPlanner = new LogicalPlanner(stateMachine.getSession(), planOptimizers, idAllocator, metadata, sqlParser, statsCalculator, costCalculator, stateMachine.getWarningCollector()); Plan plan = logicalPlanner.plan(analysis); queryPlan.set(plan); @@ -353,16 +427,16 @@ private PlanRoot doAnalyzeQuery() stateMachine.setOutput(output); // fragment the plan - SubPlan fragmentedPlan = planFragmenter.createSubPlans(stateMachine.getSession(), metadata, nodePartitioningManager, plan, false); + SubPlan fragmentedPlan = planFragmenter.createSubPlans(stateMachine.getSession(), plan, false); // record analysis time - stateMachine.recordAnalysisTime(analysisStart); + stateMachine.endAnalysis(); boolean explainAnalyze = analysis.getStatement() instanceof Explain && ((Explain) analysis.getStatement()).isAnalyze(); return new PlanRoot(fragmentedPlan, !explainAnalyze, extractConnectors(analysis)); } - private Set extractConnectors(Analysis analysis) + private static Set extractConnectors(Analysis analysis) { ImmutableSet.Builder connectors = ImmutableSet.builder(); @@ -381,12 +455,12 @@ private Set extractConnectors(Analysis analysis) private void planDistribution(PlanRoot plan) { // time distribution planning - long distributedPlanningStart = System.nanoTime(); + stateMachine.beginDistributedPlanning(); // plan the execution on the active nodes DistributedExecutionPlanner distributedPlanner = new DistributedExecutionPlanner(splitManager); StageExecutionPlan outputStageExecutionPlan = distributedPlanner.plan(plan.getRoot(), stateMachine.getSession()); - stateMachine.recordDistributedPlanningTime(distributedPlanningStart); + stateMachine.endDistributedPlanning(); // ensure split sources are closed stateMachine.addStateChangeListener(state -> { @@ -409,7 +483,7 @@ private void planDistribution(PlanRoot plan) .withNoMoreBufferIds(); // build the stage execution objects (this doesn't schedule execution) - SqlQueryScheduler scheduler = new SqlQueryScheduler( + SqlQueryScheduler scheduler = createSqlQueryScheduler( stateMachine, locationFactory, outputStageExecutionPlan, @@ -480,6 +554,12 @@ public void fail(Throwable cause) stateMachine.transitionToFailed(cause); } + @Override + public boolean isDone() + { + return getState().isDone(); + } + @Override public void addOutputInfoListener(Consumer listener) { @@ -519,12 +599,7 @@ public QueryInfo getQueryInfo() // the query finishes. SqlQueryScheduler scheduler = queryScheduler.get(); - Optional finalQueryInfo = stateMachine.getFinalQueryInfo(); - if (finalQueryInfo.isPresent()) { - return finalQueryInfo.get(); - } - - return buildQueryInfo(scheduler); + return stateMachine.getFinalQueryInfo().orElseGet(() -> buildQueryInfo(scheduler)); } } @@ -535,17 +610,6 @@ public QueryState getState() } @Override - public Optional getResourceGroup() - { - return stateMachine.getResourceGroup(); - } - - @Override - public void setResourceGroup(ResourceGroupId resourceGroupId) - { - stateMachine.setResourceGroup(resourceGroupId); - } - public Plan getQueryPlan() { return queryPlan.get(); @@ -618,12 +682,12 @@ public static class SqlQueryExecutionFactory private final FailureDetector failureDetector; private final NodeTaskMap nodeTaskMap; private final Map executionPolicies; - private final ClusterMemoryManager clusterMemoryManager; - private final DataSize preAllocateMemoryThreshold; + private final ClusterSizeMonitor clusterSizeMonitor; + private final StatsCalculator statsCalculator; + private final CostCalculator costCalculator; @Inject SqlQueryExecutionFactory(QueryManagerConfig config, - FeaturesConfig featuresConfig, Metadata metadata, AccessControl accessControl, SqlParser sqlParser, @@ -642,7 +706,9 @@ public static class SqlQueryExecutionFactory QueryExplainer queryExplainer, Map executionPolicies, SplitSchedulerStats schedulerStats, - ClusterMemoryManager clusterMemoryManager) + ClusterSizeMonitor clusterSizeMonitor, + StatsCalculator statsCalculator, + CostCalculator costCalculator) { requireNonNull(config, "config is null"); this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); @@ -664,24 +730,31 @@ public static class SqlQueryExecutionFactory this.nodeTaskMap = requireNonNull(nodeTaskMap, "nodeTaskMap is null"); this.queryExplainer = requireNonNull(queryExplainer, "queryExplainer is null"); this.executionPolicies = requireNonNull(executionPolicies, "schedulerPolicies is null"); - this.clusterMemoryManager = requireNonNull(clusterMemoryManager, "clusterMemoryManager is null"); - this.preAllocateMemoryThreshold = requireNonNull(featuresConfig, "featuresConfig is null").getPreAllocateMemoryThreshold(); + this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.planOptimizers = planOptimizers.get(); + this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); + this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); } @Override - public QueryExecution createQueryExecution(QueryId queryId, String query, Session session, Statement statement, List parameters) + public QueryExecution createQueryExecution( + String query, + Session session, + PreparedQuery preparedQuery, + ResourceGroupId resourceGroup, + WarningCollector warningCollector) { String executionPolicyName = SystemSessionProperties.getExecutionPolicy(session); ExecutionPolicy executionPolicy = executionPolicies.get(executionPolicyName); checkArgument(executionPolicy != null, "No execution policy %s", executionPolicy); SqlQueryExecution execution = new SqlQueryExecution( - queryId, query, session, - locationFactory.createQueryLocation(queryId), - statement, + locationFactory.createQueryLocation(session.getQueryId()), + resourceGroup, + preparedQuery, + clusterSizeMonitor, transactionManager, metadata, accessControl, @@ -700,13 +773,10 @@ public QueryExecution createQueryExecution(QueryId queryId, String query, Sessio nodeTaskMap, queryExplainer, executionPolicy, - parameters, - schedulerStats); - - if (preAllocateMemoryThreshold.toBytes() > 0 && session.getResourceEstimates().getPeakMemory().isPresent() && - session.getResourceEstimates().getPeakMemory().get().compareTo(preAllocateMemoryThreshold) >= 0) { - return new MemoryAwareQueryExecution(clusterMemoryManager, execution); - } + schedulerStats, + statsCalculator, + costCalculator, + warningCollector); return execution; } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java index 8156339bad17e..6dd0b51ea0cfd 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java @@ -15,45 +15,37 @@ import com.facebook.presto.ExceededCpuLimitException; import com.facebook.presto.Session; -import com.facebook.presto.SystemSessionProperties; -import com.facebook.presto.event.query.QueryMonitor; +import com.facebook.presto.event.QueryMonitor; import com.facebook.presto.execution.QueryExecution.QueryExecutionFactory; import com.facebook.presto.execution.QueryExecution.QueryOutputInfo; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.execution.warnings.WarningCollectorFactory; import com.facebook.presto.memory.ClusterMemoryManager; -import com.facebook.presto.metadata.InternalNodeManager; -import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.SessionPropertyManager; +import com.facebook.presto.security.AccessControl; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.SessionContext; +import com.facebook.presto.server.SessionPropertyDefaults; import com.facebook.presto.server.SessionSupplier; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.QueryType; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.resourceGroups.SelectionContext; import com.facebook.presto.spi.resourceGroups.SelectionCriteria; import com.facebook.presto.sql.SqlEnvironmentConfig; import com.facebook.presto.sql.SqlPath; -import com.facebook.presto.sql.analyzer.SemanticException; -import com.facebook.presto.sql.parser.ParsingException; -import com.facebook.presto.sql.parser.ParsingOptions; -import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.Plan; -import com.facebook.presto.sql.tree.Execute; -import com.facebook.presto.sql.tree.Explain; -import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.transaction.TransactionManager; -import com.facebook.presto.util.StatementUtils; +import com.facebook.presto.version.EmbedVersion; import com.google.common.collect.Ordering; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.concurrent.ThreadPoolExecutorMBean; import io.airlift.log.Logger; import io.airlift.units.Duration; -import org.joda.time.DateTime; import org.weakref.jmx.Flatten; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; @@ -63,18 +55,13 @@ import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; -import java.net.URI; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -82,27 +69,18 @@ import java.util.function.Consumer; import static com.facebook.presto.SystemSessionProperties.getQueryMaxCpuTime; -import static com.facebook.presto.execution.ParameterExtractor.getParameterCount; import static com.facebook.presto.execution.QueryState.RUNNING; -import static com.facebook.presto.spi.NodeState.ACTIVE; -import static com.facebook.presto.spi.StandardErrorCode.ABANDONED_QUERY; -import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_TIME_LIMIT; +import static com.facebook.presto.execution.QueryStateMachine.QUERY_STATE_LOG; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; -import static com.facebook.presto.spi.StandardErrorCode.SERVER_SHUTTING_DOWN; -import static com.facebook.presto.spi.StandardErrorCode.SERVER_STARTING_UP; -import static com.facebook.presto.sql.ParsingUtil.createParsingOptions; -import static com.facebook.presto.sql.analyzer.ConstantExpressionVerifier.verifyExpressionIsConstant; -import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_PARAMETER_USAGE; -import static com.facebook.presto.sql.parser.ParsingOptions.DecimalLiteralTreatment.AS_DECIMAL; +import static com.facebook.presto.util.Failures.toFailure; +import static com.facebook.presto.util.StatementUtils.getQueryType; +import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static io.airlift.concurrent.Threads.threadsNamed; -import static io.airlift.units.Duration.nanosSince; import static java.lang.String.format; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptySet; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newCachedThreadPool; @@ -112,27 +90,19 @@ public class SqlQueryManager { private static final Logger log = Logger.get(SqlQueryManager.class); - private final SqlParser sqlParser; + private final QueryPreparer queryPreparer; + private final EmbedVersion embedVersion; private final ExecutorService queryExecutor; private final ThreadPoolExecutorMBean queryExecutorMBean; - private final ResourceGroupManager resourceGroupManager; + private final ResourceGroupManager resourceGroupManager; private final ClusterMemoryManager memoryManager; private final Optional path; - private final boolean isIncludeCoordinator; - private final int maxQueryHistory; - private final Duration minQueryExpireAge; private final int maxQueryLength; - private final int initializationRequiredWorkers; - private final Duration initializationTimeout; - private final long initialNanos; private final Duration maxQueryCpuTime; - private final ConcurrentMap queries = new ConcurrentHashMap<>(); - private final Queue expirationQueue = new LinkedBlockingQueue<>(); - - private final Duration clientTimeout; + private final QueryTracker queryTracker; private final ScheduledExecutorService queryManagementExecutor; private final ThreadPoolExecutorMBean queryManagementExecutorMBean; @@ -140,40 +110,45 @@ public class SqlQueryManager private final QueryMonitor queryMonitor; private final LocationFactory locationFactory; - private final Metadata metadata; private final TransactionManager transactionManager; + private final AccessControl accessControl; private final QueryIdGenerator queryIdGenerator; private final SessionSupplier sessionSupplier; + private final SessionPropertyDefaults sessionPropertyDefaults; - private final InternalNodeManager internalNodeManager; + private final ClusterSizeMonitor clusterSizeMonitor; private final Map, QueryExecutionFactory> executionFactories; private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); - private final AtomicBoolean acceptQueries = new AtomicBoolean(); + private final WarningCollectorFactory warningCollectorFactory; @Inject public SqlQueryManager( - SqlParser sqlParser, + QueryPreparer queryPreparer, + EmbedVersion embedVersion, NodeSchedulerConfig nodeSchedulerConfig, QueryManagerConfig queryManagerConfig, SqlEnvironmentConfig sqlEnvironmentConfig, QueryMonitor queryMonitor, - ResourceGroupManager resourceGroupManager, + ResourceGroupManager resourceGroupManager, ClusterMemoryManager memoryManager, LocationFactory locationFactory, TransactionManager transactionManager, + AccessControl accessControl, QueryIdGenerator queryIdGenerator, SessionSupplier sessionSupplier, - InternalNodeManager internalNodeManager, + SessionPropertyDefaults sessionPropertyDefaults, + ClusterSizeMonitor clusterSizeMonitor, Map, QueryExecutionFactory> executionFactories, - Metadata metadata) + WarningCollectorFactory warningCollectorFactory) { - this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); + this.queryPreparer = requireNonNull(queryPreparer, "queryPreparer is null"); + this.embedVersion = requireNonNull(embedVersion, "embedVersion is null"); this.executionFactories = requireNonNull(executionFactories, "executionFactories is null"); this.queryExecutor = newCachedThreadPool(threadsNamed("query-scheduler-%s")); @@ -188,78 +163,44 @@ public SqlQueryManager( this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); - this.metadata = requireNonNull(metadata, "transactionManager is null"); + this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.queryIdGenerator = requireNonNull(queryIdGenerator, "queryIdGenerator is null"); this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); + this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); - this.internalNodeManager = requireNonNull(internalNodeManager, "internalNodeManager is null"); + this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.path = sqlEnvironmentConfig.getPath(); - this.isIncludeCoordinator = nodeSchedulerConfig.isIncludeCoordinator(); - this.minQueryExpireAge = queryManagerConfig.getMinQueryExpireAge(); - this.maxQueryHistory = queryManagerConfig.getMaxQueryHistory(); - this.clientTimeout = queryManagerConfig.getClientTimeout(); this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); this.maxQueryCpuTime = queryManagerConfig.getQueryMaxCpuTime(); - this.initializationRequiredWorkers = queryManagerConfig.getInitializationRequiredWorkers(); - this.initializationTimeout = queryManagerConfig.getInitializationTimeout(); - this.initialNanos = System.nanoTime(); + + this.warningCollectorFactory = requireNonNull(warningCollectorFactory, "warningCollectorFactory is null"); queryManagementExecutor = Executors.newScheduledThreadPool(queryManagerConfig.getQueryManagerExecutorPoolSize(), threadsNamed("query-management-%s")); queryManagementExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) queryManagementExecutor); + + this.queryTracker = new QueryTracker<>(queryManagerConfig, queryManagementExecutor); } @PostConstruct public void start() { - queryManagementExecutor.scheduleWithFixedDelay(new Runnable() - { - @Override - public void run() - { - try { - failAbandonedQueries(); - } - catch (Throwable e) { - log.error(e, "Error cancelling abandoned queries"); - } - - try { - enforceMemoryLimits(); - } - catch (Throwable e) { - log.error(e, "Error enforcing memory limits"); - } - - try { - enforceTimeLimits(); - } - catch (Throwable e) { - log.error(e, "Error enforcing query timeout limits"); - } - - try { - enforceCpuLimits(); - } - catch (Throwable e) { - log.error(e, "Error enforcing query CPU time limits"); - } - - try { - removeExpiredQueries(); - } - catch (Throwable e) { - log.error(e, "Error removing expired queries"); - } + queryTracker.start(); + queryManagementExecutor.scheduleWithFixedDelay(() -> { + try { + enforceMemoryLimits(); + } + catch (Throwable e) { + log.error(e, "Error enforcing memory limits"); + } - try { - pruneExpiredQueries(); - } - catch (Throwable e) { - log.error(e, "Error pruning expired queries"); - } + try { + enforceCpuLimits(); + } + catch (Throwable e) { + log.error(e, "Error enforcing query CPU time limits"); } }, 1, 1, TimeUnit.SECONDS); } @@ -267,35 +208,18 @@ public void run() @PreDestroy public void stop() { - boolean queryCancelled = false; - for (QueryExecution queryExecution : queries.values()) { - if (queryExecution.getState().isDone()) { - continue; - } - - log.info("Server shutting down. Query %s has been cancelled", queryExecution.getQueryId()); - queryExecution.fail(new PrestoException(SERVER_SHUTTING_DOWN, "Server is shutting down. Query " + queryExecution.getQueryId() + " has been cancelled")); - queryCancelled = true; - } - if (queryCancelled) { - try { - TimeUnit.SECONDS.sleep(5); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } + queryTracker.stop(); queryManagementExecutor.shutdownNow(); queryExecutor.shutdownNow(); } @Override - public List getAllQueryInfo() + public List getQueries() { - return queries.values().stream() + return queryTracker.getAllQueries().stream() .map(queryExecution -> { try { - return queryExecution.getQueryInfo(); + return queryExecution.getBasicQueryInfo(); } catch (RuntimeException ignored) { return null; @@ -310,7 +234,7 @@ public void addOutputInfoListener(QueryId queryId, Consumer lis { requireNonNull(listener, "listener is null"); - getQuery(queryId).addOutputInfoListener(listener); + queryTracker.getQuery(queryId).addOutputInfoListener(listener); } @Override @@ -318,47 +242,49 @@ public void addStateChangeListener(QueryId queryId, StateChangeListener getStateChange(QueryId queryId, QueryState currentState) { - return tryGetQuery(queryId) + return queryTracker.tryGetQuery(queryId) .map(query -> query.getStateChange(currentState)) .orElseGet(() -> immediateFailedFuture(new NoSuchElementException())); } @Override - public QueryInfo getQueryInfo(QueryId queryId) + public BasicQueryInfo getQueryInfo(QueryId queryId) { - return getQuery(queryId).getQueryInfo(); + return queryTracker.getQuery(queryId).getBasicQueryInfo(); } @Override - public Optional getQueryResourceGroup(QueryId queryId) + public QueryInfo getFullQueryInfo(QueryId queryId) { - return tryGetQuery(queryId) - .flatMap(QueryExecution::getResourceGroup); + return queryTracker.getQuery(queryId).getQueryInfo(); } - @Override public Plan getQueryPlan(QueryId queryId) { - return getQuery(queryId).getQueryPlan(); + return queryTracker.getQuery(queryId).getQueryPlan(); + } + + public void addFinalQueryInfoListener(QueryId queryId, StateChangeListener stateChangeListener) + { + queryTracker.getQuery(queryId).addFinalQueryInfoListener(stateChangeListener); } @Override - public Optional getQueryState(QueryId queryId) + public QueryState getQueryState(QueryId queryId) { - return tryGetQuery(queryId) - .map(QueryExecution::getState); + return queryTracker.getQuery(queryId).getState(); } @Override public void recordHeartbeat(QueryId queryId) { - tryGetQuery(queryId) + queryTracker.tryGetQuery(queryId) .ifPresent(QueryExecution::recordHeartbeat); } @@ -372,43 +298,32 @@ public QueryId createQueryId() public ListenableFuture createQuery(QueryId queryId, SessionContext sessionContext, String query) { QueryCreationFuture queryCreationFuture = new QueryCreationFuture(); - queryExecutor.submit(() -> { + queryExecutor.submit(embedVersion.embedVersion(() -> { try { - createQueryInternal(queryId, sessionContext, query); + createQueryInternal(queryId, sessionContext, query, resourceGroupManager); queryCreationFuture.set(null); } catch (Throwable e) { queryCreationFuture.setException(e); } - }); + })); return queryCreationFuture; } - private void createQueryInternal(QueryId queryId, SessionContext sessionContext, String query) + private void createQueryInternal(QueryId queryId, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) { requireNonNull(queryId, "queryId is null"); requireNonNull(sessionContext, "sessionFactory is null"); requireNonNull(query, "query is null"); checkArgument(!query.isEmpty(), "query must not be empty string"); - checkArgument(!queries.containsKey(queryId), "query %s already exists", queryId); + checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); Session session = null; - SelectionContext selectionContext; + SelectionContext selectionContext = null; QueryExecution queryExecution; - Statement statement; + PreparedQuery preparedQuery; try { - if (!acceptQueries.get()) { - int activeWorkerCount = internalNodeManager.getNodes(ACTIVE).size(); - if (!isIncludeCoordinator) { - activeWorkerCount--; - } - if (nanosSince(initialNanos).compareTo(initializationTimeout) < 0 && activeWorkerCount < initializationRequiredWorkers) { - throw new PrestoException( - SERVER_STARTING_UP, - String.format("Cluster is still initializing, there are insufficient active worker nodes (%s) to run query", activeWorkerCount)); - } - acceptQueries.set(true); - } + clusterSizeMonitor.verifyInitialMinimumWorkersRequirement(); if (query.length() > maxQueryLength) { int queryLength = query.length(); @@ -416,7 +331,14 @@ private void createQueryInternal(QueryId queryId, SessionContext sessionContext, throw new PrestoException(QUERY_TEXT_TOO_LARGE, format("Query text length (%s) exceeds the maximum length (%s)", queryLength, maxQueryLength)); } - Optional queryType = getQueryType(query); + // decode session + session = sessionSupplier.createSession(queryId, sessionContext); + + // prepare query + preparedQuery = queryPreparer.prepareQuery(session, query); + + // select resource group + Optional queryType = getQueryType(preparedQuery.getStatement().getClass()).map(Enum::name); selectionContext = resourceGroupManager.selectGroup(new SelectionCriteria( sessionContext.getIdentity().getPrincipal().isPresent(), sessionContext.getIdentity().getUser(), @@ -425,28 +347,34 @@ private void createQueryInternal(QueryId queryId, SessionContext sessionContext, sessionContext.getResourceEstimates(), queryType)); - session = sessionSupplier.createSession(queryId, sessionContext, queryType, selectionContext.getResourceGroupId()); - Statement wrappedStatement = sqlParser.createStatement(query, createParsingOptions(session)); - statement = unwrapExecuteStatement(wrappedStatement, sqlParser, session); - List parameters = wrappedStatement instanceof Execute ? ((Execute) wrappedStatement).getParameters() : emptyList(); - validateParameters(statement, parameters); - QueryExecutionFactory queryExecutionFactory = executionFactories.get(statement.getClass()); + // apply system defaults for query + session = sessionPropertyDefaults.newSessionWithDefaultProperties(session, queryType, selectionContext.getResourceGroupId()); + + // mark existing transaction as active + transactionManager.activateTransaction(session, isTransactionControlStatement(preparedQuery.getStatement()), accessControl); + + // create query execution + QueryExecutionFactory queryExecutionFactory = executionFactories.get(preparedQuery.getStatement().getClass()); if (queryExecutionFactory == null) { - throw new PrestoException(NOT_SUPPORTED, "Unsupported statement type: " + statement.getClass().getSimpleName()); + throw new PrestoException(NOT_SUPPORTED, "Unsupported statement type: " + preparedQuery.getStatement().getClass().getSimpleName()); } - if (statement instanceof Explain && ((Explain) statement).isAnalyze()) { - Statement innerStatement = ((Explain) statement).getStatement(); - Optional innerQueryType = StatementUtils.getQueryType(innerStatement.getClass()); - if (!innerQueryType.isPresent() || innerQueryType.get() == QueryType.DATA_DEFINITION) { - throw new PrestoException(NOT_SUPPORTED, "EXPLAIN ANALYZE doesn't support statement type: " + innerStatement.getClass().getSimpleName()); + queryExecution = queryExecutionFactory.createQueryExecution( + query, + session, + preparedQuery, + selectionContext.getResourceGroupId(), + warningCollectorFactory.create()); + + // mark existing transaction as inactive + queryExecution.addStateChangeListener(newState -> { + if (newState.isDone()) { + queryExecution.getSession().getTransactionId().ifPresent(transactionManager::trySetInactive); } - } - queryExecution = queryExecutionFactory.createQueryExecution(queryId, query, session, statement, parameters); + }); } - catch (ParsingException | PrestoException | SemanticException e) { + catch (RuntimeException e) { // This is intentionally not a method, since after the state change listener is registered // it's not safe to do any of this, and we had bugs before where people reused this code in a method - URI self = locationFactory.createQueryLocation(queryId); // if session creation failed, create a minimal session object if (session == null) { @@ -456,86 +384,64 @@ private void createQueryInternal(QueryId queryId, SessionContext sessionContext, .setPath(new SqlPath(Optional.empty())) .build(); } + QUERY_STATE_LOG.debug(e, "Query %s failed", session.getQueryId()); + + // query failure fails the transaction + session.getTransactionId().ifPresent(transactionManager::fail); + QueryExecution execution = new FailedQueryExecution( - queryId, - query, - Optional.empty(), session, - self, - transactionManager, + query, + locationFactory.createQueryLocation(queryId), + Optional.ofNullable(selectionContext).map(SelectionContext::getResourceGroupId), queryExecutor, - metadata, e); try { - queries.putIfAbsent(queryId, execution); + queryTracker.addQuery(execution); - QueryInfo queryInfo = execution.getQueryInfo(); + BasicQueryInfo queryInfo = execution.getBasicQueryInfo(); queryMonitor.queryCreatedEvent(queryInfo); - queryMonitor.queryCompletedEvent(queryInfo); + queryMonitor.queryImmediateFailureEvent(queryInfo, toFailure(e)); stats.queryQueued(); stats.queryStarted(); stats.queryStopped(); - stats.queryFinished(queryInfo); + stats.queryFinished(execution.getQueryInfo()); } finally { // execution MUST be added to the expiration queue or there will be a leak - expirationQueue.add(execution); + queryTracker.expireQuery(queryId); } return; } - QueryInfo queryInfo = queryExecution.getQueryInfo(); - queryMonitor.queryCreatedEvent(queryInfo); + queryMonitor.queryCreatedEvent(queryExecution.getBasicQueryInfo()); queryExecution.addFinalQueryInfoListener(finalQueryInfo -> { try { - QueryInfo info = queryExecution.getQueryInfo(); - stats.queryFinished(info); - queryMonitor.queryCompletedEvent(info); + stats.queryFinished(finalQueryInfo); + queryMonitor.queryCompletedEvent(finalQueryInfo); } finally { // execution MUST be added to the expiration queue or there will be a leak - expirationQueue.add(queryExecution); + queryTracker.expireQuery(queryId); } }); addStatsListeners(queryExecution); - if (queries.putIfAbsent(queryId, queryExecution) != null) { + if (!queryTracker.addQuery(queryExecution)) { // query already created, so just exit return; } // start the query in the background - resourceGroupManager.submit(statement, queryExecution, selectionContext, queryExecutor); - } - - private Optional getQueryType(String query) - { - Statement statement = sqlParser.createStatement(query, new ParsingOptions(AS_DECIMAL)); - return StatementUtils.getQueryType(statement.getClass()).map(Enum::name); - } - - public static Statement unwrapExecuteStatement(Statement statement, SqlParser sqlParser, Session session) - { - if ((!(statement instanceof Execute))) { - return statement; - } - - String sql = session.getPreparedStatementFromExecute((Execute) statement); - return sqlParser.createStatement(sql, createParsingOptions(session)); - } - - public static void validateParameters(Statement node, List parameterValues) - { - int parameterCount = getParameterCount(node); - if (parameterValues.size() != parameterCount) { - throw new SemanticException(INVALID_PARAMETER_USAGE, node, "Incorrect number of parameters: expected %s but found %s", parameterCount, parameterValues.size()); + try { + resourceGroupManager.submit(preparedQuery.getStatement(), queryExecution, selectionContext, queryExecutor); } - for (Expression expression : parameterValues) { - verifyExpressionIsConstant(emptySet(), expression); + catch (Throwable e) { + failQuery(queryId, e); } } @@ -544,7 +450,7 @@ public void failQuery(QueryId queryId, Throwable cause) { requireNonNull(cause, "cause is null"); - tryGetQuery(queryId) + queryTracker.tryGetQuery(queryId) .ifPresent(query -> query.fail(cause)); } @@ -553,7 +459,7 @@ public void cancelQuery(QueryId queryId) { log.debug("Cancel query %s", queryId); - tryGetQuery(queryId) + queryTracker.tryGetQuery(queryId) .ifPresent(QueryExecution::cancelQuery); } @@ -564,7 +470,7 @@ public void cancelStage(StageId stageId) log.debug("Cancel stage %s", stageId); - tryGetQuery(stageId.getQueryId()) + queryTracker.tryGetQuery(stageId.getQueryId()) .ifPresent(query -> query.cancelStage(stageId)); } @@ -583,7 +489,7 @@ public ThreadPoolExecutorMBean getExecutor() return queryExecutorMBean; } - @Managed(description = "Query garbage collector executor") + @Managed(description = "Query query management executor") @Nested public ThreadPoolExecutorMBean getManagementExecutor() { @@ -593,42 +499,20 @@ public ThreadPoolExecutorMBean getManagementExecutor() /** * Enforce memory limits at the query level */ - public void enforceMemoryLimits() + private void enforceMemoryLimits() { - List runningQueries = queries.values().stream() + List runningQueries = queryTracker.getAllQueries().stream() .filter(query -> query.getState() == RUNNING) .collect(toImmutableList()); - memoryManager.process(runningQueries, this::getAllQueryInfo); - } - - /** - * Enforce query max runtime/execution time limits - */ - public void enforceTimeLimits() - { - for (QueryExecution query : queries.values()) { - if (query.getState().isDone()) { - continue; - } - Duration queryMaxRunTime = SystemSessionProperties.getQueryMaxRunTime(query.getSession()); - Duration queryMaxExecutionTime = SystemSessionProperties.getQueryMaxExecutionTime(query.getSession()); - DateTime executionStartTime = query.getQueryInfo().getQueryStats().getExecutionStartTime(); - DateTime createTime = query.getQueryInfo().getQueryStats().getCreateTime(); - if (executionStartTime != null && executionStartTime.plus(queryMaxExecutionTime.toMillis()).isBeforeNow()) { - query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "Query exceeded the maximum execution time limit of " + queryMaxExecutionTime)); - } - if (createTime.plus(queryMaxRunTime.toMillis()).isBeforeNow()) { - query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "Query exceeded maximum time limit of " + queryMaxRunTime)); - } - } + memoryManager.process(runningQueries, this::getQueries); } /** * Enforce query CPU time limits */ - public void enforceCpuLimits() + private void enforceCpuLimits() { - for (QueryExecution query : queries.values()) { + for (QueryExecution query : queryTracker.getAllQueries()) { Duration cpuTime = query.getTotalCpuTime(); Duration sessionLimit = getQueryMaxCpuTime(query.getSession()); Duration limit = Ordering.natural().min(maxQueryCpuTime, sessionLimit); @@ -638,81 +522,6 @@ public void enforceCpuLimits() } } - /** - * Prune extraneous info from old queries - */ - private void pruneExpiredQueries() - { - if (expirationQueue.size() <= maxQueryHistory) { - return; - } - - int count = 0; - // we're willing to keep full info for up to maxQueryHistory queries - for (QueryExecution query : expirationQueue) { - if (expirationQueue.size() - count <= maxQueryHistory) { - break; - } - query.pruneInfo(); - count++; - } - } - - /** - * Remove completed queries after a waiting period - */ - private void removeExpiredQueries() - { - DateTime timeHorizon = DateTime.now().minus(minQueryExpireAge.toMillis()); - - // we're willing to keep queries beyond timeHorizon as long as we have fewer than maxQueryHistory - while (expirationQueue.size() > maxQueryHistory) { - QueryInfo queryInfo = expirationQueue.peek().getQueryInfo(); - - // expirationQueue is FIFO based on query end time. Stop when we see the - // first query that's too young to expire - if (queryInfo.getQueryStats().getEndTime().isAfter(timeHorizon)) { - return; - } - - // only expire them if they are older than minQueryExpireAge. We need to keep them - // around for a while in case clients come back asking for status - QueryId queryId = queryInfo.getQueryId(); - - log.debug("Remove query %s", queryId); - queries.remove(queryId); - expirationQueue.remove(); - } - } - - public void failAbandonedQueries() - { - for (QueryExecution queryExecution : queries.values()) { - try { - QueryInfo queryInfo = queryExecution.getQueryInfo(); - if (queryInfo.getState().isDone()) { - continue; - } - - if (isAbandoned(queryInfo)) { - log.info("Failing abandoned query %s", queryExecution.getQueryId()); - queryExecution.fail(new PrestoException(ABANDONED_QUERY, format("Query %s has not been accessed since %s: currentTime %s", queryInfo.getQueryId(), queryInfo.getQueryStats().getLastHeartbeat(), DateTime.now()))); - } - } - catch (RuntimeException e) { - log.error(e, "Exception failing abandoned query %s", queryExecution.getQueryId()); - } - } - } - - private boolean isAbandoned(QueryInfo queryInfo) - { - DateTime oldestAllowedHeartbeat = DateTime.now().minus(clientTimeout.toMillis()); - DateTime lastHeartbeat = queryInfo.getQueryStats().getLastHeartbeat(); - - return lastHeartbeat != null && lastHeartbeat.isBefore(oldestAllowedHeartbeat); - } - private void addStatsListeners(QueryExecution queryExecution) { Object lock = new Object(); @@ -728,12 +537,6 @@ private void addStatsListeners(QueryExecution queryExecution) } } }); - synchronized (lock) { - // Need to do this check in case the state changed before we added the previous state change listener - if (queryExecution.getState() == RUNNING && !started.getAndSet(true)) { - stats.queryStarted(); - } - } AtomicBoolean stopped = new AtomicBoolean(); queryExecution.addStateChangeListener(newValue -> { @@ -743,24 +546,6 @@ private void addStatsListeners(QueryExecution queryExecution) } } }); - synchronized (lock) { - // Need to do this check in case the state changed before we added the previous state change listener - if (queryExecution.getState().isDone() && !stopped.getAndSet(true) && started.get()) { - stats.queryStopped(); - } - } - } - - private QueryExecution getQuery(QueryId queryId) - { - return tryGetQuery(queryId) - .orElseThrow(NoSuchElementException::new); - } - - private Optional tryGetQuery(QueryId queryId) - { - requireNonNull(queryId, "queryId is null"); - return Optional.ofNullable(queries.get(queryId)); } private static class QueryCreationFuture diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlStageExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlStageExecution.java index e1d5625dcc800..16f3e6282aaf4 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlStageExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlStageExecution.java @@ -22,14 +22,12 @@ import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.StandardErrorCode; import com.facebook.presto.split.RemoteSplit; import com.facebook.presto.sql.planner.PlanFragment; import com.facebook.presto.sql.planner.plan.PlanFragmentId; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.RemoteSourceNode; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; @@ -37,6 +35,7 @@ import com.google.common.collect.Sets; import io.airlift.units.Duration; +import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; import java.net.URI; @@ -46,6 +45,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -59,6 +59,7 @@ import static com.facebook.presto.failureDetector.FailureDetector.State.GONE; import static com.facebook.presto.operator.ExchangeOperator.REMOTE_CONNECTOR_ID; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.REMOTE_HOST_GONE; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -80,20 +81,30 @@ public final class SqlStageExecution private final Map exchangeSources; private final Map> tasks = new ConcurrentHashMap<>(); + + @GuardedBy("this") private final AtomicInteger nextTaskId = new AtomicInteger(); + @GuardedBy("this") private final Set allTasks = newConcurrentHashSet(); + @GuardedBy("this") private final Set finishedTasks = newConcurrentHashSet(); + @GuardedBy("this") + private final Set tasksWithFinalInfo = newConcurrentHashSet(); + @GuardedBy("this") private final AtomicBoolean splitsScheduled = new AtomicBoolean(); + @GuardedBy("this") private final Multimap sourceTasks = HashMultimap.create(); + @GuardedBy("this") private final Set completeSources = newConcurrentHashSet(); + @GuardedBy("this") private final Set completeSourceFragments = newConcurrentHashSet(); private final AtomicReference outputBuffers = new AtomicReference<>(); private final ListenerManager> completedLifespansChangeListeners = new ListenerManager<>(); - public SqlStageExecution( + public static SqlStageExecution createSqlStageExecution( StageId stageId, URI location, PlanFragment fragment, @@ -105,21 +116,28 @@ public SqlStageExecution( FailureDetector failureDetector, SplitSchedulerStats schedulerStats) { - this(new StageStateMachine( - requireNonNull(stageId, "stageId is null"), - requireNonNull(location, "location is null"), - requireNonNull(session, "session is null"), - requireNonNull(fragment, "fragment is null"), - requireNonNull(executor, "executor is null"), - requireNonNull(schedulerStats, "schedulerStats is null")), + requireNonNull(stageId, "stageId is null"); + requireNonNull(location, "location is null"); + requireNonNull(fragment, "fragment is null"); + requireNonNull(remoteTaskFactory, "remoteTaskFactory is null"); + requireNonNull(session, "session is null"); + requireNonNull(nodeTaskMap, "nodeTaskMap is null"); + requireNonNull(executor, "executor is null"); + requireNonNull(failureDetector, "failureDetector is null"); + requireNonNull(schedulerStats, "schedulerStats is null"); + + SqlStageExecution sqlStageExecution = new SqlStageExecution( + new StageStateMachine(stageId, location, session, fragment, executor, schedulerStats), remoteTaskFactory, nodeTaskMap, summarizeTaskInfo, executor, failureDetector); + sqlStageExecution.initialize(); + return sqlStageExecution; } - public SqlStageExecution(StageStateMachine stateMachine, RemoteTaskFactory remoteTaskFactory, NodeTaskMap nodeTaskMap, boolean summarizeTaskInfo, Executor executor, FailureDetector failureDetector) + private SqlStageExecution(StageStateMachine stateMachine, RemoteTaskFactory remoteTaskFactory, NodeTaskMap nodeTaskMap, boolean summarizeTaskInfo, Executor executor, FailureDetector failureDetector) { this.stateMachine = stateMachine; this.remoteTaskFactory = requireNonNull(remoteTaskFactory, "remoteTaskFactory is null"); @@ -137,6 +155,12 @@ public SqlStageExecution(StageStateMachine stateMachine, RemoteTaskFactory remot this.exchangeSources = fragmentToExchangeSource.build(); } + // this is a separate method to ensure that the `this` reference is not leaked during construction + private void initialize() + { + stateMachine.addStateChangeListener(newState -> checkAllTaskFinal()); + } + public StageId getStageId() { return stateMachine.getStageId(); @@ -147,11 +171,26 @@ public StageState getState() return stateMachine.getState(); } + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. + */ public void addStateChangeListener(StateChangeListener stateChangeListener) { stateMachine.addStateChangeListener(stateChangeListener); } + /** + * Add a listener for the final stage info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ + public void addFinalStageInfoListener(StateChangeListener stateChangeListener) + { + stateMachine.addFinalStageInfoListener(stateChangeListener); + } + public void addCompletedDriverGroupsChangedListener(Consumer> newlyCompletedDriverGroupConsumer) { completedLifespansChangeListeners.addListener(newlyCompletedDriverGroupConsumer); @@ -233,13 +272,21 @@ public synchronized Duration getTotalCpuTime() return new Duration(millis, TimeUnit.MILLISECONDS); } + public BasicStageStats getBasicStageStats() + { + return stateMachine.getBasicStageStats(this::getAllTaskInfo); + } + public StageInfo getStageInfo() { - return stateMachine.getStageInfo( - () -> getAllTasks().stream() - .map(RemoteTask::getTaskInfo) - .collect(toImmutableList()), - ImmutableList::of); + return stateMachine.getStageInfo(this::getAllTaskInfo); + } + + private Iterable getAllTaskInfo() + { + return getAllTasks().stream() + .map(RemoteTask::getTaskInfo) + .collect(toImmutableList()); } public synchronized void addExchangeLocations(PlanFragmentId fragmentId, Set sourceTasks, boolean noMoreExchangeLocations) @@ -312,12 +359,15 @@ public List getAllTasks() .collect(toImmutableList()); } - public synchronized RemoteTask scheduleTask(Node node, int partition, OptionalInt totalPartitions) + public synchronized Optional scheduleTask(Node node, int partition, OptionalInt totalPartitions) { requireNonNull(node, "node is null"); + if (stateMachine.getState().isDone()) { + return Optional.empty(); + } checkState(!splitsScheduled.get(), "scheduleTask can not be called once splits have been scheduled"); - return scheduleTask(node, new TaskId(stateMachine.getStageId(), partition), ImmutableMultimap.of(), totalPartitions); + return Optional.of(scheduleTask(node, new TaskId(stateMachine.getStageId(), partition), ImmutableMultimap.of(), totalPartitions)); } public synchronized Set scheduleSplits(Node node, Multimap splits, Multimap noMoreSplitsNotification) @@ -325,6 +375,9 @@ public synchronized Set scheduleSplits(Node node, Multimap + private synchronized void updateTaskStatus(TaskStatus taskStatus) { - private long previousUserMemory; - private long previousSystemMemory; - private final Set completedDriverGroups = new HashSet<>(); - - @Override - public void stateChanged(TaskStatus taskStatus) - { - updateMemoryUsage(taskStatus); - updateCompletedDriverGroups(taskStatus); - + try { StageState stageState = getState(); if (stageState.isDone()) { return; @@ -449,12 +487,12 @@ public void stateChanged(TaskStatus taskStatus) .findFirst() .map(this::rewriteTransportFailure) .map(ExecutionFailureInfo::toException) - .orElse(new PrestoException(StandardErrorCode.GENERIC_INTERNAL_ERROR, "A task failed for an unknown reason")); + .orElse(new PrestoException(GENERIC_INTERNAL_ERROR, "A task failed for an unknown reason")); stateMachine.transitionToFailed(failure); } else if (taskState == TaskState.ABORTED) { // A task should only be in the aborted state if the STAGE is done (ABORTED or FAILED) - stateMachine.transitionToFailed(new PrestoException(StandardErrorCode.GENERIC_INTERNAL_ERROR, "A task is in the ABORTED state but stage is " + stageState)); + stateMachine.transitionToFailed(new PrestoException(GENERIC_INTERNAL_ERROR, "A task is in the ABORTED state but stage is " + stageState)); } else if (taskState == TaskState.FINISHED) { finishedTasks.add(taskStatus.getTaskId()); @@ -469,6 +507,69 @@ else if (taskState == TaskState.FINISHED) { } } } + finally { + // after updating state, check if all tasks have final status information + checkAllTaskFinal(); + } + } + + private synchronized void updateFinalTaskInfo(TaskInfo finalTaskInfo) + { + tasksWithFinalInfo.add(finalTaskInfo.getTaskStatus().getTaskId()); + checkAllTaskFinal(); + } + + private synchronized void checkAllTaskFinal() + { + if (stateMachine.getState().isDone() && tasksWithFinalInfo.containsAll(allTasks)) { + List finalTaskInfos = getAllTasks().stream() + .map(RemoteTask::getTaskInfo) + .collect(toImmutableList()); + stateMachine.setAllTasksFinal(finalTaskInfos); + } + } + + private ExecutionFailureInfo rewriteTransportFailure(ExecutionFailureInfo executionFailureInfo) + { + if (executionFailureInfo.getRemoteHost() == null || failureDetector.getState(executionFailureInfo.getRemoteHost()) != GONE) { + return executionFailureInfo; + } + + return new ExecutionFailureInfo( + executionFailureInfo.getType(), + executionFailureInfo.getMessage(), + executionFailureInfo.getCause(), + executionFailureInfo.getSuppressed(), + executionFailureInfo.getStack(), + executionFailureInfo.getErrorLocation(), + REMOTE_HOST_GONE.toErrorCode(), + executionFailureInfo.getRemoteHost()); + } + + @Override + public String toString() + { + return stateMachine.toString(); + } + + private class StageTaskListener + implements StateChangeListener + { + private long previousUserMemory; + private long previousSystemMemory; + private final Set completedDriverGroups = new HashSet<>(); + + @Override + public void stateChanged(TaskStatus taskStatus) + { + try { + updateMemoryUsage(taskStatus); + updateCompletedDriverGroups(taskStatus); + } + finally { + updateTaskStatus(taskStatus); + } + } private synchronized void updateMemoryUsage(TaskStatus taskStatus) { @@ -497,25 +598,6 @@ private synchronized void updateCompletedDriverGroups(TaskStatus taskStatus) // Making changes to completedDriverGroups will change newlyCompletedDriverGroups. completedDriverGroups.addAll(newlyCompletedDriverGroups); } - - private ExecutionFailureInfo rewriteTransportFailure(ExecutionFailureInfo executionFailureInfo) - { - if (executionFailureInfo.getRemoteHost() != null && - failureDetector.getState(executionFailureInfo.getRemoteHost()) == GONE) { - return new ExecutionFailureInfo( - executionFailureInfo.getType(), - executionFailureInfo.getMessage(), - executionFailureInfo.getCause(), - executionFailureInfo.getSuppressed(), - executionFailureInfo.getStack(), - executionFailureInfo.getErrorLocation(), - REMOTE_HOST_GONE.toErrorCode(), - executionFailureInfo.getRemoteHost()); - } - else { - return executionFailureInfo; - } - } } private static class ListenerManager diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlTask.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlTask.java index c6520219e9f77..d6b0cca6989e4 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlTask.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlTask.java @@ -85,16 +85,30 @@ public class SqlTask private final AtomicReference taskHolderReference = new AtomicReference<>(new TaskHolder()); private final AtomicBoolean needsPlan = new AtomicBoolean(true); - public SqlTask( + public static SqlTask createSqlTask( TaskId taskId, URI location, String nodeId, QueryContext queryContext, SqlTaskExecutionFactory sqlTaskExecutionFactory, ExecutorService taskNotificationExecutor, - final Function onDone, + Function onDone, DataSize maxBufferSize, CounterStat failedTasks) + { + SqlTask sqlTask = new SqlTask(taskId, location, nodeId, queryContext, sqlTaskExecutionFactory, taskNotificationExecutor, maxBufferSize); + sqlTask.initialize(onDone, failedTasks); + return sqlTask; + } + + private SqlTask( + TaskId taskId, + URI location, + String nodeId, + QueryContext queryContext, + SqlTaskExecutionFactory sqlTaskExecutionFactory, + ExecutorService taskNotificationExecutor, + DataSize maxBufferSize) { this.taskId = requireNonNull(taskId, "taskId is null"); this.taskInstanceId = UUID.randomUUID().toString(); @@ -103,7 +117,6 @@ public SqlTask( this.queryContext = requireNonNull(queryContext, "queryContext is null"); this.sqlTaskExecutionFactory = requireNonNull(sqlTaskExecutionFactory, "sqlTaskExecutionFactory is null"); requireNonNull(taskNotificationExecutor, "taskNotificationExecutor is null"); - requireNonNull(onDone, "onDone is null"); requireNonNull(maxBufferSize, "maxBufferSize is null"); outputBuffer = new LazyOutputBuffer( @@ -115,6 +128,13 @@ public SqlTask( // because we haven't created the task context that holds the the memory context yet. () -> queryContext.getTaskContextByTaskId(taskId).localSystemMemoryContext()); taskStateMachine = new TaskStateMachine(taskId, taskNotificationExecutor); + } + + // this is a separate method to ensure that the `this` reference is not leaked during construction + private void initialize(Function onDone, CounterStat failedTasks) + { + requireNonNull(onDone, "onDone is null"); + requireNonNull(failedTasks, "failedTasks is null"); taskStateMachine.addStateChangeListener(new StateChangeListener() { @Override @@ -307,8 +327,7 @@ private TaskInfo createTaskInfo(TaskHolder taskHolder) outputBuffer.getInfo(), noMoreSplits, taskStats, - needsPlan.get(), - taskStatus.getState().isDone()); + needsPlan.get()); } public ListenableFuture getTaskStatus(TaskState callersCurrentState) @@ -489,6 +508,11 @@ public SqlTaskIoStats getIoStats() } } + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ public void addStateChangeListener(StateChangeListener stateChangeListener) { taskStateMachine.addStateChangeListener(stateChangeListener); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecution.java index d602b96a2b565..62a766a682d0e 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecution.java @@ -15,7 +15,7 @@ import com.facebook.presto.ScheduledSplit; import com.facebook.presto.TaskSource; -import com.facebook.presto.event.query.QueryMonitor; +import com.facebook.presto.event.SplitMonitor; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.buffer.BufferState; import com.facebook.presto.execution.buffer.OutputBuffer; @@ -65,6 +65,7 @@ import java.util.stream.Collectors; import static com.facebook.presto.SystemSessionProperties.getInitialSplitsPerNode; +import static com.facebook.presto.SystemSessionProperties.getMaxDriversPerTask; import static com.facebook.presto.SystemSessionProperties.getSplitConcurrencyAdjustmentInterval; import static com.facebook.presto.execution.SqlTaskExecution.SplitsState.ADDING_SPLITS; import static com.facebook.presto.execution.SqlTaskExecution.SplitsState.FINISHED; @@ -119,7 +120,7 @@ public class SqlTaskExecution private final Executor notificationExecutor; - private final QueryMonitor queryMonitor; + private final SplitMonitor splitMonitor; private final List> drivers = new CopyOnWriteArrayList<>(); @@ -150,7 +151,7 @@ static SqlTaskExecution createSqlTaskExecution( LocalExecutionPlan localExecutionPlan, TaskExecutor taskExecutor, Executor notificationExecutor, - QueryMonitor queryMonitor) + SplitMonitor queryMonitor) { SqlTaskExecution task = new SqlTaskExecution( taskStateMachine, @@ -175,7 +176,7 @@ private SqlTaskExecution( OutputBuffer outputBuffer, LocalExecutionPlan localExecutionPlan, TaskExecutor taskExecutor, - QueryMonitor queryMonitor, + SplitMonitor splitMonitor, Executor notificationExecutor) { this.taskStateMachine = requireNonNull(taskStateMachine, "taskStateMachine is null"); @@ -186,7 +187,7 @@ private SqlTaskExecution( this.taskExecutor = requireNonNull(taskExecutor, "driverExecutor is null"); this.notificationExecutor = requireNonNull(notificationExecutor, "notificationExecutor is null"); - this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); + this.splitMonitor = requireNonNull(splitMonitor, "splitMonitor is null"); try (SetThreadName ignored = new SetThreadName("Task-%s", taskId)) { // index driver factories @@ -197,15 +198,15 @@ private SqlTaskExecution( for (DriverFactory driverFactory : localExecutionPlan.getDriverFactories()) { Optional sourceId = driverFactory.getSourceId(); if (sourceId.isPresent() && partitionedSources.contains(sourceId.get())) { - driverRunnerFactoriesWithSplitLifeCycle.put(sourceId.get(), new DriverSplitRunnerFactory(driverFactory)); + driverRunnerFactoriesWithSplitLifeCycle.put(sourceId.get(), new DriverSplitRunnerFactory(driverFactory, true)); } else { switch (driverFactory.getPipelineExecutionStrategy()) { case GROUPED_EXECUTION: - driverRunnerFactoriesWithDriverGroupLifeCycle.add(new DriverSplitRunnerFactory(driverFactory)); + driverRunnerFactoriesWithDriverGroupLifeCycle.add(new DriverSplitRunnerFactory(driverFactory, false)); break; case UNGROUPED_EXECUTION: - driverRunnerFactoriesWithTaskLifeCycle.add(new DriverSplitRunnerFactory(driverFactory)); + driverRunnerFactoriesWithTaskLifeCycle.add(new DriverSplitRunnerFactory(driverFactory, false)); break; default: throw new UnsupportedOperationException(); @@ -239,15 +240,7 @@ private SqlTaskExecution( // don't register the task if it is already completed (most likely failed during planning above) if (!taskStateMachine.getState().isDone()) { - taskHandle = taskExecutor.addTask(taskId, outputBuffer::getUtilization, getInitialSplitsPerNode(taskContext.getSession()), getSplitConcurrencyAdjustmentInterval(taskContext.getSession())); - taskStateMachine.addStateChangeListener(state -> { - if (state.isDone()) { - taskExecutor.removeTask(taskHandle); - for (DriverFactory factory : localExecutionPlan.getDriverFactories()) { - factory.noMoreDrivers(); - } - } - }); + taskHandle = createTaskHandle(taskStateMachine, taskContext, outputBuffer, localExecutionPlan, taskExecutor); } else { taskHandle = null; @@ -257,6 +250,31 @@ private SqlTaskExecution( } } + // this is a separate method to ensure that the `this` reference is not leaked during construction + private static TaskHandle createTaskHandle( + TaskStateMachine taskStateMachine, + TaskContext taskContext, + OutputBuffer outputBuffer, + LocalExecutionPlan localExecutionPlan, + TaskExecutor taskExecutor) + { + TaskHandle taskHandle = taskExecutor.addTask( + taskStateMachine.getTaskId(), + outputBuffer::getUtilization, + getInitialSplitsPerNode(taskContext.getSession()), + getSplitConcurrencyAdjustmentInterval(taskContext.getSession()), + getMaxDriversPerTask(taskContext.getSession())); + taskStateMachine.addStateChangeListener(state -> { + if (state.isDone()) { + taskExecutor.removeTask(taskHandle); + for (DriverFactory factory : localExecutionPlan.getDriverFactories()) { + factory.noMoreDrivers(); + } + } + }); + return taskHandle; + } + public TaskId getTaskId() { return taskId; @@ -354,6 +372,7 @@ private void mergeIntoPendingSplits(PlanNodeId planNodeId, Set s DriverSplitRunnerFactory partitionedDriverFactory = driverRunnerFactoriesWithSplitLifeCycle.get(planNodeId); PendingSplitsForPlanNode pendingSplitsForPlanNode = pendingSplitsByPlanNode.get(planNodeId); + partitionedDriverFactory.splitsAdded(scheduledSplits.size()); for (ScheduledSplit scheduledSplit : scheduledSplits) { Lifespan lifespan = scheduledSplit.getSplit().getLifespan(); checkLifespan(partitionedDriverFactory.getPipelineExecutionStrategy(), lifespan); @@ -435,7 +454,7 @@ private synchronized void schedulePartitionedSource(TaskSource sourceUpdate) ImmutableList.Builder runners = ImmutableList.builder(); for (ScheduledSplit scheduledSplit : pendingSplits.removeAllSplits()) { // create a new driver for the split - runners.add(partitionedDriverRunnerFactory.createDriverRunner(scheduledSplit, true, lifespan)); + runners.add(partitionedDriverRunnerFactory.createDriverRunner(scheduledSplit, lifespan)); } enqueueDriverSplitRunner(false, runners.build()); @@ -494,7 +513,7 @@ private void scheduleDriversForTaskLifeCycle() List runners = new ArrayList<>(); for (DriverSplitRunnerFactory driverRunnerFactory : driverRunnerFactoriesWithTaskLifeCycle) { for (int i = 0; i < driverRunnerFactory.getDriverInstances().orElse(1); i++) { - runners.add(driverRunnerFactory.createDriverRunner(null, false, Lifespan.taskWide())); + runners.add(driverRunnerFactory.createDriverRunner(null, Lifespan.taskWide())); } } enqueueDriverSplitRunner(true, runners); @@ -516,7 +535,7 @@ private void scheduleDriversForDriverGroupLifeCycle(Lifespan lifespan) List runners = new ArrayList<>(); for (DriverSplitRunnerFactory driverSplitRunnerFactory : driverRunnerFactoriesWithDriverGroupLifeCycle) { for (int i = 0; i < driverSplitRunnerFactory.getDriverInstances().orElse(1); i++) { - runners.add(driverSplitRunnerFactory.createDriverRunner(null, false, lifespan)); + runners.add(driverSplitRunnerFactory.createDriverRunner(null, lifespan)); } } enqueueDriverSplitRunner(true, runners); @@ -550,7 +569,7 @@ public void onSuccess(Object result) checkTaskCompletion(); - queryMonitor.splitCompletedEvent(taskId, getDriverStats()); + splitMonitor.splitCompletedEvent(taskId, getDriverStats()); } } @@ -564,7 +583,7 @@ public void onFailure(Throwable cause) status.decrementRemainingDriver(splitRunner.getLifespan()); // fire failed event with cause - queryMonitor.splitFailedEvent(taskId, getDriverStats(), cause); + splitMonitor.splitFailedEvent(taskId, getDriverStats(), cause); } } @@ -901,21 +920,21 @@ private class DriverSplitRunnerFactory private final PipelineContext pipelineContext; private boolean closed; - private DriverSplitRunnerFactory(DriverFactory driverFactory) + private DriverSplitRunnerFactory(DriverFactory driverFactory, boolean partitioned) { this.driverFactory = driverFactory; - this.pipelineContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver()); + this.pipelineContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver(), partitioned); } // TODO: split this method into two: createPartitionedDriverRunner and createUnpartitionedDriverRunner. // The former will take two arguments, and the latter will take one. This will simplify the signature quite a bit. - public DriverSplitRunner createDriverRunner(@Nullable ScheduledSplit partitionedSplit, boolean partitioned, Lifespan lifespan) + public DriverSplitRunner createDriverRunner(@Nullable ScheduledSplit partitionedSplit, Lifespan lifespan) { checkLifespan(driverFactory.getPipelineExecutionStrategy(), lifespan); status.incrementPendingCreation(pipelineContext.getPipelineId(), lifespan); // create driver context immediately so the driver existence is recorded in the stats // the number of drivers is used to balance work across nodes - DriverContext driverContext = pipelineContext.addDriverContext(partitioned, lifespan); + DriverContext driverContext = pipelineContext.addDriverContext(lifespan); return new DriverSplitRunner(this, driverContext, partitionedSplit, lifespan); } @@ -984,6 +1003,11 @@ public OptionalInt getDriverInstances() { return driverFactory.getDriverInstances(); } + + public void splitsAdded(int count) + { + pipelineContext.splitsAdded(count); + } } private static class DriverSplitRunner diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecutionFactory.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecutionFactory.java index 8c63e5bbc530a..515b3c3f10e87 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecutionFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskExecutionFactory.java @@ -15,12 +15,11 @@ import com.facebook.presto.Session; import com.facebook.presto.TaskSource; -import com.facebook.presto.event.query.QueryMonitor; +import com.facebook.presto.event.SplitMonitor; import com.facebook.presto.execution.buffer.OutputBuffer; import com.facebook.presto.execution.executor.TaskExecutor; import com.facebook.presto.memory.QueryContext; import com.facebook.presto.operator.TaskContext; -import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.planner.LocalExecutionPlanner; import com.facebook.presto.sql.planner.LocalExecutionPlanner.LocalExecutionPlan; import com.facebook.presto.sql.planner.PlanFragment; @@ -32,45 +31,42 @@ import java.util.concurrent.Executor; import static com.facebook.presto.execution.SqlTaskExecution.createSqlTaskExecution; -import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.google.common.base.Throwables.throwIfUnchecked; import static java.util.Objects.requireNonNull; public class SqlTaskExecutionFactory { - private static final String VERBOSE_STATS_PROPERTY = "verbose_stats"; private final Executor taskNotificationExecutor; private final TaskExecutor taskExecutor; private final LocalExecutionPlanner planner; - private final QueryMonitor queryMonitor; - private final boolean verboseStats; + private final SplitMonitor splitMonitor; + private final boolean perOperatorCpuTimerEnabled; private final boolean cpuTimerEnabled; public SqlTaskExecutionFactory( Executor taskNotificationExecutor, TaskExecutor taskExecutor, LocalExecutionPlanner planner, - QueryMonitor queryMonitor, + SplitMonitor splitMonitor, TaskManagerConfig config) { this.taskNotificationExecutor = requireNonNull(taskNotificationExecutor, "taskNotificationExecutor is null"); this.taskExecutor = requireNonNull(taskExecutor, "taskExecutor is null"); this.planner = requireNonNull(planner, "planner is null"); - this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); + this.splitMonitor = requireNonNull(splitMonitor, "splitMonitor is null"); requireNonNull(config, "config is null"); - this.verboseStats = config.isVerboseStats(); + this.perOperatorCpuTimerEnabled = config.isPerOperatorCpuTimerEnabled(); this.cpuTimerEnabled = config.isTaskCpuTimerEnabled(); } public SqlTaskExecution create(Session session, QueryContext queryContext, TaskStateMachine taskStateMachine, OutputBuffer outputBuffer, PlanFragment fragment, List sources, OptionalInt totalPartitions) { - boolean verboseStats = getVerboseStats(session); TaskContext taskContext = queryContext.addTaskContext( taskStateMachine, session, - verboseStats, + perOperatorCpuTimerEnabled, cpuTimerEnabled, totalPartitions); @@ -101,21 +97,6 @@ public SqlTaskExecution create(Session session, QueryContext queryContext, TaskS localExecutionPlan, taskExecutor, taskNotificationExecutor, - queryMonitor); - } - - private boolean getVerboseStats(Session session) - { - String verboseStats = session.getSystemProperties().get(VERBOSE_STATS_PROPERTY); - if (verboseStats == null) { - return this.verboseStats; - } - - try { - return Boolean.valueOf(verboseStats.toUpperCase()); - } - catch (IllegalArgumentException e) { - throw new PrestoException(NOT_SUPPORTED, "Invalid property '" + VERBOSE_STATS_PROPERTY + "=" + verboseStats + "'"); - } + splitMonitor); } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskManager.java index 6a14373f5f80b..e839677c3bc70 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlTaskManager.java @@ -17,13 +17,14 @@ import com.facebook.presto.OutputBuffers.OutputBufferId; import com.facebook.presto.Session; import com.facebook.presto.TaskSource; -import com.facebook.presto.event.query.QueryMonitor; +import com.facebook.presto.event.SplitMonitor; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.buffer.BufferResult; import com.facebook.presto.execution.executor.TaskExecutor; import com.facebook.presto.memory.DefaultQueryContext; import com.facebook.presto.memory.LegacyQueryContext; import com.facebook.presto.memory.LocalMemoryManager; +import com.facebook.presto.memory.MemoryPool; import com.facebook.presto.memory.MemoryPoolAssignment; import com.facebook.presto.memory.MemoryPoolAssignmentsRequest; import com.facebook.presto.memory.NodeMemoryConfig; @@ -67,10 +68,14 @@ import java.util.concurrent.TimeUnit; import static com.facebook.presto.SystemSessionProperties.resourceOvercommit; +import static com.facebook.presto.execution.SqlTask.createSqlTask; +import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; +import static com.facebook.presto.memory.LocalMemoryManager.RESERVED_POOL; import static com.facebook.presto.spi.StandardErrorCode.ABANDONED_TASK; import static com.facebook.presto.spi.StandardErrorCode.SERVER_SHUTTING_DOWN; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Predicates.notNull; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; import static io.airlift.concurrent.Threads.threadsNamed; @@ -112,7 +117,7 @@ public SqlTaskManager( LocalExecutionPlanner planner, LocationFactory locationFactory, TaskExecutor taskExecutor, - QueryMonitor queryMonitor, + SplitMonitor splitMonitor, NodeInfo nodeInfo, LocalMemoryManager localMemoryManager, TaskManagementExecutor taskManagementExecutor, @@ -135,7 +140,7 @@ public SqlTaskManager( this.taskManagementExecutor = requireNonNull(taskManagementExecutor, "taskManagementExecutor cannot be null").getExecutor(); this.driverYieldExecutor = newScheduledThreadPool(config.getTaskYieldThreads(), threadsNamed("task-yield-%s")); - SqlTaskExecutionFactory sqlTaskExecutionFactory = new SqlTaskExecutionFactory(taskNotificationExecutor, taskExecutor, planner, queryMonitor, config); + SqlTaskExecutionFactory sqlTaskExecutionFactory = new SqlTaskExecutionFactory(taskNotificationExecutor, taskExecutor, planner, splitMonitor, config); this.localMemoryManager = requireNonNull(localMemoryManager, "localMemoryManager is null"); DataSize maxQueryUserMemoryPerNode = nodeMemoryConfig.getMaxQueryMemoryPerNode(); @@ -146,7 +151,7 @@ public SqlTaskManager( queryId -> createQueryContext(queryId, localMemoryManager, nodeMemoryConfig, localSpillManager, gcMonitor, maxQueryUserMemoryPerNode, maxQueryTotalMemoryPerNode, maxQuerySpillPerNode))); tasks = CacheBuilder.newBuilder().build(CacheLoader.from( - taskId -> new SqlTask( + taskId -> createSqlTask( taskId, locationFactory.createLocalTaskLocation(taskId), nodeInfo.getNodeId(), @@ -172,11 +177,13 @@ private QueryContext createQueryContext( DataSize maxQuerySpillPerNode) { if (nodeMemoryConfig.isLegacySystemPoolEnabled()) { + Optional systemPool = localMemoryManager.getSystemPool(); + verify(systemPool.isPresent(), "systemPool must be present"); return new LegacyQueryContext( queryId, maxQueryUserMemoryPerNode, - localMemoryManager.getPool(LocalMemoryManager.GENERAL_POOL), - localMemoryManager.getPool(LocalMemoryManager.SYSTEM_POOL), + localMemoryManager.getGeneralPool(), + systemPool.get(), gcMonitor, taskNotificationExecutor, driverYieldExecutor, @@ -188,7 +195,7 @@ private QueryContext createQueryContext( queryId, maxQueryUserMemoryPerNode, maxQueryTotalMemoryPerNode, - localMemoryManager.getPool(LocalMemoryManager.GENERAL_POOL), + localMemoryManager.getGeneralPool(), gcMonitor, taskNotificationExecutor, driverYieldExecutor, @@ -209,7 +216,17 @@ public synchronized void updateMemoryPoolAssignments(MemoryPoolAssignmentsReques coordinatorId = assignments.getCoordinatorId(); for (MemoryPoolAssignment assignment : assignments.getAssignments()) { - queryContexts.getUnchecked(assignment.getQueryId()).setMemoryPool(localMemoryManager.getPool(assignment.getPoolId())); + if (assignment.getPoolId().equals(GENERAL_POOL)) { + queryContexts.getUnchecked(assignment.getQueryId()).setMemoryPool(localMemoryManager.getGeneralPool()); + } + else if (assignment.getPoolId().equals(RESERVED_POOL)) { + MemoryPool reservedPool = localMemoryManager.getReservedPool() + .orElseThrow(() -> new IllegalArgumentException(format("Cannot move %s to the reserved pool as the reserved pool is not enabled", assignment.getQueryId()))); + queryContexts.getUnchecked(assignment.getQueryId()).setMemoryPool(reservedPool); + } + else { + new IllegalArgumentException(format("Cannot move %s to %s as the target memory pool id is invalid", assignment.getQueryId(), assignment.getPoolId())); + } } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/StageInfo.java b/presto-main/src/main/java/com/facebook/presto/execution/StageInfo.java index 5ff9b64fa9196..ca5fc8b2799a5 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/StageInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/StageInfo.java @@ -159,6 +159,6 @@ private static void addAllStages(Optional stageInfo, ImmutableList.Bu public boolean isCompleteInfo() { - return state.isDone() && tasks.stream().allMatch(TaskInfo::isComplete); + return state.isDone() && tasks.stream().allMatch(taskInfo -> taskInfo.getTaskStatus().getState().isDone()); } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/StageStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/StageStateMachine.java index dbc9d488e54f9..16ee7834bb9d5 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/StageStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/StageStateMachine.java @@ -22,10 +22,12 @@ import com.facebook.presto.operator.TaskStats; import com.facebook.presto.spi.eventlistener.StageGcStatistics; import com.facebook.presto.sql.planner.PlanFragment; +import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.util.Failures; import com.google.common.collect.ImmutableList; import io.airlift.log.Logger; import io.airlift.stats.Distribution; +import io.airlift.units.Duration; import org.joda.time.DateTime; import javax.annotation.concurrent.ThreadSafe; @@ -35,9 +37,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.OptionalDouble; import java.util.Set; import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -53,8 +57,12 @@ import static com.facebook.presto.execution.StageState.SCHEDULING_SPLITS; import static com.facebook.presto.execution.StageState.TERMINAL_STAGE_STATES; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import static io.airlift.units.DataSize.succinctBytes; import static io.airlift.units.Duration.succinctDuration; +import static java.lang.Math.max; +import static java.lang.Math.min; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -72,12 +80,11 @@ public class StageStateMachine private final SplitSchedulerStats scheduledStats; private final StateMachine stageState; + private final StateMachine> finalStageInfo; private final AtomicReference failureCause = new AtomicReference<>(); private final AtomicReference schedulingComplete = new AtomicReference<>(); private final Distribution getSplitDistribution = new Distribution(); - private final Distribution scheduleTaskDistribution = new Distribution(); - private final Distribution addSplitDistribution = new Distribution(); private final AtomicLong peakUserMemory = new AtomicLong(); private final AtomicLong currentUserMemory = new AtomicLong(); @@ -99,6 +106,8 @@ public StageStateMachine( stageState = new StateMachine<>("stage " + stageId, executor, PLANNED, TERMINAL_STAGE_STATES); stageState.addStateChangeListener(state -> log.debug("Stage %s is %s", stageId, state)); + + finalStageInfo = new StateMachine<>("final stage " + stageId, executor, Optional.empty()); } public StageId getStageId() @@ -126,6 +135,11 @@ public PlanFragment getFragment() return fragment; } + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ public void addStateChangeListener(StateChangeListener stateChangeListener) { stageState.addStateChangeListener(stateChangeListener); @@ -182,6 +196,32 @@ public boolean transitionToFailed(Throwable throwable) return failed; } + /** + * Add a listener for the final stage info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ + public void addFinalStageInfoListener(StateChangeListener finalStatusListener) + { + AtomicBoolean done = new AtomicBoolean(); + StateChangeListener> fireOnceStateChangeListener = finalStageInfo -> { + if (finalStageInfo.isPresent() && done.compareAndSet(false, true)) { + finalStatusListener.stateChanged(finalStageInfo.get()); + } + }; + finalStageInfo.addStateChangeListener(fireOnceStateChangeListener); + } + + public void setAllTasksFinal(Iterable finalTaskInfos) + { + requireNonNull(finalTaskInfos, "finalTaskInfos is null"); + checkState(stageState.get().isDone()); + StageInfo stageInfo = getStageInfo(() -> finalTaskInfos); + checkArgument(stageInfo.isCompleteInfo(), "finalTaskInfos are not all done"); + finalStageInfo.compareAndSet(Optional.empty(), Optional.of(stageInfo)); + } + public long getUserMemoryReservation() { return currentUserMemory.get(); @@ -196,11 +236,110 @@ public void updateMemoryUsage(long deltaUserMemoryInBytes, long deltaTotalMemory { currentTotalMemory.addAndGet(deltaTotalMemoryInBytes); currentUserMemory.addAndGet(deltaUserMemoryInBytes); - peakUserMemory.updateAndGet(currentPeakValue -> Math.max(currentUserMemory.get(), currentPeakValue)); + peakUserMemory.updateAndGet(currentPeakValue -> max(currentUserMemory.get(), currentPeakValue)); } - public StageInfo getStageInfo(Supplier> taskInfosSupplier, Supplier> subStageInfosSupplier) + public BasicStageStats getBasicStageStats(Supplier> taskInfosSupplier) { + Optional finalStageInfo = this.finalStageInfo.get(); + if (finalStageInfo.isPresent()) { + return finalStageInfo.get() + .getStageStats() + .toBasicStageStats(finalStageInfo.get().getState()); + } + + // stage state must be captured first in order to provide a + // consistent view of the stage. For example, building this + // information, the stage could finish, and the task states would + // never be visible. + StageState state = stageState.get(); + boolean isScheduled = (state == RUNNING) || state.isDone(); + + List taskInfos = ImmutableList.copyOf(taskInfosSupplier.get()); + + int totalDrivers = 0; + int queuedDrivers = 0; + int runningDrivers = 0; + int completedDrivers = 0; + + long cumulativeUserMemory = 0; + long userMemoryReservation = 0; + long totalMemoryReservation = 0; + + long totalScheduledTime = 0; + long totalCpuTime = 0; + + long rawInputDataSize = 0; + long rawInputPositions = 0; + + boolean fullyBlocked = true; + Set blockedReasons = new HashSet<>(); + + for (TaskInfo taskInfo : taskInfos) { + TaskState taskState = taskInfo.getTaskStatus().getState(); + TaskStats taskStats = taskInfo.getStats(); + + totalDrivers += taskStats.getTotalDrivers(); + queuedDrivers += taskStats.getQueuedDrivers(); + runningDrivers += taskStats.getRunningDrivers(); + completedDrivers += taskStats.getCompletedDrivers(); + + cumulativeUserMemory += taskStats.getCumulativeUserMemory(); + + long taskUserMemory = taskStats.getUserMemoryReservation().toBytes(); + long taskSystemMemory = taskStats.getSystemMemoryReservation().toBytes(); + userMemoryReservation += taskUserMemory; + totalMemoryReservation += taskUserMemory + taskSystemMemory; + + totalScheduledTime += taskStats.getTotalScheduledTime().roundTo(NANOSECONDS); + totalCpuTime += taskStats.getTotalCpuTime().roundTo(NANOSECONDS); + if (!taskState.isDone()) { + fullyBlocked &= taskStats.isFullyBlocked(); + blockedReasons.addAll(taskStats.getBlockedReasons()); + } + + if (fragment.getPartitionedSourceNodes().stream().anyMatch(TableScanNode.class::isInstance)) { + rawInputDataSize += taskStats.getRawInputDataSize().toBytes(); + rawInputPositions += taskStats.getRawInputPositions(); + } + } + + OptionalDouble progressPercentage = OptionalDouble.empty(); + if (isScheduled && totalDrivers != 0) { + progressPercentage = OptionalDouble.of(min(100, (completedDrivers * 100.0) / totalDrivers)); + } + + return new BasicStageStats( + isScheduled, + + totalDrivers, + queuedDrivers, + runningDrivers, + completedDrivers, + + succinctBytes(rawInputDataSize), + rawInputPositions, + + cumulativeUserMemory, + succinctBytes(userMemoryReservation), + succinctBytes(totalMemoryReservation), + + new Duration(totalCpuTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(totalScheduledTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), + + fullyBlocked, + blockedReasons, + + progressPercentage); + } + + public StageInfo getStageInfo(Supplier> taskInfosSupplier) + { + Optional finalStageInfo = this.finalStageInfo.get(); + if (finalStageInfo.isPresent()) { + return finalStageInfo.get(); + } + // stage state must be captured first in order to provide a // consistent view of the stage. For example, building this // information, the stage could finish, and the task states would @@ -208,7 +347,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su StageState state = stageState.get(); List taskInfos = ImmutableList.copyOf(taskInfosSupplier.get()); - List subStageInfos = ImmutableList.copyOf(subStageInfosSupplier.get()); int totalTasks = taskInfos.size(); int runningTasks = 0; @@ -227,7 +365,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su long totalScheduledTime = 0; long totalCpuTime = 0; - long totalUserTime = 0; long totalBlockedTime = 0; long rawInputDataSize = 0; @@ -278,7 +415,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su totalScheduledTime += taskStats.getTotalScheduledTime().roundTo(NANOSECONDS); totalCpuTime += taskStats.getTotalCpuTime().roundTo(NANOSECONDS); - totalUserTime += taskStats.getTotalUserTime().roundTo(NANOSECONDS); totalBlockedTime += taskStats.getTotalBlockedTime().roundTo(NANOSECONDS); if (!taskState.isDone()) { fullyBlocked &= taskStats.isFullyBlocked(); @@ -302,8 +438,8 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su int gcSec = toIntExact(taskStats.getFullGcTime().roundTo(SECONDS)); totalFullGcSec += gcSec; - minFullGcSec = Math.min(minFullGcSec, gcSec); - maxFullGcSec = Math.max(maxFullGcSec, gcSec); + minFullGcSec = min(minFullGcSec, gcSec); + maxFullGcSec = max(maxFullGcSec, gcSec); for (PipelineStats pipeline : taskStats.getPipelines()) { for (OperatorStats operatorStats : pipeline.getOperatorSummaries()) { @@ -316,8 +452,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su StageStats stageStats = new StageStats( schedulingComplete.get(), getSplitDistribution.snapshot(), - scheduleTaskDistribution.snapshot(), - addSplitDistribution.snapshot(), totalTasks, runningTasks, @@ -335,7 +469,6 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su succinctBytes(peakUserMemoryReservation), succinctDuration(totalScheduledTime, NANOSECONDS), succinctDuration(totalCpuTime, NANOSECONDS), - succinctDuration(totalUserTime, NANOSECONDS), succinctDuration(totalBlockedTime, NANOSECONDS), fullyBlocked && runningTasks > 0, blockedReasons, @@ -371,7 +504,7 @@ public StageInfo getStageInfo(Supplier> taskInfosSupplier, Su fragment.getTypes(), stageStats, taskInfos, - subStageInfos, + ImmutableList.of(), failureInfo); } @@ -379,17 +512,7 @@ public void recordGetSplitTime(long startNanos) { long elapsedNanos = System.nanoTime() - startNanos; getSplitDistribution.add(elapsedNanos); - scheduledStats.getGetSplitTime().add(elapsedNanos, TimeUnit.NANOSECONDS); - } - - public void recordScheduleTaskTime(long startNanos) - { - scheduleTaskDistribution.add(System.nanoTime() - startNanos); - } - - public void recordAddSplit(long startNanos) - { - addSplitDistribution.add(System.nanoTime() - startNanos); + scheduledStats.getGetSplitTime().add(elapsedNanos, NANOSECONDS); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/execution/StageStats.java b/presto-main/src/main/java/com/facebook/presto/execution/StageStats.java index 4981219ac6bbb..2ec6f4c1416ce 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/StageStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/StageStats.java @@ -28,9 +28,12 @@ import javax.annotation.concurrent.Immutable; import java.util.List; +import java.util.OptionalDouble; import java.util.Set; +import static com.facebook.presto.execution.StageState.RUNNING; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Math.min; import static java.util.Objects.requireNonNull; @Immutable @@ -39,8 +42,6 @@ public class StageStats private final DateTime schedulingComplete; private final DistributionSnapshot getSplitDistribution; - private final DistributionSnapshot scheduleTaskDistribution; - private final DistributionSnapshot addSplitDistribution; private final int totalTasks; private final int runningTasks; @@ -59,7 +60,6 @@ public class StageStats private final Duration totalScheduledTime; private final Duration totalCpuTime; - private final Duration totalUserTime; private final Duration totalBlockedTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -85,8 +85,6 @@ public StageStats( @JsonProperty("schedulingComplete") DateTime schedulingComplete, @JsonProperty("getSplitDistribution") DistributionSnapshot getSplitDistribution, - @JsonProperty("scheduleTaskDistribution") DistributionSnapshot scheduleTaskDistribution, - @JsonProperty("addSplitDistribution") DistributionSnapshot addSplitDistribution, @JsonProperty("totalTasks") int totalTasks, @JsonProperty("runningTasks") int runningTasks, @@ -105,7 +103,6 @@ public StageStats( @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("totalCpuTime") Duration totalCpuTime, - @JsonProperty("totalUserTime") Duration totalUserTime, @JsonProperty("totalBlockedTime") Duration totalBlockedTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @@ -128,8 +125,6 @@ public StageStats( { this.schedulingComplete = schedulingComplete; this.getSplitDistribution = requireNonNull(getSplitDistribution, "getSplitDistribution is null"); - this.scheduleTaskDistribution = requireNonNull(scheduleTaskDistribution, "scheduleTaskDistribution is null"); - this.addSplitDistribution = requireNonNull(addSplitDistribution, "addSplitDistribution is null"); checkArgument(totalTasks >= 0, "totalTasks is negative"); this.totalTasks = totalTasks; @@ -156,7 +151,6 @@ public StageStats( this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); - this.totalUserTime = requireNonNull(totalUserTime, "totalUserTime is null"); this.totalBlockedTime = requireNonNull(totalBlockedTime, "totalBlockedTime is null"); this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -193,18 +187,6 @@ public DistributionSnapshot getGetSplitDistribution() return getSplitDistribution; } - @JsonProperty - public DistributionSnapshot getScheduleTaskDistribution() - { - return scheduleTaskDistribution; - } - - @JsonProperty - public DistributionSnapshot getAddSplitDistribution() - { - return addSplitDistribution; - } - @JsonProperty public int getTotalTasks() { @@ -289,12 +271,6 @@ public Duration getTotalCpuTime() return totalCpuTime; } - @JsonProperty - public Duration getTotalUserTime() - { - return totalUserTime; - } - @JsonProperty public Duration getTotalBlockedTime() { @@ -372,4 +348,31 @@ public List getOperatorSummaries() { return operatorSummaries; } + + public BasicStageStats toBasicStageStats(StageState stageState) + { + boolean isScheduled = (stageState == RUNNING) || stageState.isDone(); + + OptionalDouble progressPercentage = OptionalDouble.empty(); + if (isScheduled && totalDrivers != 0) { + progressPercentage = OptionalDouble.of(min(100, (completedDrivers * 100.0) / totalDrivers)); + } + + return new BasicStageStats( + isScheduled, + totalDrivers, + queuedDrivers, + runningDrivers, + completedDrivers, + rawInputDataSize, + rawInputPositions, + (long) cumulativeUserMemory, + userMemoryReservation, + totalMemoryReservation, + totalCpuTime, + totalScheduledTime, + fullyBlocked, + blockedReasons, + progressPercentage); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/StateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/StateMachine.java index 865b92cc76ff9..a8e359b2955b8 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/StateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/StateMachine.java @@ -20,7 +20,6 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.log.Logger; -import javax.annotation.Nonnull; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; @@ -87,7 +86,8 @@ public StateMachine(String name, Executor executor, T initialState, Iterable this.terminalStates = ImmutableSet.copyOf(requireNonNull(terminalStates, "terminalStates is null")); } - @Nonnull + // state changes are atomic and state is volatile, so a direct read is safe here + @SuppressWarnings("FieldAccessNotGuarded") public T get() { return state; @@ -99,7 +99,6 @@ public T get() * * @return the old state */ - @Nonnull public T set(T newState) { checkState(!Thread.holdsLock(lock), "Can not set state while holding the lock"); @@ -209,6 +208,7 @@ private void fireStateChanged(T newState, FutureStateChange futureStateChange checkState(!Thread.holdsLock(lock), "Can not fire state change event while holding the lock"); requireNonNull(newState, "newState is null"); + // always fire listener callbacks from a different thread safeExecute(() -> { checkState(!Thread.holdsLock(lock), "Can not notify while holding the lock"); try { @@ -218,16 +218,21 @@ private void fireStateChanged(T newState, FutureStateChange futureStateChange log.error(e, "Error setting future state for %s", name); } for (StateChangeListener stateChangeListener : stateChangeListeners) { - try { - stateChangeListener.stateChanged(newState); - } - catch (Throwable e) { - log.error(e, "Error notifying state change listener for %s", name); - } + fireStateChangedListener(newState, stateChangeListener); } }); } + private void fireStateChangedListener(T newState, StateChangeListener stateChangeListener) + { + try { + stateChangeListener.stateChanged(newState); + } + catch (Throwable e) { + log.error(e, "Error notifying state change listener for %s", name); + } + } + /** * Gets a future that completes when the state is no longer {@code .equals()} to {@code currentState)}. */ @@ -238,7 +243,7 @@ public ListenableFuture getStateChange(T currentState) synchronized (lock) { // return a completed future if the state has already changed, or we are in a terminal state - if (isPossibleStateChange(currentState)) { + if (!state.equals(currentState) || isTerminalState(state)) { return immediateFuture(state); } @@ -248,28 +253,28 @@ public ListenableFuture getStateChange(T currentState) /** * Adds a listener to be notified when the state instance changes according to {@code .equals()}. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. The listener is + * immediately notified immediately of the current state. */ public void addStateChangeListener(StateChangeListener stateChangeListener) { requireNonNull(stateChangeListener, "stateChangeListener is null"); boolean inTerminalState; + T currentState; synchronized (lock) { - inTerminalState = isTerminalState(state); + currentState = state; + inTerminalState = isTerminalState(currentState); if (!inTerminalState) { stateChangeListeners.add(stateChangeListener); } } - // state machine will never transition from a terminal state, so fire state change immediately - if (inTerminalState) { - stateChangeListener.stateChanged(state); - } - } - - private boolean isPossibleStateChange(T currentState) - { - return !state.equals(currentState) || isTerminalState(state); + // fire state change listener with the current state + // always fire listener callbacks from a different thread + safeExecute(() -> stateChangeListener.stateChanged(currentState)); } @VisibleForTesting @@ -279,9 +284,11 @@ boolean isTerminalState(T state) } @VisibleForTesting - synchronized List> getStateChangeListeners() + List> getStateChangeListeners() { - return ImmutableList.copyOf(stateChangeListeners); + synchronized (lock) { + return ImmutableList.copyOf(stateChangeListeners); + } } public interface StateChangeListener diff --git a/presto-main/src/main/java/com/facebook/presto/execution/TaskInfo.java b/presto-main/src/main/java/com/facebook/presto/execution/TaskInfo.java index dbb861862e35b..87638c379a458 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/TaskInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/TaskInfo.java @@ -43,7 +43,6 @@ public class TaskInfo private final TaskStats stats; private final boolean needsPlan; - private final boolean complete; @JsonCreator public TaskInfo(@JsonProperty("taskStatus") TaskStatus taskStatus, @@ -51,8 +50,7 @@ public TaskInfo(@JsonProperty("taskStatus") TaskStatus taskStatus, @JsonProperty("outputBuffers") OutputBufferInfo outputBuffers, @JsonProperty("noMoreSplits") Set noMoreSplits, @JsonProperty("stats") TaskStats stats, - @JsonProperty("needsPlan") boolean needsPlan, - @JsonProperty("complete") boolean complete) + @JsonProperty("needsPlan") boolean needsPlan) { this.taskStatus = requireNonNull(taskStatus, "taskStatus is null"); this.lastHeartbeat = requireNonNull(lastHeartbeat, "lastHeartbeat is null"); @@ -61,7 +59,6 @@ public TaskInfo(@JsonProperty("taskStatus") TaskStatus taskStatus, this.stats = requireNonNull(stats, "stats is null"); this.needsPlan = needsPlan; - this.complete = complete; } @JsonProperty @@ -100,18 +97,12 @@ public boolean isNeedsPlan() return needsPlan; } - @JsonProperty - public boolean isComplete() - { - return complete; - } - public TaskInfo summarize() { if (taskStatus.getState().isDone()) { - return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.summarize(), noMoreSplits, stats.summarizeFinal(), needsPlan, complete); + return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.summarize(), noMoreSplits, stats.summarizeFinal(), needsPlan); } - return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.summarize(), noMoreSplits, stats.summarize(), needsPlan, complete); + return new TaskInfo(taskStatus, lastHeartbeat, outputBuffers.summarize(), noMoreSplits, stats.summarize(), needsPlan); } @Override @@ -131,12 +122,11 @@ public static TaskInfo createInitialTask(TaskId taskId, URI location, String nod new OutputBufferInfo("UNINITIALIZED", OPEN, true, true, 0, 0, 0, 0, bufferStates), ImmutableSet.of(), taskStats, - true, - false); + true); } public TaskInfo withTaskStatus(TaskStatus newTaskStatus) { - return new TaskInfo(newTaskStatus, lastHeartbeat, outputBuffers, noMoreSplits, stats, needsPlan, complete); + return new TaskInfo(newTaskStatus, lastHeartbeat, outputBuffers, noMoreSplits, stats, needsPlan); } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/TaskManager.java b/presto-main/src/main/java/com/facebook/presto/execution/TaskManager.java index 2f55d65371dbe..da57821a1b0c0 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/TaskManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/TaskManager.java @@ -125,6 +125,9 @@ public interface TaskManager /** * Adds a state change listener to the specified task. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. */ void addStateChangeListener(TaskId taskId, StateChangeListener stateChangeListener); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/TaskManagerConfig.java b/presto-main/src/main/java/com/facebook/presto/execution/TaskManagerConfig.java index 75185be12faa1..d9221c3135928 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/TaskManagerConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/TaskManagerConfig.java @@ -41,8 +41,9 @@ "task.level-absolute-priority"}) public class TaskManagerConfig { - private boolean verboseStats; + private boolean perOperatorCpuTimerEnabled = true; private boolean taskCpuTimerEnabled = true; + private boolean statisticsCpuTimerEnabled = true; private DataSize maxPartialAggregationMemoryUsage = new DataSize(16, Unit.MEGABYTE); private DataSize maxLocalExchangeBufferSize = new DataSize(32, Unit.MEGABYTE); private DataSize maxIndexMemoryUsage = new DataSize(64, Unit.MEGABYTE); @@ -104,15 +105,16 @@ public TaskManagerConfig setInfoUpdateInterval(Duration infoUpdateInterval) return this; } - public boolean isVerboseStats() + public boolean isPerOperatorCpuTimerEnabled() { - return verboseStats; + return perOperatorCpuTimerEnabled; } - @Config("task.verbose-stats") - public TaskManagerConfig setVerboseStats(boolean verboseStats) + @LegacyConfig("task.verbose-stats") + @Config("task.per-operator-cpu-timer-enabled") + public TaskManagerConfig setPerOperatorCpuTimerEnabled(boolean perOperatorCpuTimerEnabled) { - this.verboseStats = verboseStats; + this.perOperatorCpuTimerEnabled = perOperatorCpuTimerEnabled; return this; } @@ -128,6 +130,18 @@ public TaskManagerConfig setTaskCpuTimerEnabled(boolean taskCpuTimerEnabled) return this; } + public boolean isStatisticsCpuTimerEnabled() + { + return statisticsCpuTimerEnabled; + } + + @Config("task.statistics-cpu-timer-enabled") + public TaskManagerConfig setStatisticsCpuTimerEnabled(boolean statisticsCpuTimerEnabled) + { + this.statisticsCpuTimerEnabled = statisticsCpuTimerEnabled; + return this; + } + @NotNull public DataSize getMaxPartialAggregationMemoryUsage() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/TaskStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/TaskStateMachine.java index 57d131697a7bf..1b55d52d1d0f9 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/TaskStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/TaskStateMachine.java @@ -49,7 +49,7 @@ public TaskStateMachine(TaskId taskId, Executor executor) @Override public void stateChanged(TaskState newState) { - log.debug("Task %s is %s", TaskStateMachine.this.taskId, newState); + log.debug("Task %s is %s", taskId, newState); } }); } @@ -116,6 +116,11 @@ private void transitionToDoneState(TaskState doneState) taskState.setIf(doneState, currentState -> !currentState.isDone()); } + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ public void addStateChangeListener(StateChangeListener stateChangeListener) { taskState.addStateChangeListener(stateChangeListener); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/buffer/OutputBuffer.java b/presto-main/src/main/java/com/facebook/presto/execution/buffer/OutputBuffer.java index 2eeaaa50db167..d98ce097f86a2 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/buffer/OutputBuffer.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/buffer/OutputBuffer.java @@ -47,6 +47,9 @@ public interface OutputBuffer /** * Add a listener which fires anytime the buffer state changes. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. */ void addStateChangeListener(StateChangeListener stateChangeListener); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/executor/PrioritizedSplitRunner.java b/presto-main/src/main/java/com/facebook/presto/execution/executor/PrioritizedSplitRunner.java index 8267234b3ad3e..3a21c839afb9e 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/executor/PrioritizedSplitRunner.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/executor/PrioritizedSplitRunner.java @@ -31,7 +31,7 @@ import static com.facebook.presto.operator.Operator.NOT_BLOCKED; import static java.util.concurrent.TimeUnit.NANOSECONDS; -class PrioritizedSplitRunner +public class PrioritizedSplitRunner implements Comparable { private static final AtomicLong NEXT_WORKER_ID = new AtomicLong(); @@ -39,7 +39,7 @@ class PrioritizedSplitRunner private static final Logger log = Logger.get(PrioritizedSplitRunner.class); // each time we run a split, run it for this length before returning to the pool - private static final Duration SPLIT_RUN_QUANTA = new Duration(1, TimeUnit.SECONDS); + public static final Duration SPLIT_RUN_QUANTA = new Duration(1, TimeUnit.SECONDS); private final long createdNanos = System.nanoTime(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskExecutor.java b/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskExecutor.java index ec4eda6dfa52a..5f6df55c76906 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskExecutor.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskExecutor.java @@ -16,7 +16,9 @@ import com.facebook.presto.execution.SplitRunner; import com.facebook.presto.execution.TaskId; import com.facebook.presto.execution.TaskManagerConfig; +import com.facebook.presto.server.ServerConfig; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.version.EmbedVersion; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Ticker; import com.google.common.collect.ComparisonChain; @@ -43,6 +45,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import java.util.SortedSet; import java.util.concurrent.ConcurrentHashMap; @@ -58,6 +61,7 @@ import java.util.function.DoubleSupplier; import static com.facebook.presto.execution.executor.MultilevelSplitQueue.computeLevel; +import static com.facebook.presto.util.MoreMath.min; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -68,7 +72,6 @@ import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.MICROSECONDS; -import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; @ThreadSafe @@ -77,7 +80,7 @@ public class TaskExecutor private static final Logger log = Logger.get(TaskExecutor.class); // print out split call stack if it has been running for a certain amount of time - private static final Duration LONG_SPLIT_WARNING_THRESHOLD = new Duration(1000, TimeUnit.SECONDS); + private static final Duration LONG_SPLIT_WARNING_THRESHOLD = new Duration(600, TimeUnit.SECONDS); private static final AtomicLong NEXT_RUNNER_ID = new AtomicLong(); @@ -86,8 +89,9 @@ public class TaskExecutor private final int runnerThreads; private final int minimumNumberOfDrivers; - private final int minimumNumberOfDriversPerTask; + private final int guaranteedNumberOfDriversPerTask; private final int maximumNumberOfDriversPerTask; + private final EmbedVersion embedVersion; private final Ticker ticker; @@ -152,39 +156,54 @@ public class TaskExecutor private volatile boolean closed; @Inject - public TaskExecutor(TaskManagerConfig config, MultilevelSplitQueue splitQueue) + public TaskExecutor(TaskManagerConfig config, EmbedVersion embedVersion, MultilevelSplitQueue splitQueue) { this(requireNonNull(config, "config is null").getMaxWorkerThreads(), config.getMinDrivers(), config.getMinDriversPerTask(), config.getMaxDriversPerTask(), + embedVersion, splitQueue, Ticker.systemTicker()); } @VisibleForTesting - public TaskExecutor(int runnerThreads, int minDrivers, int minimumNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, Ticker ticker) + public TaskExecutor(int runnerThreads, int minDrivers, int guaranteedNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, Ticker ticker) { - this(runnerThreads, minDrivers, minimumNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new MultilevelSplitQueue(2), ticker); + this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new EmbedVersion(new ServerConfig()), new MultilevelSplitQueue(2), ticker); } @VisibleForTesting - public TaskExecutor(int runnerThreads, int minDrivers, int minimumNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, MultilevelSplitQueue splitQueue, Ticker ticker) + public TaskExecutor(int runnerThreads, int minDrivers, int guaranteedNumberOfDriversPerTask, int maximumNumberOfDriversPerTask, MultilevelSplitQueue splitQueue, Ticker ticker) + { + this(runnerThreads, minDrivers, guaranteedNumberOfDriversPerTask, maximumNumberOfDriversPerTask, new EmbedVersion(new ServerConfig()), splitQueue, ticker); + } + + @VisibleForTesting + public TaskExecutor( + int runnerThreads, + int minDrivers, + int guaranteedNumberOfDriversPerTask, + int maximumNumberOfDriversPerTask, + EmbedVersion embedVersion, + MultilevelSplitQueue splitQueue, + Ticker ticker) { checkArgument(runnerThreads > 0, "runnerThreads must be at least 1"); - checkArgument(minimumNumberOfDriversPerTask > 0, "minimumNumberOfDriversPerTask must be at least 1"); + checkArgument(guaranteedNumberOfDriversPerTask > 0, "guaranteedNumberOfDriversPerTask must be at least 1"); checkArgument(maximumNumberOfDriversPerTask > 0, "maximumNumberOfDriversPerTask must be at least 1"); - checkArgument(minimumNumberOfDriversPerTask <= maximumNumberOfDriversPerTask, "minimumNumberOfDriversPerTask cannot be greater than maximumNumberOfDriversPerTask"); + checkArgument(guaranteedNumberOfDriversPerTask <= maximumNumberOfDriversPerTask, "guaranteedNumberOfDriversPerTask cannot be greater than maximumNumberOfDriversPerTask"); // we manage thread pool size directly, so create an unlimited pool this.executor = newCachedThreadPool(threadsNamed("task-processor-%s")); this.executorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) executor); this.runnerThreads = runnerThreads; + this.embedVersion = requireNonNull(embedVersion, "embedVersion is null"); this.ticker = requireNonNull(ticker, "ticker is null"); this.minimumNumberOfDrivers = minDrivers; - this.minimumNumberOfDriversPerTask = minimumNumberOfDriversPerTask; + this.guaranteedNumberOfDriversPerTask = guaranteedNumberOfDriversPerTask; this.maximumNumberOfDriversPerTask = maximumNumberOfDriversPerTask; this.waitingSplits = requireNonNull(splitQueue, "splitQueue is null"); this.tasks = new LinkedList<>(); @@ -197,7 +216,6 @@ public synchronized void start() for (int i = 0; i < runnerThreads; i++) { addRunnerThread(); } - splitMonitorExecutor.scheduleWithFixedDelay(this::monitorActiveSplits, 1, 1, MINUTES); } @PreDestroy @@ -224,20 +242,27 @@ public synchronized String toString() private synchronized void addRunnerThread() { try { - executor.execute(new TaskRunner()); + executor.execute(embedVersion.embedVersion(new TaskRunner())); } catch (RejectedExecutionException ignored) { } } - public synchronized TaskHandle addTask(TaskId taskId, DoubleSupplier utilizationSupplier, int initialSplitConcurrency, Duration splitConcurrencyAdjustFrequency) + public synchronized TaskHandle addTask( + TaskId taskId, + DoubleSupplier utilizationSupplier, + int initialSplitConcurrency, + Duration splitConcurrencyAdjustFrequency, + OptionalInt maxDriversPerTask) { requireNonNull(taskId, "taskId is null"); requireNonNull(utilizationSupplier, "utilizationSupplier is null"); + checkArgument(!maxDriversPerTask.isPresent() || maxDriversPerTask.getAsInt() <= maximumNumberOfDriversPerTask, + "maxDriversPerTask cannot be greater than the configured value"); log.debug("Task scheduled " + taskId); - TaskHandle taskHandle = new TaskHandle(taskId, waitingSplits, utilizationSupplier, initialSplitConcurrency, splitConcurrencyAdjustFrequency); + TaskHandle taskHandle = new TaskHandle(taskId, waitingSplits, utilizationSupplier, initialSplitConcurrency, splitConcurrencyAdjustFrequency, maxDriversPerTask); tasks.add(taskHandle); return taskHandle; @@ -361,7 +386,7 @@ private synchronized void scheduleTaskIfNecessary(TaskHandle taskHandle) // immediately schedule a new split for this task. This assures // that a task gets its fair amount of consideration (you have to // have splits to be considered for running on a thread). - if (taskHandle.getRunningLeafSplits() < minimumNumberOfDriversPerTask) { + if (taskHandle.getRunningLeafSplits() < min(guaranteedNumberOfDriversPerTask, taskHandle.getMaxDriversPerTask().orElse(Integer.MAX_VALUE))) { PrioritizedSplitRunner split = taskHandle.pollNextSplit(); if (split != null) { startSplit(split); @@ -410,7 +435,7 @@ private synchronized PrioritizedSplitRunner pollNextSplitWorker() for (Iterator iterator = tasks.iterator(); iterator.hasNext(); ) { TaskHandle task = iterator.next(); // skip tasks that are already running the configured max number of drivers - if (task.getRunningLeafSplits() >= maximumNumberOfDriversPerTask) { + if (task.getRunningLeafSplits() >= task.getMaxDriversPerTask().orElse(maximumNumberOfDriversPerTask)) { continue; } PrioritizedSplitRunner split = task.pollNextSplit(); @@ -427,25 +452,6 @@ private synchronized PrioritizedSplitRunner pollNextSplitWorker() return null; } - private void monitorActiveSplits() - { - for (RunningSplitInfo splitInfo : runningSplitInfos) { - Duration duration = Duration.succinctNanos(ticker.read() - splitInfo.getStartTime()); - if (duration.compareTo(LONG_SPLIT_WARNING_THRESHOLD) < 0) { - return; - } - if (splitInfo.isPrinted()) { - continue; - } - splitInfo.setPrinted(); - - String currentMaxActiveSplit = splitInfo.getThreadId(); - Exception exception = new Exception("Long running split"); - exception.setStackTrace(splitInfo.getThread().getStackTrace()); - log.warn(exception, "Split thread %s has been running longer than %s", currentMaxActiveSplit, duration); - } - } - private class TaskRunner implements Runnable { @@ -775,14 +781,52 @@ private synchronized int getRunningTasksForLevel(int level) return count; } + public String getMaxActiveSplitsInfo() + { + // Sample output: + // + // 2 splits have been continuously active for more than 600.00ms seconds + // + // "20180907_054754_00000_88xi4.1.0-2" tid=99 + // at java.util.Formatter$FormatSpecifier.(Formatter.java:2708) + // at java.util.Formatter.parse(Formatter.java:2560) + // at java.util.Formatter.format(Formatter.java:2501) + // at ... (more lines of stacktrace) + // + // "20180907_054754_00000_88xi4.1.0-3" tid=106 + // at java.util.Formatter$FormatSpecifier.(Formatter.java:2709) + // at java.util.Formatter.parse(Formatter.java:2560) + // at java.util.Formatter.format(Formatter.java:2501) + // at ... (more line of stacktrace) + StringBuilder stackTrace = new StringBuilder(); + int maxActiveSplitCount = 0; + String message = "%s splits have been continuously active for more than %s seconds\n"; + for (RunningSplitInfo splitInfo : runningSplitInfos) { + Duration duration = Duration.succinctNanos(ticker.read() - splitInfo.getStartTime()); + if (duration.compareTo(LONG_SPLIT_WARNING_THRESHOLD) >= 0) { + maxActiveSplitCount++; + stackTrace.append("\n"); + stackTrace.append(String.format("\"%s\" tid=%s", splitInfo.getThreadId(), splitInfo.getThread().getId())).append("\n"); + for (StackTraceElement traceElement : splitInfo.getThread().getStackTrace()) { + stackTrace.append("\tat ").append(traceElement).append("\n"); + } + } + } + + return String.format(message, maxActiveSplitCount, LONG_SPLIT_WARNING_THRESHOLD).concat(stackTrace.toString()); + } + @Managed - public long getMaxActiveSplitTime() + public long getRunAwaySplitCount() { - Iterator iterator = runningSplitInfos.iterator(); - if (iterator.hasNext()) { - return NANOSECONDS.toMillis(ticker.read() - iterator.next().getStartTime()); + int count = 0; + for (RunningSplitInfo splitInfo : runningSplitInfos) { + Duration duration = Duration.succinctNanos(ticker.read() - splitInfo.getStartTime()); + if (duration.compareTo(LONG_SPLIT_WARNING_THRESHOLD) > 0) { + count++; + } } - return 0; + return count; } private static class RunningSplitInfo diff --git a/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskHandle.java b/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskHandle.java index adeaf6452ccec..532fb1804bb23 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskHandle.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/executor/TaskHandle.java @@ -24,6 +24,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; +import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -56,12 +57,20 @@ public class TaskHandle protected final AtomicReference priority = new AtomicReference<>(new Priority(0, 0)); private final MultilevelSplitQueue splitQueue; - - public TaskHandle(TaskId taskId, MultilevelSplitQueue splitQueue, DoubleSupplier utilizationSupplier, int initialSplitConcurrency, Duration splitConcurrencyAdjustFrequency) + private final OptionalInt maxDriversPerTask; + + public TaskHandle( + TaskId taskId, + MultilevelSplitQueue splitQueue, + DoubleSupplier utilizationSupplier, + int initialSplitConcurrency, + Duration splitConcurrencyAdjustFrequency, + OptionalInt maxDriversPerTask) { this.taskId = requireNonNull(taskId, "taskId is null"); this.splitQueue = requireNonNull(splitQueue, "splitQueue is null"); this.utilizationSupplier = requireNonNull(utilizationSupplier, "utilizationSupplier is null"); + this.maxDriversPerTask = requireNonNull(maxDriversPerTask, "maxDriversPerTask is null"); this.concurrencyController = new SplitConcurrencyController( initialSplitConcurrency, requireNonNull(splitConcurrencyAdjustFrequency, "splitConcurrencyAdjustFrequency is null")); @@ -105,6 +114,11 @@ public TaskId getTaskId() return taskId; } + public OptionalInt getMaxDriversPerTask() + { + return maxDriversPerTask; + } + // Returns any remaining splits. The caller must destroy these. public synchronized List destroy() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java index 6ec501e5f5db2..79e9f7033e4ff 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java @@ -13,8 +13,7 @@ */ package com.facebook.presto.execution.resourceGroups; -import com.facebook.presto.execution.QueryExecution; -import com.facebook.presto.execution.QueryState; +import com.facebook.presto.execution.ManagedQueryExecution; import com.facebook.presto.execution.resourceGroups.WeightedFairQueue.Usage; import com.facebook.presto.server.QueryStateInfo; import com.facebook.presto.server.ResourceGroupInfo; @@ -48,7 +47,7 @@ import static com.facebook.presto.SystemSessionProperties.getQueryPriority; import static com.facebook.presto.server.QueryStateInfo.createQueryStateInfo; import static com.facebook.presto.spi.ErrorType.USER_ERROR; -import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_TIME_LIMIT; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_RESOURCE_GROUP; import static com.facebook.presto.spi.resourceGroups.ResourceGroupState.CAN_QUEUE; import static com.facebook.presto.spi.resourceGroups.ResourceGroupState.CAN_RUN; import static com.facebook.presto.spi.resourceGroups.ResourceGroupState.FULL; @@ -65,6 +64,7 @@ import static com.google.common.math.LongMath.saturatedSubtract; import static io.airlift.units.DataSize.Unit.BYTE; import static java.lang.Math.min; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -108,10 +108,6 @@ public class InternalResourceGroup private SchedulingPolicy schedulingPolicy = FAIR; @GuardedBy("root") private boolean jmxExport; - @GuardedBy("root") - private Duration queuedTimeLimit = new Duration(Long.MAX_VALUE, MILLISECONDS); - @GuardedBy("root") - private Duration runningTimeLimit = new Duration(Long.MAX_VALUE, MILLISECONDS); // Live data structures // ==================== @@ -125,9 +121,9 @@ public class InternalResourceGroup @GuardedBy("root") private final Set dirtySubGroups = new HashSet<>(); @GuardedBy("root") - private UpdateablePriorityQueue queuedQueries = new FifoQueue<>(); + private UpdateablePriorityQueue queuedQueries = new FifoQueue<>(); @GuardedBy("root") - private final Set runningQueries = new HashSet<>(); + private final Set runningQueries = new HashSet<>(); @GuardedBy("root") private int descendantRunningQueries; @GuardedBy("root") @@ -170,8 +166,6 @@ public ResourceGroupInfo getFullInfo() softConcurrencyLimit, hardConcurrencyLimit, maxQueuedQueries, - runningTimeLimit, - queuedTimeLimit, DataSize.succinctBytes(cachedMemoryUsageBytes), getQueuedQueries(), getRunningQueries(), @@ -196,8 +190,6 @@ public ResourceGroupInfo getInfo() softConcurrencyLimit, hardConcurrencyLimit, maxQueuedQueries, - runningTimeLimit, - queuedTimeLimit, DataSize.succinctBytes(cachedMemoryUsageBytes), getQueuedQueries(), getRunningQueries(), @@ -222,8 +214,6 @@ private ResourceGroupInfo getSummaryInfo() softConcurrencyLimit, hardConcurrencyLimit, maxQueuedQueries, - runningTimeLimit, - queuedTimeLimit, DataSize.succinctBytes(cachedMemoryUsageBytes), getQueuedQueries(), getRunningQueries(), @@ -253,7 +243,7 @@ private List getAggregatedRunningQueriesInfo() synchronized (root) { if (subGroups.isEmpty()) { return runningQueries.stream() - .map(QueryExecution::getQueryInfo) + .map(ManagedQueryExecution::getBasicQueryInfo) .map(queryInfo -> createQueryStateInfo(queryInfo, Optional.of(id))) .collect(toImmutableList()); } @@ -517,7 +507,7 @@ public void setSchedulingPolicy(SchedulingPolicy policy) // Switch to the appropriate queue implementation to implement the desired policy Queue queue; - UpdateablePriorityQueue queryQueue; + UpdateablePriorityQueue queryQueue; switch (policy) { case FAIR: queue = new FifoQueue<>(); @@ -542,17 +532,17 @@ public void setSchedulingPolicy(SchedulingPolicy policy) default: throw new UnsupportedOperationException("Unsupported scheduling policy: " + policy); } + schedulingPolicy = policy; while (!eligibleSubGroups.isEmpty()) { InternalResourceGroup group = eligibleSubGroups.poll(); - addOrUpdateSubGroup(group); + addOrUpdateSubGroup(queue, group); } eligibleSubGroups = queue; while (!queuedQueries.isEmpty()) { - QueryExecution query = queuedQueries.poll(); + ManagedQueryExecution query = queuedQueries.poll(); queryQueue.addOrUpdate(query, getQueryPriority(query.getSession())); } queuedQueries = queryQueue; - schedulingPolicy = policy; } } @@ -573,38 +563,6 @@ public void setJmxExport(boolean export) jmxExportListener.accept(this, export); } - @Override - public Duration getQueuedTimeLimit() - { - synchronized (root) { - return queuedTimeLimit; - } - } - - @Override - public void setQueuedTimeLimit(Duration queuedTimeLimit) - { - synchronized (root) { - this.queuedTimeLimit = queuedTimeLimit; - } - } - - @Override - public Duration getRunningTimeLimit() - { - synchronized (root) { - return runningTimeLimit; - } - } - - @Override - public void setRunningTimeLimit(Duration runningTimeLimit) - { - synchronized (root) { - this.runningTimeLimit = runningTimeLimit; - } - } - public InternalResourceGroup getOrCreateSubGroup(String name) { requireNonNull(name, "name is null"); @@ -623,12 +581,13 @@ public InternalResourceGroup getOrCreateSubGroup(String name) } } - public void run(QueryExecution query) + public void run(ManagedQueryExecution query) { synchronized (root) { - checkState(subGroups.isEmpty(), "Cannot add queries to %s. It is not a leaf group.", id); + if (!subGroups.isEmpty()) { + throw new PrestoException(INVALID_RESOURCE_GROUP, format("Cannot add queries to %s. It is not a leaf group.", id)); + } // Check all ancestors for capacity - query.setResourceGroup(id); InternalResourceGroup group = this; boolean canQueue = true; boolean canRun = true; @@ -655,13 +614,10 @@ public void run(QueryExecution query) queryFinished(query); } }); - if (query.getState().isDone()) { - queryFinished(query); - } } } - private void enqueueQuery(QueryExecution query) + private void enqueueQuery(ManagedQueryExecution query) { checkState(Thread.holdsLock(root), "Must hold lock to enqueue a query"); synchronized (root) { @@ -694,7 +650,7 @@ private void updateEligibility() } } - private void startInBackground(QueryExecution query) + private void startInBackground(ManagedQueryExecution query) { checkState(Thread.holdsLock(root), "Must hold lock to start a query"); synchronized (root) { @@ -710,7 +666,7 @@ private void startInBackground(QueryExecution query) } } - private void queryFinished(QueryExecution query) + private void queryFinished(ManagedQueryExecution query) { synchronized (root) { if (!runningQueries.contains(query) && !queuedQueries.contains(query)) { @@ -718,7 +674,7 @@ private void queryFinished(QueryExecution query) return; } // Only count the CPU time if the query succeeded, or the failure was the fault of the user - if (query.getState() == QueryState.FINISHED || query.getQueryInfo().getErrorType() == USER_ERROR) { + if (!query.getErrorCode().isPresent() || query.getErrorCode().get().getType() == USER_ERROR) { InternalResourceGroup group = this; while (group != null) { group.cpuUsageMillis = saturatedAdd(group.cpuUsageMillis, query.getTotalCpuTime().toMillis()); @@ -752,8 +708,8 @@ protected void internalRefreshStats() synchronized (root) { if (subGroups.isEmpty()) { cachedMemoryUsageBytes = 0; - for (QueryExecution query : runningQueries) { - cachedMemoryUsageBytes += query.getUserMemoryReservation(); + for (ManagedQueryExecution query : runningQueries) { + cachedMemoryUsageBytes += query.getUserMemoryReservation().toBytes(); } } else { @@ -796,7 +752,7 @@ protected boolean internalStartNext() if (!canRunMore()) { return false; } - QueryExecution query = queuedQueries.poll(); + ManagedQueryExecution query = queuedQueries.poll(); if (query != null) { startInBackground(query); return true; @@ -825,36 +781,19 @@ protected boolean internalStartNext() } } - protected void enforceTimeLimits() + private void addOrUpdateSubGroup(Queue queue, InternalResourceGroup group) { - checkState(Thread.holdsLock(root), "Must hold lock to enforce time limits"); - synchronized (root) { - for (InternalResourceGroup group : subGroups.values()) { - group.enforceTimeLimits(); - } - for (QueryExecution query : runningQueries) { - Duration runningTime = query.getQueryInfo().getQueryStats().getExecutionTime(); - if (runningQueries.contains(query) && runningTime != null && runningTime.compareTo(runningTimeLimit) > 0) { - query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "query exceeded resource group runtime limit")); - } - } - for (QueryExecution query : queuedQueries) { - Duration elapsedTime = query.getQueryInfo().getQueryStats().getElapsedTime(); - if (queuedQueries.contains(query) && elapsedTime != null && elapsedTime.compareTo(queuedTimeLimit) > 0) { - query.fail(new PrestoException(EXCEEDED_TIME_LIMIT, "query exceeded resource group queued time limit")); - } - } + if (schedulingPolicy == WEIGHTED_FAIR) { + ((WeightedFairQueue) queue).addOrUpdate(group, new Usage(group.getSchedulingWeight(), group.getRunningQueries())); + } + else { + ((UpdateablePriorityQueue) queue).addOrUpdate(group, getSubGroupSchedulingPriority(schedulingPolicy, group)); } } private void addOrUpdateSubGroup(InternalResourceGroup group) { - if (schedulingPolicy == WEIGHTED_FAIR) { - ((WeightedFairQueue) eligibleSubGroups).addOrUpdate(group, new Usage(group.getSchedulingWeight(), group.getRunningQueries())); - } - else { - ((UpdateablePriorityQueue) eligibleSubGroups).addOrUpdate(group, getSubGroupSchedulingPriority(schedulingPolicy, group)); - } + addOrUpdateSubGroup(eligibleSubGroups, group); } private static long getSubGroupSchedulingPriority(SchedulingPolicy policy, InternalResourceGroup group) @@ -985,7 +924,6 @@ public RootInternalResourceGroup(String name, BiConsumer getPathToRoot(ResourceGroupId id) } @Override - public void submit(Statement statement, QueryExecution queryExecution, SelectionContext selectionContext, Executor executor) + public void submit(Statement statement, ManagedQueryExecution queryExecution, SelectionContext selectionContext, Executor executor) { checkState(configurationManager.get() != null, "configurationManager not set"); createGroupIfNecessary(selectionContext, executor); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/NoOpResourceGroupManager.java b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/NoOpResourceGroupManager.java index b1a7a21aeb746..6673a400c1158 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/NoOpResourceGroupManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/NoOpResourceGroupManager.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.execution.resourceGroups; -import com.facebook.presto.execution.QueryExecution; +import com.facebook.presto.execution.ManagedQueryExecution; import com.facebook.presto.server.ResourceGroupInfo; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManagerFactory; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -31,7 +31,7 @@ public final class NoOpResourceGroupManager implements ResourceGroupManager { @Override - public void submit(Statement statement, QueryExecution queryExecution, SelectionContext selectionContext, Executor executor) + public void submit(Statement statement, ManagedQueryExecution queryExecution, SelectionContext selectionContext, Executor executor) { throw new UnsupportedOperationException(); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/ResourceGroupManager.java b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/ResourceGroupManager.java index 5c1c6e31d73d0..9218695f7e3de 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/ResourceGroupManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/ResourceGroupManager.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.execution.resourceGroups; -import com.facebook.presto.execution.QueryExecution; +import com.facebook.presto.execution.ManagedQueryExecution; import com.facebook.presto.server.ResourceGroupInfo; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManagerFactory; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -33,7 +33,7 @@ @ThreadSafe public interface ResourceGroupManager { - void submit(Statement statement, QueryExecution queryExecution, SelectionContext selectionContext, Executor executor); + void submit(Statement statement, ManagedQueryExecution queryExecution, SelectionContext selectionContext, Executor executor); SelectionContext selectGroup(SelectionCriteria criteria); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/AllAtOnceExecutionSchedule.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/AllAtOnceExecutionSchedule.java index c125b886d674f..6d16d4952525e 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/AllAtOnceExecutionSchedule.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/AllAtOnceExecutionSchedule.java @@ -24,6 +24,7 @@ import com.facebook.presto.sql.planner.plan.PlanVisitor; import com.facebook.presto.sql.planner.plan.RemoteSourceNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.UnionNode; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -147,6 +148,14 @@ public Void visitSemiJoin(SemiJoinNode node, Void context) return null; } + @Override + public Void visitSpatialJoin(SpatialJoinNode node, Void context) + { + node.getRight().accept(this, context); + node.getLeft().accept(this, context); + return null; + } + @Override public Void visitIndexJoin(IndexJoinNode node, Void context) { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/BucketNodeMap.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/BucketNodeMap.java new file mode 100644 index 0000000000000..87ef872edad78 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/BucketNodeMap.java @@ -0,0 +1,45 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler; + +import com.facebook.presto.metadata.Split; +import com.facebook.presto.spi.Node; + +import java.util.Optional; +import java.util.function.ToIntFunction; + +import static java.util.Objects.requireNonNull; + +public abstract class BucketNodeMap +{ + private final ToIntFunction splitToBucket; + + public BucketNodeMap(ToIntFunction splitToBucket) + { + this.splitToBucket = requireNonNull(splitToBucket, "splitToBucket is null"); + } + + public abstract int getBucketCount(); + + public abstract Optional getAssignedNode(int bucketedId); + + public abstract void assignBucketToNode(int bucketedId, Node node); + + public abstract boolean isDynamic(); + + public final Optional getAssignedNode(Split split) + { + return getAssignedNode(splitToBucket.applyAsInt(split)); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedBucketNodeMap.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedBucketNodeMap.java new file mode 100644 index 0000000000000..9e977edbe20ae --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedBucketNodeMap.java @@ -0,0 +1,61 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler; + +import com.facebook.presto.metadata.Split; +import com.facebook.presto.spi.Node; +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Optional; +import java.util.function.ToIntFunction; + +import static java.util.Objects.requireNonNull; + +// the bucket to node mapping is fixed and pre-assigned +public class FixedBucketNodeMap + extends BucketNodeMap +{ + private final List bucketToNode; + + public FixedBucketNodeMap(ToIntFunction splitToBucket, List bucketToNode) + { + super(splitToBucket); + this.bucketToNode = ImmutableList.copyOf(requireNonNull(bucketToNode, "bucketToNode is null")); + } + + @Override + public Optional getAssignedNode(int bucketedId) + { + return Optional.of(bucketToNode.get(bucketedId)); + } + + @Override + public int getBucketCount() + { + return bucketToNode.size(); + } + + @Override + public void assignBucketToNode(int bucketedId, Node node) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDynamic() + { + return false; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedCountScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedCountScheduler.java index e056a9f373c50..afafbe61179f0 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedCountScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedCountScheduler.java @@ -19,8 +19,9 @@ import com.google.common.annotations.VisibleForTesting; import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.OptionalInt; +import java.util.stream.IntStream; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @@ -30,13 +31,13 @@ public class FixedCountScheduler { public interface TaskScheduler { - RemoteTask scheduleTask(Node node, int partition, OptionalInt totalPartitions); + Optional scheduleTask(Node node, int partition, OptionalInt totalPartitions); } private final TaskScheduler taskScheduler; - private final Map partitionToNode; + private final List partitionToNode; - public FixedCountScheduler(SqlStageExecution stage, Map partitionToNode) + public FixedCountScheduler(SqlStageExecution stage, List partitionToNode) { requireNonNull(stage, "stage is null"); this.taskScheduler = stage::scheduleTask; @@ -44,7 +45,7 @@ public FixedCountScheduler(SqlStageExecution stage, Map partition } @VisibleForTesting - public FixedCountScheduler(TaskScheduler taskScheduler, Map partitionToNode) + public FixedCountScheduler(TaskScheduler taskScheduler, List partitionToNode) { this.taskScheduler = requireNonNull(taskScheduler, "taskScheduler is null"); this.partitionToNode = requireNonNull(partitionToNode, "partitionToNode is null"); @@ -54,8 +55,10 @@ public FixedCountScheduler(TaskScheduler taskScheduler, Map parti public ScheduleResult schedule() { OptionalInt totalPartitions = OptionalInt.of(partitionToNode.size()); - List newTasks = partitionToNode.entrySet().stream() - .map(entry -> taskScheduler.scheduleTask(entry.getValue(), entry.getKey(), totalPartitions)) + List newTasks = IntStream.range(0, partitionToNode.size()) + .mapToObj(partition -> taskScheduler.scheduleTask(partitionToNode.get(partition), partition, totalPartitions)) + .filter(Optional::isPresent) + .map(Optional::get) .collect(toImmutableList()); return new ScheduleResult(true, newTasks, 0); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedSourcePartitionedScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedSourcePartitionedScheduler.java index 64676b943618a..d360e7992bff9 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedSourcePartitionedScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/FixedSourcePartitionedScheduler.java @@ -17,29 +17,22 @@ import com.facebook.presto.execution.RemoteTask; import com.facebook.presto.execution.SqlStageExecution; import com.facebook.presto.execution.scheduler.ScheduleResult.BlockedReason; +import com.facebook.presto.execution.scheduler.group.DynamicLifespanScheduler; +import com.facebook.presto.execution.scheduler.group.FixedLifespanScheduler; +import com.facebook.presto.execution.scheduler.group.LifespanScheduler; import com.facebook.presto.metadata.Split; import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; import com.facebook.presto.split.SplitSource; -import com.facebook.presto.sql.planner.NodePartitionMap; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Streams; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; import io.airlift.log.Logger; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.ints.IntListIterator; - -import javax.annotation.concurrent.GuardedBy; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -55,6 +48,7 @@ import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.concurrent.MoreFutures.whenAnyComplete; +import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; public class FixedSourcePartitionedScheduler @@ -63,7 +57,7 @@ public class FixedSourcePartitionedScheduler private static final Logger log = Logger.get(FixedSourcePartitionedScheduler.class); private final SqlStageExecution stage; - private final NodePartitionMap partitioning; + private final List nodes; private final List sourceSchedulers; private final List partitionHandles; private boolean scheduledTasks; @@ -74,49 +68,54 @@ public FixedSourcePartitionedScheduler( Map splitSources, StageExecutionStrategy stageExecutionStrategy, List schedulingOrder, - NodePartitionMap partitioning, + List nodes, + BucketNodeMap bucketNodeMap, int splitBatchSize, - OptionalInt concurrentLifespans, + OptionalInt concurrentLifespansPerTask, NodeSelector nodeSelector, List partitionHandles) { requireNonNull(stage, "stage is null"); requireNonNull(splitSources, "splitSources is null"); - requireNonNull(partitioning, "partitioning is null"); + requireNonNull(bucketNodeMap, "bucketNodeMap is null"); + checkArgument(!requireNonNull(nodes, "nodes is null").isEmpty(), "nodes is empty"); requireNonNull(partitionHandles, "partitionHandles is null"); this.stage = stage; - this.partitioning = partitioning; + this.nodes = ImmutableList.copyOf(nodes); this.partitionHandles = ImmutableList.copyOf(partitionHandles); checkArgument(splitSources.keySet().equals(ImmutableSet.copyOf(schedulingOrder))); - FixedSplitPlacementPolicy splitPlacementPolicy = new FixedSplitPlacementPolicy(nodeSelector, partitioning, stage::getAllTasks); + BucketedSplitPlacementPolicy splitPlacementPolicy = new BucketedSplitPlacementPolicy(nodeSelector, nodes, bucketNodeMap, stage::getAllTasks); ArrayList sourceSchedulers = new ArrayList<>(); checkArgument( partitionHandles.equals(ImmutableList.of(NOT_PARTITIONED)) != stageExecutionStrategy.isAnyScanGroupedExecution(), "PartitionHandles should be [NOT_PARTITIONED] if and only if all scan nodes use ungrouped execution strategy"); - int effectiveConcurrentLifespans; - if (!concurrentLifespans.isPresent() || concurrentLifespans.getAsInt() > partitionHandles.size()) { - effectiveConcurrentLifespans = partitionHandles.size(); + int nodeCount = nodes.size(); + int concurrentLifespans; + if (concurrentLifespansPerTask.isPresent() && concurrentLifespansPerTask.getAsInt() * nodeCount <= partitionHandles.size()) { + concurrentLifespans = concurrentLifespansPerTask.getAsInt() * nodeCount; } else { - effectiveConcurrentLifespans = concurrentLifespans.getAsInt(); + concurrentLifespans = partitionHandles.size(); } boolean firstPlanNode = true; Optional groupedLifespanScheduler = Optional.empty(); for (PlanNodeId planNodeId : schedulingOrder) { SplitSource splitSource = splitSources.get(planNodeId); + boolean groupedExecutionForScanNode = stageExecutionStrategy.isGroupedExecution(planNodeId); SourceScheduler sourceScheduler = newSourcePartitionedSchedulerAsSourceScheduler( stage, planNodeId, splitSource, splitPlacementPolicy, - Math.max(splitBatchSize / effectiveConcurrentLifespans, 1)); + Math.max(splitBatchSize / concurrentLifespans, 1), + groupedExecutionForScanNode); - if (stageExecutionStrategy.isAnyScanGroupedExecution() && !stageExecutionStrategy.isGroupedExecution(planNodeId)) { + if (stageExecutionStrategy.isAnyScanGroupedExecution() && !groupedExecutionForScanNode) { sourceScheduler = new AsGroupedSourceScheduler(sourceScheduler); } sourceSchedulers.add(sourceScheduler); @@ -125,9 +124,22 @@ public FixedSourcePartitionedScheduler( firstPlanNode = false; if (!stageExecutionStrategy.isAnyScanGroupedExecution()) { sourceScheduler.startLifespan(Lifespan.taskWide(), NOT_PARTITIONED); + sourceScheduler.noMoreLifespans(); } else { - LifespanScheduler lifespanScheduler = new LifespanScheduler(partitioning, partitionHandles); + LifespanScheduler lifespanScheduler; + if (bucketNodeMap.isDynamic()) { + // Callee of the constructor guarantees dynamic bucket node map will only be + // used when the stage has no remote source. + // + // When the stage has no remote source, any scan is grouped execution guarantees + // all scan is grouped execution. + lifespanScheduler = new DynamicLifespanScheduler(bucketNodeMap, nodes, partitionHandles, concurrentLifespansPerTask); + } + else { + lifespanScheduler = new FixedLifespanScheduler(bucketNodeMap, partitionHandles, concurrentLifespansPerTask); + } + // Schedule the first few lifespans lifespanScheduler.scheduleInitial(sourceScheduler); // Schedule new lifespans for finished ones @@ -154,9 +166,12 @@ public ScheduleResult schedule() // schedule a task on every node in the distribution List newTasks = ImmutableList.of(); if (!scheduledTasks) { - OptionalInt totalPartitions = OptionalInt.of(partitioning.getPartitionToNode().size()); - newTasks = partitioning.getPartitionToNode().entrySet().stream() - .map(entry -> stage.scheduleTask(entry.getValue(), entry.getKey(), totalPartitions)) + OptionalInt totalPartitions = OptionalInt.of(nodes.size()); + newTasks = Streams.mapWithIndex( + nodes.stream(), + (node, id) -> stage.scheduleTask(node, toIntExact(id), totalPartitions)) + .filter(Optional::isPresent) + .map(Optional::get) .collect(toImmutableList()); scheduledTasks = true; } @@ -177,12 +192,16 @@ public ScheduleResult schedule() int splitsScheduled = 0; Iterator schedulerIterator = sourceSchedulers.iterator(); List driverGroupsToStart = ImmutableList.of(); + boolean shouldInvokeNoMoreDriverGroups = false; while (schedulerIterator.hasNext()) { SourceScheduler sourceScheduler = schedulerIterator.next(); for (Lifespan lifespan : driverGroupsToStart) { sourceScheduler.startLifespan(lifespan, partitionHandleFor(lifespan)); } + if (shouldInvokeNoMoreDriverGroups) { + sourceScheduler.noMoreLifespans(); + } ScheduleResult schedule = sourceScheduler.schedule(); splitsScheduled += schedule.getSplitsScheduled(); @@ -201,6 +220,10 @@ public ScheduleResult schedule() stage.schedulingComplete(sourceScheduler.getPlanNodeId()); schedulerIterator.remove(); sourceScheduler.close(); + shouldInvokeNoMoreDriverGroups = true; + } + else { + shouldInvokeNoMoreDriverGroups = false; } } @@ -226,26 +249,30 @@ public void close() sourceSchedulers.clear(); } - public static class FixedSplitPlacementPolicy + public static class BucketedSplitPlacementPolicy implements SplitPlacementPolicy { private final NodeSelector nodeSelector; - private final NodePartitionMap partitioning; + private final List allNodes; + private final BucketNodeMap bucketNodeMap; private final Supplier> remoteTasks; - public FixedSplitPlacementPolicy(NodeSelector nodeSelector, - NodePartitionMap partitioning, + public BucketedSplitPlacementPolicy( + NodeSelector nodeSelector, + List allNodes, + BucketNodeMap bucketNodeMap, Supplier> remoteTasks) { - this.nodeSelector = nodeSelector; - this.partitioning = partitioning; - this.remoteTasks = remoteTasks; + this.nodeSelector = requireNonNull(nodeSelector, "nodeSelector is null"); + this.allNodes = ImmutableList.copyOf(requireNonNull(allNodes, "allNodes is null")); + this.bucketNodeMap = requireNonNull(bucketNodeMap, "bucketNodeMap is null"); + this.remoteTasks = requireNonNull(remoteTasks, "remoteTasks is null"); } @Override public SplitPlacementResult computeAssignments(Set splits) { - return nodeSelector.computeAssignments(splits, remoteTasks.get(), partitioning); + return nodeSelector.computeAssignments(splits, remoteTasks.get(), bucketNodeMap); } @Override @@ -256,93 +283,12 @@ public void lockDownNodes() @Override public List allNodes() { - return ImmutableList.copyOf(partitioning.getPartitionToNode().values()); + return allNodes; } public Node getNodeForBucket(int bucketId) { - return partitioning.getPartitionToNode().get(partitioning.getBucketToPartition()[bucketId]); - } - } - - private static class LifespanScheduler - { - private final Int2ObjectMap driverGroupToNodeMap; - private final Map nodeToDriverGroupsMap; - private final List partitionHandles; - - private boolean initialScheduled; - private SettableFuture newDriverGroupReady = SettableFuture.create(); - @GuardedBy("this") - private final List recentlyCompletedDriverGroups = new ArrayList<>(); - - public LifespanScheduler(NodePartitionMap nodePartitionMap, List partitionHandles) - { - Map nodeToDriverGroupMap = new HashMap<>(); - Int2ObjectMap driverGroupToNodeMap = new Int2ObjectOpenHashMap<>(); - int[] bucketToPartition = nodePartitionMap.getBucketToPartition(); - Map partitionToNode = nodePartitionMap.getPartitionToNode(); - for (int bucket = 0; bucket < bucketToPartition.length; bucket++) { - int partition = bucketToPartition[bucket]; - Node node = partitionToNode.get(partition); - nodeToDriverGroupMap.computeIfAbsent(node, key -> new IntArrayList()).add(bucket); - driverGroupToNodeMap.put(bucket, node); - } - - this.driverGroupToNodeMap = driverGroupToNodeMap; - this.nodeToDriverGroupsMap = nodeToDriverGroupMap.entrySet().stream() - .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, entry -> entry.getValue().iterator())); - this.partitionHandles = requireNonNull(partitionHandles, "partitionHandles is null"); - } - - public void scheduleInitial(SourceScheduler scheduler) - { - checkState(!initialScheduled); - initialScheduled = true; - - for (Map.Entry entry : nodeToDriverGroupsMap.entrySet()) { - int driverGroupId = entry.getValue().nextInt(); - scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); - } - } - - public void onLifespanFinished(Iterable newlyCompletedDriverGroups) - { - checkState(initialScheduled); - - synchronized (this) { - for (Lifespan newlyCompletedDriverGroup : newlyCompletedDriverGroups) { - checkArgument(!newlyCompletedDriverGroup.isTaskWide()); - recentlyCompletedDriverGroups.add(newlyCompletedDriverGroup); - } - newDriverGroupReady.set(null); - } - } - - public SettableFuture schedule(SourceScheduler scheduler) - { - // Return a new future even if newDriverGroupReady has not finished. - // Returning the same SettableFuture instance could lead to ListenableFuture retaining too many listener objects. - - checkState(initialScheduled); - - List recentlyCompletedDriverGroups; - synchronized (this) { - recentlyCompletedDriverGroups = ImmutableList.copyOf(this.recentlyCompletedDriverGroups); - this.recentlyCompletedDriverGroups.clear(); - newDriverGroupReady = SettableFuture.create(); - } - - for (Lifespan driverGroup : recentlyCompletedDriverGroups) { - IntListIterator driverGroupsIterator = nodeToDriverGroupsMap.get(driverGroupToNodeMap.get(driverGroup.getId())); - if (!driverGroupsIterator.hasNext()) { - continue; - } - int driverGroupId = driverGroupsIterator.nextInt(); - scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); - } - - return newDriverGroupReady; + return bucketNodeMap.getAssignedNode(bucketId).get(); } } @@ -387,6 +333,13 @@ public void startLifespan(Lifespan lifespan, ConnectorPartitionHandle partitionH } started = true; sourceScheduler.startLifespan(Lifespan.taskWide(), NOT_PARTITIONED); + sourceScheduler.noMoreLifespans(); + } + + @Override + public void noMoreLifespans() + { + checkState(started); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NetworkTopology.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NetworkTopology.java index c0badaed5fbb1..583d5d3207998 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NetworkTopology.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NetworkTopology.java @@ -16,7 +16,6 @@ import com.facebook.presto.spi.HostAddress; import javax.annotation.concurrent.ThreadSafe; -import javax.validation.constraints.NotNull; import java.util.List; @@ -26,13 +25,11 @@ @ThreadSafe public interface NetworkTopology { - @NotNull NetworkLocation locate(HostAddress address); /** * Strings describing the meaning of each segment of a NetworkLocation returned from locate(). * This method must return a constant. */ - @NotNull List getLocationSegmentNames(); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeScheduler.java index f20c746b895f8..86da57359480e 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeScheduler.java @@ -20,7 +20,6 @@ import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.HostAddress; import com.facebook.presto.spi.Node; -import com.facebook.presto.sql.planner.NodePartitionMap; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; @@ -90,7 +89,7 @@ public NodeScheduler( this.maxSplitsPerNode = config.getMaxSplitsPerNode(); this.maxPendingSplitsPerTask = config.getMaxPendingSplitsPerTask(); this.nodeTaskMap = requireNonNull(nodeTaskMap, "nodeTaskMap is null"); - checkArgument(maxSplitsPerNode > maxPendingSplitsPerTask, "maxSplitsPerNode must be > maxPendingSplitsPerTask"); + checkArgument(maxSplitsPerNode >= maxPendingSplitsPerTask, "maxSplitsPerNode must be > maxPendingSplitsPerTask"); this.useNetworkTopology = !config.getNetworkTopology().equals(NetworkTopologyType.LEGACY); ImmutableList.Builder builder = ImmutableList.builder(); @@ -266,15 +265,15 @@ public static SplitPlacementResult selectDistributionNodes( int maxPendingSplitsPerTask, Set splits, List existingTasks, - NodePartitionMap partitioning) + BucketNodeMap bucketNodeMap) { Multimap assignments = HashMultimap.create(); NodeAssignmentStats assignmentStats = new NodeAssignmentStats(nodeTaskMap, nodeMap, existingTasks); Set blockedNodes = new HashSet<>(); for (Split split : splits) { - // node placement is forced by the partitioning - Node node = partitioning.getNode(split); + // node placement is forced by the bucket to node map + Node node = bucketNodeMap.getAssignedNode(split).get(); // if node is full, don't schedule now, which will push back on the scheduling of splits if (assignmentStats.getTotalSplitCount(node) < maxSplitsPerNode || diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeSelector.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeSelector.java index bcdc109010123..ae346530a44e3 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeSelector.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/NodeSelector.java @@ -16,7 +16,6 @@ import com.facebook.presto.execution.RemoteTask; import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.Node; -import com.facebook.presto.sql.planner.NodePartitionMap; import com.google.common.collect.ImmutableSet; import java.util.List; @@ -55,5 +54,5 @@ default List selectRandomNodes(int limit) * If we cannot find an assignment for a split, it is not included in the map. Also returns a future indicating when * to reattempt scheduling of this batch of splits, if some of them could not be scheduled. */ - SplitPlacementResult computeAssignments(Set splits, List existingTasks, NodePartitionMap partitioning); + SplitPlacementResult computeAssignments(Set splits, List existingTasks, BucketNodeMap bucketNodeMap); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/PhasedExecutionSchedule.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/PhasedExecutionSchedule.java index 3750de9b05810..3dcb106b10bb6 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/PhasedExecutionSchedule.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/PhasedExecutionSchedule.java @@ -24,6 +24,7 @@ import com.facebook.presto.sql.planner.plan.PlanVisitor; import com.facebook.presto.sql.planner.plan.RemoteSourceNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.UnionNode; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -205,6 +206,12 @@ public Set visitJoin(JoinNode node, PlanFragmentId currentFragme return processJoin(node.getRight(), node.getLeft(), currentFragmentId); } + @Override + public Set visitSpatialJoin(SpatialJoinNode node, PlanFragmentId currentFragmentId) + { + return processJoin(node.getRight(), node.getLeft(), currentFragmentId); + } + @Override public Set visitSemiJoin(SemiJoinNode node, PlanFragmentId currentFragmentId) { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScaledWriterScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScaledWriterScheduler.java index 50474325ed2c8..4b2ba9a56e290 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScaledWriterScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScaledWriterScheduler.java @@ -24,6 +24,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; @@ -39,12 +40,7 @@ public class ScaledWriterScheduler implements StageScheduler { - private interface TaskScheduler - { - RemoteTask scheduleTask(Node node, int partition, OptionalInt totalPartitions); - } - - private final TaskScheduler taskScheduler; + private final SqlStageExecution stage; private final Supplier> sourceTasksProvider; private final Supplier> writerTasksProvider; private final NodeSelector nodeSelector; @@ -62,8 +58,7 @@ public ScaledWriterScheduler( ScheduledExecutorService executor, DataSize writerMinSize) { - requireNonNull(stage, "stage is null"); - this.taskScheduler = stage::scheduleTask; + this.stage = requireNonNull(stage, "stage is null"); this.sourceTasksProvider = requireNonNull(sourceTasksProvider, "sourceTasksProvider is null"); this.writerTasksProvider = requireNonNull(writerTasksProvider, "writerTasksProvider is null"); this.nodeSelector = requireNonNull(nodeSelector, "nodeSelector is null"); @@ -125,8 +120,11 @@ private List scheduleTasks(int count) ImmutableList.Builder tasks = ImmutableList.builder(); for (Node node : nodes) { - tasks.add(taskScheduler.scheduleTask(node, scheduledNodes.size(), OptionalInt.empty())); - scheduledNodes.add(node); + Optional remoteTask = stage.scheduleTask(node, scheduledNodes.size(), OptionalInt.empty()); + remoteTask.ifPresent(task -> { + tasks.add(task); + scheduledNodes.add(node); + }); } return tasks.build(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScheduleResult.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScheduleResult.java index 975032a3d220e..3df9ab7039961 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScheduleResult.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/ScheduleResult.java @@ -110,7 +110,7 @@ public String toString() return toStringHelper(this) .add("finished", finished) .add("newTasks", newTasks.size()) - .add("blocked", blocked.isDone()) + .add("blocked", !blocked.isDone()) .add("splitsScheduled", splitsScheduled) .add("blockedReason", blockedReason) .toString(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SimpleNodeSelector.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SimpleNodeSelector.java index 63b40ca827a2f..1b4d78588aaf3 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SimpleNodeSelector.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SimpleNodeSelector.java @@ -19,7 +19,6 @@ import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.sql.planner.NodePartitionMap; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; @@ -170,8 +169,8 @@ else if (!splitWaitingForAnyNode) { } @Override - public SplitPlacementResult computeAssignments(Set splits, List existingTasks, NodePartitionMap partitioning) + public SplitPlacementResult computeAssignments(Set splits, List existingTasks, BucketNodeMap bucketNodeMap) { - return selectDistributionNodes(nodeMap.get().get(), nodeTaskMap, maxSplitsPerNode, maxPendingSplitsPerTask, splits, existingTasks, partitioning); + return selectDistributionNodes(nodeMap.get().get(), nodeTaskMap, maxSplitsPerNode, maxPendingSplitsPerTask, splits, existingTasks, bucketNodeMap); } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourcePartitionedScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourcePartitionedScheduler.java index 3125948c3e03a..807991503ded0 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourcePartitionedScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourcePartitionedScheduler.java @@ -16,7 +16,7 @@ import com.facebook.presto.execution.Lifespan; import com.facebook.presto.execution.RemoteTask; import com.facebook.presto.execution.SqlStageExecution; -import com.facebook.presto.execution.scheduler.FixedSourcePartitionedScheduler.FixedSplitPlacementPolicy; +import com.facebook.presto.execution.scheduler.FixedSourcePartitionedScheduler.BucketedSplitPlacementPolicy; import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; @@ -57,7 +57,7 @@ import static java.util.Objects.requireNonNull; public class SourcePartitionedScheduler - implements StageScheduler + implements SourceScheduler { private enum State { @@ -89,9 +89,10 @@ private enum State private final SplitPlacementPolicy splitPlacementPolicy; private final int splitBatchSize; private final PlanNodeId partitionedNode; - private final boolean autoDropCompletedLifespans; + private final boolean groupedExecution; private final Map scheduleGroups = new HashMap<>(); + private boolean noMoreScheduleGroups; private State state = State.INITIALIZED; private SettableFuture whenFinishedOrNewLifespanAdded = SettableFuture.create(); @@ -102,7 +103,7 @@ private SourcePartitionedScheduler( SplitSource splitSource, SplitPlacementPolicy splitPlacementPolicy, int splitBatchSize, - boolean autoDropCompletedLifespans) + boolean groupedExecution) { this.stage = requireNonNull(stage, "stage is null"); this.partitionedNode = requireNonNull(partitionedNode, "partitionedNode is null"); @@ -111,8 +112,7 @@ private SourcePartitionedScheduler( checkArgument(splitBatchSize > 0, "splitBatchSize must be at least one"); this.splitBatchSize = splitBatchSize; - - this.autoDropCompletedLifespans = autoDropCompletedLifespans; + this.groupedExecution = groupedExecution; } public PlanNodeId getPlanNodeId() @@ -134,12 +134,25 @@ public static StageScheduler newSourcePartitionedSchedulerAsStageScheduler( SplitPlacementPolicy splitPlacementPolicy, int splitBatchSize) { - // TODO: SourcePartitionedScheduler should be in two parts. - // The first part fulfills the responsibility of SourceScheduler. - // The second part wraps it to provide a StageScheduler. - SourcePartitionedScheduler result = new SourcePartitionedScheduler(stage, partitionedNode, splitSource, splitPlacementPolicy, splitBatchSize, true); - result.startLifespan(Lifespan.taskWide(), NOT_PARTITIONED); - return result; + SourcePartitionedScheduler sourcePartitionedScheduler = new SourcePartitionedScheduler(stage, partitionedNode, splitSource, splitPlacementPolicy, splitBatchSize, false); + sourcePartitionedScheduler.startLifespan(Lifespan.taskWide(), NOT_PARTITIONED); + sourcePartitionedScheduler.noMoreLifespans(); + + return new StageScheduler() { + @Override + public ScheduleResult schedule() + { + ScheduleResult scheduleResult = sourcePartitionedScheduler.schedule(); + sourcePartitionedScheduler.drainCompletedLifespans(); + return scheduleResult; + } + + @Override + public void close() + { + sourcePartitionedScheduler.close(); + } + }; } /** @@ -158,44 +171,14 @@ public static SourceScheduler newSourcePartitionedSchedulerAsSourceScheduler( PlanNodeId partitionedNode, SplitSource splitSource, SplitPlacementPolicy splitPlacementPolicy, - int splitBatchSize) + int splitBatchSize, + boolean groupedExecution) { - SourcePartitionedScheduler sourcePartitionedScheduler = new SourcePartitionedScheduler(stage, partitionedNode, splitSource, splitPlacementPolicy, splitBatchSize, false); - return new SourceScheduler() - { - @Override - public ScheduleResult schedule() - { - return sourcePartitionedScheduler.schedule(); - } - - @Override - public void close() - { - sourcePartitionedScheduler.close(); - } - - @Override - public PlanNodeId getPlanNodeId() - { - return sourcePartitionedScheduler.getPlanNodeId(); - } - - @Override - public void startLifespan(Lifespan lifespan, ConnectorPartitionHandle partitionHandle) - { - sourcePartitionedScheduler.startLifespan(lifespan, partitionHandle); - } - - @Override - public List drainCompletedLifespans() - { - return sourcePartitionedScheduler.drainCompletedLifespans(); - } - }; + return new SourcePartitionedScheduler(stage, partitionedNode, splitSource, splitPlacementPolicy, splitBatchSize, groupedExecution); } - private synchronized void startLifespan(Lifespan lifespan, ConnectorPartitionHandle partitionHandle) + @Override + public synchronized void startLifespan(Lifespan lifespan, ConnectorPartitionHandle partitionHandle) { checkState(state == State.INITIALIZED || state == State.SPLITS_ADDED); scheduleGroups.put(lifespan, new ScheduleGroup(partitionHandle)); @@ -203,6 +186,18 @@ private synchronized void startLifespan(Lifespan lifespan, ConnectorPartitionHan whenFinishedOrNewLifespanAdded = SettableFuture.create(); } + @Override + public synchronized void noMoreLifespans() + { + checkState(state == State.INITIALIZED || state == State.SPLITS_ADDED); + noMoreScheduleGroups = true; + // The listener is waiting for "new lifespan added" because new lifespans would bring new works to scheduler. + // "No more lifespans" would be of interest to such listeners because it signals that is not going to happen anymore, + // and the listener should stop waiting. + whenFinishedOrNewLifespanAdded.set(null); + whenFinishedOrNewLifespanAdded = SettableFuture.create(); + } + @Override public synchronized ScheduleResult schedule() { @@ -263,6 +258,7 @@ else if (pendingSplits.isEmpty()) { Multimap splitAssignment = ImmutableMultimap.of(); if (!pendingSplits.isEmpty()) { if (!scheduleGroup.placementFuture.isDone()) { + anyBlockedOnPlacements = true; continue; } @@ -294,7 +290,7 @@ else if (pendingSplits.isEmpty()) { if (pendingSplits.isEmpty() && scheduleGroup.state == ScheduleGroupState.NO_MORE_SPLITS) { scheduleGroup.state = ScheduleGroupState.DONE; if (!lifespan.isTaskWide()) { - Node node = ((FixedSplitPlacementPolicy) splitPlacementPolicy).getNodeForBucket(lifespan.getId()); + Node node = ((BucketedSplitPlacementPolicy) splitPlacementPolicy).getNodeForBucket(lifespan.getId()); noMoreSplitsNotification = ImmutableMultimap.of(node, lifespan); } } @@ -314,10 +310,6 @@ else if (pendingSplits.isEmpty()) { } } - if (autoDropCompletedLifespans) { - drainCompletedLifespans(); - } - // * `splitSource.isFinished` invocation may fail after `splitSource.close` has been invoked. // If state is NO_MORE_SPLITS/FINISHED, splitSource.isFinished has previously returned true, and splitSource is closed now. // * Even if `splitSource.isFinished()` return true, it is not necessarily safe to tear down the split source. @@ -325,7 +317,7 @@ else if (pendingSplits.isEmpty()) { // which may contain recently published splits. We must not ignore those. // * If any scheduleGroup is still in DISCOVERING_SPLITS state, it means it hasn't realized that there will be no more splits. // Next time it invokes getNextBatch, it will realize that. However, the invocation will fail we tear down splitSource now. - if ((state == State.NO_MORE_SPLITS || state == State.FINISHED) || (scheduleGroups.isEmpty() && splitSource.isFinished())) { + if ((state == State.NO_MORE_SPLITS || state == State.FINISHED) || (noMoreScheduleGroups && scheduleGroups.isEmpty() && splitSource.isFinished())) { switch (state) { case INITIALIZED: // We have not scheduled a single split so far. @@ -353,8 +345,19 @@ else if (pendingSplits.isEmpty()) { return new ScheduleResult(false, overallNewTasks.build(), overallSplitAssignmentCount); } - // Only try to finalize task creation when scheduling would block - overallNewTasks.addAll(finalizeTaskCreationIfNecessary()); + if (anyBlockedOnPlacements || groupedExecution) { + // In a broadcast join, output buffers of the tasks in build source stage have to + // hold onto all data produced before probe side task scheduling finishes, + // even if the data is acknowledged by all known consumers. This is because + // new consumers may be added until the probe side task scheduling finishes. + // + // As a result, the following line is necessary to prevent deadlock + // due to neither build nor probe can make any progress. + // The build side blocks due to a full output buffer. + // In the meantime the probe side split cannot be consumed since + // builder side hash table construction has not finished. + overallNewTasks.addAll(finalizeTaskCreationIfNecessary()); + } ScheduleResult.BlockedReason blockedReason; if (anyBlockedOnNextSplitBatch) { @@ -398,7 +401,8 @@ public void close() splitSource.close(); } - private synchronized List drainCompletedLifespans() + @Override + public synchronized List drainCompletedLifespans() { if (scheduleGroups.isEmpty()) { // Invoking splitSource.isFinished would fail if it was already closed, which is possible if scheduleGroups is empty. diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourceScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourceScheduler.java index e244766d23f36..dfc70779474da 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourceScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SourceScheduler.java @@ -20,7 +20,7 @@ import java.util.List; -interface SourceScheduler +public interface SourceScheduler { ScheduleResult schedule(); @@ -30,5 +30,7 @@ interface SourceScheduler void startLifespan(Lifespan lifespan, ConnectorPartitionHandle partitionHandle); + void noMoreLifespans(); + List drainCompletedLifespans(); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SqlQueryScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SqlQueryScheduler.java index 138cdce8db269..11b4d304818f7 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SqlQueryScheduler.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/SqlQueryScheduler.java @@ -17,6 +17,7 @@ import com.facebook.presto.OutputBuffers.OutputBufferId; import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.BasicStageStats; import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.execution.NodeTaskMap; import com.facebook.presto.execution.QueryState; @@ -53,13 +54,13 @@ import java.net.URI; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; @@ -71,7 +72,10 @@ import static com.facebook.presto.SystemSessionProperties.getConcurrentLifespansPerNode; import static com.facebook.presto.SystemSessionProperties.getWriterMinSize; +import static com.facebook.presto.SystemSessionProperties.isDynamicSchduleForGroupedExecution; import static com.facebook.presto.connector.ConnectorId.isInternalSystemConnector; +import static com.facebook.presto.execution.BasicStageStats.aggregateBasicStageStats; +import static com.facebook.presto.execution.SqlStageExecution.createSqlStageExecution; import static com.facebook.presto.execution.StageState.ABORTED; import static com.facebook.presto.execution.StageState.CANCELED; import static com.facebook.presto.execution.StageState.FAILED; @@ -88,6 +92,7 @@ import static com.facebook.presto.util.Failures.checkCondition; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableSet.toImmutableSet; @@ -96,7 +101,6 @@ import static io.airlift.concurrent.MoreFutures.tryGetFutureValue; import static io.airlift.concurrent.MoreFutures.whenAnyComplete; import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; -import static java.lang.Math.toIntExact; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -118,7 +122,47 @@ public class SqlQueryScheduler private final boolean summarizeTaskInfo; private final AtomicBoolean started = new AtomicBoolean(); - public SqlQueryScheduler(QueryStateMachine queryStateMachine, + public static SqlQueryScheduler createSqlQueryScheduler( + QueryStateMachine queryStateMachine, + LocationFactory locationFactory, + StageExecutionPlan plan, + NodePartitioningManager nodePartitioningManager, + NodeScheduler nodeScheduler, + RemoteTaskFactory remoteTaskFactory, + Session session, + boolean summarizeTaskInfo, + int splitBatchSize, + ExecutorService queryExecutor, + ScheduledExecutorService schedulerExecutor, + FailureDetector failureDetector, + OutputBuffers rootOutputBuffers, + NodeTaskMap nodeTaskMap, + ExecutionPolicy executionPolicy, + SplitSchedulerStats schedulerStats) + { + SqlQueryScheduler sqlQueryScheduler = new SqlQueryScheduler( + queryStateMachine, + locationFactory, + plan, + nodePartitioningManager, + nodeScheduler, + remoteTaskFactory, + session, + summarizeTaskInfo, + splitBatchSize, + queryExecutor, + schedulerExecutor, + failureDetector, + rootOutputBuffers, + nodeTaskMap, + executionPolicy, + schedulerStats); + sqlQueryScheduler.initialize(); + return sqlQueryScheduler; + } + + private SqlQueryScheduler( + QueryStateMachine queryStateMachine, LocationFactory locationFactory, StageExecutionPlan plan, NodePartitioningManager nodePartitioningManager, @@ -177,7 +221,12 @@ public SqlQueryScheduler(QueryStateMachine queryStateMachine, this.stageLinkages = stageLinkages.build(); this.executor = queryExecutor; + } + // this is a separate method to ensure that the `this` reference is not leaked during construction + private void initialize() + { + SqlStageExecution rootStage = stages.get(rootStageId); rootStage.addStateChangeListener(state -> { if (state == FINISHED) { queryStateMachine.transitionToFinishing(); @@ -188,7 +237,7 @@ else if (state == CANCELED) { } }); - for (SqlStageExecution stage : stages) { + for (SqlStageExecution stage : stages.values()) { stage.addStateChangeListener(state -> { if (queryStateMachine.isDone()) { return; @@ -208,6 +257,16 @@ else if (queryStateMachine.getQueryState() == QueryState.STARTING) { } }); } + + // when query is done or any time a stage completes, attempt to transition query to "final query info ready" + queryStateMachine.addStateChangeListener(newState -> { + if (newState.isDone()) { + queryStateMachine.updateQueryInfo(Optional.ofNullable(getStageInfo())); + } + }); + for (SqlStageExecution stage : stages.values()) { + stage.addFinalStageInfoListener(status -> queryStateMachine.updateQueryInfo(Optional.ofNullable(getStageInfo()))); + } } private static void updateQueryOutputLocations(QueryStateMachine queryStateMachine, OutputBufferId rootBufferId, Set tasks, boolean noMoreExchangeLocations) @@ -240,7 +299,7 @@ private List createStages( ImmutableList.Builder stages = ImmutableList.builder(); StageId stageId = new StageId(queryStateMachine.getQueryId(), nextStageId.getAndIncrement()); - SqlStageExecution stage = new SqlStageExecution( + SqlStageExecution stage = createSqlStageExecution( stageId, locationFactory.createStageLocation(stageId), plan.getFragment(), @@ -276,37 +335,62 @@ else if (partitioningHandle.equals(SCALED_WRITER_DISTRIBUTION)) { bucketToPartition = Optional.of(new int[1]); } else { - // nodes are pre determined by the nodePartitionMap - NodePartitionMap nodePartitionMap = partitioningCache.apply(plan.getFragment().getPartitioning()); - long nodeCount = nodePartitionMap.getPartitionToNode().values().stream().distinct().count(); - OptionalInt concurrentLifespansPerTask = getConcurrentLifespansPerNode(session); - Map splitSources = plan.getSplitSources(); if (!splitSources.isEmpty()) { + // contains local source List schedulingOrder = plan.getFragment().getPartitionedSources(); + ConnectorId connectorId = partitioningHandle.getConnectorId().orElseThrow(IllegalStateException::new); List connectorPartitionHandles; - if (plan.getFragment().getStageExecutionStrategy().isAnyScanGroupedExecution()) { + boolean groupedExecutionForStage = plan.getFragment().getStageExecutionStrategy().isAnyScanGroupedExecution(); + if (groupedExecutionForStage) { connectorPartitionHandles = nodePartitioningManager.listPartitionHandles(session, partitioningHandle); - checkState(connectorPartitionHandles.size() == nodePartitionMap.getBucketToPartition().length); checkState(!ImmutableList.of(NOT_PARTITIONED).equals(connectorPartitionHandles)); } else { connectorPartitionHandles = ImmutableList.of(NOT_PARTITIONED); } + + BucketNodeMap bucketNodeMap; + List stageNodeList; + if (plan.getSubStages().isEmpty()) { + // no remote source + boolean preferDynamic = groupedExecutionForStage && isDynamicSchduleForGroupedExecution(session); + bucketNodeMap = nodePartitioningManager.getBucketNodeMap(session, partitioningHandle, preferDynamic); + if (bucketNodeMap.isDynamic()) { + verify(preferDynamic); + } + + stageNodeList = new ArrayList<>(nodeScheduler.createNodeSelector(connectorId).allNodes()); + Collections.shuffle(stageNodeList); + bucketToPartition = Optional.empty(); + } + else { + // remote source requires nodePartitionMap + NodePartitionMap nodePartitionMap = partitioningCache.apply(plan.getFragment().getPartitioning()); + if (groupedExecutionForStage) { + checkState(connectorPartitionHandles.size() == nodePartitionMap.getBucketToPartition().length); + } + stageNodeList = nodePartitionMap.getPartitionToNode(); + bucketNodeMap = nodePartitionMap.asBucketNodeMap(); + bucketToPartition = Optional.of(nodePartitionMap.getBucketToPartition()); + } + stageSchedulers.put(stageId, new FixedSourcePartitionedScheduler( stage, splitSources, plan.getFragment().getStageExecutionStrategy(), schedulingOrder, - nodePartitionMap, + stageNodeList, + bucketNodeMap, splitBatchSize, - concurrentLifespansPerTask.isPresent() ? OptionalInt.of(toIntExact(concurrentLifespansPerTask.getAsInt() * nodeCount)) : OptionalInt.empty(), - nodeScheduler.createNodeSelector(null), + getConcurrentLifespansPerNode(session), + nodeScheduler.createNodeSelector(connectorId), connectorPartitionHandles)); - bucketToPartition = Optional.of(nodePartitionMap.getBucketToPartition()); } else { - Map partitionToNode = nodePartitionMap.getPartitionToNode(); + // all sources are remote + NodePartitionMap nodePartitionMap = partitioningCache.apply(plan.getFragment().getPartitioning()); + List partitionToNode = nodePartitionMap.getPartitionToNode(); // todo this should asynchronously wait a standard timeout period before failing checkCondition(!partitionToNode.isEmpty(), NO_NODES_AVAILABLE, "No worker nodes available"); stageSchedulers.put(stageId, new FixedCountScheduler(stage, partitionToNode)); @@ -373,6 +457,15 @@ else if (partitioningHandle.equals(SCALED_WRITER_DISTRIBUTION)) { return stages.build(); } + public BasicStageStats getBasicStageStats() + { + List stageStats = stages.values().stream() + .map(SqlStageExecution::getBasicStageStats) + .collect(toImmutableList()); + + return aggregateBasicStageStats(stageStats); + } + public StageInfo getStageInfo() { Map stageInfos = stages.values().stream() diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/TopologyAwareNodeSelector.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/TopologyAwareNodeSelector.java index 565641a27a94b..34c94fee1a7d2 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/TopologyAwareNodeSelector.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/TopologyAwareNodeSelector.java @@ -20,7 +20,6 @@ import com.facebook.presto.spi.HostAddress; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.sql.planner.NodePartitionMap; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.HashMultimap; @@ -219,9 +218,9 @@ private int calculateMaxPendingSplits(int splitAffinity, int totalDepth) } @Override - public SplitPlacementResult computeAssignments(Set splits, List existingTasks, NodePartitionMap partitioning) + public SplitPlacementResult computeAssignments(Set splits, List existingTasks, BucketNodeMap bucketNodeMap) { - return selectDistributionNodes(nodeMap.get().get(), nodeTaskMap, maxSplitsPerNode, maxPendingSplitsPerTask, splits, existingTasks, partitioning); + return selectDistributionNodes(nodeMap.get().get(), nodeTaskMap, maxSplitsPerNode, maxPendingSplitsPerTask, splits, existingTasks, bucketNodeMap); } @Nullable diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicBucketNodeMap.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicBucketNodeMap.java new file mode 100644 index 0000000000000..4bfb13ca33bb4 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicBucketNodeMap.java @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler.group; + +import com.facebook.presto.execution.scheduler.BucketNodeMap; +import com.facebook.presto.metadata.Split; +import com.facebook.presto.spi.Node; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.Optional; +import java.util.function.ToIntFunction; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +public class DynamicBucketNodeMap + extends BucketNodeMap +{ + private final int bucketCount; + private final Int2ObjectMap bucketToNode = new Int2ObjectOpenHashMap<>(); + + public DynamicBucketNodeMap(ToIntFunction splitToBucket, int bucketCount) + { + super(splitToBucket); + checkArgument(bucketCount > 0, "bucketCount must be positive"); + this.bucketCount = bucketCount; + } + + @Override + public Optional getAssignedNode(int bucketedId) + { + return Optional.ofNullable(bucketToNode.get(bucketedId)); + } + + @Override + public int getBucketCount() + { + return bucketCount; + } + + @Override + public void assignBucketToNode(int bucketedId, Node node) + { + checkArgument(bucketedId >= 0 && bucketedId < bucketCount); + requireNonNull(node, "node is null"); + checkState(!bucketToNode.containsKey(bucketedId), "bucket already assigned"); + bucketToNode.put(bucketedId, node); + } + + @Override + public boolean isDynamic() + { + return true; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicLifespanScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicLifespanScheduler.java new file mode 100644 index 0000000000000..92f926b297d34 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/DynamicLifespanScheduler.java @@ -0,0 +1,150 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler.group; + +import com.facebook.presto.execution.Lifespan; +import com.facebook.presto.execution.scheduler.BucketNodeMap; +import com.facebook.presto.execution.scheduler.SourceScheduler; +import com.facebook.presto.spi.Node; +import com.facebook.presto.spi.connector.ConnectorPartitionHandle; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.SettableFuture; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntListIterator; + +import javax.annotation.concurrent.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalInt; +import java.util.stream.IntStream; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static java.util.Collections.unmodifiableList; +import static java.util.Objects.requireNonNull; + +/** + * See {@link LifespanScheduler} about thread safety + */ +public class DynamicLifespanScheduler + implements LifespanScheduler +{ + private final BucketNodeMap bucketNodeMap; + private final List allNodes; + private final List partitionHandles; + private final OptionalInt concurrentLifespansPerTask; + + private final IntListIterator driverGroups; + + // initialScheduled does not need to be guarded because this object + // is safely published after its mutation. + private boolean initialScheduled; + // Write to newDriverGroupReady field is guarded. Read of the reference + // is either guarded, or is guaranteed to happen in the same thread as the write. + private SettableFuture newDriverGroupReady = SettableFuture.create(); + + @GuardedBy("this") + private final List recentlyCompletedDriverGroups = new ArrayList<>(); + + public DynamicLifespanScheduler(BucketNodeMap bucketNodeMap, List allNodes, List partitionHandles, OptionalInt concurrentLifespansPerTask) + { + this.bucketNodeMap = requireNonNull(bucketNodeMap, "bucketNodeMap is null"); + this.allNodes = requireNonNull(allNodes, "allNodes is null"); + this.partitionHandles = unmodifiableList(new ArrayList<>( + requireNonNull(partitionHandles, "partitionHandles is null"))); + + this.concurrentLifespansPerTask = requireNonNull(concurrentLifespansPerTask, "concurrentLifespansPerTask is null"); + concurrentLifespansPerTask.ifPresent(lifespansPerTask -> checkArgument(lifespansPerTask >= 1, "concurrentLifespansPerTask must be great or equal to 1 if present")); + + int bucketCount = partitionHandles.size(); + verify(bucketCount > 0); + this.driverGroups = new IntArrayList(IntStream.range(0, bucketCount).toArray()).iterator(); + } + + @Override + public void scheduleInitial(SourceScheduler scheduler) + { + checkState(!initialScheduled); + initialScheduled = true; + + int driverGroupsScheduledPerTask = 0; + while (driverGroups.hasNext()) { + for (int i = 0; i < allNodes.size() && driverGroups.hasNext(); i++) { + int driverGroupId = driverGroups.nextInt(); + checkState(!bucketNodeMap.getAssignedNode(driverGroupId).isPresent()); + bucketNodeMap.assignBucketToNode(driverGroupId, allNodes.get(i)); + scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); + } + + driverGroupsScheduledPerTask++; + if (concurrentLifespansPerTask.isPresent() && driverGroupsScheduledPerTask == concurrentLifespansPerTask.getAsInt()) { + break; + } + } + + if (!driverGroups.hasNext()) { + scheduler.noMoreLifespans(); + } + } + + @Override + public void onLifespanFinished(Iterable newlyCompletedDriverGroups) + { + checkState(initialScheduled); + + SettableFuture newDriverGroupReady; + synchronized (this) { + for (Lifespan newlyCompletedDriverGroup : newlyCompletedDriverGroups) { + checkArgument(!newlyCompletedDriverGroup.isTaskWide()); + recentlyCompletedDriverGroups.add(newlyCompletedDriverGroup); + } + newDriverGroupReady = this.newDriverGroupReady; + } + newDriverGroupReady.set(null); + } + + @Override + public SettableFuture schedule(SourceScheduler scheduler) + { + // Return a new future even if newDriverGroupReady has not finished. + // Returning the same SettableFuture instance could lead to ListenableFuture retaining too many listener objects. + + checkState(initialScheduled); + + List recentlyCompletedDriverGroups; + synchronized (this) { + recentlyCompletedDriverGroups = ImmutableList.copyOf(this.recentlyCompletedDriverGroups); + this.recentlyCompletedDriverGroups.clear(); + newDriverGroupReady = SettableFuture.create(); + } + + for (Lifespan driverGroup : recentlyCompletedDriverGroups) { + if (!driverGroups.hasNext()) { + break; + } + int driverGroupId = driverGroups.nextInt(); + + Node nodeForCompletedDriverGroup = bucketNodeMap.getAssignedNode(driverGroup.getId()).orElseThrow(IllegalStateException::new); + bucketNodeMap.assignBucketToNode(driverGroupId, nodeForCompletedDriverGroup); + scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); + } + + if (!driverGroups.hasNext()) { + scheduler.noMoreLifespans(); + } + return newDriverGroupReady; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/FixedLifespanScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/FixedLifespanScheduler.java new file mode 100644 index 0000000000000..48420ec5ea65d --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/FixedLifespanScheduler.java @@ -0,0 +1,156 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler.group; + +import com.facebook.presto.execution.Lifespan; +import com.facebook.presto.execution.scheduler.BucketNodeMap; +import com.facebook.presto.execution.scheduler.SourceScheduler; +import com.facebook.presto.spi.Node; +import com.facebook.presto.spi.connector.ConnectorPartitionHandle; +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.SettableFuture; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntListIterator; + +import javax.annotation.concurrent.GuardedBy; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.OptionalInt; + +import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableMap.toImmutableMap; +import static java.util.Objects.requireNonNull; + +/** + * See {@link LifespanScheduler} about thread safety + */ +public class FixedLifespanScheduler + implements LifespanScheduler +{ + private final Int2ObjectMap driverGroupToNodeMap; + private final Map nodeToDriverGroupsMap; + private final List partitionHandles; + private final OptionalInt concurrentLifespansPerTask; + + private boolean initialScheduled; + private SettableFuture newDriverGroupReady = SettableFuture.create(); + @GuardedBy("this") + private final List recentlyCompletedDriverGroups = new ArrayList<>(); + private int totalDriverGroupsScheduled; + + public FixedLifespanScheduler(BucketNodeMap bucketNodeMap, List partitionHandles, OptionalInt concurrentLifespansPerTask) + { + checkArgument(!partitionHandles.equals(ImmutableList.of(NOT_PARTITIONED))); + checkArgument(partitionHandles.size() == bucketNodeMap.getBucketCount()); + + Map nodeToDriverGroupMap = new HashMap<>(); + Int2ObjectMap driverGroupToNodeMap = new Int2ObjectOpenHashMap<>(); + for (int bucket = 0; bucket < bucketNodeMap.getBucketCount(); bucket++) { + Node node = bucketNodeMap.getAssignedNode(bucket).get(); + nodeToDriverGroupMap.computeIfAbsent(node, key -> new IntArrayList()).add(bucket); + driverGroupToNodeMap.put(bucket, node); + } + + this.driverGroupToNodeMap = driverGroupToNodeMap; + this.nodeToDriverGroupsMap = nodeToDriverGroupMap.entrySet().stream() + .collect(toImmutableMap(Map.Entry::getKey, entry -> entry.getValue().iterator())); + this.partitionHandles = requireNonNull(partitionHandles, "partitionHandles is null"); + if (concurrentLifespansPerTask.isPresent()) { + checkArgument(concurrentLifespansPerTask.getAsInt() >= 1, "concurrentLifespansPerTask must be great or equal to 1 if present"); + } + this.concurrentLifespansPerTask = requireNonNull(concurrentLifespansPerTask, "concurrentLifespansPerTask is null"); + } + + public void scheduleInitial(SourceScheduler scheduler) + { + checkState(!initialScheduled); + initialScheduled = true; + + for (Map.Entry entry : nodeToDriverGroupsMap.entrySet()) { + IntListIterator driverGroupsIterator = entry.getValue(); + int driverGroupsScheduled = 0; + while (driverGroupsIterator.hasNext()) { + int driverGroupId = driverGroupsIterator.nextInt(); + scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); + + totalDriverGroupsScheduled++; + driverGroupsScheduled++; + if (concurrentLifespansPerTask.isPresent() && driverGroupsScheduled == concurrentLifespansPerTask.getAsInt()) { + break; + } + } + } + + verify(totalDriverGroupsScheduled <= driverGroupToNodeMap.size()); + if (totalDriverGroupsScheduled == driverGroupToNodeMap.size()) { + scheduler.noMoreLifespans(); + } + } + + public void onLifespanFinished(Iterable newlyCompletedDriverGroups) + { + checkState(initialScheduled); + + SettableFuture newDriverGroupReady; + synchronized (this) { + for (Lifespan newlyCompletedDriverGroup : newlyCompletedDriverGroups) { + checkArgument(!newlyCompletedDriverGroup.isTaskWide()); + recentlyCompletedDriverGroups.add(newlyCompletedDriverGroup); + } + newDriverGroupReady = this.newDriverGroupReady; + } + newDriverGroupReady.set(null); + } + + public SettableFuture schedule(SourceScheduler scheduler) + { + // Return a new future even if newDriverGroupReady has not finished. + // Returning the same SettableFuture instance could lead to ListenableFuture retaining too many listener objects. + + checkState(initialScheduled); + + List recentlyCompletedDriverGroups; + synchronized (this) { + recentlyCompletedDriverGroups = ImmutableList.copyOf(this.recentlyCompletedDriverGroups); + this.recentlyCompletedDriverGroups.clear(); + newDriverGroupReady = SettableFuture.create(); + } + + for (Lifespan driverGroup : recentlyCompletedDriverGroups) { + IntListIterator driverGroupsIterator = nodeToDriverGroupsMap.get(driverGroupToNodeMap.get(driverGroup.getId())); + if (!driverGroupsIterator.hasNext()) { + continue; + } + int driverGroupId = driverGroupsIterator.nextInt(); + scheduler.startLifespan(Lifespan.driverGroup(driverGroupId), partitionHandles.get(driverGroupId)); + totalDriverGroupsScheduled++; + } + + verify(totalDriverGroupsScheduled <= driverGroupToNodeMap.size()); + if (totalDriverGroupsScheduled == driverGroupToNodeMap.size()) { + scheduler.noMoreLifespans(); + } + + return newDriverGroupReady; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/LifespanScheduler.java b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/LifespanScheduler.java new file mode 100644 index 0000000000000..deff2c33bd9c9 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/scheduler/group/LifespanScheduler.java @@ -0,0 +1,34 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.scheduler.group; + +import com.facebook.presto.execution.Lifespan; +import com.facebook.presto.execution.scheduler.SourceScheduler; +import com.google.common.util.concurrent.SettableFuture; + +public interface LifespanScheduler +{ + // Thread Safety: + // * Invocation of onLifespanFinished can be parallel and in any thread. + // There may be multiple invocations in flight at the same time, + // and may overlap with any other methods. + // * Invocation of schedule happens sequentially in a single thread. + // * This object is safely published after invoking scheduleInitial. + + void scheduleInitial(SourceScheduler scheduler); + + void onLifespanFinished(Iterable newlyCompletedDriverGroups); + + SettableFuture schedule(SourceScheduler scheduler); +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/warnings/DefaultWarningCollector.java b/presto-main/src/main/java/com/facebook/presto/execution/warnings/DefaultWarningCollector.java new file mode 100644 index 0000000000000..ca6ec3f88c9ea --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/warnings/DefaultWarningCollector.java @@ -0,0 +1,56 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import com.google.common.collect.ImmutableList; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +@ThreadSafe +public class DefaultWarningCollector + implements WarningCollector +{ + @GuardedBy("this") + private final Map warnings = new LinkedHashMap<>(); + private final WarningCollectorConfig config; + + public DefaultWarningCollector(WarningCollectorConfig config) + { + this.config = requireNonNull(config, "config is null"); + } + + @Override + public synchronized void add(PrestoWarning warning) + { + requireNonNull(warning, "warning is null"); + if (warnings.size() < config.getMaxWarnings()) { + warnings.putIfAbsent(warning.getWarningCode(), warning); + } + } + + @Override + public synchronized List getWarnings() + { + return ImmutableList.copyOf(warnings.values()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollector.java b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollector.java new file mode 100644 index 0000000000000..ce69b66fb64d4 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollector.java @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +import com.facebook.presto.spi.PrestoWarning; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +public interface WarningCollector +{ + WarningCollector NOOP = + new WarningCollector() + { + @Override + public void add(PrestoWarning warning) {} + + @Override + public List getWarnings() + { + return ImmutableList.of(); + } + }; + + void add(PrestoWarning warning); + + List getWarnings(); +} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FrameHeader.java b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorConfig.java similarity index 51% rename from presto-orc/src/main/java/com/facebook/presto/orc/zstd/FrameHeader.java rename to presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorConfig.java index 466b864c0efdc..26fc5f97d7f3b 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FrameHeader.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorConfig.java @@ -11,22 +11,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.orc.zstd; +package com.facebook.presto.execution.warnings; -class FrameHeader +import io.airlift.configuration.Config; + +import static com.google.common.base.Preconditions.checkArgument; + +public class WarningCollectorConfig { - final long headerSize; - final int windowSize; - final long contentSize; - final long dictionaryId; - final boolean hasChecksum; + private int maxWarnings = Integer.MAX_VALUE; + + @Config("warning-collector.max-warnings") + public WarningCollectorConfig setMaxWarnings(int maxWarnings) + { + checkArgument(maxWarnings >= 0, "maxWarnings must be >= 0"); + this.maxWarnings = maxWarnings; + return this; + } - public FrameHeader(long headerSize, int windowSize, long contentSize, long dictionaryId, boolean hasChecksum) + public int getMaxWarnings() { - this.headerSize = headerSize; - this.windowSize = windowSize; - this.contentSize = contentSize; - this.dictionaryId = dictionaryId; - this.hasChecksum = hasChecksum; + return maxWarnings; } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorFactory.java b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorFactory.java new file mode 100644 index 0000000000000..b8421be7234a4 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorFactory.java @@ -0,0 +1,19 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +public interface WarningCollectorFactory +{ + WarningCollector create(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorModule.java b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorModule.java new file mode 100644 index 0000000000000..6a431c2897672 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/execution/warnings/WarningCollectorModule.java @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.Singleton; + +import static io.airlift.configuration.ConfigBinder.configBinder; +import static java.util.Objects.requireNonNull; + +public class WarningCollectorModule + implements Module +{ + @Override + public void configure(Binder binder) + { + configBinder(binder).bindConfig(WarningCollectorConfig.class); + } + + @Provides + @Singleton + public WarningCollectorFactory createWarningCollectorFactory(WarningCollectorConfig config) + { + requireNonNull(config, "config is null"); + return () -> new DefaultWarningCollector(config); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryLeakDetector.java b/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryLeakDetector.java index 8e8574921a3fb..972b2245959bd 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryLeakDetector.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryLeakDetector.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.memory; -import com.facebook.presto.execution.QueryInfo; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.QueryId; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -51,12 +51,12 @@ public class ClusterMemoryLeakDetector * @param queryInfoSupplier All queries that the coordinator knows about. * @param queryMemoryReservations The memory reservations of queries in the GENERAL cluster memory pool. */ - void checkForMemoryLeaks(Supplier> queryInfoSupplier, Map queryMemoryReservations) + void checkForMemoryLeaks(Supplier> queryInfoSupplier, Map queryMemoryReservations) { requireNonNull(queryInfoSupplier); requireNonNull(queryMemoryReservations); - Map queryIdToInfo = Maps.uniqueIndex(queryInfoSupplier.get(), QueryInfo::getQueryId); + Map queryIdToInfo = Maps.uniqueIndex(queryInfoSupplier.get(), BasicQueryInfo::getQueryId); Map leakedQueryReservations = queryMemoryReservations.entrySet() .stream() @@ -74,9 +74,9 @@ void checkForMemoryLeaks(Supplier> queryInfoSupplier, Map queryIdToInfo, QueryId queryId) + private static boolean isLeaked(Map queryIdToInfo, QueryId queryId) { - QueryInfo queryInfo = queryIdToInfo.get(queryId); + BasicQueryInfo queryInfo = queryIdToInfo.get(queryId); if (queryInfo == null) { return true; diff --git a/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryManager.java b/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryManager.java index c8adb7f03dd30..13bb78598caad 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/ClusterMemoryManager.java @@ -16,10 +16,10 @@ import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.execution.QueryExecution; import com.facebook.presto.execution.QueryIdGenerator; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; import com.facebook.presto.memory.LowMemoryKiller.QueryMemoryInfo; import com.facebook.presto.metadata.InternalNodeManager; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.ServerConfig; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; @@ -50,6 +50,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -73,7 +74,6 @@ import static com.facebook.presto.spi.NodeState.ACTIVE; import static com.facebook.presto.spi.NodeState.SHUTTING_DOWN; import static com.facebook.presto.spi.StandardErrorCode.CLUSTER_OUT_OF_MEMORY; -import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_MEMORY_LIMIT; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; @@ -89,8 +89,6 @@ public class ClusterMemoryManager implements ClusterMemoryPoolManager { - private static final Set POOLS = ImmutableSet.of(GENERAL_POOL, RESERVED_POOL, SYSTEM_POOL); - private static final Logger log = Logger.get(ClusterMemoryManager.class); private final ExecutorService listenerExecutor = Executors.newSingleThreadExecutor(); @@ -114,9 +112,6 @@ public class ClusterMemoryManager private final AtomicLong queriesKilledDueToOutOfMemory = new AtomicLong(); private final boolean isWorkScheduledOnCoordinator; - private final Map preAllocations = new HashMap<>(); - private final Map preAllocationsConsumed = new HashMap<>(); - @GuardedBy("this") private final Map nodes = new HashMap<>(); @@ -172,8 +167,22 @@ public ClusterMemoryManager( verify(maxQueryMemory.toBytes() <= maxQueryTotalMemory.toBytes(), "maxQueryMemory cannot be greater than maxQueryTotalMemory"); + this.pools = createClusterMemoryPools(nodeMemoryConfig.isLegacySystemPoolEnabled(), nodeMemoryConfig.isReservedPoolEnabled()); + } + + private Map createClusterMemoryPools(boolean systemPoolEnabled, boolean reservedPoolEnabled) + { + Set memoryPools = new HashSet<>(); + memoryPools.add(GENERAL_POOL); + if (systemPoolEnabled) { + memoryPools.add(SYSTEM_POOL); + } + if (reservedPoolEnabled) { + memoryPools.add(RESERVED_POOL); + } + ImmutableMap.Builder builder = ImmutableMap.builder(); - for (MemoryPoolId poolId : POOLS) { + for (MemoryPoolId poolId : memoryPools) { ClusterMemoryPool pool = new ClusterMemoryPool(poolId); builder.put(poolId, pool); try { @@ -183,16 +192,22 @@ public ClusterMemoryManager( log.error(e, "Error exporting memory pool %s", poolId); } } - this.pools = builder.build(); + return builder.build(); } @Override public synchronized void addChangeListener(MemoryPoolId poolId, Consumer listener) { + verify(memoryPoolExists(poolId), "Memory pool does not exist: %s", poolId); changeListeners.computeIfAbsent(poolId, id -> new ArrayList<>()).add(listener); } - public synchronized void process(Iterable runningQueries, Supplier> allQueryInfoSupplier) + public synchronized boolean memoryPoolExists(MemoryPoolId poolId) + { + return pools.containsKey(poolId); + } + + public synchronized void process(Iterable runningQueries, Supplier> allQueryInfoSupplier) { if (!enabled) { return; @@ -207,15 +222,13 @@ public synchronized void process(Iterable runningQueries, Suppli lastTimeNotOutOfMemory = System.nanoTime(); } - preAllocationsConsumed.clear(); - boolean queryKilled = false; long totalUserMemoryBytes = 0L; long totalMemoryBytes = 0L; for (QueryExecution query : runningQueries) { boolean resourceOvercommit = resourceOvercommit(query.getSession()); - long userMemoryReservation = query.getUserMemoryReservation(); - long totalMemoryReservation = query.getTotalMemoryReservation(); + long userMemoryReservation = query.getUserMemoryReservation().toBytes(); + long totalMemoryReservation = query.getTotalMemoryReservation().toBytes(); if (resourceOvercommit && outOfMemory) { // If a query has requested resource overcommit, only kill it if the cluster has run out of memory @@ -240,10 +253,6 @@ public synchronized void process(Iterable runningQueries, Suppli } } - if (preAllocations.containsKey(query.getQueryId())) { - preAllocationsConsumed.put(query.getQueryId(), userMemoryReservation); - } - totalUserMemoryBytes += userMemoryReservation; totalMemoryBytes += totalMemoryReservation; } @@ -271,7 +280,18 @@ public synchronized void process(Iterable runningQueries, Suppli updatePools(countByPool); - updateNodes(updateAssignments(runningQueries)); + MemoryPoolAssignmentsRequest assignmentsRequest; + if (pools.containsKey(RESERVED_POOL)) { + assignmentsRequest = updateAssignments(runningQueries); + } + else { + // If reserved pool is not enabled, we don't create a MemoryPoolAssignmentsRequest that puts all the queries + // in the general pool (as they already are). In this case we create an effectively NOOP MemoryPoolAssignmentsRequest. + // Once the reserved pool is removed we should get rid of the logic of putting queries into reserved pool including + // this piece of code. + assignmentsRequest = new MemoryPoolAssignmentsRequest(coordinatorId, Long.MIN_VALUE, ImmutableList.of()); + } + updateNodes(assignmentsRequest); } private synchronized void callOomKiller(Iterable runningQueries) @@ -345,43 +365,6 @@ private void logQueryKill(QueryId killedQueryId, List nodes) log.info(nodeDescription.toString()); } - public synchronized boolean preAllocateQueryMemory(QueryId queryId, long requiredBytes) - { - if (requiredBytes > maxQueryMemory.toBytes()) { - throw new PrestoException(EXCEEDED_MEMORY_LIMIT, format("Cannot pre-allocate memory, exceeds maximum limit %s", maxQueryMemory)); - } - - ClusterMemoryPool generalPool = pools.get(GENERAL_POOL); - ClusterMemoryPool reservedPool = pools.get(RESERVED_POOL); - if (generalPool.getBlockedNodes() > 0 || reservedPool.getAssignedQueries() > 0) { - return false; - } - - long totalPreAllocation = preAllocations.values().stream() - .mapToLong(Long::longValue) - .sum(); - - long totalPreAllocationConsumed = preAllocationsConsumed.values().stream() - .mapToLong(Long::longValue) - .sum(); - - if (generalPool.getFreeDistributedBytes() - (totalPreAllocation - totalPreAllocationConsumed) >= requiredBytes) { - preAllocations.put(queryId, requiredBytes); - return true; - } - - return false; - } - - public synchronized void removePreAllocation(QueryId queryId) - { - preAllocations.remove(queryId); - MemoryPoolInfo info = pools.get(GENERAL_POOL).getInfo(); - for (Consumer listener : changeListeners.get(GENERAL_POOL)) { - listenerExecutor.execute(() -> listener.accept(info)); - } - } - @VisibleForTesting synchronized Map getPools() { @@ -399,17 +382,24 @@ private synchronized boolean isClusterOutOfMemory() { ClusterMemoryPool reservedPool = pools.get(RESERVED_POOL); ClusterMemoryPool generalPool = pools.get(GENERAL_POOL); - return reservedPool != null && generalPool != null && reservedPool.getAssignedQueries() > 0 && generalPool.getBlockedNodes() > 0; + if (reservedPool == null) { + return generalPool.getBlockedNodes() > 0; + } + return reservedPool.getAssignedQueries() > 0 && generalPool.getBlockedNodes() > 0; } + // TODO once the reserved pool is removed we can remove this method. We can also update + // RemoteNodeMemory as we don't need to POST anything. private synchronized MemoryPoolAssignmentsRequest updateAssignments(Iterable queries) { ClusterMemoryPool reservedPool = pools.get(RESERVED_POOL); ClusterMemoryPool generalPool = pools.get(GENERAL_POOL); + verify(generalPool != null, "generalPool is null"); + verify(reservedPool != null, "reservedPool is null"); long version = memoryPoolAssignmentsVersion.incrementAndGet(); // Check that all previous assignments have propagated to the visible nodes. This doesn't account for temporary network issues, // and is more of a safety check than a guarantee - if (reservedPool != null && generalPool != null && allAssignmentsHavePropagated(queries)) { + if (allAssignmentsHavePropagated(queries)) { if (reservedPool.getAssignedQueries() == 0 && generalPool.getBlockedNodes() > 0) { QueryExecution biggestQuery = null; long maxMemory = -1; @@ -427,6 +417,7 @@ private synchronized MemoryPoolAssignmentsRequest updateAssignments(Iterable queries) diff --git a/presto-main/src/main/java/com/facebook/presto/memory/DefaultQueryContext.java b/presto-main/src/main/java/com/facebook/presto/memory/DefaultQueryContext.java index 7adf6ca4af8f4..04989f948a0f5 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/DefaultQueryContext.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/DefaultQueryContext.java @@ -124,14 +124,13 @@ MemoryTrackingContext getQueryMemoryContext() /** * Deadlock is possible for concurrent user and system allocations when updateSystemMemory()/updateUserMemory * calls queryMemoryContext.getUserMemory()/queryMemoryContext.getSystemMemory(), respectively. + * * @see this##updateSystemMemory(long) for details. */ private synchronized ListenableFuture updateUserMemory(String allocationTag, long delta) { if (delta >= 0) { - if (queryMemoryContext.getUserMemory() + delta > maxUserMemory) { - throw exceededLocalUserMemoryLimit(succinctBytes(maxUserMemory)); - } + enforceUserMemoryLimit(queryMemoryContext.getUserMemory(), delta, maxUserMemory); return memoryPool.reserve(queryId, allocationTag, delta); } memoryPool.free(queryId, allocationTag, -delta); @@ -169,9 +168,7 @@ private synchronized ListenableFuture updateSystemMemory(String allocationTag // RootAggregatedMemoryContext instance and this will be acquired in the same order). long totalMemory = memoryPool.getQueryMemoryReservation(queryId); if (delta >= 0) { - if (totalMemory + delta > maxTotalMemory) { - throw exceededLocalTotalMemoryLimit(succinctBytes(maxTotalMemory)); - } + enforceTotalMemoryLimit(totalMemory, delta, maxTotalMemory); return memoryPool.reserve(queryId, allocationTag, delta); } memoryPool.free(queryId, allocationTag, -delta); @@ -195,7 +192,11 @@ private synchronized boolean tryUpdateUserMemory(String allocationTag, long delt { if (delta <= 0) { ListenableFuture future = updateUserMemory(allocationTag, delta); - verify(future.isDone(), "future should be done"); + // When delta == 0 and the pool is full the future can still not be done, + // but, for negative deltas it must always be done. + if (delta < 0) { + verify(future.isDone(), "future should be done"); + } return true; } if (queryMemoryContext.getUserMemory() + delta > maxUserMemory) { @@ -247,7 +248,7 @@ public synchronized MemoryPool getMemoryPool() } @Override - public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean verboseStats, boolean cpuTimerEnabled, OptionalInt totalPartitions) + public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean perOperatorCpuTimerEnabled, boolean cpuTimerEnabled, OptionalInt totalPartitions) { TaskContext taskContext = TaskContext.createTaskContext( this, @@ -257,7 +258,7 @@ public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session ses yieldExecutor, session, queryMemoryContext.newMemoryTrackingContext(), - verboseStats, + perOperatorCpuTimerEnabled, cpuTimerEnabled, totalPartitions); taskContexts.put(taskStateMachine.getTaskId(), taskContext); @@ -318,4 +319,18 @@ private boolean tryReserveMemoryNotSupported(String allocationTag, long bytes) { throw new UnsupportedOperationException("tryReserveMemory is not supported"); } + + private static void enforceUserMemoryLimit(long allocated, long delta, long maxMemory) + { + if (allocated + delta > maxMemory) { + throw exceededLocalUserMemoryLimit(succinctBytes(maxMemory), succinctBytes(allocated), succinctBytes(delta)); + } + } + + private static void enforceTotalMemoryLimit(long allocated, long delta, long maxMemory) + { + if (allocated + delta > maxMemory) { + throw exceededLocalTotalMemoryLimit(succinctBytes(maxMemory), succinctBytes(allocated), succinctBytes(delta)); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/memory/LegacyQueryContext.java b/presto-main/src/main/java/com/facebook/presto/memory/LegacyQueryContext.java index cd7e3cd4aacd6..e59b83251d021 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/LegacyQueryContext.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/LegacyQueryContext.java @@ -116,9 +116,7 @@ public synchronized void setResourceOvercommit() private synchronized ListenableFuture updateUserMemory(String allocationTag, long delta) { if (delta >= 0) { - if (queryMemoryContext.getUserMemory() + delta > maxMemory) { - throw exceededLocalUserMemoryLimit(succinctBytes(maxMemory)); - } + enforceUserMemoryLimit(queryMemoryContext.getUserMemory(), delta, maxMemory); return memoryPool.reserve(queryId, allocationTag, delta); } memoryPool.free(queryId, allocationTag, -delta); @@ -164,7 +162,11 @@ private synchronized boolean tryUpdateUserMemory(String allocationTag, long delt { if (delta <= 0) { ListenableFuture future = updateUserMemory(allocationTag, delta); - verify(future.isDone(), "future should be done"); + // When delta == 0 and the pool is full the future can still not be done, + // but, for negative deltas it must always be done. + if (delta < 0) { + verify(future.isDone(), "future should be done"); + } return true; } if (queryMemoryContext.getUserMemory() + delta > maxMemory) { @@ -216,7 +218,7 @@ public synchronized MemoryPool getMemoryPool() } @Override - public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean verboseStats, boolean cpuTimerEnabled, OptionalInt totalPartitions) + public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean perOperatorCpuTimerEnabled, boolean cpuTimerEnabled, OptionalInt totalPartitions) { TaskContext taskContext = TaskContext.createTaskContext( this, @@ -226,7 +228,7 @@ public TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session ses yieldExecutor, session, queryMemoryContext.newMemoryTrackingContext(), - verboseStats, + perOperatorCpuTimerEnabled, cpuTimerEnabled, totalPartitions); taskContexts.put(taskStateMachine.getTaskId(), taskContext); @@ -287,4 +289,11 @@ private boolean tryReserveMemoryNotSupported(String allocationTag, long bytes) { throw new UnsupportedOperationException("tryReserveMemory is not supported"); } + + private static void enforceUserMemoryLimit(long allocated, long delta, long maxMemory) + { + if (allocated + delta > maxMemory) { + throw exceededLocalUserMemoryLimit(succinctBytes(maxMemory), succinctBytes(allocated), succinctBytes(delta)); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/memory/LocalMemoryManager.java b/presto-main/src/main/java/com/facebook/presto/memory/LocalMemoryManager.java index c8b6fd2df743d..c1442fdc450c8 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/LocalMemoryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/LocalMemoryManager.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; +import java.util.Optional; import static com.facebook.presto.memory.NodeMemoryConfig.QUERY_MAX_MEMORY_PER_NODE_CONFIG; import static com.facebook.presto.memory.NodeMemoryConfig.QUERY_MAX_TOTAL_MEMORY_PER_NODE_CONFIG; @@ -65,8 +66,11 @@ private void configureMemoryPools(NodeMemoryConfig config, long availableMemory) QUERY_MAX_MEMORY_PER_NODE_CONFIG, QUERY_MAX_TOTAL_MEMORY_PER_NODE_CONFIG); ImmutableMap.Builder builder = ImmutableMap.builder(); - builder.put(RESERVED_POOL, new MemoryPool(RESERVED_POOL, config.getMaxQueryTotalMemoryPerNode())); - long generalPoolSize = maxMemory.toBytes() - config.getMaxQueryTotalMemoryPerNode().toBytes(); + long generalPoolSize = maxMemory.toBytes(); + if (config.isReservedPoolEnabled()) { + builder.put(RESERVED_POOL, new MemoryPool(RESERVED_POOL, config.getMaxQueryTotalMemoryPerNode())); + generalPoolSize -= config.getMaxQueryTotalMemoryPerNode().toBytes(); + } verify(generalPoolSize > 0, "general memory pool size is 0"); builder.put(GENERAL_POOL, new MemoryPool(GENERAL_POOL, new DataSize(generalPoolSize, BYTE))); this.pools = builder.build(); @@ -79,9 +83,12 @@ private void configureLegacyMemoryPools(NodeMemoryConfig config, ReservedSystemM ImmutableMap.Builder builder = ImmutableMap.builder(); checkArgument(config.getMaxQueryMemoryPerNode().toBytes() <= maxMemory.toBytes(), format("%s set to %s, but only %s of useable heap available", QUERY_MAX_MEMORY_PER_NODE_CONFIG, config.getMaxQueryMemoryPerNode(), maxMemory)); - builder.put(RESERVED_POOL, new MemoryPool(RESERVED_POOL, config.getMaxQueryMemoryPerNode())); - DataSize generalPoolSize = new DataSize(Math.max(0, maxMemory.toBytes() - config.getMaxQueryMemoryPerNode().toBytes()), BYTE); - builder.put(GENERAL_POOL, new MemoryPool(GENERAL_POOL, generalPoolSize)); + long generalPoolSize = maxMemory.toBytes(); + if (config.isReservedPoolEnabled()) { + builder.put(RESERVED_POOL, new MemoryPool(RESERVED_POOL, config.getMaxQueryMemoryPerNode())); + generalPoolSize -= config.getMaxQueryMemoryPerNode().toBytes(); + } + builder.put(GENERAL_POOL, new MemoryPool(GENERAL_POOL, new DataSize(generalPoolSize, BYTE))); builder.put(SYSTEM_POOL, new MemoryPool(SYSTEM_POOL, systemMemoryConfig.getReservedSystemMemory())); this.pools = builder.build(); } @@ -116,8 +123,18 @@ public List getPools() return ImmutableList.copyOf(pools.values()); } - public MemoryPool getPool(MemoryPoolId id) + public MemoryPool getGeneralPool() + { + return pools.get(GENERAL_POOL); + } + + public Optional getSystemPool() + { + return Optional.ofNullable(pools.get(SYSTEM_POOL)); + } + + public Optional getReservedPool() { - return pools.get(id); + return Optional.ofNullable(pools.get(RESERVED_POOL)); } } diff --git a/presto-main/src/main/java/com/facebook/presto/memory/MemoryResource.java b/presto-main/src/main/java/com/facebook/presto/memory/MemoryResource.java index 6b7696d9dcf11..d7c1a8548da73 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/MemoryResource.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/MemoryResource.java @@ -14,7 +14,7 @@ package com.facebook.presto.memory; import com.facebook.presto.execution.TaskManager; -import com.facebook.presto.spi.memory.MemoryPoolId; +import com.facebook.presto.spi.memory.MemoryPoolInfo; import javax.inject.Inject; import javax.ws.rs.Consumes; @@ -26,6 +26,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; +import static com.facebook.presto.memory.LocalMemoryManager.RESERVED_POOL; +import static com.facebook.presto.memory.LocalMemoryManager.SYSTEM_POOL; import static java.util.Objects.requireNonNull; import static javax.ws.rs.core.Response.Status.NOT_FOUND; @@ -58,12 +61,25 @@ public MemoryInfo getMemoryInfo(MemoryPoolAssignmentsRequest request) @Path("{poolId}") public Response getMemoryInfo(@PathParam("poolId") String poolId) { - MemoryPool memoryPool = memoryManager.getPool(new MemoryPoolId(poolId)); - if (memoryPool == null) { - return Response.status(NOT_FOUND).build(); + if (GENERAL_POOL.getId().equals(poolId)) { + return toSuccessfulResponse(memoryManager.getGeneralPool().getInfo()); } + + if (SYSTEM_POOL.getId().equals(poolId) && memoryManager.getSystemPool().isPresent()) { + return toSuccessfulResponse(memoryManager.getSystemPool().get().getInfo()); + } + + if (RESERVED_POOL.getId().equals(poolId) && memoryManager.getReservedPool().isPresent()) { + return toSuccessfulResponse(memoryManager.getReservedPool().get().getInfo()); + } + + return Response.status(NOT_FOUND).build(); + } + + private Response toSuccessfulResponse(MemoryPoolInfo memoryInfo) + { return Response.ok() - .entity(memoryPool.getInfo()) + .entity(memoryInfo) .build(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/memory/NodeMemoryConfig.java b/presto-main/src/main/java/com/facebook/presto/memory/NodeMemoryConfig.java index b6778fb15a35c..47c71e86a365d 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/NodeMemoryConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/NodeMemoryConfig.java @@ -29,6 +29,7 @@ public class NodeMemoryConfig public static final String QUERY_MAX_TOTAL_MEMORY_PER_NODE_CONFIG = "query.max-total-memory-per-node"; private boolean isLegacySystemPoolEnabled; + private boolean isReservedPoolEnabled = true; private DataSize maxQueryMemoryPerNode = new DataSize(AVAILABLE_HEAP_MEMORY * 0.1, BYTE); @@ -61,6 +62,18 @@ public NodeMemoryConfig setLegacySystemPoolEnabled(boolean legacySystemPoolEnabl return this; } + public boolean isReservedPoolEnabled() + { + return isReservedPoolEnabled; + } + + @Config("experimental.reserved-pool-enabled") + public NodeMemoryConfig setReservedPoolEnabled(boolean reservedPoolEnabled) + { + isReservedPoolEnabled = reservedPoolEnabled; + return this; + } + @NotNull public DataSize getMaxQueryTotalMemoryPerNode() { diff --git a/presto-main/src/main/java/com/facebook/presto/memory/QueryContext.java b/presto-main/src/main/java/com/facebook/presto/memory/QueryContext.java index 11fca604e2759..14ebc9e12f6cf 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/QueryContext.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/QueryContext.java @@ -39,7 +39,7 @@ public interface QueryContext TaskContext getTaskContextByTaskId(TaskId taskId); - TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean verboseStats, boolean cpuTimerEnabled, OptionalInt totalPartitions); + TaskContext addTaskContext(TaskStateMachine taskStateMachine, Session session, boolean perOperatorCpuTimerEnabled, boolean cpuTimerEnabled, OptionalInt totalPartitions); MemoryPool getMemoryPool(); diff --git a/presto-main/src/main/java/com/facebook/presto/memory/RemoteNodeMemory.java b/presto-main/src/main/java/com/facebook/presto/memory/RemoteNodeMemory.java index c8e36794d6097..fb55d1aa547f9 100644 --- a/presto-main/src/main/java/com/facebook/presto/memory/RemoteNodeMemory.java +++ b/presto-main/src/main/java/com/facebook/presto/memory/RemoteNodeMemory.java @@ -41,8 +41,6 @@ import static io.airlift.http.client.Request.Builder.preparePost; import static io.airlift.units.Duration.nanosSince; import static java.util.Objects.requireNonNull; -import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; import static java.util.concurrent.TimeUnit.SECONDS; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; @@ -56,7 +54,7 @@ public class RemoteNodeMemory private final URI memoryInfoUri; private final JsonCodec memoryInfoCodec; private final JsonCodec assignmentsRequestJsonCodec; - private final AtomicReference> memoryInfo = new AtomicReference<>(empty()); + private final AtomicReference> memoryInfo = new AtomicReference<>(Optional.empty()); private final AtomicReference> future = new AtomicReference<>(); private final AtomicLong lastUpdateNanos = new AtomicLong(); private final AtomicLong lastWarningLogged = new AtomicLong(); @@ -119,7 +117,7 @@ public void onSuccess(@Nullable JsonResponse result) long version = currentAssignmentVersion.get(); if (result != null) { if (result.hasValue()) { - memoryInfo.set(ofNullable(result.getValue())); + memoryInfo.set(Optional.ofNullable(result.getValue())); } if (result.getStatusCode() != OK.code()) { log.warn("Error fetching memory info from %s returned status %d: %s", memoryInfoUri, result.getStatusCode(), result.getStatusMessage()); diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/AllNodes.java b/presto-main/src/main/java/com/facebook/presto/metadata/AllNodes.java index c686a0ca79b26..46deda0d43b76 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/AllNodes.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/AllNodes.java @@ -25,12 +25,14 @@ public class AllNodes private final Set activeNodes; private final Set inactiveNodes; private final Set shuttingDownNodes; + private final Set activeCoordinators; - public AllNodes(Set activeNodes, Set inactiveNodes, Set shuttingDownNodes) + public AllNodes(Set activeNodes, Set inactiveNodes, Set shuttingDownNodes, Set activeCoordinators) { this.activeNodes = ImmutableSet.copyOf(requireNonNull(activeNodes, "activeNodes is null")); this.inactiveNodes = ImmutableSet.copyOf(requireNonNull(inactiveNodes, "inactiveNodes is null")); this.shuttingDownNodes = ImmutableSet.copyOf(requireNonNull(shuttingDownNodes, "shuttingDownNodes is null")); + this.activeCoordinators = ImmutableSet.copyOf(requireNonNull(activeCoordinators, "activeCoordinators is null")); } public Set getActiveNodes() @@ -47,4 +49,9 @@ public Set getShuttingDownNodes() { return shuttingDownNodes; } + + public Set getActiveCoordinators() + { + return activeCoordinators; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/DiscoveryNodeManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/DiscoveryNodeManager.java index ca55deb9f1d64..480548628e31e 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/DiscoveryNodeManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/DiscoveryNodeManager.java @@ -21,6 +21,7 @@ import com.facebook.presto.spi.Node; import com.facebook.presto.spi.NodeState; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; @@ -32,7 +33,6 @@ import io.airlift.http.client.HttpClient; import io.airlift.log.Logger; import io.airlift.node.NodeInfo; -import io.airlift.units.Duration; import org.weakref.jmx.Managed; import javax.annotation.PostConstruct; @@ -43,11 +43,15 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import static com.facebook.presto.spi.NodeState.ACTIVE; import static com.facebook.presto.spi.NodeState.INACTIVE; @@ -59,6 +63,7 @@ import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; @ThreadSafe @@ -66,17 +71,17 @@ public final class DiscoveryNodeManager implements InternalNodeManager { private static final Logger log = Logger.get(DiscoveryNodeManager.class); - private static final Duration MAX_AGE = new Duration(5, TimeUnit.SECONDS); private static final Splitter CONNECTOR_ID_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); private final ServiceSelector serviceSelector; - private final NodeInfo nodeInfo; private final FailureDetector failureDetector; private final NodeVersion expectedNodeVersion; private final ConcurrentHashMap nodeStates = new ConcurrentHashMap<>(); private final HttpClient httpClient; private final ScheduledExecutorService nodeStateUpdateExecutor; + private final ExecutorService nodeStateEventExecutor; private final boolean httpsRequired; + private final PrestoNode currentNode; @GuardedBy("this") private SetMultimap activeNodesByConnectorId; @@ -85,12 +90,10 @@ public final class DiscoveryNodeManager private AllNodes allNodes; @GuardedBy("this") - private long lastUpdateTimestamp; - - private final PrestoNode currentNode; + private Set coordinators; @GuardedBy("this") - private Set coordinators; + private final List> listeners = new ArrayList<>(); @Inject public DiscoveryNodeManager( @@ -102,47 +105,88 @@ public DiscoveryNodeManager( InternalCommunicationConfig internalCommunicationConfig) { this.serviceSelector = requireNonNull(serviceSelector, "serviceSelector is null"); - this.nodeInfo = requireNonNull(nodeInfo, "nodeInfo is null"); this.failureDetector = requireNonNull(failureDetector, "failureDetector is null"); this.expectedNodeVersion = requireNonNull(expectedNodeVersion, "expectedNodeVersion is null"); this.httpClient = requireNonNull(httpClient, "httpClient is null"); this.nodeStateUpdateExecutor = newSingleThreadScheduledExecutor(threadsNamed("node-state-poller-%s")); + this.nodeStateEventExecutor = newCachedThreadPool(threadsNamed("node-state-events-%s")); this.httpsRequired = internalCommunicationConfig.isHttpsRequired(); - this.currentNode = refreshNodesInternal(); + + this.currentNode = findCurrentNode( + serviceSelector.selectAllServices(), + requireNonNull(nodeInfo, "nodeInfo is null").getNodeId(), + expectedNodeVersion, + httpsRequired); + + refreshNodesInternal(); + } + + private static PrestoNode findCurrentNode(List allServices, String currentNodeId, NodeVersion expectedNodeVersion, boolean httpsRequired) + { + for (ServiceDescriptor service : allServices) { + URI uri = getHttpUri(service, httpsRequired); + NodeVersion nodeVersion = getNodeVersion(service); + if (uri != null && nodeVersion != null) { + PrestoNode node = new PrestoNode(service.getNodeId(), uri, nodeVersion, isCoordinator(service)); + + if (node.getNodeIdentifier().equals(currentNodeId)) { + checkState( + node.getNodeVersion().equals(expectedNodeVersion), + "INVARIANT: current node version (%s) should be equal to %s", + node.getNodeVersion(), + expectedNodeVersion); + return node; + } + } + } + throw new IllegalStateException("INVARIANT: current node not returned from service selector"); } @PostConstruct public void startPollingNodeStates() { // poll worker states only on the coordinators - if (getCoordinators().contains(currentNode)) { + if (currentNode.isCoordinator()) { nodeStateUpdateExecutor.scheduleWithFixedDelay(() -> { - ImmutableSet.Builder nodeSetBuilder = ImmutableSet.builder(); - AllNodes allNodes = getAllNodes(); - Set aliveNodes = nodeSetBuilder - .addAll(allNodes.getActiveNodes()) - .addAll(allNodes.getShuttingDownNodes()) - .build(); - - ImmutableSet aliveNodeIds = aliveNodes.stream() - .map(Node::getNodeIdentifier) - .collect(toImmutableSet()); - - // Remove nodes that don't exist anymore - // Make a copy to materialize the set difference - Set deadNodes = difference(nodeStates.keySet(), aliveNodeIds).immutableCopy(); - nodeStates.keySet().removeAll(deadNodes); - - // Add new nodes - for (Node node : aliveNodes) { - nodeStates.putIfAbsent(node.getNodeIdentifier(), - new RemoteNodeState(httpClient, uriBuilderFrom(node.getHttpUri()).appendPath("/v1/info/state").build())); + try { + pollWorkers(); + } + catch (Exception e) { + log.error(e, "Error polling state of nodes"); } + }, 5, 5, TimeUnit.SECONDS); + } + pollWorkers(); + } + + private void pollWorkers() + { + AllNodes allNodes = getAllNodes(); + Set aliveNodes = ImmutableSet.builder() + .addAll(allNodes.getActiveNodes()) + .addAll(allNodes.getShuttingDownNodes()) + .build(); + + ImmutableSet aliveNodeIds = aliveNodes.stream() + .map(Node::getNodeIdentifier) + .collect(toImmutableSet()); - // Schedule refresh - nodeStates.values().forEach(RemoteNodeState::asyncRefresh); - }, 1, 5, TimeUnit.SECONDS); + // Remove nodes that don't exist anymore + // Make a copy to materialize the set difference + Set deadNodes = difference(nodeStates.keySet(), aliveNodeIds).immutableCopy(); + nodeStates.keySet().removeAll(deadNodes); + + // Add new nodes + for (Node node : aliveNodes) { + nodeStates.putIfAbsent(node.getNodeIdentifier(), + new RemoteNodeState(httpClient, uriBuilderFrom(node.getHttpUri()).appendPath("/v1/info/state").build())); } + + // Schedule refresh + nodeStates.values().forEach(RemoteNodeState::asyncRefresh); + + // update indexes + refreshNodesInternal(); } @PreDestroy @@ -157,18 +201,14 @@ public void refreshNodes() refreshNodesInternal(); } - private synchronized PrestoNode refreshNodesInternal() + private synchronized void refreshNodesInternal() { - lastUpdateTimestamp = System.nanoTime(); - // This is currently a blacklist. // TODO: make it a whitelist (a failure-detecting service selector) and maybe build in support for injecting this in airlift Set services = serviceSelector.selectAllServices().stream() .filter(service -> !failureDetector.getFailed().contains(service)) .collect(toImmutableSet()); - PrestoNode currentNode = null; - ImmutableSet.Builder activeNodesBuilder = ImmutableSet.builder(); ImmutableSet.Builder inactiveNodesBuilder = ImmutableSet.builder(); ImmutableSet.Builder shuttingDownNodesBuilder = ImmutableSet.builder(); @@ -176,19 +216,13 @@ private synchronized PrestoNode refreshNodesInternal() ImmutableSetMultimap.Builder byConnectorIdBuilder = ImmutableSetMultimap.builder(); for (ServiceDescriptor service : services) { - URI uri = getHttpUri(service); + URI uri = getHttpUri(service, httpsRequired); NodeVersion nodeVersion = getNodeVersion(service); boolean coordinator = isCoordinator(service); if (uri != null && nodeVersion != null) { PrestoNode node = new PrestoNode(service.getNodeId(), uri, nodeVersion, coordinator); NodeState nodeState = getNodeState(node); - // record current node - if (node.getNodeIdentifier().equals(nodeInfo.getNodeId())) { - currentNode = node; - checkState(currentNode.getNodeVersion().equals(expectedNodeVersion), "INVARIANT: current node version (%s) should be equal to %s", currentNode.getNodeVersion(), expectedNodeVersion); - } - switch (nodeState) { case ACTIVE: activeNodesBuilder.add(node); @@ -215,7 +249,7 @@ private synchronized PrestoNode refreshNodesInternal() shuttingDownNodesBuilder.add(node); break; default: - throw new IllegalArgumentException("Unknown node state " + nodeState); + log.error("Unknown state %s for node %s", nodeState, node); } } } @@ -228,19 +262,15 @@ private synchronized PrestoNode refreshNodesInternal() } } - allNodes = new AllNodes(activeNodesBuilder.build(), inactiveNodesBuilder.build(), shuttingDownNodesBuilder.build()); + // assign allNodes to a local variable for use in the callback below + AllNodes allNodes = new AllNodes(activeNodesBuilder.build(), inactiveNodesBuilder.build(), shuttingDownNodesBuilder.build(), coordinatorsBuilder.build()); + this.allNodes = allNodes; activeNodesByConnectorId = byConnectorIdBuilder.build(); coordinators = coordinatorsBuilder.build(); - checkState(currentNode != null, "INVARIANT: current node not returned from service selector"); - return currentNode; - } - - private synchronized void refreshIfNecessary() - { - if (Duration.nanosSince(lastUpdateTimestamp).compareTo(MAX_AGE) > 0) { - refreshNodesInternal(); - } + // notify listeners + List> listeners = ImmutableList.copyOf(this.listeners); + nodeStateEventExecutor.submit(() -> listeners.forEach(listener -> listener.accept(allNodes))); } private NodeState getNodeState(PrestoNode node) @@ -263,13 +293,12 @@ private boolean isNodeShuttingDown(String nodeId) Optional remoteNodeState = nodeStates.containsKey(nodeId) ? nodeStates.get(nodeId).getNodeState() : Optional.empty(); - return remoteNodeState.isPresent() && remoteNodeState.get().equals(SHUTTING_DOWN); + return remoteNodeState.isPresent() && remoteNodeState.get() == SHUTTING_DOWN; } @Override public synchronized AllNodes getAllNodes() { - refreshIfNecessary(); return allNodes; } @@ -309,7 +338,6 @@ public Set getNodes(NodeState state) @Override public synchronized Set getActiveConnectorNodes(ConnectorId connectorId) { - refreshIfNecessary(); return activeNodesByConnectorId.get(connectorId); } @@ -322,11 +350,22 @@ public Node getCurrentNode() @Override public synchronized Set getCoordinators() { - refreshIfNecessary(); return coordinators; } - private URI getHttpUri(ServiceDescriptor descriptor) + @Override + public synchronized void addNodeChangeListener(Consumer listener) + { + listeners.add(requireNonNull(listener, "listener is null")); + } + + @Override + public synchronized void removeNodeChangeListener(Consumer listener) + { + listeners.remove(requireNonNull(listener, "listener is null")); + } + + private static URI getHttpUri(ServiceDescriptor descriptor, boolean httpsRequired) { String url = descriptor.getProperties().get(httpsRequired ? "https" : "http"); if (url != null) { diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionInvokerProvider.java b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionInvokerProvider.java index 21c5714d8d8d1..4ff7a5e209076 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionInvokerProvider.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionInvokerProvider.java @@ -17,10 +17,10 @@ import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ScalarImplementationChoice; -import com.facebook.presto.spi.InvocationConvention; -import com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention; -import com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention; +import com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention; import com.google.common.annotations.VisibleForTesting; import java.util.List; diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionRegistry.java b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionRegistry.java index 7760d014e8227..0d9b9ded21bd5 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/FunctionRegistry.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/FunctionRegistry.java @@ -45,7 +45,7 @@ import com.facebook.presto.operator.aggregation.LongSumAggregation; import com.facebook.presto.operator.aggregation.MaxDataSizeForStats; import com.facebook.presto.operator.aggregation.MergeHyperLogLogAggregation; -import com.facebook.presto.operator.aggregation.RealAverageAggregation; +import com.facebook.presto.operator.aggregation.MergeQuantileDigestFunction; import com.facebook.presto.operator.aggregation.RealCorrelationAggregation; import com.facebook.presto.operator.aggregation.RealCovarianceAggregation; import com.facebook.presto.operator.aggregation.RealGeometricMeanAggregations; @@ -56,6 +56,7 @@ import com.facebook.presto.operator.aggregation.VarianceAggregation; import com.facebook.presto.operator.aggregation.arrayagg.ArrayAggregationFunction; import com.facebook.presto.operator.aggregation.histogram.Histogram; +import com.facebook.presto.operator.aggregation.multimapagg.MultimapAggregationFunction; import com.facebook.presto.operator.scalar.ArrayCardinalityFunction; import com.facebook.presto.operator.scalar.ArrayContains; import com.facebook.presto.operator.scalar.ArrayDistinctFromOperator; @@ -74,6 +75,7 @@ import com.facebook.presto.operator.scalar.ArrayLessThanOrEqualOperator; import com.facebook.presto.operator.scalar.ArrayMaxFunction; import com.facebook.presto.operator.scalar.ArrayMinFunction; +import com.facebook.presto.operator.scalar.ArrayNgramsFunction; import com.facebook.presto.operator.scalar.ArrayNotEqualOperator; import com.facebook.presto.operator.scalar.ArrayPositionFunction; import com.facebook.presto.operator.scalar.ArrayRemoveFunction; @@ -88,6 +90,7 @@ import com.facebook.presto.operator.scalar.CharacterStringCasts; import com.facebook.presto.operator.scalar.ColorFunctions; import com.facebook.presto.operator.scalar.CombineHashFunction; +import com.facebook.presto.operator.scalar.DataSizeFunctions; import com.facebook.presto.operator.scalar.DateTimeFunctions; import com.facebook.presto.operator.scalar.EmptyMapConstructor; import com.facebook.presto.operator.scalar.FailureFunction; @@ -111,6 +114,7 @@ import com.facebook.presto.operator.scalar.MathFunctions; import com.facebook.presto.operator.scalar.MathFunctions.LegacyLogFunction; import com.facebook.presto.operator.scalar.MultimapFromEntriesFunction; +import com.facebook.presto.operator.scalar.QuantileDigestFunctions; import com.facebook.presto.operator.scalar.Re2JRegexpFunctions; import com.facebook.presto.operator.scalar.Re2JRegexpReplaceLambdaFunction; import com.facebook.presto.operator.scalar.RepeatFunction; @@ -164,6 +168,7 @@ import com.facebook.presto.type.IntervalYearMonthOperators; import com.facebook.presto.type.IpAddressOperators; import com.facebook.presto.type.LikeFunctions; +import com.facebook.presto.type.QuantileDigestOperators; import com.facebook.presto.type.RealOperators; import com.facebook.presto.type.SmallintOperators; import com.facebook.presto.type.TimeOperators; @@ -224,7 +229,11 @@ import static com.facebook.presto.operator.aggregation.MaxNAggregationFunction.MAX_N_AGGREGATION; import static com.facebook.presto.operator.aggregation.MinAggregationFunction.MIN_AGGREGATION; import static com.facebook.presto.operator.aggregation.MinNAggregationFunction.MIN_N_AGGREGATION; -import static com.facebook.presto.operator.aggregation.MultimapAggregationFunction.MULTIMAP_AGG; +import static com.facebook.presto.operator.aggregation.QuantileDigestAggregationFunction.QDIGEST_AGG; +import static com.facebook.presto.operator.aggregation.QuantileDigestAggregationFunction.QDIGEST_AGG_WITH_WEIGHT; +import static com.facebook.presto.operator.aggregation.QuantileDigestAggregationFunction.QDIGEST_AGG_WITH_WEIGHT_AND_ERROR; +import static com.facebook.presto.operator.aggregation.RealAverageAggregation.REAL_AVERAGE_AGGREGATION; +import static com.facebook.presto.operator.aggregation.ReduceAggregationFunction.REDUCE_AGG; import static com.facebook.presto.operator.aggregation.minmaxby.MaxByAggregationFunction.MAX_BY; import static com.facebook.presto.operator.aggregation.minmaxby.MaxByNAggregationFunction.MAX_BY_N_AGGREGATION; import static com.facebook.presto.operator.aggregation.minmaxby.MinByAggregationFunction.MIN_BY; @@ -440,13 +449,15 @@ public FunctionRegistry(TypeManager typeManager, BlockEncodingSerde blockEncodin .aggregates(IntervalDayToSecondSumAggregation.class) .aggregates(IntervalYearToMonthSumAggregation.class) .aggregates(AverageAggregations.class) - .aggregates(RealAverageAggregation.class) + .function(REAL_AVERAGE_AGGREGATION) .aggregates(IntervalDayToSecondAverageAggregation.class) .aggregates(IntervalYearToMonthAverageAggregation.class) .aggregates(GeometricMeanAggregations.class) .aggregates(RealGeometricMeanAggregations.class) .aggregates(MergeHyperLogLogAggregation.class) .aggregates(ApproximateSetAggregation.class) + .functions(QDIGEST_AGG, QDIGEST_AGG_WITH_WEIGHT, QDIGEST_AGG_WITH_WEIGHT_AND_ERROR) + .function(MergeQuantileDigestFunction.MERGE) .aggregates(DoubleHistogramAggregation.class) .aggregates(RealHistogramAggregation.class) .aggregates(DoubleCovarianceAggregation.class) @@ -471,10 +482,8 @@ public FunctionRegistry(TypeManager typeManager, BlockEncodingSerde blockEncodin .scalar(MathFunctions.Sign.class) .scalar(MathFunctions.Round.class) .scalar(MathFunctions.RoundN.class) - .scalar(MathFunctions.RoundNBigintDecimals.class) .scalar(MathFunctions.Truncate.class) .scalar(MathFunctions.TruncateN.class) - .scalar(MathFunctions.TruncateNBigintDecimals.class) .scalar(MathFunctions.Ceiling.class) .scalar(MathFunctions.Floor.class) .scalars(BitwiseFunctions.class) @@ -482,40 +491,64 @@ public FunctionRegistry(TypeManager typeManager, BlockEncodingSerde blockEncodin .scalars(JsonFunctions.class) .scalars(ColorFunctions.class) .scalars(ColorOperators.class) + .scalar(ColorOperators.ColorDistinctFromOperator.class) .scalars(HyperLogLogFunctions.class) + .scalars(QuantileDigestFunctions.class) .scalars(UnknownOperators.class) + .scalar(UnknownOperators.UnknownDistinctFromOperator.class) .scalars(BooleanOperators.class) + .scalar(BooleanOperators.BooleanDistinctFromOperator.class) .scalars(BigintOperators.class) + .scalar(BigintOperators.BigintDistinctFromOperator.class) .scalars(IntegerOperators.class) + .scalar(IntegerOperators.IntegerDistinctFromOperator.class) .scalars(SmallintOperators.class) + .scalar(SmallintOperators.SmallintDistinctFromOperator.class) .scalars(TinyintOperators.class) + .scalar(TinyintOperators.TinyintDistinctFromOperator.class) .scalars(DoubleOperators.class) + .scalar(DoubleOperators.DoubleDistinctFromOperator.class) .scalars(RealOperators.class) + .scalar(RealOperators.RealDistinctFromOperator.class) .scalars(VarcharOperators.class) + .scalar(VarcharOperators.VarcharDistinctFromOperator.class) .scalars(VarbinaryOperators.class) + .scalar(VarbinaryOperators.VarbinaryDistinctFromOperator.class) .scalars(DateOperators.class) + .scalar(DateOperators.DateDistinctFromOperator.class) .scalars(TimeOperators.class) + .scalar(TimeOperators.TimeDistinctFromOperator.class) .scalars(TimestampOperators.class) + .scalar(TimestampOperators.TimestampDistinctFromOperator.class) .scalars(IntervalDayTimeOperators.class) + .scalar(IntervalDayTimeOperators.IntervalDayTimeDistinctFromOperator.class) .scalars(IntervalYearMonthOperators.class) + .scalar(IntervalYearMonthOperators.IntervalYearMonthDistinctFromOperator.class) .scalars(TimeWithTimeZoneOperators.class) + .scalar(TimeWithTimeZoneOperators.TimeWithTimeZoneDistinctFromOperator.class) .scalars(TimestampWithTimeZoneOperators.class) + .scalar(TimestampWithTimeZoneOperators.TimestampWithTimeZoneDistinctFromOperator.class) .scalars(DateTimeOperators.class) .scalars(HyperLogLogOperators.class) + .scalars(QuantileDigestOperators.class) .scalars(IpAddressOperators.class) + .scalar(IpAddressOperators.IpAddressDistinctFromOperator.class) .scalars(LikeFunctions.class) .scalars(ArrayFunctions.class) .scalars(HmacFunctions.class) + .scalars(DataSizeFunctions.class) .scalar(ArrayCardinalityFunction.class) .scalar(ArrayContains.class) .scalar(ArrayFilterFunction.class) .scalar(ArrayPositionFunction.class) .scalars(CombineHashFunction.class) .scalars(JsonOperators.class) + .scalar(JsonOperators.JsonDistinctFromOperator.class) .scalars(FailureFunction.class) .scalars(JoniRegexpCasts.class) .scalars(CharacterStringCasts.class) .scalars(CharOperators.class) + .scalar(CharOperators.CharDistinctFromOperator.class) .scalar(DecimalOperators.Negation.class) .scalar(DecimalOperators.HashCode.class) .scalar(DecimalOperators.Indeterminate.class) @@ -544,6 +577,7 @@ public FunctionRegistry(TypeManager typeManager, BlockEncodingSerde blockEncodin .scalar(ArrayExceptFunction.class) .scalar(ArraySliceFunction.class) .scalar(ArrayIndeterminateOperator.class) + .scalar(ArrayNgramsFunction.class) .scalar(MapDistinctFromOperator.class) .scalar(MapEqualOperator.class) .scalar(MapEntriesFunction.class) @@ -572,7 +606,9 @@ public FunctionRegistry(TypeManager typeManager, BlockEncodingSerde blockEncodin .function(new ArrayAggregationFunction(featuresConfig.isLegacyArrayAgg(), featuresConfig.getArrayAggGroupImplementation())) .functions(new MapSubscriptOperator(featuresConfig.isLegacyMapSubscript())) .functions(MAP_CONSTRUCTOR, MAP_TO_JSON, JSON_TO_MAP, JSON_STRING_TO_MAP) - .functions(MAP_AGG, MULTIMAP_AGG, MAP_UNION) + .functions(MAP_AGG, MAP_UNION) + .function(REDUCE_AGG) + .function(new MultimapAggregationFunction(featuresConfig.getMultimapAggGroupImplementation())) .functions(DECIMAL_TO_VARCHAR_CAST, DECIMAL_TO_INTEGER_CAST, DECIMAL_TO_BIGINT_CAST, DECIMAL_TO_DOUBLE_CAST, DECIMAL_TO_REAL_CAST, DECIMAL_TO_BOOLEAN_CAST, DECIMAL_TO_TINYINT_CAST, DECIMAL_TO_SMALLINT_CAST) .functions(VARCHAR_TO_DECIMAL_CAST, INTEGER_TO_DECIMAL_CAST, BIGINT_TO_DECIMAL_CAST, DOUBLE_TO_DECIMAL_CAST, REAL_TO_DECIMAL_CAST, BOOLEAN_TO_DECIMAL_CAST, TINYINT_TO_DECIMAL_CAST, SMALLINT_TO_DECIMAL_CAST) .functions(JSON_TO_DECIMAL_CAST, DECIMAL_TO_JSON_CAST) diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/InMemoryNodeManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/InMemoryNodeManager.java index 64808e39b7366..8df5edec44637 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/InMemoryNodeManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/InMemoryNodeManager.java @@ -23,10 +23,16 @@ import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; +import javax.annotation.concurrent.GuardedBy; import javax.inject.Inject; import java.net.URI; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import java.util.function.Consumer; + +import static java.util.Objects.requireNonNull; public class InMemoryNodeManager implements InternalNodeManager @@ -34,6 +40,9 @@ public class InMemoryNodeManager private final Node localNode; private final SetMultimap remoteNodes = Multimaps.synchronizedSetMultimap(HashMultimap.create()); + @GuardedBy("this") + private final List> listeners = new ArrayList<>(); + @Inject public InMemoryNodeManager() { @@ -58,6 +67,13 @@ public void addNode(ConnectorId connectorId, Node... nodes) public void addNode(ConnectorId connectorId, Iterable nodes) { remoteNodes.putAll(connectorId, nodes); + + List> listeners; + synchronized (this) { + listeners = ImmutableList.copyOf(this.listeners); + } + AllNodes allNodes = getAllNodes(); + listeners.forEach(listener -> listener.accept(allNodes)); } @Override @@ -84,7 +100,7 @@ public Set getActiveConnectorNodes(ConnectorId connectorId) @Override public AllNodes getAllNodes() { - return new AllNodes(ImmutableSet.builder().add(localNode).addAll(remoteNodes.values()).build(), ImmutableSet.of(), ImmutableSet.of()); + return new AllNodes(ImmutableSet.builder().add(localNode).addAll(remoteNodes.values()).build(), ImmutableSet.of(), ImmutableSet.of(), ImmutableSet.of(localNode)); } @Override @@ -105,4 +121,16 @@ public void refreshNodes() { // no-op } + + @Override + public synchronized void addNodeChangeListener(Consumer listener) + { + listeners.add(requireNonNull(listener, "listener is null")); + } + + @Override + public synchronized void removeNodeChangeListener(Consumer listener) + { + listeners.remove(requireNonNull(listener, "listener is null")); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/IndexHandle.java b/presto-main/src/main/java/com/facebook/presto/metadata/IndexHandle.java index 2b5a8c3e4f7d4..26a41ea540a7d 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/IndexHandle.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/IndexHandle.java @@ -82,6 +82,6 @@ public boolean equals(Object obj) @Override public String toString() { - return connectorId + ":" + transactionHandle + ":" + connectorHandle; + return connectorId + ":" + connectorHandle; } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/InsertTableHandle.java b/presto-main/src/main/java/com/facebook/presto/metadata/InsertTableHandle.java index df34b726250b1..e2de6b9c367bf 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/InsertTableHandle.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/InsertTableHandle.java @@ -82,6 +82,6 @@ public boolean equals(Object obj) @Override public String toString() { - return connectorId + ":" + transactionHandle + ":" + connectorHandle; + return connectorId + ":" + connectorHandle; } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/InternalNodeManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/InternalNodeManager.java index 45932ff2928cb..a0d13c6f2754d 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/InternalNodeManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/InternalNodeManager.java @@ -18,6 +18,7 @@ import com.facebook.presto.spi.NodeState; import java.util.Set; +import java.util.function.Consumer; public interface InternalNodeManager { @@ -32,4 +33,8 @@ public interface InternalNodeManager AllNodes getAllNodes(); void refreshNodes(); + + void addNodeChangeListener(Consumer listener); + + void removeNodeChangeListener(Consumer listener); } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java b/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java index ffccf7e630e6b..20720546d264f 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/Metadata.java @@ -33,6 +33,7 @@ import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.sql.planner.PartitioningHandle; import com.facebook.presto.sql.tree.QualifiedName; import io.airlift.slice.Slice; @@ -72,6 +73,20 @@ public interface Metadata TableLayout getLayout(Session session, TableLayoutHandle handle); + /** + * Return a table layout handle whose partitioning is converted to the provided partitioning handle, + * but otherwise identical to the provided table layout handle. + * The provided table layout handle must be one that the connector can transparently convert to from + * the original partitioning handle associated with the provided table layout handle, + * as promised by {@link #getCommonPartitioning}. + */ + TableLayoutHandle getAlternativeLayoutHandle(Session session, TableLayoutHandle tableLayoutHandle, PartitioningHandle partitioningHandle); + + /** + * Return a partitioning handle which the connector can transparently convert both {@code left} and {@code right} into. + */ + Optional getCommonPartitioning(Session session, PartitioningHandle left, PartitioningHandle right); + Optional getInfo(Session session, TableLayoutHandle handle); /** diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java b/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java index 46cb3a03e787c..ec54c2015d725 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/MetadataManager.java @@ -25,6 +25,7 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorTableHandle; import com.facebook.presto.spi.ConnectorTableLayout; +import com.facebook.presto.spi.ConnectorTableLayoutHandle; import com.facebook.presto.spi.ConnectorTableLayoutResult; import com.facebook.presto.spi.ConnectorTableMetadata; import com.facebook.presto.spi.ConnectorViewDefinition; @@ -37,6 +38,7 @@ import com.facebook.presto.spi.block.BlockEncodingSerde; import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorOutputMetadata; +import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.predicate.TupleDomain; @@ -49,6 +51,7 @@ import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.sql.analyzer.FeaturesConfig; +import com.facebook.presto.sql.planner.PartitioningHandle; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.transaction.TransactionManager; import com.facebook.presto.type.TypeDeserializer; @@ -99,6 +102,7 @@ import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.lang.String.format; import static java.util.Locale.ENGLISH; @@ -181,6 +185,11 @@ public static MetadataManager createTestMetadataManager(CatalogManager catalogMa } public static MetadataManager createTestMetadataManager(CatalogManager catalogManager, FeaturesConfig featuresConfig) + { + return createTestMetadataManager(createTestTransactionManager(catalogManager), featuresConfig); + } + + public static MetadataManager createTestMetadataManager(TransactionManager transactionManager, FeaturesConfig featuresConfig) { TypeManager typeManager = new TypeRegistry(ImmutableSet.of(), featuresConfig); return new MetadataManager( @@ -191,7 +200,7 @@ public static MetadataManager createTestMetadataManager(CatalogManager catalogMa new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), - createTestTransactionManager(catalogManager)); + transactionManager); } @Override @@ -366,6 +375,37 @@ public TableLayout getLayout(Session session, TableLayoutHandle handle) return fromConnectorLayout(connectorId, transaction, metadata.getTableLayout(session.toConnectorSession(connectorId), handle.getConnectorHandle())); } + @Override + public TableLayoutHandle getAlternativeLayoutHandle(Session session, TableLayoutHandle tableLayoutHandle, PartitioningHandle partitioningHandle) + { + checkArgument(partitioningHandle.getConnectorId().isPresent(), "Expect partitioning handle from connector, got system partitioning handle"); + ConnectorId connectorId = partitioningHandle.getConnectorId().get(); + checkArgument(connectorId.equals(tableLayoutHandle.getConnectorId()), "ConnectorId of tableLayoutHandle and partitioningHandle does not match"); + CatalogMetadata catalogMetadata = getCatalogMetadata(session, connectorId); + ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId); + ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(connectorId); + ConnectorTableLayoutHandle newTableLayoutHandle = metadata.getAlternativeLayoutHandle(session.toConnectorSession(connectorId), tableLayoutHandle.getConnectorHandle(), partitioningHandle.getConnectorHandle()); + return new TableLayoutHandle(connectorId, transaction, newTableLayoutHandle); + } + + @Override + public Optional getCommonPartitioning(Session session, PartitioningHandle left, PartitioningHandle right) + { + Optional leftConnectorId = left.getConnectorId(); + Optional rightConnectorId = right.getConnectorId(); + if (!leftConnectorId.isPresent() || !rightConnectorId.isPresent() || !leftConnectorId.equals(rightConnectorId)) { + return Optional.empty(); + } + if (!left.getTransactionHandle().equals(right.getTransactionHandle())) { + return Optional.empty(); + } + ConnectorId connectorId = leftConnectorId.get(); + CatalogMetadata catalogMetadata = getCatalogMetadata(session, connectorId); + ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId); + Optional commonHandle = metadata.getCommonPartitioningHandle(session.toConnectorSession(connectorId), left.getConnectorHandle(), right.getConnectorHandle()); + return commonHandle.map(handle -> new PartitioningHandle(Optional.of(connectorId), left.getTransactionHandle(), handle)); + } + @Override public Optional getInfo(Session session, TableLayoutHandle handle) { diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/OutputTableHandle.java b/presto-main/src/main/java/com/facebook/presto/metadata/OutputTableHandle.java index 8bd148e96f885..1718148e59abf 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/OutputTableHandle.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/OutputTableHandle.java @@ -82,6 +82,6 @@ public boolean equals(Object obj) @Override public String toString() { - return connectorId + ":" + transactionHandle + ":" + connectorHandle; + return connectorId + ":" + connectorHandle; } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunction.java b/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunction.java index 8929d20b77b3a..f7820174df591 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunction.java @@ -13,15 +13,18 @@ */ package com.facebook.presto.metadata; +import com.facebook.presto.metadata.PolymorphicScalarFunctionBuilder.MethodAndNativeContainerTypes; import com.facebook.presto.metadata.PolymorphicScalarFunctionBuilder.MethodsGroup; import com.facebook.presto.metadata.PolymorphicScalarFunctionBuilder.SpecializeContext; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention; +import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ScalarImplementationChoice; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.util.Reflection; +import com.google.common.collect.ImmutableList; import com.google.common.primitives.Primitives; import java.lang.invoke.MethodHandle; @@ -31,7 +34,7 @@ import java.util.Optional; import static com.facebook.presto.metadata.SignatureBinder.applyBoundVariables; -import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_BOXED_TYPE; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.BLOCK_AND_POSITION; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_NULL_FLAG; import static com.facebook.presto.type.TypeUtils.resolveTypes; import static com.google.common.base.Preconditions.checkState; @@ -44,27 +47,21 @@ class PolymorphicScalarFunction private final String description; private final boolean hidden; private final boolean deterministic; - private final boolean nullableResult; - private final List argumentProperties; - private final List methodsGroups; + private final List choices; PolymorphicScalarFunction( Signature signature, String description, boolean hidden, boolean deterministic, - boolean nullableResult, - List argumentProperties, - List methodsGroups) + List choices) { super(signature); this.description = description; this.hidden = hidden; this.deterministic = deterministic; - this.nullableResult = nullableResult; - this.argumentProperties = requireNonNull(argumentProperties, "argumentProperties is null"); - this.methodsGroups = requireNonNull(methodsGroups, "methodsWithExtraParametersFunctions is null"); + this.choices = requireNonNull(choices, "choices is null"); } @Override @@ -87,21 +84,35 @@ public String getDescription() @Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) + { + ImmutableList.Builder implementationChoices = ImmutableList.builder(); + + for (PolymorphicScalarFunctionChoice choice : choices) { + implementationChoices.add(getScalarFunctionImplementationChoice(boundVariables, typeManager, functionRegistry, choice)); + } + + return new ScalarFunctionImplementation(implementationChoices.build(), deterministic); + } + + private ScalarImplementationChoice getScalarFunctionImplementationChoice( + BoundVariables boundVariables, + TypeManager typeManager, + FunctionRegistry functionRegistry, + PolymorphicScalarFunctionChoice choice) { List resolvedParameterTypeSignatures = applyBoundVariables(getSignature().getArgumentTypes(), boundVariables); List resolvedParameterTypes = resolveTypes(resolvedParameterTypeSignatures, typeManager); TypeSignature resolvedReturnTypeSignature = applyBoundVariables(getSignature().getReturnType(), boundVariables); Type resolvedReturnType = typeManager.getType(resolvedReturnTypeSignature); - SpecializeContext context = new SpecializeContext(boundVariables, resolvedParameterTypes, resolvedReturnType, typeManager, functionRegistry); - Optional matchingMethod = Optional.empty(); + Optional matchingMethod = Optional.empty(); Optional matchingMethodsGroup = Optional.empty(); - for (MethodsGroup candidateMethodsGroup : methodsGroups) { - for (Method candidateMethod : candidateMethodsGroup.getMethods()) { - if (matchesParameterAndReturnTypes(candidateMethod, resolvedParameterTypes, resolvedReturnType)) { + for (MethodsGroup candidateMethodsGroup : choice.getMethodsGroups()) { + for (MethodAndNativeContainerTypes candidateMethod : candidateMethodsGroup.getMethods()) { + if (matchesParameterAndReturnTypes(candidateMethod, resolvedParameterTypes, resolvedReturnType, choice.getArgumentProperties(), choice.isNullableResult())) { if (matchingMethod.isPresent()) { - throw new IllegalStateException("two matching methods (" + matchingMethod.get().getName() + " and " + candidateMethod.getName() + ") for parameter types " + resolvedParameterTypeSignatures); + throw new IllegalStateException("two matching methods (" + matchingMethod.get().getMethod().getName() + " and " + candidateMethod.getMethod().getName() + ") for parameter types " + resolvedParameterTypeSignatures); } matchingMethod = Optional.of(candidateMethod); @@ -112,25 +123,47 @@ public ScalarFunctionImplementation specialize(BoundVariables boundVariables, in checkState(matchingMethod.isPresent(), "no matching method for parameter types %s", resolvedParameterTypes); List extraParameters = computeExtraParameters(matchingMethodsGroup.get(), context); - MethodHandle matchingMethodHandle = applyExtraParameters(matchingMethod.get(), extraParameters); - - return new ScalarFunctionImplementation( - nullableResult, - argumentProperties, - matchingMethodHandle, - deterministic); + MethodHandle methodHandle = applyExtraParameters(matchingMethod.get().getMethod(), extraParameters, choice.getArgumentProperties()); + return new ScalarImplementationChoice(choice.isNullableResult(), choice.getArgumentProperties(), methodHandle, Optional.empty()); } - private boolean matchesParameterAndReturnTypes(Method method, List resolvedTypes, Type returnType) + private static boolean matchesParameterAndReturnTypes( + MethodAndNativeContainerTypes methodAndNativeContainerTypes, + List resolvedTypes, + Type returnType, + List argumentProperties, + boolean nullableResult) { + Method method = methodAndNativeContainerTypes.getMethod(); checkState(method.getParameterCount() >= resolvedTypes.size(), "method %s has not enough arguments: %s (should have at least %s)", method.getName(), method.getParameterCount(), resolvedTypes.size()); Class[] methodParameterJavaTypes = method.getParameterTypes(); for (int i = 0, methodParameterIndex = 0; i < resolvedTypes.size(); i++) { NullConvention nullConvention = argumentProperties.get(i).getNullConvention(); - Class type = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), nullConvention == USE_BOXED_TYPE); - if (!methodParameterJavaTypes[methodParameterIndex].equals(type)) { + Class expectedType = null; + Class actualType; + switch (nullConvention) { + case RETURN_NULL_ON_NULL: + case USE_NULL_FLAG: + expectedType = methodParameterJavaTypes[methodParameterIndex]; + actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), false); + break; + case USE_BOXED_TYPE: + expectedType = methodParameterJavaTypes[methodParameterIndex]; + actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), true); + break; + case BLOCK_AND_POSITION: + Optional> explicitNativeContainerTypes = methodAndNativeContainerTypes.getExplicitNativeContainerTypes().get(i); + if (explicitNativeContainerTypes.isPresent()) { + expectedType = explicitNativeContainerTypes.get(); + } + actualType = getNullAwareContainerType(resolvedTypes.get(i).getJavaType(), false); + break; + default: + throw new UnsupportedOperationException("unknown NullConvention"); + } + if (!actualType.equals(expectedType)) { return false; } methodParameterIndex += nullConvention.getParameterCount(); @@ -143,17 +176,24 @@ private static List computeExtraParameters(MethodsGroup methodsGroup, Sp return methodsGroup.getExtraParametersFunction().map(function -> function.apply(context)).orElse(emptyList()); } - private int getNullFlagsCount() + private static int getNullFlagsCount(List argumentProperties) { return (int) argumentProperties.stream() .filter(argumentProperty -> argumentProperty.getNullConvention() == USE_NULL_FLAG) .count(); } - private MethodHandle applyExtraParameters(Method matchingMethod, List extraParameters) + private static int getBlockPositionCount(List argumentProperties) + { + return (int) argumentProperties.stream() + .filter(argumentProperty -> argumentProperty.getNullConvention() == BLOCK_AND_POSITION) + .count(); + } + + private MethodHandle applyExtraParameters(Method matchingMethod, List extraParameters, List argumentProperties) { Signature signature = getSignature(); - int expectedArgumentsCount = signature.getArgumentTypes().size() + getNullFlagsCount() + extraParameters.size(); + int expectedArgumentsCount = signature.getArgumentTypes().size() + getNullFlagsCount(argumentProperties) + getBlockPositionCount(argumentProperties) + extraParameters.size(); int matchingMethodArgumentCount = matchingMethod.getParameterCount(); checkState(matchingMethodArgumentCount == expectedArgumentsCount, "method %s has invalid number of arguments: %s (should have %s)", matchingMethod.getName(), matchingMethodArgumentCount, expectedArgumentsCount); @@ -173,4 +213,36 @@ private static Class getNullAwareContainerType(Class clazz, boolean nullab } return clazz; } + + static final class PolymorphicScalarFunctionChoice + { + private final boolean nullableResult; + private final List argumentProperties; + private final List methodsGroups; + + PolymorphicScalarFunctionChoice( + boolean nullableResult, + List argumentProperties, + List methodsGroups) + { + this.nullableResult = nullableResult; + this.argumentProperties = ImmutableList.copyOf(requireNonNull(argumentProperties, "argumentProperties is null")); + this.methodsGroups = ImmutableList.copyOf(requireNonNull(methodsGroups, "methodsWithExtraParametersFunctions is null")); + } + + boolean isNullableResult() + { + return nullableResult; + } + + List getMethodsGroups() + { + return methodsGroups; + } + + List getArgumentProperties() + { + return argumentProperties; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunctionBuilder.java b/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunctionBuilder.java index af3103c6efe89..7bb1bfc64eb3c 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunctionBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/PolymorphicScalarFunctionBuilder.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.metadata; +import com.facebook.presto.metadata.PolymorphicScalarFunction.PolymorphicScalarFunctionChoice; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.type.Type; @@ -21,18 +22,19 @@ import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.function.Function; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.BLOCK_AND_POSITION; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.RETURN_NULL_ON_NULL; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; +import static java.util.Collections.nCopies; import static java.util.Objects.requireNonNull; public final class PolymorphicScalarFunctionBuilder @@ -42,9 +44,7 @@ public final class PolymorphicScalarFunctionBuilder private String description; private Optional hidden = Optional.empty(); private Boolean deterministic; - private boolean nullableResult; - private List argumentProperties = emptyList(); - private final List methodsGroups = new ArrayList<>(); + private final List choices = new ArrayList<>(); public PolymorphicScalarFunctionBuilder(Class clazz) { @@ -76,31 +76,11 @@ public PolymorphicScalarFunctionBuilder deterministic(boolean deterministic) return this; } - public PolymorphicScalarFunctionBuilder nullableResult(boolean nullableResult) + public PolymorphicScalarFunctionBuilder choice(Function choiceSpecification) { - this.nullableResult = nullableResult; - return this; - } - - public PolymorphicScalarFunctionBuilder argumentProperties(ArgumentProperty... argumentProperties) - { - requireNonNull(argumentProperties, "argumentProperties is null"); - this.argumentProperties = ImmutableList.copyOf(argumentProperties); - return this; - } - - public PolymorphicScalarFunctionBuilder argumentProperties(List argumentProperties) - { - this.argumentProperties = ImmutableList.copyOf(requireNonNull(argumentProperties, "argumentProperties is null")); - return this; - } - - public PolymorphicScalarFunctionBuilder implementation(Function methodGroupSpecification) - { - MethodsGroupBuilder methodsGroupBuilder = new MethodsGroupBuilder(clazz); - methodGroupSpecification.apply(methodsGroupBuilder); - MethodsGroup methodsGroup = methodsGroupBuilder.build(); - methodsGroups.add(methodsGroup); + ChoiceBuilder choiceBuilder = new ChoiceBuilder(clazz, signature); + choiceSpecification.apply(choiceBuilder); + choices.add(choiceBuilder.build()); return this; } @@ -109,18 +89,12 @@ public SqlScalarFunction build() checkState(signature != null, "signature is null"); checkState(deterministic != null, "deterministic is null"); - if (argumentProperties.isEmpty()) { - argumentProperties = Collections.nCopies(signature.getArgumentTypes().size(), valueTypeArgumentProperty(RETURN_NULL_ON_NULL)); - } - return new PolymorphicScalarFunction( signature, description, hidden.orElse(false), deterministic, - nullableResult, - argumentProperties, - methodsGroups); + choices); } @SafeVarargs @@ -151,7 +125,7 @@ private static boolean isOperator(Signature signature) return false; } - public static class SpecializeContext + public static final class SpecializeContext { private final BoundVariables boundVariables; private final List parameterTypes; @@ -202,12 +176,17 @@ public FunctionRegistry getFunctionRegistry() public static class MethodsGroupBuilder { private final Class clazz; - private List methods; + private final Signature signature; + private List argumentProperties; + private final ImmutableList.Builder methodAndNativeContainerTypesList = ImmutableList.builder(); + private Optional>> extraParametersFunction = Optional.empty(); - private MethodsGroupBuilder(Class clazz) + private MethodsGroupBuilder(Class clazz, Signature signature, List argumentProperties) { this.clazz = clazz; + this.signature = signature; + this.argumentProperties = argumentProperties; } public MethodsGroupBuilder methods(String... methodNames) @@ -219,52 +198,115 @@ public MethodsGroupBuilder methods(List methodNames) { requireNonNull(methodNames, "methodNames is null"); checkArgument(!methodNames.isEmpty(), "methods list is empty"); + methodNames.forEach(methodName -> this.methodWithExplicitJavaTypes(methodName, nCopies(signature.getArgumentTypes().size(), Optional.empty()))); + return this; + } - List matchingMethods = asList(clazz.getMethods()).stream() - .filter(method -> methodNames.contains(method.getName())) - .collect(toImmutableList()); - List matchingMethodNames = matchingMethods.stream() - .map(Method::getName) + public MethodsGroupBuilder withExtraParameters(Function> extraParametersFunction) + { + checkState(!methodAndNativeContainerTypesList.build().isEmpty(), "methods must be selected first"); + requireNonNull(extraParametersFunction, "extraParametersFunction is null"); + this.extraParametersFunction = Optional.of(extraParametersFunction); + return this; + } + + public MethodsGroupBuilder methodWithExplicitJavaTypes(String methodName, List>> types) + { + requireNonNull(methodName, "methodName is null"); + List matchingMethod = asList(clazz.getMethods()).stream() + .filter(method -> methodName.equals(method.getName())) + .map(method -> new MethodAndNativeContainerTypes(method, types)) .collect(toImmutableList()); - for (String methodName : methodNames) { - checkState(matchingMethodNames.contains(methodName), "method %s was not found in %s", methodName, clazz); + checkState(!matchingMethod.isEmpty(), "method %s was not found in %s", methodName, clazz); + checkState(matchingMethod.size() == 1, "multiple methods %s was not found in %s", methodName, clazz); + MethodAndNativeContainerTypes methodAndNativeContainerTypes = matchingMethod.get(0); + int argumentSize = signature.getArgumentTypes().size(); + checkState(types.size() == argumentSize, "not matching number of arguments from signature: %s (should have %s)", + types.size(), argumentSize); + checkState(types.size() == argumentProperties.size(), "not matching number of arguments from argument properties: %s (should have %s)", + types.size(), argumentProperties.size()); + Iterator argumentPropertyIterator = argumentProperties.iterator(); + Iterator>> typesIterator = types.iterator(); + while (argumentPropertyIterator.hasNext() && typesIterator.hasNext()) { + Optional> classOptional = typesIterator.next(); + ArgumentProperty argumentProperty = argumentPropertyIterator.next(); + checkState((argumentProperty.getNullConvention() == BLOCK_AND_POSITION) == classOptional.isPresent(), + "Explicit type is not set when null convention is BLOCK_AND_POSITION"); + } + methodAndNativeContainerTypesList.add(methodAndNativeContainerTypes); + return this; + } + + public MethodsGroup build() + { + return new MethodsGroup(methodAndNativeContainerTypesList.build(), extraParametersFunction); + } + } + + public static class ChoiceBuilder + { + private final Class clazz; + private final Signature signature; + private boolean nullableResult; + private List argumentProperties; + private final ImmutableList.Builder methodsGroups = ImmutableList.builder(); + + private ChoiceBuilder(Class clazz, Signature signature) + { + this.clazz = requireNonNull(clazz, "clazz is null"); + this.signature = requireNonNull(signature, "signature is null"); + } + + public ChoiceBuilder implementation(Function methodsGroupSpecification) + { + // if the argumentProperties is not set yet. We assume it is set to the default value. + if (argumentProperties == null) { + argumentProperties = nCopies(signature.getArgumentTypes().size(), valueTypeArgumentProperty(RETURN_NULL_ON_NULL)); } + MethodsGroupBuilder methodsGroupBuilder = new MethodsGroupBuilder(clazz, signature, argumentProperties); + methodsGroupSpecification.apply(methodsGroupBuilder); + methodsGroups.add(methodsGroupBuilder.build()); + return this; + } - this.methods = matchingMethods; + public ChoiceBuilder nullableResult(boolean nullableResult) + { + this.nullableResult = nullableResult; return this; } - public MethodsGroupBuilder withExtraParameters(Function> extraParametersFunction) + public ChoiceBuilder argumentProperties(ArgumentProperty... argumentProperties) { - checkState(methods != null, "methods must be selected first"); - requireNonNull(extraParametersFunction, "extraParametersFunction is null"); - this.extraParametersFunction = Optional.of(extraParametersFunction); + requireNonNull(argumentProperties, "argumentProperties is null"); + checkState(this.argumentProperties == null, + "The `argumentProperties` method must be invoked only once, and must be invoked before the `implementation` method"); + this.argumentProperties = ImmutableList.copyOf(argumentProperties); return this; } - public MethodsGroup build() + public PolymorphicScalarFunctionChoice build() { - return new MethodsGroup(methods, extraParametersFunction); + return new PolymorphicScalarFunctionChoice(nullableResult, argumentProperties, methodsGroups.build()); } } - static class MethodsGroup + static final class MethodsGroup { - private final List methods; private final Optional>> extraParametersFunction; + private final List methodAndNativeContainerTypes; - private MethodsGroup( - List methods, + MethodsGroup( + List methodAndNativeContainerTypes, Optional>> extraParametersFunction) { - this.methods = requireNonNull(methods, "methods is null"); + this.methodAndNativeContainerTypes = requireNonNull(methodAndNativeContainerTypes, "methodAndNativeContainerTypes is null"); this.extraParametersFunction = requireNonNull(extraParametersFunction, "extraParametersFunction is null"); } - List getMethods() + List getMethods() { - return methods; + return methodAndNativeContainerTypes; } Optional>> getExtraParametersFunction() @@ -272,4 +314,31 @@ Optional>> getExtraParametersFunction() return extraParametersFunction; } } + + static class MethodAndNativeContainerTypes + { + private final Method method; + private List>> explicitNativeContainerTypes; + + MethodAndNativeContainerTypes(Method method, List>> explicitNativeContainerTypes) + { + this.method = method; + this.explicitNativeContainerTypes = explicitNativeContainerTypes; + } + + public Method getMethod() + { + return method; + } + + List>> getExplicitNativeContainerTypes() + { + return explicitNativeContainerTypes; + } + + void setExplicitNativeContainerTypes(List>> explicitNativeContainerTypes) + { + this.explicitNativeContainerTypes = explicitNativeContainerTypes; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/QualifiedTablePrefix.java b/presto-main/src/main/java/com/facebook/presto/metadata/QualifiedTablePrefix.java index 4ee685c4320aa..e185c52887518 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/QualifiedTablePrefix.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/QualifiedTablePrefix.java @@ -14,6 +14,7 @@ package com.facebook.presto.metadata; import com.facebook.presto.spi.SchemaTablePrefix; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.concurrent.Immutable; @@ -53,6 +54,7 @@ public QualifiedTablePrefix(String catalogName, String schemaName, String tableN this.tableName = Optional.of(checkTableName(tableName)); } + @JsonCreator public QualifiedTablePrefix( @JsonProperty("catalogName") String catalogName, @JsonProperty("schemaName") Optional schemaName, diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/RemoteNodeState.java b/presto-main/src/main/java/com/facebook/presto/metadata/RemoteNodeState.java index 954a494b04c8f..222295b29070f 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/RemoteNodeState.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/RemoteNodeState.java @@ -40,8 +40,6 @@ import static io.airlift.json.JsonCodec.jsonCodec; import static io.airlift.units.Duration.nanosSince; import static java.util.Objects.requireNonNull; -import static java.util.Optional.empty; -import static java.util.Optional.ofNullable; import static java.util.concurrent.TimeUnit.SECONDS; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; @@ -52,7 +50,7 @@ public class RemoteNodeState private final HttpClient httpClient; private final URI stateInfoUri; - private final AtomicReference> nodeState = new AtomicReference<>(empty()); + private final AtomicReference> nodeState = new AtomicReference<>(Optional.empty()); private final AtomicReference> future = new AtomicReference<>(); private final AtomicLong lastUpdateNanos = new AtomicLong(); private final AtomicLong lastWarningLogged = new AtomicLong(); @@ -94,7 +92,7 @@ public void onSuccess(@Nullable JsonResponse result) future.compareAndSet(responseFuture, null); if (result != null) { if (result.hasValue()) { - nodeState.set(ofNullable(result.getValue())); + nodeState.set(Optional.ofNullable(result.getValue())); } if (result.getStatusCode() != OK.code()) { log.warn("Error fetching node state from %s returned status %d: %s", stateInfoUri, result.getStatusCode(), result.getStatusMessage()); diff --git a/presto-main/src/main/java/com/facebook/presto/metadata/TableLayoutResult.java b/presto-main/src/main/java/com/facebook/presto/metadata/TableLayoutResult.java index bc51820d52a2f..ea4315bf7344b 100644 --- a/presto-main/src/main/java/com/facebook/presto/metadata/TableLayoutResult.java +++ b/presto-main/src/main/java/com/facebook/presto/metadata/TableLayoutResult.java @@ -14,13 +14,17 @@ package com.facebook.presto.metadata; import com.facebook.presto.spi.ColumnHandle; +import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.sql.planner.plan.TableScanNode; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.List; +import java.util.Map; import java.util.Set; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; public class TableLayoutResult @@ -56,4 +60,54 @@ public boolean hasAllOutputs(TableScanNode node) return columns.containsAll(nodeColumnHandles); } + + public static TupleDomain computeEnforced(TupleDomain predicate, TupleDomain unenforced) + { + if (predicate.isNone()) { + // If the engine requests that the connector provides a layout with a domain of "none". The connector can have two possible reactions, either: + // 1. The connector can provide an empty table layout. + // * There would be no unenforced predicate, i.e., unenforced predicate is TupleDomain.all(). + // * The predicate was successfully enforced. Enforced predicate would be same as predicate: TupleDomain.none(). + // 2. The connector can't/won't. + // * The connector would tell the engine to put a filter on top of the scan, i.e., unenforced predicate is TupleDomain.none(). + // * The connector didn't successfully enforce anything. Therefore, enforced predicate would be TupleDomain.all(). + if (unenforced.isNone()) { + return TupleDomain.all(); + } + if (unenforced.isAll()) { + return TupleDomain.none(); + } + throw new IllegalArgumentException(); + } + + // The engine requested the connector provides a layout with a non-none TupleDomain. + // A TupleDomain is effectively a list of column-Domain pairs. + // The connector is expected enforce the respective domain entirely on none, some, or all of the columns. + // 1. When the connector could enforce none of the domains, the unenforced would be equal to predicate; + // 2. When the connector could enforce some of the domains, the unenforced would contain a subset of the column-Domain pairs; + // 3. When the connector could enforce all of the domains, the unenforced would be TupleDomain.all(). + + // In all 3 cases shown above, the unenforced is not TupleDomain.none(). + checkArgument(!unenforced.isNone()); + + Map predicateDomains = predicate.getDomains().get(); + Map unenforcedDomains = unenforced.getDomains().get(); + ImmutableMap.Builder enforcedDomainsBuilder = ImmutableMap.builder(); + for (Map.Entry entry : predicateDomains.entrySet()) { + ColumnHandle predicateColumnHandle = entry.getKey(); + if (unenforcedDomains.containsKey(predicateColumnHandle)) { + checkArgument( + entry.getValue().equals(unenforcedDomains.get(predicateColumnHandle)), + "Enforced tuple domain cannot be determined. The connector is expected to enforce the respective domain entirely on none, some, or all of the column."); + } + else { + enforcedDomainsBuilder.put(predicateColumnHandle, entry.getValue()); + } + } + Map enforcedDomains = enforcedDomainsBuilder.build(); + checkArgument( + enforcedDomains.size() + unenforcedDomains.size() == predicateDomains.size(), + "Enforced tuple domain cannot be determined. Connector returned an unenforced TupleDomain that contains columns not in predicate."); + return TupleDomain.withColumnDomains(enforcedDomains); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/AggregationOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/AggregationOperator.java index 01a932f80dc69..5c3450e51f480 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/AggregationOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/AggregationOperator.java @@ -35,8 +35,6 @@ public class AggregationOperator implements Operator { - private final boolean partial; - public static class AggregationOperatorFactory implements OperatorFactory { @@ -44,14 +42,16 @@ public static class AggregationOperatorFactory private final PlanNodeId planNodeId; private final Step step; private final List accumulatorFactories; + private final boolean useSystemMemory; private boolean closed; - public AggregationOperatorFactory(int operatorId, PlanNodeId planNodeId, Step step, List accumulatorFactories) + public AggregationOperatorFactory(int operatorId, PlanNodeId planNodeId, Step step, List accumulatorFactories, boolean useSystemMemory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); this.step = step; this.accumulatorFactories = ImmutableList.copyOf(accumulatorFactories); + this.useSystemMemory = useSystemMemory; } @Override @@ -59,7 +59,7 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, AggregationOperator.class.getSimpleName()); - return new AggregationOperator(operatorContext, step, accumulatorFactories); + return new AggregationOperator(operatorContext, step, accumulatorFactories, useSystemMemory); } @Override @@ -71,7 +71,7 @@ public void noMoreOperators() @Override public OperatorFactory duplicate() { - return new AggregationOperatorFactory(operatorId, planNodeId, step, accumulatorFactories); + return new AggregationOperatorFactory(operatorId, planNodeId, step, accumulatorFactories, useSystemMemory); } } @@ -86,17 +86,18 @@ private enum State private final LocalMemoryContext systemMemoryContext; private final LocalMemoryContext userMemoryContext; private final List aggregates; + private final boolean useSystemMemory; private State state = State.NEEDS_INPUT; - public AggregationOperator(OperatorContext operatorContext, Step step, List accumulatorFactories) + public AggregationOperator(OperatorContext operatorContext, Step step, List accumulatorFactories, boolean useSystemMemory) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.systemMemoryContext = operatorContext.newLocalSystemMemoryContext(AggregationOperator.class.getSimpleName()); this.userMemoryContext = operatorContext.localUserMemoryContext(); + this.useSystemMemory = useSystemMemory; requireNonNull(step, "step is null"); - this.partial = step.isOutputPartial(); // wrapper each function with an aggregator requireNonNull(accumulatorFactories, "accumulatorFactories is null"); @@ -151,7 +152,7 @@ public void addInput(Page page) aggregate.processPage(page); memorySize += aggregate.getEstimatedSize(); } - if (partial) { + if (useSystemMemory) { systemMemoryContext.setBytes(memorySize); } else { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/Driver.java b/presto-main/src/main/java/com/facebook/presto/operator/Driver.java index 7397cd4c49205..2627cb4e486d7 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/Driver.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/Driver.java @@ -274,12 +274,13 @@ public ListenableFuture processFor(Duration duration) long maxRuntime = duration.roundTo(TimeUnit.NANOSECONDS); Optional> result = tryWithLock(100, TimeUnit.MILLISECONDS, () -> { + OperationTimer operationTimer = createTimer(); driverContext.startProcessTimer(); driverContext.getYieldSignal().setWithDelay(maxRuntime, driverContext.getYieldExecutor()); try { long start = System.nanoTime(); do { - ListenableFuture future = processInternal(); + ListenableFuture future = processInternal(operationTimer); if (!future.isDone()) { return updateDriverBlockedFuture(future); } @@ -288,7 +289,7 @@ public ListenableFuture processFor(Duration duration) } finally { driverContext.getYieldSignal().reset(); - driverContext.recordProcessed(); + driverContext.recordProcessed(operationTimer); } return NOT_BLOCKED; }); @@ -306,12 +307,19 @@ public ListenableFuture process() } Optional> result = tryWithLock(100, TimeUnit.MILLISECONDS, () -> { - ListenableFuture future = processInternal(); + ListenableFuture future = processInternal(createTimer()); return updateDriverBlockedFuture(future); }); return result.orElse(NOT_BLOCKED); } + private OperationTimer createTimer() + { + return new OperationTimer( + driverContext.isCpuTimerEnabled(), + driverContext.isCpuTimerEnabled() && driverContext.isPerOperatorCpuTimerEnabled()); + } + private ListenableFuture updateDriverBlockedFuture(ListenableFuture sourceBlockedFuture) { // driverBlockedFuture will be completed as soon as the sourceBlockedFuture is completed @@ -336,7 +344,7 @@ private ListenableFuture updateDriverBlockedFuture(ListenableFuture source } @GuardedBy("exclusiveLock") - private ListenableFuture processInternal() + private ListenableFuture processInternal(OperationTimer operationTimer) { checkLockHeld("Lock must be held to call processInternal"); @@ -351,9 +359,8 @@ private ListenableFuture processInternal() // Note: finish should not be called on the natural source of the pipeline as this could cause the task to finish early if (!activeOperators.isEmpty() && activeOperators.size() != allOperators.size()) { Operator rootOperator = activeOperators.get(0); - rootOperator.getOperatorContext().startIntervalTimer(); rootOperator.finish(); - rootOperator.getOperatorContext().recordFinish(); + rootOperator.getOperatorContext().recordFinish(operationTimer); } boolean movedPage = false; @@ -369,15 +376,13 @@ private ListenableFuture processInternal() // if the current operator is not finished and next operator isn't blocked and needs input... if (!current.isFinished() && !getBlockedFuture(next).isPresent() && next.needsInput()) { // get an output page from current operator - current.getOperatorContext().startIntervalTimer(); Page page = current.getOutput(); - current.getOperatorContext().recordGetOutput(page); + current.getOperatorContext().recordGetOutput(operationTimer, page); // if we got an output page, add it to the next operator if (page != null && page.getPositionCount() != 0) { - next.getOperatorContext().startIntervalTimer(); next.addInput(page); - next.getOperatorContext().recordAddInput(page); + next.getOperatorContext().recordAddInput(operationTimer, page); movedPage = true; } @@ -389,9 +394,8 @@ private ListenableFuture processInternal() // if current operator is finished... if (current.isFinished()) { // let next operator know there will be no more data - next.getOperatorContext().startIntervalTimer(); next.finish(); - next.getOperatorContext().recordFinish(); + next.getOperatorContext().recordFinish(operationTimer); } } @@ -408,9 +412,8 @@ private ListenableFuture processInternal() // Finish the next operator, which is now the first operator. if (!activeOperators.isEmpty()) { Operator newRootOperator = activeOperators.get(0); - newRootOperator.getOperatorContext().startIntervalTimer(); newRootOperator.finish(); - newRootOperator.getOperatorContext().recordFinish(); + newRootOperator.getOperatorContext().recordFinish(operationTimer); } break; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/DriverContext.java b/presto-main/src/main/java/com/facebook/presto/operator/DriverContext.java index 3f8c496ad0df5..4ed804f0686f0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/DriverContext.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/DriverContext.java @@ -18,6 +18,7 @@ import com.facebook.presto.execution.TaskId; import com.facebook.presto.memory.QueryContextVisitor; import com.facebook.presto.memory.context.MemoryTrackingContext; +import com.facebook.presto.operator.OperationTimer.OperationTiming; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; @@ -28,8 +29,6 @@ import io.airlift.units.Duration; import org.joda.time.DateTime; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -44,6 +43,7 @@ import static com.google.common.collect.Iterables.transform; import static io.airlift.units.DataSize.Unit.BYTE; import static io.airlift.units.DataSize.succinctBytes; +import static java.lang.Math.max; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -54,8 +54,6 @@ */ public class DriverContext { - private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); - private final PipelineContext pipelineContext; private final Executor notificationExecutor; private final ScheduledExecutorService yieldExecutor; @@ -68,14 +66,7 @@ public class DriverContext private final AtomicLong startNanos = new AtomicLong(); private final AtomicLong endNanos = new AtomicLong(); - private final AtomicLong intervalWallStart = new AtomicLong(); - private final AtomicLong intervalCpuStart = new AtomicLong(); - private final AtomicLong intervalUserStart = new AtomicLong(); - - private final AtomicLong processCalls = new AtomicLong(); - private final AtomicLong processWallNanos = new AtomicLong(); - private final AtomicLong processCpuNanos = new AtomicLong(); - private final AtomicLong processUserNanos = new AtomicLong(); + private final OperationTiming overallTiming = new OperationTiming(); private final AtomicReference blockedMonitor = new AtomicReference<>(); private final AtomicLong blockedWallNanos = new AtomicLong(); @@ -88,7 +79,6 @@ public class DriverContext private final DriverYieldSignal yieldSignal; private final List operatorContexts = new CopyOnWriteArrayList<>(); - private final boolean partitioned; private final Lifespan lifespan; public DriverContext( @@ -96,14 +86,12 @@ public DriverContext( Executor notificationExecutor, ScheduledExecutorService yieldExecutor, MemoryTrackingContext driverMemoryContext, - boolean partitioned, Lifespan lifespan) { this.pipelineContext = requireNonNull(pipelineContext, "pipelineContext is null"); this.notificationExecutor = requireNonNull(notificationExecutor, "notificationExecutor is null"); this.yieldExecutor = requireNonNull(yieldExecutor, "scheduler is null"); this.driverMemoryContext = requireNonNull(driverMemoryContext, "driverMemoryContext is null"); - this.partitioned = partitioned; this.lifespan = requireNonNull(lifespan, "lifespan is null"); this.yieldSignal = new DriverYieldSignal(); } @@ -153,18 +141,11 @@ public void startProcessTimer() pipelineContext.start(); executionStartTime.set(DateTime.now()); } - - intervalWallStart.set(System.nanoTime()); - intervalCpuStart.set(currentThreadCpuTime()); - intervalUserStart.set(currentThreadUserTime()); } - public void recordProcessed() + public void recordProcessed(OperationTimer operationTimer) { - processCalls.incrementAndGet(); - processWallNanos.getAndAdd(nanosBetween(intervalWallStart.get(), System.nanoTime())); - processCpuNanos.getAndAdd(nanosBetween(intervalCpuStart.get(), currentThreadCpuTime())); - processUserNanos.getAndAdd(nanosBetween(intervalUserStart.get(), currentThreadUserTime())); + operationTimer.end(overallTiming); } public void recordBlocked(ListenableFuture blocked) @@ -243,9 +224,9 @@ public void moreMemoryAvailable() operatorContexts.forEach(OperatorContext::moreMemoryAvailable); } - public boolean isVerboseStats() + public boolean isPerOperatorCpuTimerEnabled() { - return pipelineContext.isVerboseStats(); + return pipelineContext.isPerOperatorCpuTimerEnabled(); } public boolean isCpuTimerEnabled() @@ -316,9 +297,8 @@ public boolean isFullyBlocked() public DriverStats getDriverStats() { - long totalScheduledTime = processWallNanos.get(); - long totalCpuTime = processCpuNanos.get(); - long totalUserTime = processUserNanos.get(); + long totalScheduledTime = overallTiming.getWallNanos(); + long totalCpuTime = overallTiming.getCpuNanos(); long totalBlockedTime = blockedWallNanos.get(); BlockedMonitor blockedMonitor = this.blockedMonitor.get(); @@ -336,12 +316,12 @@ public DriverStats getDriverStats() DataSize outputDataSize; long outputPositions; if (inputOperator != null) { - rawInputDataSize = inputOperator.getInputDataSize(); + rawInputDataSize = inputOperator.getRawInputDataSize(); rawInputPositions = inputOperator.getInputPositions(); rawInputReadTime = inputOperator.getAddInputWall(); - processedInputDataSize = inputOperator.getOutputDataSize(); - processedInputPositions = inputOperator.getOutputPositions(); + processedInputDataSize = inputOperator.getInputDataSize(); + processedInputPositions = inputOperator.getInputPositions(); OperatorStats outputOperator = requireNonNull(getLast(operators, null)); outputDataSize = outputOperator.getOutputDataSize(); @@ -399,7 +379,6 @@ public DriverStats getDriverStats() succinctBytes(driverMemoryContext.getSystemMemory()), new Duration(totalScheduledTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalCpuTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(totalUserTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalBlockedTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), blockedMonitor != null, builder.build(), @@ -426,11 +405,6 @@ public List acceptChildren(QueryContextVisitor visitor, C contex .collect(toList()); } - public boolean isPartitioned() - { - return partitioned; - } - public Lifespan getLifespan() { return lifespan; @@ -441,25 +415,9 @@ public ScheduledExecutorService getYieldExecutor() return yieldExecutor; } - private long currentThreadUserTime() - { - if (!isCpuTimerEnabled()) { - return 0; - } - return THREAD_MX_BEAN.getCurrentThreadUserTime(); - } - - private long currentThreadCpuTime() - { - if (!isCpuTimerEnabled()) { - return 0; - } - return THREAD_MX_BEAN.getCurrentThreadCpuTime(); - } - private static long nanosBetween(long start, long end) { - return Math.abs(end - start); + return max(0, end - start); } private class BlockedMonitor diff --git a/presto-main/src/main/java/com/facebook/presto/operator/DriverStats.java b/presto-main/src/main/java/com/facebook/presto/operator/DriverStats.java index ac3261dba79c6..74d2816ab521a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/DriverStats.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/DriverStats.java @@ -51,7 +51,6 @@ public class DriverStats private final Duration totalScheduledTime; private final Duration totalCpuTime; - private final Duration totalUserTime; private final Duration totalBlockedTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -86,7 +85,6 @@ public DriverStats() this.totalScheduledTime = new Duration(0, MILLISECONDS); this.totalCpuTime = new Duration(0, MILLISECONDS); - this.totalUserTime = new Duration(0, MILLISECONDS); this.totalBlockedTime = new Duration(0, MILLISECONDS); this.fullyBlocked = false; this.blockedReasons = ImmutableSet.of(); @@ -122,7 +120,6 @@ public DriverStats( @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("totalCpuTime") Duration totalCpuTime, - @JsonProperty("totalUserTime") Duration totalUserTime, @JsonProperty("totalBlockedTime") Duration totalBlockedTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @@ -155,7 +152,6 @@ public DriverStats( this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); - this.totalUserTime = requireNonNull(totalUserTime, "totalUserTime is null"); this.totalBlockedTime = requireNonNull(totalBlockedTime, "totalBlockedTime is null"); this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -246,12 +242,6 @@ public Duration getTotalCpuTime() return totalCpuTime; } - @JsonProperty - public Duration getTotalUserTime() - { - return totalUserTime; - } - @JsonProperty public Duration getTotalBlockedTime() { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ExchangeClient.java b/presto-main/src/main/java/com/facebook/presto/operator/ExchangeClient.java index 5af693ad6f579..6b8565da47a2d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ExchangeClient.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ExchangeClient.java @@ -16,7 +16,7 @@ import com.facebook.presto.execution.buffer.SerializedPage; import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.operator.HttpPageBufferClient.ClientCallback; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; +import com.facebook.presto.operator.WorkProcessor.ProcessState; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -45,9 +45,6 @@ import java.util.concurrent.atomic.AtomicReference; import static com.facebook.presto.execution.buffer.PageCompression.UNCOMPRESSED; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.blocked; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.finished; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.yield; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.collect.Sets.newConcurrentHashSet; @@ -185,18 +182,18 @@ public WorkProcessor pages() SerializedPage page = pollPage(); if (page == null) { if (isFinished()) { - return finished(); + return ProcessState.finished(); } ListenableFuture blocked = isBlocked(); if (!blocked.isDone()) { - return blocked(blocked); + return ProcessState.blocked(blocked); } - return yield(); + return ProcessState.yield(); } - return ProcessorState.ofResult(page); + return ProcessState.ofResult(page); }); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ExchangeOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/ExchangeOperator.java index 0dd5825f5e7b7..940c89518da9a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ExchangeOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ExchangeOperator.java @@ -180,8 +180,12 @@ public Page getOutput() return null; } - operatorContext.recordGeneratedInput(page.getSizeInBytes(), page.getPositionCount()); - return serde.deserialize(page); + operatorContext.recordRawInput(page.getSizeInBytes()); + + Page deserializedPage = serde.deserialize(page); + operatorContext.recordProcessedInput(deserializedPage.getSizeInBytes(), page.getPositionCount()); + + return deserializedPage; } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ExplainAnalyzeOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/ExplainAnalyzeOperator.java index a4049062c28f6..98d6ee3275dbe 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ExplainAnalyzeOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ExplainAnalyzeOperator.java @@ -13,8 +13,6 @@ */ package com.facebook.presto.operator; -import com.facebook.presto.cost.CostCalculator; -import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryPerformanceFetcher; import com.facebook.presto.execution.StageId; @@ -43,8 +41,6 @@ public static class ExplainAnalyzeOperatorFactory private final PlanNodeId planNodeId; private final QueryPerformanceFetcher queryPerformanceFetcher; private final FunctionRegistry functionRegistry; - private final StatsCalculator statsCalculator; - private final CostCalculator costCalculator; private final boolean verbose; private boolean closed; @@ -53,16 +49,12 @@ public ExplainAnalyzeOperatorFactory( PlanNodeId planNodeId, QueryPerformanceFetcher queryPerformanceFetcher, FunctionRegistry functionRegistry, - StatsCalculator statsCalculator, - CostCalculator costCalculator, boolean verbose) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); this.queryPerformanceFetcher = requireNonNull(queryPerformanceFetcher, "queryPerformanceFetcher is null"); this.functionRegistry = requireNonNull(functionRegistry, "functionRegistry is null"); - this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); - this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); this.verbose = verbose; } @@ -71,7 +63,7 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, ExplainAnalyzeOperator.class.getSimpleName()); - return new ExplainAnalyzeOperator(operatorContext, queryPerformanceFetcher, functionRegistry, statsCalculator, costCalculator, verbose); + return new ExplainAnalyzeOperator(operatorContext, queryPerformanceFetcher, functionRegistry, verbose); } @Override @@ -83,15 +75,13 @@ public void noMoreOperators() @Override public OperatorFactory duplicate() { - return new ExplainAnalyzeOperatorFactory(operatorId, planNodeId, queryPerformanceFetcher, functionRegistry, statsCalculator, costCalculator, verbose); + return new ExplainAnalyzeOperatorFactory(operatorId, planNodeId, queryPerformanceFetcher, functionRegistry, verbose); } } private final OperatorContext operatorContext; private final QueryPerformanceFetcher queryPerformanceFetcher; private final FunctionRegistry functionRegistry; - private final StatsCalculator statsCalculator; - private final CostCalculator costCalculator; private final boolean verbose; private boolean finishing; private boolean outputConsumed; @@ -100,15 +90,11 @@ public ExplainAnalyzeOperator( OperatorContext operatorContext, QueryPerformanceFetcher queryPerformanceFetcher, FunctionRegistry functionRegistry, - StatsCalculator statsCalculator, - CostCalculator costCalculator, boolean verbose) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.queryPerformanceFetcher = requireNonNull(queryPerformanceFetcher, "queryPerformanceFetcher is null"); this.functionRegistry = requireNonNull(functionRegistry, "functionRegistry is null"); - this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); - this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); this.verbose = verbose; } @@ -159,7 +145,7 @@ public Page getOutput() return null; } - String plan = textDistributedPlan(queryInfo.getOutputStage().get().getSubStages().get(0), functionRegistry, statsCalculator, costCalculator, operatorContext.getSession(), verbose); + String plan = textDistributedPlan(queryInfo.getOutputStage().get().getSubStages().get(0), functionRegistry, operatorContext.getSession(), verbose); BlockBuilder builder = VARCHAR.createBlockBuilder(null, 1); VARCHAR.writeString(builder, plan); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/FilterAndProjectOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/FilterAndProjectOperator.java index 1e7451b660a18..f6ffe91ae837e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/FilterAndProjectOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/FilterAndProjectOperator.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.function.Supplier; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; @@ -32,6 +33,7 @@ public class FilterAndProjectOperator implements Operator { private final OperatorContext operatorContext; + private final LocalMemoryContext pageProcessorMemoryContext; private final LocalMemoryContext outputMemoryContext; private final PageProcessor processor; @@ -45,6 +47,7 @@ public FilterAndProjectOperator( { this.processor = requireNonNull(processor, "processor is null"); this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + this.pageProcessorMemoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(ScanFilterAndProjectOperator.class.getSimpleName()); this.outputMemoryContext = operatorContext.newLocalSystemMemoryContext(FilterAndProjectOperator.class.getSimpleName()); this.mergingOutput = requireNonNull(mergingOutput, "mergingOutput is null"); } @@ -85,8 +88,12 @@ public final void addInput(Page page) requireNonNull(page, "page is null"); checkState(mergingOutput.needsInput(), "Page buffer is full"); - mergingOutput.addInput(processor.process(operatorContext.getSession().toConnectorSession(), operatorContext.getDriverContext().getYieldSignal(), page)); - outputMemoryContext.setBytes(mergingOutput.getRetainedSizeInBytes()); + mergingOutput.addInput(processor.process( + operatorContext.getSession().toConnectorSession(), + operatorContext.getDriverContext().getYieldSignal(), + pageProcessorMemoryContext, + page)); + outputMemoryContext.setBytes(mergingOutput.getRetainedSizeInBytes() + pageProcessorMemoryContext.getBytes()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/GroupByIdBlock.java b/presto-main/src/main/java/com/facebook/presto/operator/GroupByIdBlock.java index abb8831c232ea..b73e70117bd7e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/GroupByIdBlock.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/GroupByIdBlock.java @@ -18,7 +18,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.google.common.base.MoreObjects.toStringHelper; @@ -61,6 +61,12 @@ public long getRegionSizeInBytes(int positionOffset, int length) return block.getRegionSizeInBytes(positionOffset, length); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return block.getPositionsSizeInBytes(positions); + } + @Override public Block copyRegion(int positionOffset, int length) { @@ -188,7 +194,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(block, block.getRetainedSizeInBytes()); consumer.accept(this, (long) INSTANCE_SIZE); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java index cca583508f5af..db9a3b343dfd5 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/HashAggregationOperator.java @@ -40,6 +40,7 @@ import static com.facebook.presto.sql.planner.optimizations.HashGenerationOptimizer.INITIAL_HASH_VALUE; import static com.facebook.presto.type.TypeUtils.NULL_HASH_CODE; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.util.Objects.requireNonNull; @@ -63,12 +64,13 @@ public static class HashAggregationOperatorFactory private final Optional groupIdChannel; private final int expectedGroups; - private final DataSize maxPartialMemory; + private final Optional maxPartialMemory; private final boolean spillEnabled; private final DataSize memoryLimitForMerge; private final DataSize memoryLimitForMergeWithMemory; private final SpillerFactory spillerFactory; private final JoinCompiler joinCompiler; + private final boolean useSystemMemory; private boolean closed; @@ -84,8 +86,9 @@ public HashAggregationOperatorFactory( Optional hashChannel, Optional groupIdChannel, int expectedGroups, - DataSize maxPartialMemory, - JoinCompiler joinCompiler) + Optional maxPartialMemory, + JoinCompiler joinCompiler, + boolean useSystemMemory) { this(operatorId, planNodeId, @@ -105,7 +108,8 @@ public HashAggregationOperatorFactory( (types, spillContext, memoryContext) -> { throw new UnsupportedOperationException(); }, - joinCompiler); + joinCompiler, + useSystemMemory); } public HashAggregationOperatorFactory( @@ -120,11 +124,12 @@ public HashAggregationOperatorFactory( Optional hashChannel, Optional groupIdChannel, int expectedGroups, - DataSize maxPartialMemory, + Optional maxPartialMemory, boolean spillEnabled, DataSize unspillMemoryLimit, SpillerFactory spillerFactory, - JoinCompiler joinCompiler) + JoinCompiler joinCompiler, + boolean useSystemMemory) { this(operatorId, planNodeId, @@ -142,7 +147,8 @@ public HashAggregationOperatorFactory( unspillMemoryLimit, DataSize.succinctBytes((long) (unspillMemoryLimit.toBytes() * MERGE_WITH_MEMORY_RATIO)), spillerFactory, - joinCompiler); + joinCompiler, + useSystemMemory); } @VisibleForTesting @@ -158,12 +164,13 @@ public HashAggregationOperatorFactory( Optional hashChannel, Optional groupIdChannel, int expectedGroups, - DataSize maxPartialMemory, + Optional maxPartialMemory, boolean spillEnabled, DataSize memoryLimitForMerge, DataSize memoryLimitForMergeWithMemory, SpillerFactory spillerFactory, - JoinCompiler joinCompiler) + JoinCompiler joinCompiler, + boolean useSystemMemory) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); @@ -182,6 +189,7 @@ public HashAggregationOperatorFactory( this.memoryLimitForMergeWithMemory = requireNonNull(memoryLimitForMergeWithMemory, "memoryLimitForMergeWithMemory is null"); this.spillerFactory = requireNonNull(spillerFactory, "spillerFactory is null"); this.joinCompiler = requireNonNull(joinCompiler, "joinCompiler is null"); + this.useSystemMemory = useSystemMemory; } @Override @@ -206,7 +214,8 @@ public Operator createOperator(DriverContext driverContext) memoryLimitForMerge, memoryLimitForMergeWithMemory, spillerFactory, - joinCompiler); + joinCompiler, + useSystemMemory); return hashAggregationOperator; } @@ -236,7 +245,8 @@ public OperatorFactory duplicate() memoryLimitForMerge, memoryLimitForMergeWithMemory, spillerFactory, - joinCompiler); + joinCompiler, + useSystemMemory); } } @@ -250,12 +260,13 @@ public OperatorFactory duplicate() private final Optional hashChannel; private final Optional groupIdChannel; private final int expectedGroups; - private final DataSize maxPartialMemory; + private final Optional maxPartialMemory; private final boolean spillEnabled; private final DataSize memoryLimitForMerge; private final DataSize memoryLimitForMergeWithMemory; private final SpillerFactory spillerFactory; private final JoinCompiler joinCompiler; + private final boolean useSystemMemory; private final List types; private final HashCollisionsCounter hashCollisionsCounter; @@ -280,12 +291,13 @@ public HashAggregationOperator( Optional hashChannel, Optional groupIdChannel, int expectedGroups, - DataSize maxPartialMemory, + Optional maxPartialMemory, boolean spillEnabled, DataSize memoryLimitForMerge, DataSize memoryLimitForMergeWithMemory, SpillerFactory spillerFactory, - JoinCompiler joinCompiler) + JoinCompiler joinCompiler, + boolean useSystemMemory) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); requireNonNull(step, "step is null"); @@ -310,6 +322,7 @@ public HashAggregationOperator( this.joinCompiler = requireNonNull(joinCompiler, "joinCompiler is null"); this.hashCollisionsCounter = new HashCollisionsCounter(operatorContext); operatorContext.setInfoSupplier(hashCollisionsCounter); + this.useSystemMemory = useSystemMemory; } @Override @@ -365,9 +378,11 @@ public void addInput(Page page) operatorContext, maxPartialMemory, joinCompiler, - true); + true, + useSystemMemory); } else { + verify(!useSystemMemory, "using system memory in spillable aggregations is not supported"); aggregationBuilder = new SpillableHashAggregationBuilder( accumulatorFactories, step, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java index e349803c55762..a84beea5d4318 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java @@ -56,7 +56,7 @@ public static class HashBuilderOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; - private final JoinBridgeDataManager lookupSourceFactoryManager; + private final JoinBridgeManager lookupSourceFactoryManager; private final List outputChannels; private final List hashChannels; private final OptionalInt preComputedHashChannel; @@ -76,7 +76,7 @@ public static class HashBuilderOperatorFactory public HashBuilderOperatorFactory( int operatorId, PlanNodeId planNodeId, - JoinBridgeDataManager lookupSourceFactory, + JoinBridgeManager lookupSourceFactoryManager, List outputChannels, List hashChannels, OptionalInt preComputedHashChannel, @@ -93,7 +93,7 @@ public HashBuilderOperatorFactory( requireNonNull(sortChannel, "sortChannel can not be null"); requireNonNull(searchFunctionFactories, "searchFunctionFactories is null"); checkArgument(sortChannel.isPresent() != searchFunctionFactories.isEmpty(), "both or none sortChannel and searchFunctionFactories must be set"); - this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactory, "lookupSourceFactoryManager"); + this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactoryManager, "lookupSourceFactoryManager is null"); this.outputChannels = ImmutableList.copyOf(requireNonNull(outputChannels, "outputChannels is null")); this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null")); @@ -114,7 +114,7 @@ public HashBuilderOperator createOperator(DriverContext driverContext) checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, HashBuilderOperator.class.getSimpleName()); - LookupSourceFactory lookupSourceFactory = this.lookupSourceFactoryManager.forLifespan(driverContext.getLifespan()); + PartitionedLookupSourceFactory lookupSourceFactory = this.lookupSourceFactoryManager.getJoinBridge(driverContext.getLifespan()); int partitionIndex = getAndIncrementPartitionIndex(driverContext.getLifespan()); verify(partitionIndex < lookupSourceFactory.partitions()); return new HashBuilderOperator( @@ -195,7 +195,7 @@ public enum State private final OperatorContext operatorContext; private final LocalMemoryContext localUserMemoryContext; private final LocalMemoryContext localRevocableMemoryContext; - private final LookupSourceFactory lookupSourceFactory; + private final PartitionedLookupSourceFactory lookupSourceFactory; private final ListenableFuture lookupSourceFactoryDestroyed; private final int partitionIndex; @@ -227,7 +227,7 @@ public enum State public HashBuilderOperator( OperatorContext operatorContext, - LookupSourceFactory lookupSourceFactory, + PartitionedLookupSourceFactory lookupSourceFactory, int partitionIndex, List outputChannels, List hashChannels, @@ -346,7 +346,7 @@ private void updateIndex(Page page) localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); } } - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } private void spillInput(Page page) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridge.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridge.java new file mode 100644 index 0000000000000..3edf46242660b --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridge.java @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * A bridge that connects build, probe, and outer operators of a join. + * It often carries data that lets probe find out what is available on + * the build side, and lets outer find the orphaned rows. + */ +public interface JoinBridge +{ + /** + * Can be called only after build and probe are finished. + */ + OuterPositionIterator getOuterPositionIterator(); + + void destroy(); + + ListenableFuture whenBuildFinishes(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeDataManager.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeDataManager.java deleted file mode 100644 index 1ce0085508a4f..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeDataManager.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.presto.operator; - -import com.facebook.presto.execution.Lifespan; -import com.facebook.presto.spi.type.Type; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -import static com.facebook.presto.operator.PipelineExecutionStrategy.UNGROUPED_EXECUTION; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; -import static java.util.Objects.requireNonNull; - -public class JoinBridgeDataManager -{ - public static JoinBridgeDataManager lookup( - PipelineExecutionStrategy probeExecutionStrategy, - PipelineExecutionStrategy lookupSourceExecutionStrategy, - Function lookupSourceFactoryProvider, - List buildOutputTypes) - { - return new JoinBridgeDataManager<>( - probeExecutionStrategy, - lookupSourceExecutionStrategy, - lookupSourceFactoryProvider, - buildOutputTypes, - SharedLookupSourceFactory::new, - LookupSourceFactory::destroy); - } - - public static JoinBridgeDataManager nestedLoop( - PipelineExecutionStrategy probeExecutionStrategy, - PipelineExecutionStrategy buildExecutionStrategy, - Function nestedLoopJoinPagesSupplierProvider, - List buildOutputTypes) - { - // Build side of nested loop join is always ungrouped today. - // If we want to change this in the future, this code will likely work, but needs to be tested. - checkArgument(buildExecutionStrategy == PipelineExecutionStrategy.UNGROUPED_EXECUTION, "Grouped execution for nested loop build is not supported"); - - return new JoinBridgeDataManager<>( - probeExecutionStrategy, - buildExecutionStrategy, - nestedLoopJoinPagesSupplierProvider, - buildOutputTypes, - SharedNestedLoopJoinPagesBridge::new, - NestedLoopJoinPagesBridge::destroy); - } - - @VisibleForTesting - public static JoinBridgeDataManager lookupAllAtOnce(LookupSourceFactory factory) - { - return lookup( - UNGROUPED_EXECUTION, - UNGROUPED_EXECUTION, - ignored -> factory, - factory.getOutputTypes()); - } - - private final List buildOutputTypes; - - private final InternalJoinBridgeDataManager internalJoinBridgeDataManager; - - private JoinBridgeDataManager( - PipelineExecutionStrategy probeExecutionStrategy, - PipelineExecutionStrategy lookupSourceExecutionStrategy, - Function lookupSourceFactoryProvider, - List buildOutputTypes, - BiFunction sharedWrapper, - Consumer destroy) - { - requireNonNull(probeExecutionStrategy, "probeExecutionStrategy is null"); - requireNonNull(lookupSourceExecutionStrategy, "lookupSourceExecutionStrategy is null"); - requireNonNull(lookupSourceFactoryProvider, "joinBridgeProvider is null"); - - this.internalJoinBridgeDataManager = internalJoinBridgeDataManager(probeExecutionStrategy, lookupSourceExecutionStrategy, lookupSourceFactoryProvider, sharedWrapper, destroy); - this.buildOutputTypes = requireNonNull(buildOutputTypes, "buildOutputTypes is null"); - } - - public List getBuildOutputTypes() - { - return buildOutputTypes; - } - - public T forLifespan(Lifespan lifespan) - { - return internalJoinBridgeDataManager.get(lifespan); - } - - public void noMoreJoinBridge() - { - internalJoinBridgeDataManager.noMoreLookupSourceFactory(); - } - - private static InternalJoinBridgeDataManager internalJoinBridgeDataManager( - PipelineExecutionStrategy probeExecutionStrategy, - PipelineExecutionStrategy lookupSourceExecutionStrategy, - Function lookupSourceFactoryProvider, - BiFunction sharedWrapper, - Consumer destroy) - { - switch (probeExecutionStrategy) { - case UNGROUPED_EXECUTION: - switch (lookupSourceExecutionStrategy) { - case UNGROUPED_EXECUTION: - return new TaskWideInternalJoinBridgeDataManager<>(lookupSourceFactoryProvider); - case GROUPED_EXECUTION: - throw new UnsupportedOperationException("Invalid combination. Lookup source should not be grouped if probe is not going to take advantage of it."); - default: - throw new IllegalArgumentException("Unknown lookupSourceExecutionStrategy: " + lookupSourceExecutionStrategy); - } - case GROUPED_EXECUTION: - switch (lookupSourceExecutionStrategy) { - case UNGROUPED_EXECUTION: - return new SharedInternalJoinBridgeDataManager<>(lookupSourceFactoryProvider, sharedWrapper, destroy); - case GROUPED_EXECUTION: - return new OneToOneInternalJoinBridgeDataManager<>(lookupSourceFactoryProvider); - default: - throw new IllegalArgumentException("Unknown lookupSourceExecutionStrategy: " + lookupSourceExecutionStrategy); - } - default: - throw new UnsupportedOperationException(); - } - } - - private interface InternalJoinBridgeDataManager - { - T get(Lifespan lifespan); - - default void noMoreLookupSourceFactory() - { - // do nothing - } - } - - // 1 probe, 1 lookup source - private static class TaskWideInternalJoinBridgeDataManager - implements InternalJoinBridgeDataManager - { - private final Supplier supplier; - - public TaskWideInternalJoinBridgeDataManager(Function lookupSourceFactoryProvider) - { - supplier = Suppliers.memoize(() -> lookupSourceFactoryProvider.apply(Lifespan.taskWide())); - } - - @Override - public T get(Lifespan lifespan) - { - checkArgument(Lifespan.taskWide().equals(lifespan)); - return supplier.get(); - } - } - - // N probe, N lookup source; one-to-one mapping, bijective - private static class OneToOneInternalJoinBridgeDataManager - implements InternalJoinBridgeDataManager - { - private final Map map = new ConcurrentHashMap<>(); - private final Function joinBridgeProvider; - - public OneToOneInternalJoinBridgeDataManager(Function joinBridgeProvider) - { - this.joinBridgeProvider = joinBridgeProvider; - } - - @Override - public T get(Lifespan lifespan) - { - checkArgument(!Lifespan.taskWide().equals(lifespan)); - return map.computeIfAbsent(lifespan, joinBridgeProvider); - } - } - - // N probe, 1 lookup source - private static class SharedInternalJoinBridgeDataManager - implements InternalJoinBridgeDataManager - { - private final T taskWideJoinBridge; - private final BiFunction sharedWrapper; - private final Map map = new ConcurrentHashMap<>(); - private final ReferenceCount referenceCount; - - public SharedInternalJoinBridgeDataManager(Function lookupSourceFactoryProvider, BiFunction sharedWrapper, Consumer destroy) - { - this.taskWideJoinBridge = lookupSourceFactoryProvider.apply(Lifespan.taskWide()); - this.referenceCount = new ReferenceCount(1); - this.sharedWrapper = requireNonNull(sharedWrapper, "sharedWrapper is null"); - referenceCount.getFreeFuture().addListener(() -> destroy.accept(taskWideJoinBridge), directExecutor()); - } - - @Override - public T get(Lifespan lifespan) - { - if (Lifespan.taskWide().equals(lifespan)) { - // build - return taskWideJoinBridge; - } - // probe - return map.computeIfAbsent(lifespan, ignored -> { - referenceCount.retain(); - return sharedWrapper.apply(taskWideJoinBridge, referenceCount::release); - }); - } - - @Override - public void noMoreLookupSourceFactory() - { - referenceCount.release(); - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeLifecycleManager.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeLifecycleManager.java deleted file mode 100644 index b625921d4e459..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeLifecycleManager.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.presto.operator; - -import com.facebook.presto.execution.Lifespan; -import com.facebook.presto.operator.LookupJoinOperators.JoinType; -import com.google.common.util.concurrent.ListenableFuture; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import java.util.function.Function; - -import static com.facebook.presto.operator.LookupJoinOperators.JoinType.INNER; -import static com.facebook.presto.operator.LookupJoinOperators.JoinType.PROBE_OUTER; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.util.concurrent.Futures.transform; -import static com.google.common.util.concurrent.Futures.transformAsync; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; - -public class JoinBridgeLifecycleManager -{ - public static JoinBridgeLifecycleManager lookup( - JoinType joinType, - JoinBridgeDataManager lookupSourceFactoryManager) - { - return new JoinBridgeLifecycleManager<>( - joinType, - lookupSourceFactoryManager, - LookupSourceFactory::destroy, - lookupSourceFactory -> transform( - lookupSourceFactory.createLookupSourceProvider(), - lookupSourceProvider -> { - // Close the lookupSourceProvider we just created. - // The only reason we created it is to wait until lookup source is ready. - lookupSourceProvider.close(); - return lookupSourceFactory.getOuterPositionIterator(); - }, - directExecutor())); - } - - public static JoinBridgeLifecycleManager nestedLoop( - JoinType joinType, - JoinBridgeDataManager nestedLoopJoinPagesSupplierManager) - { - checkArgument(joinType == INNER); - return new JoinBridgeLifecycleManager<>( - joinType, - nestedLoopJoinPagesSupplierManager, - NestedLoopJoinPagesBridge::destroy, - joinBridge -> { - throw new UnsupportedOperationException(); - }); - } - - private final JoinType joinType; - private final FreezeOnReadCounter factoryCount; - private final JoinBridgeDataManager joinBridgeDataManager; - private final Consumer destroy; - private final Function> outerPositionIteratorFutureFunction; - - private final Map> dataByLifespan; - private final ReferenceCount probeFactoryReferenceCount; - - private boolean closed; - - public JoinBridgeLifecycleManager( - JoinType joinType, - JoinBridgeDataManager joinBridgeDataManager, - Consumer destroy, - Function> outerPositionIteratorFutureFunction) - { - this.joinType = joinType; - this.joinBridgeDataManager = joinBridgeDataManager; - this.destroy = destroy; - this.outerPositionIteratorFutureFunction = outerPositionIteratorFutureFunction; - - this.dataByLifespan = new ConcurrentHashMap<>(); - - this.factoryCount = new FreezeOnReadCounter(); - this.factoryCount.increment(); - - // Each LookupJoinOperatorFactory count as 1 probe, until noMoreOperators() is called. - this.probeFactoryReferenceCount = new ReferenceCount(1); - this.probeFactoryReferenceCount.getFreeFuture().addListener(joinBridgeDataManager::noMoreJoinBridge, directExecutor()); - } - - private JoinBridgeLifecycleManager(JoinBridgeLifecycleManager other) - { - joinType = other.joinType; - factoryCount = other.factoryCount; - joinBridgeDataManager = other.joinBridgeDataManager; - destroy = other.destroy; - outerPositionIteratorFutureFunction = other.outerPositionIteratorFutureFunction; - dataByLifespan = other.dataByLifespan; - probeFactoryReferenceCount = other.probeFactoryReferenceCount; - - // closed is intentionally not copied. - closed = false; - - factoryCount.increment(); - probeFactoryReferenceCount.retain(); - } - - public JoinBridgeLifecycleManager duplicate() - { - return new JoinBridgeLifecycleManager<>(this); - } - - public void noMoreLifespan() - { - checkState(!closed); - closed = true; - probeFactoryReferenceCount.release(); - } - - public T getJoinBridge(Lifespan lifespan) - { - return data(lifespan).getJoinBridge(); - } - - public ReferenceCount getProbeReferenceCount(Lifespan lifespan) - { - return data(lifespan).getProbeReferenceCount(); - } - - public ReferenceCount getJoinBridgeUsersCount(Lifespan lifespan) - { - return data(lifespan).getJoinBridgeUsersCount(); - } - - public ListenableFuture getOuterPositionsFuture(Lifespan lifespan) - { - return data(lifespan).getOuterPositionsFuture(); - } - - private PerLifespanData data(Lifespan lifespan) - { - return dataByLifespan.computeIfAbsent( - lifespan, - id -> { - checkState(!closed); - return new PerLifespanData<>( - joinType, - factoryCount.get(), - joinBridgeDataManager.forLifespan(id), - destroy, - outerPositionIteratorFutureFunction); - }); - } - - public static class PerLifespanData - { - private final T joinBridge; - private final ReferenceCount probeReferenceCount; - private final ReferenceCount joinBridgeUsersCount; - private final ListenableFuture outerPositionsFuture; - - /** - * @param outerPositionIteratorFutureFunction The function returns a Future that will present OuterPositionIterator when ready. - * The function is invoked once probe has finished. However, the build might not have finished at the time of invocation. - */ - public PerLifespanData(JoinType joinType, int factoryCount, T joinBridge, Consumer destroy, Function> outerPositionIteratorFutureFunction) - { - this.joinBridge = joinBridge; - - // When all probe and lookup-outer operators finish, destroy the join bridge (freeing the memory) - // * Whole probe side (operator and operatorFactory) is counted as 1 join bridge user - // * Each LookupOuterOperatorFactory count as 1 join bridge user, until noMoreOperators(LifeSpan) is called. - // * There is at most 1 LookupOuterOperatorFactory - // * Each LookupOuterOperator count as 1 join bridge user, until close() is called. - joinBridgeUsersCount = new ReferenceCount(1); - joinBridgeUsersCount.getFreeFuture().addListener(() -> destroy.accept(joinBridge), directExecutor()); - - // * Each LookupJoinOperatorFactory count as 1 probe, until noMoreOperators(LifeSpan) is called. - // * Each LookupJoinOperator count as 1 probe, until close() is called. - probeReferenceCount = new ReferenceCount(factoryCount); - probeReferenceCount.getFreeFuture().addListener(joinBridgeUsersCount::release, directExecutor()); - - if (joinType == INNER || joinType == PROBE_OUTER) { - outerPositionsFuture = null; - } - else { - // increment the user count by 1 to account for the lookup-outer factory - joinBridgeUsersCount.retain(); - - // Set the outer position future to start the outer operator: - // 1. when all probe operators finish, and - // 2. when lookup source is ready - outerPositionsFuture = transformAsync( - probeReferenceCount.getFreeFuture(), - ignored -> outerPositionIteratorFutureFunction.apply(joinBridge), - directExecutor()); - } - } - - public T getJoinBridge() - { - return joinBridge; - } - - public ReferenceCount getProbeReferenceCount() - { - return probeReferenceCount; - } - - public ReferenceCount getJoinBridgeUsersCount() - { - return joinBridgeUsersCount; - } - - public ListenableFuture getOuterPositionsFuture() - { - return outerPositionsFuture; - } - } - - public static class FreezeOnReadCounter - { - private int count; - private boolean freezed; - - public synchronized void increment() - { - checkState(!freezed, "Counter has been read"); - count++; - } - - public synchronized int get() - { - freezed = true; - return count; - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeManager.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeManager.java new file mode 100644 index 0000000000000..6d0c4ba89c36f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinBridgeManager.java @@ -0,0 +1,538 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.operator; + +import com.facebook.presto.execution.Lifespan; +import com.facebook.presto.spi.type.Type; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import static com.facebook.presto.operator.PipelineExecutionStrategy.UNGROUPED_EXECUTION; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.util.concurrent.Futures.transform; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.Objects.requireNonNull; + +public class JoinBridgeManager +{ + @VisibleForTesting + public static JoinBridgeManager lookupAllAtOnce(PartitionedLookupSourceFactory factory) + { + return new JoinBridgeManager<>( + false, + UNGROUPED_EXECUTION, + UNGROUPED_EXECUTION, + ignored -> factory, + factory.getOutputTypes()); + } + + private final List buildOutputTypes; + private final boolean buildOuter; + private final PipelineExecutionStrategy probeExecutionStrategy; + private final PipelineExecutionStrategy buildExecutionStrategy; + private final Function joinBridgeProvider; + + private final FreezeOnReadCounter probeFactoryCount = new FreezeOnReadCounter(); + + private final AtomicBoolean initialized = new AtomicBoolean(); + private InternalJoinBridgeDataManager internalJoinBridgeDataManager; + + public JoinBridgeManager( + boolean buildOuter, + PipelineExecutionStrategy probeExecutionStrategy, + PipelineExecutionStrategy lookupSourceExecutionStrategy, + Function lookupSourceFactoryProvider, + List buildOutputTypes) + { + this.buildOuter = buildOuter; + this.probeExecutionStrategy = requireNonNull(probeExecutionStrategy, "probeExecutionStrategy is null"); + this.buildExecutionStrategy = requireNonNull(lookupSourceExecutionStrategy, "lookupSourceExecutionStrategy is null"); + this.joinBridgeProvider = requireNonNull(lookupSourceFactoryProvider, "joinBridgeProvider is null"); + this.buildOutputTypes = requireNonNull(buildOutputTypes, "buildOutputTypes is null"); + } + + private void initializeIfNecessary() + { + if (!initialized.get()) { + synchronized (this) { + if (initialized.get()) { + return; + } + int finalProbeFactoryCount = probeFactoryCount.get(); + internalJoinBridgeDataManager = internalJoinBridgeDataManager(probeExecutionStrategy, buildExecutionStrategy, joinBridgeProvider, finalProbeFactoryCount, buildOuter ? 1 : 0); + initialized.set(true); + } + } + } + + public List getBuildOutputTypes() + { + return buildOutputTypes; + } + + public PipelineExecutionStrategy getBuildExecutionStrategy() + { + return buildExecutionStrategy; + } + + public void incrementProbeFactoryCount() + { + probeFactoryCount.increment(); + } + + public T getJoinBridge(Lifespan lifespan) + { + initializeIfNecessary(); + return internalJoinBridgeDataManager.getJoinBridge(lifespan); + } + + /** + * Invoked when a probe operator factory indicates that it will not + * create any more operators, for any lifespan. + *

+ * It is expected that this method will only be invoked after + * {@link #probeOperatorFactoryClosed(Lifespan)} has been invoked + * for every known lifespan. + */ + public void probeOperatorFactoryClosedForAllLifespans() + { + initializeIfNecessary(); + internalJoinBridgeDataManager.probeOperatorFactoryClosedForAllLifespans(); + } + + public void probeOperatorFactoryClosed(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.probeOperatorFactoryClosed(lifespan); + } + + public void probeOperatorCreated(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.probeOperatorCreated(lifespan); + } + + public void probeOperatorClosed(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.probeOperatorClosed(lifespan); + } + + public void outerOperatorFactoryClosed(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.outerOperatorFactoryClosed(lifespan); + } + + public void outerOperatorCreated(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.outerOperatorCreated(lifespan); + } + + public void outerOperatorClosed(Lifespan lifespan) + { + initializeIfNecessary(); + internalJoinBridgeDataManager.outerOperatorClosed(lifespan); + } + + public ListenableFuture getOuterPositionsFuture(Lifespan lifespan) + { + initializeIfNecessary(); + return internalJoinBridgeDataManager.getOuterPositionsFuture(lifespan); + } + + private static InternalJoinBridgeDataManager internalJoinBridgeDataManager( + PipelineExecutionStrategy probeExecutionStrategy, + PipelineExecutionStrategy buildExecutionStrategy, + Function joinBridgeProvider, + int probeFactoryCount, + int outerFactoryCount) + { + checkArgument(outerFactoryCount == 0 || outerFactoryCount == 1, "outerFactoryCount should only be 0 or 1 because it is expected that outer factory never gets duplicated."); + switch (probeExecutionStrategy) { + case UNGROUPED_EXECUTION: + switch (buildExecutionStrategy) { + case UNGROUPED_EXECUTION: + return new TaskWideInternalJoinBridgeDataManager<>(joinBridgeProvider, probeFactoryCount, outerFactoryCount); + case GROUPED_EXECUTION: + throw new UnsupportedOperationException("Invalid combination. Lookup source should not be grouped if probe is not going to take advantage of it."); + default: + throw new IllegalArgumentException("Unknown buildExecutionStrategy: " + buildExecutionStrategy); + } + case GROUPED_EXECUTION: + switch (buildExecutionStrategy) { + case UNGROUPED_EXECUTION: + return new SharedInternalJoinBridgeDataManager<>(joinBridgeProvider, probeFactoryCount, outerFactoryCount); + case GROUPED_EXECUTION: + return new OneToOneInternalJoinBridgeDataManager<>(joinBridgeProvider, probeFactoryCount, outerFactoryCount); + default: + throw new IllegalArgumentException("Unknown buildExecutionStrategy: " + buildExecutionStrategy); + } + default: + throw new UnsupportedOperationException(); + } + } + + private interface InternalJoinBridgeDataManager + { + T getJoinBridge(Lifespan lifespan); + + ListenableFuture getOuterPositionsFuture(Lifespan lifespan); + + void probeOperatorFactoryClosedForAllLifespans(); + + void probeOperatorFactoryClosed(Lifespan lifespan); + + void probeOperatorCreated(Lifespan lifespan); + + void probeOperatorClosed(Lifespan lifespan); + + void outerOperatorFactoryClosed(Lifespan lifespan); + + void outerOperatorCreated(Lifespan lifespan); + + void outerOperatorClosed(Lifespan lifespan); + } + + // 1 probe, 1 lookup source + private static class TaskWideInternalJoinBridgeDataManager + implements InternalJoinBridgeDataManager + { + private final T joinBridge; + private final JoinLifecycle joinLifecycle; + + public TaskWideInternalJoinBridgeDataManager(Function lookupSourceFactoryProvider, int probeFactoryCount, int outerFactoryCount) + { + joinBridge = lookupSourceFactoryProvider.apply(Lifespan.taskWide()); + joinLifecycle = new JoinLifecycle(joinBridge, probeFactoryCount, outerFactoryCount); + } + + @Override + public T getJoinBridge(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + return joinBridge; + } + + @Override + public ListenableFuture getOuterPositionsFuture(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + return transform(joinLifecycle.whenBuildAndProbeFinishes(), ignored -> joinBridge.getOuterPositionIterator(), directExecutor()); + } + + @Override + public void probeOperatorFactoryClosedForAllLifespans() + { + // do nothing + } + + @Override + public void probeOperatorFactoryClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.releaseForProbe(); + } + + @Override + public void probeOperatorCreated(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.retainForProbe(); + } + + @Override + public void probeOperatorClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.releaseForProbe(); + } + + @Override + public void outerOperatorFactoryClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.releaseForOuter(); + } + + @Override + public void outerOperatorCreated(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.retainForOuter(); + } + + @Override + public void outerOperatorClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan)); + joinLifecycle.releaseForOuter(); + } + } + + // N probe, N lookup source; one-to-one mapping, bijective + private static class OneToOneInternalJoinBridgeDataManager + implements InternalJoinBridgeDataManager + { + private final Map> joinBridgeMap = new ConcurrentHashMap<>(); + private final Function joinBridgeProvider; + private final int probeFactoryCount; + private final int outerFactoryCount; + + public OneToOneInternalJoinBridgeDataManager(Function joinBridgeProvider, int probeFactoryCount, int outerFactoryCount) + { + this.joinBridgeProvider = joinBridgeProvider; + this.probeFactoryCount = probeFactoryCount; + this.outerFactoryCount = outerFactoryCount; + } + + @Override + public T getJoinBridge(Lifespan lifespan) + { + return data(lifespan).joinBridge; + } + + @Override + public ListenableFuture getOuterPositionsFuture(Lifespan lifespan) + { + return transform( + data(lifespan).joinLifecycle.whenBuildAndProbeFinishes(), + ignored -> data(lifespan).joinBridge.getOuterPositionIterator(), directExecutor()); + } + + @Override + public void probeOperatorFactoryClosedForAllLifespans() + { + // do nothing + } + + @Override + public void probeOperatorFactoryClosed(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.releaseForProbe(); + } + + @Override + public void probeOperatorCreated(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.retainForProbe(); + } + + @Override + public void probeOperatorClosed(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.releaseForProbe(); + } + + @Override + public void outerOperatorFactoryClosed(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.releaseForOuter(); + } + + @Override + public void outerOperatorCreated(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.retainForOuter(); + } + + @Override + public void outerOperatorClosed(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + data(lifespan).joinLifecycle.releaseForOuter(); + } + + private JoinBridgeAndLifecycle data(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan)); + return joinBridgeMap.computeIfAbsent(lifespan, span -> { + T joinBridge = joinBridgeProvider.apply(span); + return new JoinBridgeAndLifecycle<>(joinBridge, new JoinLifecycle(joinBridge, probeFactoryCount, outerFactoryCount)); + }); + } + + private static class JoinBridgeAndLifecycle + { + T joinBridge; + JoinLifecycle joinLifecycle; + + public JoinBridgeAndLifecycle(T joinBridge, JoinLifecycle joinLifecycle) + { + this.joinBridge = joinBridge; + this.joinLifecycle = joinLifecycle; + } + } + } + + // N probe, 1 lookup source + private static class SharedInternalJoinBridgeDataManager + implements InternalJoinBridgeDataManager + { + private final T taskWideJoinBridge; + + private final JoinLifecycle joinLifecycle; + + public SharedInternalJoinBridgeDataManager(Function lookupSourceFactoryProvider, int probeFactoryCount, int outerFactoryCount) + { + this.taskWideJoinBridge = lookupSourceFactoryProvider.apply(Lifespan.taskWide()); + this.joinLifecycle = new JoinLifecycle(taskWideJoinBridge, probeFactoryCount, outerFactoryCount); + } + + @Override + public T getJoinBridge(Lifespan lifespan) + { + return taskWideJoinBridge; + } + + @Override + public ListenableFuture getOuterPositionsFuture(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan), "join bridge is not partitioned"); + return transform(joinLifecycle.whenBuildAndProbeFinishes(), ignored -> taskWideJoinBridge.getOuterPositionIterator(), directExecutor()); + } + + @Override + public void probeOperatorFactoryClosedForAllLifespans() + { + joinLifecycle.releaseForProbe(); + } + + @Override + public void probeOperatorFactoryClosed(Lifespan lifespan) + { + // do nothing + } + + @Override + public void probeOperatorCreated(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan), "build operator should not produce or destroy probes"); + joinLifecycle.retainForProbe(); + } + + @Override + public void probeOperatorClosed(Lifespan lifespan) + { + checkArgument(!Lifespan.taskWide().equals(lifespan), "build operator should not produce or destroy probes"); + joinLifecycle.releaseForProbe(); + } + + @Override + public void outerOperatorFactoryClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan), "join bridge is not partitioned"); + joinLifecycle.releaseForOuter(); + } + + @Override + public void outerOperatorCreated(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan), "join bridge is not partitioned"); + joinLifecycle.retainForOuter(); + } + + @Override + public void outerOperatorClosed(Lifespan lifespan) + { + checkArgument(Lifespan.taskWide().equals(lifespan), "join bridge is not partitioned"); + joinLifecycle.releaseForOuter(); + } + } + + private static class JoinLifecycle + { + private final ReferenceCount probeReferenceCount; + private final ReferenceCount outerReferenceCount; + + private final ListenableFuture whenBuildAndProbeFinishes; + private final ListenableFuture whenAllFinishes; + + public JoinLifecycle(JoinBridge joinBridge, int probeFactoryCount, int outerFactoryCount) + { + // When all probe and lookup-outer operators finish, destroy the join bridge (freeing the memory) + // * Each LookupOuterOperatorFactory count as 1 + // * There is at most 1 LookupOuterOperatorFactory + // * Each LookupOuterOperator count as 1 + checkArgument(outerFactoryCount == 0 || outerFactoryCount == 1); + outerReferenceCount = new ReferenceCount(outerFactoryCount); + + // * Each probe operator factory count as 1 + // * Each probe operator count as 1 + probeReferenceCount = new ReferenceCount(probeFactoryCount); + + whenBuildAndProbeFinishes = Futures.whenAllSucceed(joinBridge.whenBuildFinishes(), probeReferenceCount.getFreeFuture()).call(() -> null, directExecutor()); + whenAllFinishes = Futures.whenAllSucceed(whenBuildAndProbeFinishes, outerReferenceCount.getFreeFuture()).call(() -> null, directExecutor()); + whenAllFinishes.addListener(joinBridge::destroy, directExecutor()); + } + + public ListenableFuture whenBuildAndProbeFinishes() + { + return whenBuildAndProbeFinishes; + } + + private void retainForProbe() + { + probeReferenceCount.retain(); + } + + private void releaseForProbe() + { + probeReferenceCount.release(); + } + + private void retainForOuter() + { + outerReferenceCount.retain(); + } + + private void releaseForOuter() + { + outerReferenceCount.release(); + } + } + + private static class FreezeOnReadCounter + { + private int count; + private boolean frozen; + + public synchronized void increment() + { + checkState(!frozen, "Counter has been read"); + count++; + } + + public synchronized int get() + { + frozen = true; + return count; + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinHashSupplier.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinHashSupplier.java index 5cbee24bca060..7254548047f3e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinHashSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinHashSupplier.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator; import com.facebook.presto.Session; +import com.facebook.presto.spi.Page; import com.facebook.presto.spi.block.Block; import com.facebook.presto.sql.gen.JoinFilterFunctionCompiler.JoinFilterFunctionFactory; import com.google.common.collect.ImmutableList; @@ -23,6 +24,7 @@ import java.util.Optional; import static com.facebook.presto.SystemSessionProperties.isFastInequalityJoin; +import static com.facebook.presto.operator.JoinUtils.channelsToPages; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @@ -33,7 +35,7 @@ public class JoinHashSupplier private final Session session; private final PagesHash pagesHash; private final LongArrayList addresses; - private final List> channels; + private final List pages; private final Optional positionLinks; private final Optional filterFunctionFactory; private final List searchFunctionFactories; @@ -49,9 +51,9 @@ public JoinHashSupplier( { this.session = requireNonNull(session, "session is null"); this.addresses = requireNonNull(addresses, "addresses is null"); - this.channels = requireNonNull(channels, "channels is null"); this.filterFunctionFactory = requireNonNull(filterFunctionFactory, "filterFunctionFactory is null"); this.searchFunctionFactories = ImmutableList.copyOf(searchFunctionFactories); + requireNonNull(channels, "pages is null"); requireNonNull(pagesHashStrategy, "pagesHashStrategy is null"); PositionLinks.FactoryBuilder positionLinksFactoryBuilder; @@ -67,6 +69,7 @@ public JoinHashSupplier( positionLinksFactoryBuilder = ArrayPositionLinks.builder(addresses.size()); } + this.pages = channelsToPages(channels); this.pagesHash = new PagesHash(addresses, pagesHashStrategy, positionLinksFactoryBuilder); this.positionLinks = positionLinksFactoryBuilder.isEmpty() ? Optional.empty() : Optional.of(positionLinksFactoryBuilder.build()); } @@ -95,13 +98,13 @@ public JoinHash get() // We need to create new JoinFilterFunction per each thread using it, since those functions // are not thread safe... Optional filterFunction = - filterFunctionFactory.map(factory -> factory.create(session.toConnectorSession(), addresses, channels)); + filterFunctionFactory.map(factory -> factory.create(session.toConnectorSession(), addresses, pages)); return new JoinHash( pagesHash, filterFunction, positionLinks.map(links -> { List searchFunctions = searchFunctionFactories.stream() - .map(factory -> factory.create(session.toConnectorSession(), addresses, channels)) + .map(factory -> factory.create(session.toConnectorSession(), addresses, pages)) .collect(toImmutableList()); return links.create(searchFunctions); })); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorFactory.java index a5621e14b07f1..c8374ce787ba9 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorFactory.java @@ -15,8 +15,32 @@ import java.util.Optional; +import static java.util.Objects.requireNonNull; + public interface JoinOperatorFactory extends OperatorFactory { - Optional createOuterOperatorFactory(); + Optional createOuterOperatorFactory(); + + class OuterOperatorFactoryResult + { + private final OperatorFactory outerOperatorFactory; + private final PipelineExecutionStrategy buildExecutionStrategy; + + public OuterOperatorFactoryResult(OperatorFactory outerOperatorFactory, PipelineExecutionStrategy buildExecutionStrategy) + { + this.outerOperatorFactory = requireNonNull(outerOperatorFactory, "outerOperatorFactory is null"); + this.buildExecutionStrategy = requireNonNull(buildExecutionStrategy, "buildExecutionStrategy is null"); + } + + public OperatorFactory getOuterOperatorFactory() + { + return outerOperatorFactory; + } + + public PipelineExecutionStrategy getBuildExecutionStrategy() + { + return buildExecutionStrategy; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorInfo.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorInfo.java index 91b5777b9c9d8..913b5ba6f7710 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinOperatorInfo.java @@ -77,7 +77,9 @@ public long[] getLogHistogramOutput() return logHistogramOutput; } - /** Estimated number of positions in on the build side */ + /** + * Estimated number of positions in on the build side + */ @JsonProperty public Optional getLookupSourcePositions() { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinStatisticsCounter.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinStatisticsCounter.java index 5ae226eab355c..9169e28d23d63 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/JoinStatisticsCounter.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinStatisticsCounter.java @@ -36,7 +36,9 @@ public class JoinStatisticsCounter // [2*bucket + 1] total count of rows that were produces by probe rows in this bucket. private final long[] logHistogramCounters = new long[HISTOGRAM_BUCKETS * 2]; - /** Estimated number of positions in on the build side */ + /** + * Estimated number of positions in on the build side + */ private Optional lookupSourcePositions = Optional.empty(); public JoinStatisticsCounter(JoinType joinType) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/JoinUtils.java b/presto-main/src/main/java/com/facebook/presto/operator/JoinUtils.java new file mode 100644 index 0000000000000..906182d24dd64 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/JoinUtils.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.google.common.collect.ImmutableList; + +import java.util.List; + +/** + * This class must be public as it is accessed via join compiler reflection. + */ +public final class JoinUtils +{ + private JoinUtils() {} + + public static List channelsToPages(List> channels) + { + ImmutableList.Builder pagesBuilder = ImmutableList.builder(); + if (!channels.isEmpty()) { + int pagesCount = channels.get(0).size(); + for (int pageIndex = 0; pageIndex < pagesCount; ++pageIndex) { + Block[] blocks = new Block[channels.size()]; + for (int channelIndex = 0; channelIndex < channels.size(); ++channelIndex) { + blocks[channelIndex] = channels.get(channelIndex).get(pageIndex); + } + pagesBuilder.add(new Page(blocks)); + } + } + return pagesBuilder.build(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperatorFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperatorFactory.java index 829f5fdc2d473..a4494be0e69c6 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperatorFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperatorFactory.java @@ -28,6 +28,7 @@ import static com.facebook.presto.operator.LookupJoinOperators.JoinType.INNER; import static com.facebook.presto.operator.LookupJoinOperators.JoinType.PROBE_OUTER; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @@ -41,8 +42,8 @@ public class LookupJoinOperatorFactory private final List buildOutputTypes; private final JoinType joinType; private final JoinProbeFactory joinProbeFactory; - private final Optional outerOperatorFactory; - private final JoinBridgeLifecycleManager joinBridgeManager; + private final Optional outerOperatorFactoryResult; + private final JoinBridgeManager joinBridgeManager; private final OptionalInt totalOperatorsCount; private final HashGenerator probeHashGenerator; private final PartitioningSpillerFactory partitioningSpillerFactory; @@ -52,7 +53,7 @@ public class LookupJoinOperatorFactory public LookupJoinOperatorFactory( int operatorId, PlanNodeId planNodeId, - JoinBridgeDataManager lookupSourceFactoryManager, + JoinBridgeManager lookupSourceFactoryManager, List probeTypes, List probeOutputTypes, List buildOutputTypes, @@ -70,19 +71,21 @@ public LookupJoinOperatorFactory( this.joinType = requireNonNull(joinType, "joinType is null"); this.joinProbeFactory = requireNonNull(joinProbeFactory, "joinProbeFactory is null"); - this.joinBridgeManager = JoinBridgeLifecycleManager.lookup(joinType, lookupSourceFactoryManager); + this.joinBridgeManager = lookupSourceFactoryManager; + joinBridgeManager.incrementProbeFactoryCount(); if (joinType == INNER || joinType == PROBE_OUTER) { - this.outerOperatorFactory = Optional.empty(); + this.outerOperatorFactoryResult = Optional.empty(); } else { - this.outerOperatorFactory = Optional.of(new LookupOuterOperatorFactory( - operatorId, - planNodeId, - joinBridgeManager::getOuterPositionsFuture, - probeOutputTypes, - buildOutputTypes, - joinBridgeManager::getJoinBridgeUsersCount)); + this.outerOperatorFactoryResult = Optional.of(new OuterOperatorFactoryResult( + new LookupOuterOperatorFactory( + operatorId, + planNodeId, + probeOutputTypes, + buildOutputTypes, + lookupSourceFactoryManager), + lookupSourceFactoryManager.getBuildExecutionStrategy())); } this.totalOperatorsCount = requireNonNull(totalOperatorsCount, "totalOperatorsCount is null"); @@ -104,20 +107,22 @@ public LookupJoinOperatorFactory( private LookupJoinOperatorFactory(LookupJoinOperatorFactory other) { requireNonNull(other, "other is null"); + checkArgument(!other.closed, "cannot duplicated closed OperatorFactory"); + operatorId = other.operatorId; planNodeId = other.planNodeId; probeTypes = other.probeTypes; buildOutputTypes = other.buildOutputTypes; joinType = other.joinType; joinProbeFactory = other.joinProbeFactory; - // Invokes .duplicate on joinBridgeManager - joinBridgeManager = other.joinBridgeManager.duplicate(); - outerOperatorFactory = other.outerOperatorFactory; + joinBridgeManager = other.joinBridgeManager; + outerOperatorFactoryResult = other.outerOperatorFactoryResult; totalOperatorsCount = other.totalOperatorsCount; probeHashGenerator = other.probeHashGenerator; partitioningSpillerFactory = other.partitioningSpillerFactory; - // closed is intentionally not copied + closed = false; + joinBridgeManager.incrementProbeFactoryCount(); } public int getOperatorId() @@ -130,13 +135,12 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); LookupSourceFactory lookupSourceFactory = joinBridgeManager.getJoinBridge(driverContext.getLifespan()); - ReferenceCount probeReferenceCount = joinBridgeManager.getProbeReferenceCount(driverContext.getLifespan()); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, LookupJoinOperator.class.getSimpleName()); lookupSourceFactory.setTaskContext(driverContext.getPipelineContext().getTaskContext()); - probeReferenceCount.retain(); + joinBridgeManager.probeOperatorCreated(driverContext.getLifespan()); return new LookupJoinOperator( operatorContext, probeTypes, @@ -144,7 +148,7 @@ public Operator createOperator(DriverContext driverContext) joinType, lookupSourceFactory, joinProbeFactory, - probeReferenceCount::release, + () -> joinBridgeManager.probeOperatorClosed(driverContext.getLifespan()), totalOperatorsCount, probeHashGenerator, partitioningSpillerFactory); @@ -155,13 +159,13 @@ public void noMoreOperators() { checkState(!closed); closed = true; - joinBridgeManager.noMoreLifespan(); + joinBridgeManager.probeOperatorFactoryClosedForAllLifespans(); } @Override public void noMoreOperators(Lifespan lifespan) { - joinBridgeManager.getProbeReferenceCount(lifespan).release(); + joinBridgeManager.probeOperatorFactoryClosed(lifespan); } @Override @@ -171,8 +175,8 @@ public OperatorFactory duplicate() } @Override - public Optional createOuterOperatorFactory() + public Optional createOuterOperatorFactory() { - return outerOperatorFactory; + return outerOperatorFactoryResult; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperators.java b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperators.java index a6debe8ea2fe1..71f1fceb6edfc 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinOperators.java @@ -42,22 +42,22 @@ public LookupJoinOperators() { } - public OperatorFactory innerJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) + public OperatorFactory innerJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) { return createJoinOperatorFactory(operatorId, planNodeId, lookupSourceFactory, probeTypes, probeJoinChannel, probeHashChannel, probeOutputChannels.orElse(rangeList(probeTypes.size())), JoinType.INNER, totalOperatorsCount, partitioningSpillerFactory); } - public OperatorFactory probeOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) + public OperatorFactory probeOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) { return createJoinOperatorFactory(operatorId, planNodeId, lookupSourceFactory, probeTypes, probeJoinChannel, probeHashChannel, probeOutputChannels.orElse(rangeList(probeTypes.size())), JoinType.PROBE_OUTER, totalOperatorsCount, partitioningSpillerFactory); } - public OperatorFactory lookupOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) + public OperatorFactory lookupOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) { return createJoinOperatorFactory(operatorId, planNodeId, lookupSourceFactory, probeTypes, probeJoinChannel, probeHashChannel, probeOutputChannels.orElse(rangeList(probeTypes.size())), JoinType.LOOKUP_OUTER, totalOperatorsCount, partitioningSpillerFactory); } - public OperatorFactory fullOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) + public OperatorFactory fullOuterJoin(int operatorId, PlanNodeId planNodeId, JoinBridgeManager lookupSourceFactory, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, Optional> probeOutputChannels, OptionalInt totalOperatorsCount, PartitioningSpillerFactory partitioningSpillerFactory) { return createJoinOperatorFactory(operatorId, planNodeId, lookupSourceFactory, probeTypes, probeJoinChannel, probeHashChannel, probeOutputChannels.orElse(rangeList(probeTypes.size())), JoinType.FULL_OUTER, totalOperatorsCount, partitioningSpillerFactory); } @@ -72,7 +72,7 @@ private static List rangeList(int endExclusive) private OperatorFactory createJoinOperatorFactory( int operatorId, PlanNodeId planNodeId, - JoinBridgeDataManager lookupSourceFactory, + JoinBridgeManager lookupSourceFactoryManager, List probeTypes, List probeJoinChannel, OptionalInt probeHashChannel, @@ -88,10 +88,10 @@ private OperatorFactory createJoinOperatorFactory( return new LookupJoinOperatorFactory( operatorId, planNodeId, - lookupSourceFactory, + lookupSourceFactoryManager, probeTypes, probeOutputChannelTypes, - lookupSourceFactory.getBuildOutputTypes(), + lookupSourceFactoryManager.getBuildOutputTypes(), joinType, new JoinProbeFactory(probeOutputChannels.stream().mapToInt(i -> i).toArray(), probeJoinChannel, probeHashChannel), totalOperatorsCount, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinPageBuilder.java b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinPageBuilder.java index d0aeba9fa0b0f..e03fc938e1906 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinPageBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/LookupJoinPageBuilder.java @@ -29,7 +29,7 @@ /** * This page builder creates pages with dictionary blocks: * normal dictionary blocks for the probe side and the original blocks for the build side. - * + *

* TODO use dictionary blocks (probably extended kind) to avoid data copying for build side */ public class LookupJoinPageBuilder diff --git a/presto-main/src/main/java/com/facebook/presto/operator/LookupOuterOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/LookupOuterOperator.java index b39c14b608b2e..e2c484b1abf4e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/LookupOuterOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/LookupOuterOperator.java @@ -24,7 +24,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; import static com.google.common.base.Preconditions.checkState; import static io.airlift.concurrent.MoreFutures.tryGetFutureValue; @@ -43,10 +42,9 @@ private enum State private final int operatorId; private final PlanNodeId planNodeId; - private final Function> outerPositionsFuture; private final List probeOutputTypes; private final List buildOutputTypes; - private final Function referenceCount; + private final JoinBridgeManager joinBridgeManager; private final Set createdLifespans = new HashSet<>(); private boolean closed; @@ -54,17 +52,15 @@ private enum State public LookupOuterOperatorFactory( int operatorId, PlanNodeId planNodeId, - Function> outerPositionsFuture, List probeOutputTypes, List buildOutputTypes, - Function referenceCount) + JoinBridgeManager joinBridgeManager) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); - this.outerPositionsFuture = requireNonNull(outerPositionsFuture, "outerPositionsFuture is null"); this.probeOutputTypes = ImmutableList.copyOf(requireNonNull(probeOutputTypes, "probeOutputTypes is null")); this.buildOutputTypes = ImmutableList.copyOf(requireNonNull(buildOutputTypes, "buildOutputTypes is null")); - this.referenceCount = requireNonNull(referenceCount, "referenceCount is null"); + this.joinBridgeManager = joinBridgeManager; } public int getOperatorId() @@ -76,23 +72,22 @@ public int getOperatorId() public Operator createOperator(DriverContext driverContext) { checkState(!closed, "LookupOuterOperatorFactory is closed"); - if (createdLifespans.contains(driverContext.getLifespan())) { + Lifespan lifespan = driverContext.getLifespan(); + if (createdLifespans.contains(lifespan)) { throw new IllegalStateException("Only one outer operator can be created per Lifespan"); } - createdLifespans.add(driverContext.getLifespan()); + createdLifespans.add(lifespan); - ListenableFuture outerPositionsFuture = this.outerPositionsFuture.apply(driverContext.getLifespan()); - ReferenceCount referenceCount = this.referenceCount.apply(driverContext.getLifespan()); + ListenableFuture outerPositionsFuture = joinBridgeManager.getOuterPositionsFuture(lifespan); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, LookupOuterOperator.class.getSimpleName()); - referenceCount.retain(); - return new LookupOuterOperator(operatorContext, outerPositionsFuture, probeOutputTypes, buildOutputTypes, referenceCount::release); + joinBridgeManager.outerOperatorCreated(lifespan); + return new LookupOuterOperator(operatorContext, outerPositionsFuture, probeOutputTypes, buildOutputTypes, () -> joinBridgeManager.outerOperatorClosed(lifespan)); } @Override public void noMoreOperators(Lifespan lifespan) { - ReferenceCount referenceCount = this.referenceCount.apply(lifespan); - referenceCount.release(); + joinBridgeManager.outerOperatorFactoryClosed(lifespan); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/LookupSourceFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/LookupSourceFactory.java index 3c7561c6385af..9bfa551300f06 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/LookupSourceFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/LookupSourceFactory.java @@ -26,6 +26,7 @@ import static java.util.Collections.emptyList; public interface LookupSourceFactory + extends JoinBridge { List getTypes(); @@ -49,6 +50,7 @@ default ListenableFuture>> finishP /** * Can be called only after {@link #createLookupSourceProvider()} is done and all users of {@link LookupSource}-s finished. */ + @Override OuterPositionIterator getOuterPositionIterator(); Map getLayout(); @@ -56,16 +58,7 @@ default ListenableFuture>> finishP // this is only here for the index lookup source default void setTaskContext(TaskContext taskContext) {} - default ListenableFuture lendPartitionLookupSource(int partitionIndex, Supplier partitionLookupSource) - { - throw new UnsupportedOperationException(); - } - - default void setPartitionSpilledLookupSourceHandle(int partitionIndex, SpilledLookupSourceHandle spilledLookupSourceHandle) - { - throw new UnsupportedOperationException(); - } - + @Override void destroy(); default ListenableFuture isDestroyed() diff --git a/presto-main/src/main/java/com/facebook/presto/operator/MergeOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/MergeOperator.java index a6927b58b4343..e2f2d6ac407fc 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/MergeOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/MergeOperator.java @@ -163,7 +163,11 @@ public Supplier> addSplit(Split split) ExchangeClient exchangeClient = closer.register(exchangeClientSupplier.get(operatorContext.localSystemMemoryContext())); exchangeClient.addLocation(location); exchangeClient.noMoreLocations(); - pageProducers.add(exchangeClient.pages().map(pagesSerde::deserialize)); + pageProducers.add(exchangeClient.pages() + .map(serializedPage -> { + operatorContext.recordRawInput(serializedPage.getSizeInBytes()); + return pagesSerde.deserialize(serializedPage); + })); return Optional::empty; } @@ -235,7 +239,7 @@ public Page getOutput() } Page page = mergedPages.getResult(); - operatorContext.recordGeneratedInput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); return page; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopBuildOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopBuildOperator.java index 1b2b82221bde4..5debc01974b8a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopBuildOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopBuildOperator.java @@ -32,15 +32,15 @@ public static class NestedLoopBuildOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; - private final JoinBridgeDataManager nestedLoopJoinPagesBridgeManager; + private final JoinBridgeManager nestedLoopJoinBridgeManager; private boolean closed; - public NestedLoopBuildOperatorFactory(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager nestedLoopJoinPagesBridgeManager) + public NestedLoopBuildOperatorFactory(int operatorId, PlanNodeId planNodeId, JoinBridgeManager nestedLoopJoinBridgeManager) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); - this.nestedLoopJoinPagesBridgeManager = requireNonNull(nestedLoopJoinPagesBridgeManager, "nestedLoopJoinPagesBridgeManager is null"); + this.nestedLoopJoinBridgeManager = requireNonNull(nestedLoopJoinBridgeManager, "nestedLoopJoinBridgeManager is null"); } @Override @@ -48,7 +48,7 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, NestedLoopBuildOperator.class.getSimpleName()); - return new NestedLoopBuildOperator(operatorContext, nestedLoopJoinPagesBridgeManager.forLifespan(driverContext.getLifespan())); + return new NestedLoopBuildOperator(operatorContext, nestedLoopJoinBridgeManager.getJoinBridge(driverContext.getLifespan())); } @Override @@ -63,12 +63,12 @@ public void noMoreOperators() @Override public OperatorFactory duplicate() { - return new NestedLoopBuildOperatorFactory(operatorId, planNodeId, nestedLoopJoinPagesBridgeManager); + return new NestedLoopBuildOperatorFactory(operatorId, planNodeId, nestedLoopJoinBridgeManager); } } private final OperatorContext operatorContext; - private final NestedLoopJoinPagesBridge nestedLoopJoinPagesBridge; + private final NestedLoopJoinBridge nestedLoopJoinBridge; private final NestedLoopJoinPagesBuilder nestedLoopJoinPagesBuilder; private final LocalMemoryContext localUserMemoryContext; @@ -77,10 +77,10 @@ public OperatorFactory duplicate() // When the pages are no longer needed, the isFinished method on this operator will return true. private Optional> probeDoneWithPages = Optional.empty(); - public NestedLoopBuildOperator(OperatorContext operatorContext, NestedLoopJoinPagesBridge nestedLoopJoinPagesBridge) + public NestedLoopBuildOperator(OperatorContext operatorContext, NestedLoopJoinBridge nestedLoopJoinBridge) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); - this.nestedLoopJoinPagesBridge = requireNonNull(nestedLoopJoinPagesBridge, "nestedLoopJoinPagesBridge is null"); + this.nestedLoopJoinBridge = requireNonNull(nestedLoopJoinBridge, "nestedLoopJoinBridge is null"); this.nestedLoopJoinPagesBuilder = new NestedLoopJoinPagesBuilder(operatorContext); this.localUserMemoryContext = operatorContext.localUserMemoryContext(); } @@ -100,7 +100,7 @@ public void finish() // nestedLoopJoinPagesBuilder and the built NestedLoopJoinPages will mostly share the same objects. // Extra allocation is minimal during build call. As a result, memory accounting is not updated here. - probeDoneWithPages = Optional.of(nestedLoopJoinPagesBridge.setPages(nestedLoopJoinPagesBuilder.build())); + probeDoneWithPages = Optional.of(nestedLoopJoinBridge.setPages(nestedLoopJoinPagesBuilder.build())); } @Override @@ -136,7 +136,7 @@ public void addInput(Page page) nestedLoopJoinPagesBuilder.compact(); localUserMemoryContext.setBytes(nestedLoopJoinPagesBuilder.getEstimatedSize().toBytes()); } - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinBridge.java b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinBridge.java new file mode 100644 index 0000000000000..d384d6391cc26 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinBridge.java @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.google.common.util.concurrent.ListenableFuture; + +import static com.google.common.util.concurrent.Futures.transform; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; + +public interface NestedLoopJoinBridge + extends JoinBridge +{ + ListenableFuture getPagesFuture(); + + ListenableFuture setPages(NestedLoopJoinPages nestedLoopJoinPages); + + @Override + void destroy(); + + @Override + default OuterPositionIterator getOuterPositionIterator() + { + throw new UnsupportedOperationException(); + } + + @Override + default ListenableFuture whenBuildFinishes() + { + return transform(getPagesFuture(), ignored -> null, directExecutor()); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinOperator.java index 8daed5c8ee0fb..a552fa8679c4c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinOperator.java @@ -41,14 +41,15 @@ public static class NestedLoopJoinOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; - private final JoinBridgeLifecycleManager joinBridgeManager; + private final JoinBridgeManager joinBridgeManager; private boolean closed; - public NestedLoopJoinOperatorFactory(int operatorId, PlanNodeId planNodeId, JoinBridgeDataManager nestedLoopJoinPagesSupplierManager) + public NestedLoopJoinOperatorFactory(int operatorId, PlanNodeId planNodeId, JoinBridgeManager nestedLoopJoinBridgeManager) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); - this.joinBridgeManager = JoinBridgeLifecycleManager.nestedLoop(LookupJoinOperators.JoinType.INNER, nestedLoopJoinPagesSupplierManager); + this.joinBridgeManager = nestedLoopJoinBridgeManager; + this.joinBridgeManager.incrementProbeFactoryCount(); } private NestedLoopJoinOperatorFactory(NestedLoopJoinOperatorFactory other) @@ -57,28 +58,27 @@ private NestedLoopJoinOperatorFactory(NestedLoopJoinOperatorFactory other) this.operatorId = other.operatorId; this.planNodeId = other.planNodeId; - // joinBridgeManager must be duplicated here. - // Otherwise, reference counting and lifecycle management will be wrong. - this.joinBridgeManager = other.joinBridgeManager.duplicate(); + this.joinBridgeManager = other.joinBridgeManager; // closed is intentionally not copied closed = false; + + joinBridgeManager.incrementProbeFactoryCount(); } @Override public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); - NestedLoopJoinPagesBridge nestedLoopJoinPagesBridge = joinBridgeManager.getJoinBridge(driverContext.getLifespan()); - ReferenceCount probeReferenceCount = joinBridgeManager.getProbeReferenceCount(driverContext.getLifespan()); + NestedLoopJoinBridge nestedLoopJoinBridge = joinBridgeManager.getJoinBridge(driverContext.getLifespan()); OperatorContext operatorContext = driverContext.addOperatorContext(operatorId, planNodeId, NestedLoopJoinOperator.class.getSimpleName()); - probeReferenceCount.retain(); + joinBridgeManager.probeOperatorCreated(driverContext.getLifespan()); return new NestedLoopJoinOperator( operatorContext, - nestedLoopJoinPagesBridge, - probeReferenceCount::release); + nestedLoopJoinBridge, + () -> joinBridgeManager.probeOperatorClosed(driverContext.getLifespan())); } @Override @@ -88,13 +88,13 @@ public void noMoreOperators() return; } closed = true; - joinBridgeManager.noMoreLifespan(); + joinBridgeManager.probeOperatorFactoryClosedForAllLifespans(); } @Override public void noMoreOperators(Lifespan lifespan) { - joinBridgeManager.getProbeReferenceCount(lifespan).release(); + joinBridgeManager.probeOperatorFactoryClosed(lifespan); } @Override @@ -116,10 +116,10 @@ public OperatorFactory duplicate() private boolean finishing; private boolean closed; - public NestedLoopJoinOperator(OperatorContext operatorContext, NestedLoopJoinPagesBridge buildPagesSupplier, Runnable afterClose) + private NestedLoopJoinOperator(OperatorContext operatorContext, NestedLoopJoinBridge joinBridge, Runnable afterClose) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); - this.nestedLoopJoinPagesFuture = buildPagesSupplier.getPagesFuture(); + this.nestedLoopJoinPagesFuture = joinBridge.getPagesFuture(); this.afterClose = requireNonNull(afterClose, "afterClose is null"); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesSupplier.java b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesSupplier.java index a60af8e4b6db8..3b2ea2bca9b32 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/NestedLoopJoinPagesSupplier.java @@ -23,7 +23,7 @@ import static java.util.Objects.requireNonNull; public final class NestedLoopJoinPagesSupplier - implements NestedLoopJoinPagesBridge + implements NestedLoopJoinBridge { private final SettableFuture pagesFuture = SettableFuture.create(); private final SettableFuture pagesNoLongerNeeded = SettableFuture.create(); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/OperationTimer.java b/presto-main/src/main/java/com/facebook/presto/operator/OperationTimer.java new file mode 100644 index 0000000000000..a2a5442d62175 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/OperationTimer.java @@ -0,0 +1,140 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import javax.annotation.concurrent.NotThreadSafe; +import javax.annotation.concurrent.ThreadSafe; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.util.concurrent.atomic.AtomicLong; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.lang.Math.max; +import static java.util.Objects.requireNonNull; + +@NotThreadSafe +class OperationTimer +{ + private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); + + private final boolean trackOverallCpuTime; + private final boolean trackOperationCpuTime; + + private final long wallStart; + private final long cpuStart; + + private long intervalWallStart; + private long intervalCpuStart; + + private boolean finished; + + OperationTimer(boolean trackOverallCpuTime) + { + this(trackOverallCpuTime, false); + } + + OperationTimer(boolean trackOverallCpuTime, boolean trackOperationCpuTime) + { + this.trackOverallCpuTime = trackOverallCpuTime; + this.trackOperationCpuTime = trackOperationCpuTime; + checkArgument(trackOverallCpuTime || !trackOperationCpuTime, "tracking operation cpu time without tracking overall cpu time is not supported"); + + wallStart = System.nanoTime(); + cpuStart = trackOverallCpuTime ? currentThreadCpuTime() : 0; + + intervalWallStart = wallStart; + intervalCpuStart = cpuStart; + } + + void recordOperationComplete(OperationTiming operationTiming) + { + requireNonNull(operationTiming, "operationTiming is null"); + checkState(!finished, "timer is finished"); + + long intervalCpuEnd = trackOperationCpuTime ? currentThreadCpuTime() : 0; + long intervalWallEnd = System.nanoTime(); + + long operationWallNanos = nanosBetween(intervalWallStart, intervalWallEnd); + long operationCpuNanos = trackOperationCpuTime ? nanosBetween(intervalCpuStart, intervalCpuEnd) : 0; + operationTiming.record(operationWallNanos, operationCpuNanos); + + intervalWallStart = intervalWallEnd; + intervalCpuStart = intervalCpuEnd; + } + + void end(OperationTiming overallTiming) + { + requireNonNull(overallTiming, "overallTiming is null"); + checkState(!finished, "timer is finished"); + finished = true; + + long cpuEnd = trackOverallCpuTime ? currentThreadCpuTime() : 0; + long wallEnd = System.nanoTime(); + + overallTiming.record(nanosBetween(wallStart, wallEnd), nanosBetween(cpuStart, cpuEnd)); + } + + private static long currentThreadCpuTime() + { + return THREAD_MX_BEAN.getCurrentThreadCpuTime(); + } + + private static long nanosBetween(long start, long end) + { + return max(0, end - start); + } + + @ThreadSafe + static class OperationTiming + { + private final AtomicLong calls = new AtomicLong(); + private final AtomicLong wallNanos = new AtomicLong(); + private final AtomicLong cpuNanos = new AtomicLong(); + + long getCalls() + { + return calls.get(); + } + + long getWallNanos() + { + return wallNanos.get(); + } + + long getCpuNanos() + { + return cpuNanos.get(); + } + + void record(long wallNanos, long cpuNanos) + { + this.calls.incrementAndGet(); + this.wallNanos.addAndGet(wallNanos); + this.cpuNanos.addAndGet(cpuNanos); + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("calls", calls) + .add("wallNanos", wallNanos) + .add("cpuNanos", cpuNanos) + .toString(); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/Operator.java b/presto-main/src/main/java/com/facebook/presto/operator/Operator.java index 93140f3c1f2e6..f8f5affbbf4c0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/Operator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/Operator.java @@ -56,11 +56,14 @@ default ListenableFuture isBlocked() * As soon as memory is revoked returned future should be marked as done. *

* Spawned threads can not modify OperatorContext because it's not thread safe. - * For this purpose use implement finishMemoryRevoke + * For this purpose implement {@link #finishMemoryRevoke()} *

- * After startMemoryRevoke is called on Operator the Driver is disallowed to call any - * processing methods on it (finish/isFinished/isBlocked/needsInput/addInput/getOutput) until - * finishMemoryRevoke is called. + * Since memory revoking signal is delivered asynchronously to the Operator, implementation + * must gracefully handle the case when there no longer is any revocable memory allocated. + *

+ * After this method is called on Operator the Driver is disallowed to call any + * processing methods on it (isBlocked/needsInput/addInput/getOutput) until + * {@link #finishMemoryRevoke()} is called. */ default ListenableFuture startMemoryRevoke() { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/OperatorContext.java b/presto-main/src/main/java/com/facebook/presto/operator/OperatorContext.java index 4f22c2ea5f174..09617096c824d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/OperatorContext.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/OperatorContext.java @@ -18,6 +18,7 @@ import com.facebook.presto.memory.context.AggregatedMemoryContext; import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.memory.context.MemoryTrackingContext; +import com.facebook.presto.operator.OperationTimer.OperationTiming; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.planner.plan.PlanNodeId; @@ -31,8 +32,6 @@ import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; import java.util.Optional; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicLong; @@ -46,6 +45,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.succinctBytes; +import static java.lang.Math.max; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -55,29 +55,19 @@ */ public class OperatorContext { - private static final ThreadMXBean THREAD_MX_BEAN = ManagementFactory.getThreadMXBean(); - private final int operatorId; private final PlanNodeId planNodeId; private final String operatorType; private final DriverContext driverContext; private final Executor executor; - private final AtomicLong intervalWallStart = new AtomicLong(); - private final AtomicLong intervalCpuStart = new AtomicLong(); - private final AtomicLong intervalUserStart = new AtomicLong(); + private final CounterStat rawInputDataSize = new CounterStat(); - private final AtomicLong addInputCalls = new AtomicLong(); - private final AtomicLong addInputWallNanos = new AtomicLong(); - private final AtomicLong addInputCpuNanos = new AtomicLong(); - private final AtomicLong addInputUserNanos = new AtomicLong(); + private final OperationTiming addInputTiming = new OperationTiming(); private final CounterStat inputDataSize = new CounterStat(); private final CounterStat inputPositions = new CounterStat(); - private final AtomicLong getOutputCalls = new AtomicLong(); - private final AtomicLong getOutputWallNanos = new AtomicLong(); - private final AtomicLong getOutputCpuNanos = new AtomicLong(); - private final AtomicLong getOutputUserNanos = new AtomicLong(); + private final OperationTiming getOutputTiming = new OperationTiming(); private final CounterStat outputDataSize = new CounterStat(); private final CounterStat outputPositions = new CounterStat(); @@ -88,14 +78,10 @@ public class OperatorContext private final AtomicReference blockedMonitor = new AtomicReference<>(); private final AtomicLong blockedWallNanos = new AtomicLong(); - private final AtomicLong finishCalls = new AtomicLong(); - private final AtomicLong finishWallNanos = new AtomicLong(); - private final AtomicLong finishCpuNanos = new AtomicLong(); - private final AtomicLong finishUserNanos = new AtomicLong(); + private final OperationTiming finishTiming = new OperationTiming(); private final SpillContext spillContext; private final AtomicReference> infoSupplier = new AtomicReference<>(); - private final boolean collectTimings; private final AtomicLong peakUserMemoryReservation = new AtomicLong(); private final AtomicLong peakSystemMemoryReservation = new AtomicLong(); @@ -131,8 +117,6 @@ public OperatorContext( this.revocableMemoryFuture.get().set(null); this.operatorMemoryContext = requireNonNull(operatorMemoryContext, "operatorMemoryContext is null"); operatorMemoryContext.initializeLocalMemoryContexts(operatorType); - - collectTimings = driverContext.isVerboseStats() && driverContext.isCpuTimerEnabled(); } public int getOperatorId() @@ -160,57 +144,54 @@ public boolean isDone() return driverContext.isDone(); } - public void startIntervalTimer() + void recordAddInput(OperationTimer operationTimer, Page page) { - intervalWallStart.set(System.nanoTime()); - intervalCpuStart.set(currentThreadCpuTime()); - intervalUserStart.set(currentThreadUserTime()); - } - - public void recordAddInput(Page page) - { - addInputCalls.incrementAndGet(); - recordInputWallNanos(nanosBetween(intervalWallStart.get(), System.nanoTime())); - addInputCpuNanos.getAndAdd(nanosBetween(intervalCpuStart.get(), currentThreadCpuTime())); - addInputUserNanos.getAndAdd(nanosBetween(intervalUserStart.get(), currentThreadUserTime())); - + operationTimer.recordOperationComplete(addInputTiming); if (page != null) { inputDataSize.update(page.getSizeInBytes()); inputPositions.update(page.getPositionCount()); } } - public void recordGeneratedInput(long sizeInBytes, long positions) + /** + * Record the amount of physical bytes that were read by an operator. + * This metric is valid only for source operators. + */ + public void recordRawInput(long sizeInBytes) { - recordGeneratedInput(sizeInBytes, positions, 0); + rawInputDataSize.update(sizeInBytes); } - public void recordGeneratedInput(long sizeInBytes, long positions, long readNanos) + /** + * Record the amount of physical bytes that were read by an operator and + * the time it took to read the data. This metric is valid only for source operators. + */ + public void recordRawInputWithTiming(long sizeInBytes, long readNanos) { - inputDataSize.update(sizeInBytes); - inputPositions.update(positions); - recordInputWallNanos(readNanos); + rawInputDataSize.update(sizeInBytes); + addInputTiming.record(readNanos, 0); } - public long recordInputWallNanos(long readNanos) + /** + * Record the size in bytes of input blocks that were processed by an operator. + * This metric is valid only for source operators. + */ + public void recordProcessedInput(long sizeInBytes, long positions) { - return addInputWallNanos.getAndAdd(readNanos); + inputDataSize.update(sizeInBytes); + inputPositions.update(positions); } - public void recordGetOutput(Page page) + void recordGetOutput(OperationTimer operationTimer, Page page) { - getOutputCalls.incrementAndGet(); - getOutputWallNanos.getAndAdd(nanosBetween(intervalWallStart.get(), System.nanoTime())); - getOutputCpuNanos.getAndAdd(nanosBetween(intervalCpuStart.get(), currentThreadCpuTime())); - getOutputUserNanos.getAndAdd(nanosBetween(intervalUserStart.get(), currentThreadUserTime())); - + operationTimer.recordOperationComplete(getOutputTiming); if (page != null) { outputDataSize.update(page.getSizeInBytes()); outputPositions.update(page.getPositionCount()); } } - public void recordGeneratedOutput(long sizeInBytes, long positions) + public void recordOutput(long sizeInBytes, long positions) { outputDataSize.update(sizeInBytes); outputPositions.update(positions); @@ -236,12 +217,9 @@ public void recordBlocked(ListenableFuture blocked) // Do not register blocked with driver context. The driver handles this directly. } - public void recordFinish() + void recordFinish(OperationTimer operationTimer) { - finishCalls.incrementAndGet(); - finishWallNanos.getAndAdd(nanosBetween(intervalWallStart.get(), System.nanoTime())); - finishCpuNanos.getAndAdd(nanosBetween(intervalCpuStart.get(), currentThreadCpuTime())); - finishUserNanos.getAndAdd(nanosBetween(intervalUserStart.get(), currentThreadUserTime())); + operationTimer.recordOperationComplete(finishTiming); } public ListenableFuture isWaitingForMemory() @@ -461,6 +439,7 @@ public OperatorStats getOperatorStats() long inputPositionsCount = inputPositions.getTotalCount(); return new OperatorStats( + driverContext.getTaskId().getStageId().getId(), driverContext.getPipelineContext().getPipelineId(), operatorId, planNodeId, @@ -468,18 +447,17 @@ public OperatorStats getOperatorStats() 1, - addInputCalls.get(), - new Duration(addInputWallNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(addInputCpuNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(addInputUserNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + addInputTiming.getCalls(), + new Duration(addInputTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(addInputTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + succinctBytes(rawInputDataSize.getTotalCount()), succinctBytes(inputDataSize.getTotalCount()), inputPositionsCount, (double) inputPositionsCount * inputPositionsCount, - getOutputCalls.get(), - new Duration(getOutputWallNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(getOutputCpuNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(getOutputUserNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + getOutputTiming.getCalls(), + new Duration(getOutputTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(getOutputTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), succinctBytes(outputDataSize.getTotalCount()), outputPositions.getTotalCount(), @@ -487,10 +465,9 @@ public OperatorStats getOperatorStats() new Duration(blockedWallNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - finishCalls.get(), - new Duration(finishWallNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(finishCpuNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(finishUserNanos.get(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + finishTiming.getCalls(), + new Duration(finishTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(finishTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), succinctBytes(operatorMemoryContext.getUserMemory()), succinctBytes(getReservedRevocableBytes()), @@ -509,25 +486,9 @@ public R accept(QueryContextVisitor visitor, C context) return visitor.visitOperatorContext(this, context); } - private long currentThreadUserTime() - { - if (!collectTimings) { - return 0; - } - return THREAD_MX_BEAN.getCurrentThreadUserTime(); - } - - private long currentThreadCpuTime() - { - if (!collectTimings) { - return 0; - } - return THREAD_MX_BEAN.getCurrentThreadCpuTime(); - } - private static long nanosBetween(long start, long end) { - return Math.abs(end - start); + return max(0, end - start); } private class BlockedMonitor diff --git a/presto-main/src/main/java/com/facebook/presto/operator/OperatorStats.java b/presto-main/src/main/java/com/facebook/presto/operator/OperatorStats.java index 58f867e2efb9c..d6bda1c8b5d8f 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/OperatorStats.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/OperatorStats.java @@ -35,6 +35,7 @@ @Immutable public class OperatorStats { + private final int stageId; private final int pipelineId; private final int operatorId; private final PlanNodeId planNodeId; @@ -45,7 +46,7 @@ public class OperatorStats private final long addInputCalls; private final Duration addInputWall; private final Duration addInputCpu; - private final Duration addInputUser; + private final DataSize rawInputDataSize; private final DataSize inputDataSize; private final long inputPositions; private final double sumSquaredInputPositions; @@ -53,7 +54,6 @@ public class OperatorStats private final long getOutputCalls; private final Duration getOutputWall; private final Duration getOutputCpu; - private final Duration getOutputUser; private final DataSize outputDataSize; private final long outputPositions; @@ -64,7 +64,6 @@ public class OperatorStats private final long finishCalls; private final Duration finishWall; private final Duration finishCpu; - private final Duration finishUser; private final DataSize userMemoryReservation; private final DataSize revocableMemoryReservation; @@ -79,6 +78,7 @@ public class OperatorStats @JsonCreator public OperatorStats( + @JsonProperty("stageId") int stageId, @JsonProperty("pipelineId") int pipelineId, @JsonProperty("operatorId") int operatorId, @JsonProperty("planNodeId") PlanNodeId planNodeId, @@ -89,7 +89,7 @@ public OperatorStats( @JsonProperty("addInputCalls") long addInputCalls, @JsonProperty("addInputWall") Duration addInputWall, @JsonProperty("addInputCpu") Duration addInputCpu, - @JsonProperty("addInputUser") Duration addInputUser, + @JsonProperty("rawInputDataSize") DataSize rawInputDataSize, @JsonProperty("inputDataSize") DataSize inputDataSize, @JsonProperty("inputPositions") long inputPositions, @JsonProperty("sumSquaredInputPositions") double sumSquaredInputPositions, @@ -97,7 +97,6 @@ public OperatorStats( @JsonProperty("getOutputCalls") long getOutputCalls, @JsonProperty("getOutputWall") Duration getOutputWall, @JsonProperty("getOutputCpu") Duration getOutputCpu, - @JsonProperty("getOutputUser") Duration getOutputUser, @JsonProperty("outputDataSize") DataSize outputDataSize, @JsonProperty("outputPositions") long outputPositions, @@ -108,7 +107,6 @@ public OperatorStats( @JsonProperty("finishCalls") long finishCalls, @JsonProperty("finishWall") Duration finishWall, @JsonProperty("finishCpu") Duration finishCpu, - @JsonProperty("finishUser") Duration finishUser, @JsonProperty("userMemoryReservation") DataSize userMemoryReservation, @JsonProperty("revocableMemoryReservation") DataSize revocableMemoryReservation, @@ -121,6 +119,7 @@ public OperatorStats( @JsonProperty("info") OperatorInfo info) { + this.stageId = stageId; this.pipelineId = pipelineId; checkArgument(operatorId >= 0, "operatorId is negative"); @@ -133,7 +132,7 @@ public OperatorStats( this.addInputCalls = addInputCalls; this.addInputWall = requireNonNull(addInputWall, "addInputWall is null"); this.addInputCpu = requireNonNull(addInputCpu, "addInputCpu is null"); - this.addInputUser = requireNonNull(addInputUser, "addInputUser is null"); + this.rawInputDataSize = requireNonNull(rawInputDataSize, "rawInputDataSize is null"); this.inputDataSize = requireNonNull(inputDataSize, "inputDataSize is null"); checkArgument(inputPositions >= 0, "inputPositions is negative"); this.inputPositions = inputPositions; @@ -142,7 +141,6 @@ public OperatorStats( this.getOutputCalls = getOutputCalls; this.getOutputWall = requireNonNull(getOutputWall, "getOutputWall is null"); this.getOutputCpu = requireNonNull(getOutputCpu, "getOutputCpu is null"); - this.getOutputUser = requireNonNull(getOutputUser, "getOutputUser is null"); this.outputDataSize = requireNonNull(outputDataSize, "outputDataSize is null"); checkArgument(outputPositions >= 0, "outputPositions is negative"); this.outputPositions = outputPositions; @@ -154,7 +152,6 @@ public OperatorStats( this.finishCalls = finishCalls; this.finishWall = requireNonNull(finishWall, "finishWall is null"); this.finishCpu = requireNonNull(finishCpu, "finishCpu is null"); - this.finishUser = requireNonNull(finishUser, "finishUser is null"); this.userMemoryReservation = requireNonNull(userMemoryReservation, "userMemoryReservation is null"); this.revocableMemoryReservation = requireNonNull(revocableMemoryReservation, "revocableMemoryReservation is null"); @@ -169,6 +166,12 @@ public OperatorStats( this.info = info; } + @JsonProperty + public int getStageId() + { + return stageId; + } + @JsonProperty public int getPipelineId() { @@ -218,9 +221,9 @@ public Duration getAddInputCpu() } @JsonProperty - public Duration getAddInputUser() + public DataSize getRawInputDataSize() { - return addInputUser; + return rawInputDataSize; } @JsonProperty @@ -259,12 +262,6 @@ public Duration getGetOutputCpu() return getOutputCpu; } - @JsonProperty - public Duration getGetOutputUser() - { - return getOutputUser; - } - @JsonProperty public DataSize getOutputDataSize() { @@ -307,12 +304,6 @@ public Duration getFinishCpu() return finishCpu; } - @JsonProperty - public Duration getFinishUser() - { - return finishUser; - } - @JsonProperty public DataSize getUserMemoryReservation() { @@ -374,7 +365,7 @@ public OperatorStats add(Iterable operators) long addInputCalls = this.addInputCalls; long addInputWall = this.addInputWall.roundTo(NANOSECONDS); long addInputCpu = this.addInputCpu.roundTo(NANOSECONDS); - long addInputUser = this.addInputUser.roundTo(NANOSECONDS); + long rawInputDataSize = this.rawInputDataSize.toBytes(); long inputDataSize = this.inputDataSize.toBytes(); long inputPositions = this.inputPositions; double sumSquaredInputPositions = this.sumSquaredInputPositions; @@ -382,7 +373,6 @@ public OperatorStats add(Iterable operators) long getOutputCalls = this.getOutputCalls; long getOutputWall = this.getOutputWall.roundTo(NANOSECONDS); long getOutputCpu = this.getOutputCpu.roundTo(NANOSECONDS); - long getOutputUser = this.getOutputUser.roundTo(NANOSECONDS); long outputDataSize = this.outputDataSize.toBytes(); long outputPositions = this.outputPositions; @@ -393,7 +383,6 @@ public OperatorStats add(Iterable operators) long finishCalls = this.finishCalls; long finishWall = this.finishWall.roundTo(NANOSECONDS); long finishCpu = this.finishCpu.roundTo(NANOSECONDS); - long finishUser = this.finishUser.roundTo(NANOSECONDS); long memoryReservation = this.userMemoryReservation.toBytes(); long revocableMemoryReservation = this.revocableMemoryReservation.toBytes(); @@ -413,7 +402,7 @@ public OperatorStats add(Iterable operators) addInputCalls += operator.getAddInputCalls(); addInputWall += operator.getAddInputWall().roundTo(NANOSECONDS); addInputCpu += operator.getAddInputCpu().roundTo(NANOSECONDS); - addInputUser += operator.getAddInputUser().roundTo(NANOSECONDS); + rawInputDataSize += operator.getRawInputDataSize().toBytes(); inputDataSize += operator.getInputDataSize().toBytes(); inputPositions += operator.getInputPositions(); sumSquaredInputPositions += operator.getSumSquaredInputPositions(); @@ -421,7 +410,6 @@ public OperatorStats add(Iterable operators) getOutputCalls += operator.getGetOutputCalls(); getOutputWall += operator.getGetOutputWall().roundTo(NANOSECONDS); getOutputCpu += operator.getGetOutputCpu().roundTo(NANOSECONDS); - getOutputUser += operator.getGetOutputUser().roundTo(NANOSECONDS); outputDataSize += operator.getOutputDataSize().toBytes(); outputPositions += operator.getOutputPositions(); @@ -430,7 +418,6 @@ public OperatorStats add(Iterable operators) finishCalls += operator.getFinishCalls(); finishWall += operator.getFinishWall().roundTo(NANOSECONDS); finishCpu += operator.getFinishCpu().roundTo(NANOSECONDS); - finishUser += operator.getFinishUser().roundTo(NANOSECONDS); blockedWall += operator.getBlockedWall().roundTo(NANOSECONDS); @@ -453,6 +440,7 @@ public OperatorStats add(Iterable operators) } return new OperatorStats( + stageId, pipelineId, operatorId, planNodeId, @@ -463,7 +451,7 @@ public OperatorStats add(Iterable operators) addInputCalls, new Duration(addInputWall, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(addInputCpu, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(addInputUser, NANOSECONDS).convertToMostSuccinctTimeUnit(), + succinctBytes(rawInputDataSize), succinctBytes(inputDataSize), inputPositions, sumSquaredInputPositions, @@ -471,7 +459,6 @@ public OperatorStats add(Iterable operators) getOutputCalls, new Duration(getOutputWall, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(getOutputCpu, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(getOutputUser, NANOSECONDS).convertToMostSuccinctTimeUnit(), succinctBytes(outputDataSize), outputPositions, @@ -482,7 +469,6 @@ public OperatorStats add(Iterable operators) finishCalls, new Duration(finishWall, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(finishCpu, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(finishUser, NANOSECONDS).convertToMostSuccinctTimeUnit(), succinctBytes(memoryReservation), succinctBytes(revocableMemoryReservation), @@ -515,6 +501,7 @@ private static Mergeable mergeInfo(Mergeable base, T other) public OperatorStats summarize() { return new OperatorStats( + stageId, pipelineId, operatorId, planNodeId, @@ -523,14 +510,13 @@ public OperatorStats summarize() addInputCalls, addInputWall, addInputCpu, - addInputUser, + rawInputDataSize, inputDataSize, inputPositions, sumSquaredInputPositions, getOutputCalls, getOutputWall, getOutputCpu, - getOutputUser, outputDataSize, outputPositions, physicalWrittenDataSize, @@ -538,7 +524,6 @@ public OperatorStats summarize() finishCalls, finishWall, finishCpu, - finishUser, userMemoryReservation, revocableMemoryReservation, systemMemoryReservation, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PageSourceOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/PageSourceOperator.java index 08f873bf2badd..1e05e41908bc9 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PageSourceOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PageSourceOperator.java @@ -92,10 +92,14 @@ public Page getOutput() // update operator stats long endCompletedBytes = pageSource.getCompletedBytes(); long endReadTimeNanos = pageSource.getReadTimeNanos(); - operatorContext.recordGeneratedInput(endCompletedBytes - completedBytes, page.getPositionCount(), endReadTimeNanos - readTimeNanos); + operatorContext.recordRawInputWithTiming(endCompletedBytes - completedBytes, endReadTimeNanos - readTimeNanos); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); completedBytes = endCompletedBytes; readTimeNanos = endReadTimeNanos; + // assure the page is in memory before handing to another operator + page = page.getLoadedPage(); + return page; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java index 37e3b398e8b65..2892e87de5828 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator; import com.facebook.presto.Session; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; @@ -44,6 +45,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.function.IntUnaryOperator; @@ -458,13 +460,15 @@ public PagesSpatialIndexSupplier createPagesSpatialIndex( Session session, int geometryChannel, Optional radiusChannel, + Optional partitionChannel, SpatialPredicate spatialRelationshipTest, Optional filterFunctionFactory, - List outputChannels) + List outputChannels, + Map partitions) { // TODO probably shouldn't copy to reduce memory and for memory accounting's sake List> channels = ImmutableList.copyOf(this.channels); - return new PagesSpatialIndexSupplier(session, valueAddresses, types, outputChannels, channels, geometryChannel, radiusChannel, spatialRelationshipTest, filterFunctionFactory); + return new PagesSpatialIndexSupplier(session, valueAddresses, types, outputChannels, channels, geometryChannel, radiusChannel, partitionChannel, spatialRelationshipTest, filterFunctionFactory, partitions); } public LookupSourceSupplier createLookupSourceSupplier( diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesRTreeIndex.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesRTreeIndex.java index e5675c367f83b..000aa85e97550 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PagesRTreeIndex.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesRTreeIndex.java @@ -14,7 +14,9 @@ package com.facebook.presto.operator; import com.esri.core.geometry.ogc.OGCGeometry; +import com.esri.core.geometry.ogc.OGCPoint; import com.facebook.presto.Session; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.operator.SpatialIndexBuilderOperator.SpatialPredicate; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; @@ -29,14 +31,18 @@ import org.openjdk.jol.info.ClassLayout; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; import static com.facebook.presto.geospatial.serde.GeometrySerde.deserialize; +import static com.facebook.presto.operator.JoinUtils.channelsToPages; import static com.facebook.presto.operator.SyntheticAddress.decodePosition; import static com.facebook.presto.operator.SyntheticAddress.decodeSliceIndex; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.google.common.base.Verify.verify; +import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; public class PagesRTreeIndex @@ -52,19 +58,38 @@ public class PagesRTreeIndex private final int radiusChannel; private final SpatialPredicate spatialRelationshipTest; private final JoinFilterFunction filterFunction; + private final Map partitions; public static final class GeometryWithPosition { private static final int INSTANCE_SIZE = ClassLayout.parseClass(GeometryWithPosition.class).instanceSize(); + private final OGCGeometry ogcGeometry; + private final int partition; private final int position; - public GeometryWithPosition(OGCGeometry ogcGeometry, int position) + public GeometryWithPosition(OGCGeometry ogcGeometry, int partition, int position) { - this.ogcGeometry = ogcGeometry; + this.ogcGeometry = requireNonNull(ogcGeometry, "ogcGeometry is null"); + this.partition = partition; this.position = position; } + public OGCGeometry getGeometry() + { + return ogcGeometry; + } + + public int getPartition() + { + return partition; + } + + public int getPosition() + { + return position; + } + public long getEstimatedMemorySizeInBytes() { return INSTANCE_SIZE + ogcGeometry.estimateMemorySize(); @@ -80,7 +105,8 @@ public PagesRTreeIndex( STRtree rtree, Optional radiusChannel, SpatialPredicate spatialRelationshipTest, - Optional filterFunctionFactory) + Optional filterFunctionFactory, + Map partitions) { this.addresses = requireNonNull(addresses, "addresses is null"); this.types = types; @@ -89,7 +115,8 @@ public PagesRTreeIndex( this.rtree = requireNonNull(rtree, "rtree is null"); this.radiusChannel = radiusChannel.orElse(-1); this.spatialRelationshipTest = requireNonNull(spatialRelationshipTest, "spatial relationship is null"); - this.filterFunction = filterFunctionFactory.map(factory -> factory.create(session.toConnectorSession(), addresses, channels)).orElse(null); + this.filterFunction = filterFunctionFactory.map(factory -> factory.create(session.toConnectorSession(), addresses, channelsToPages(channels))).orElse(null); + this.partitions = requireNonNull(partitions, "partitions is null"); } private static Envelope getEnvelope(OGCGeometry ogcGeometry) @@ -108,13 +135,15 @@ private static Envelope getEnvelope(OGCGeometry ogcGeometry) * for each of these addresses to apply additional join filters. */ @Override - public int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel) + public int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel, Optional probePartitionChannel) { Block probeGeometryBlock = probe.getBlock(probeGeometryChannel); if (probeGeometryBlock.isNull(probePosition)) { return EMPTY_ADDRESSES; } + int probePartition = probePartitionChannel.map(channel -> toIntExact(INTEGER.getLong(probe.getBlock(channel), probePosition))).orElse(-1); + Slice slice = probeGeometryBlock.getSlice(probePosition, 0, probeGeometryBlock.getSliceLength(probePosition)); OGCGeometry probeGeometry = deserialize(slice); verify(probeGeometry != null); @@ -122,29 +151,46 @@ public int[] findJoinPositions(int probePosition, Page probe, int probeGeometryC return EMPTY_ADDRESSES; } + boolean probeIsPoint = probeGeometry instanceof OGCPoint; + IntArrayList matchingPositions = new IntArrayList(); Envelope envelope = getEnvelope(probeGeometry); - if (radiusChannel == -1) { - rtree.query(envelope, item -> { - GeometryWithPosition geometryWithPosition = (GeometryWithPosition) item; - if (spatialRelationshipTest.apply(geometryWithPosition.ogcGeometry, probeGeometry, OptionalDouble.empty())) { - matchingPositions.add(geometryWithPosition.position); + rtree.query(envelope, item -> { + GeometryWithPosition geometryWithPosition = (GeometryWithPosition) item; + OGCGeometry buildGeometry = geometryWithPosition.getGeometry(); + if (partitions.isEmpty() || (probePartition == geometryWithPosition.getPartition() && (probeIsPoint || (buildGeometry instanceof OGCPoint) || testReferencePoint(envelope, buildGeometry, probePartition)))) { + if (radiusChannel == -1) { + if (spatialRelationshipTest.apply(buildGeometry, probeGeometry, OptionalDouble.empty())) { + matchingPositions.add(geometryWithPosition.getPosition()); + } } - }); - } - else { - rtree.query(envelope, item -> { - GeometryWithPosition geometryWithPosition = (GeometryWithPosition) item; - if (spatialRelationshipTest.apply(geometryWithPosition.ogcGeometry, probeGeometry, OptionalDouble.of(getRadius(geometryWithPosition.position)))) { - matchingPositions.add(geometryWithPosition.position); + else { + if (spatialRelationshipTest.apply(geometryWithPosition.getGeometry(), probeGeometry, OptionalDouble.of(getRadius(geometryWithPosition.getPosition())))) { + matchingPositions.add(geometryWithPosition.getPosition()); + } } - }); - } + } + }); return matchingPositions.toIntArray(null); } + private boolean testReferencePoint(Envelope probeEnvelope, OGCGeometry buildGeometry, int partition) + { + Envelope buildEnvelope = getEnvelope(buildGeometry); + Envelope intersection = buildEnvelope.intersection(probeEnvelope); + if (intersection.isNull()) { + return false; + } + + Rectangle extent = partitions.get(partition); + + double x = intersection.getMinX(); + double y = intersection.getMinY(); + return x >= extent.getXMin() && x < extent.getXMax() && y >= extent.getYMin() && y < extent.getYMax(); + } + private double getRadius(int joinPosition) { long joinAddress = addresses.getLong(joinPosition); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndex.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndex.java index ef04248995b1d..2ad78e3c13d9d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndex.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndex.java @@ -16,9 +16,11 @@ import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; +import java.util.Optional; + public interface PagesSpatialIndex { - int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel); + int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel, Optional probePartitionChannel); boolean isJoinPositionEligible(int joinPosition, int probePosition, Page probe); @@ -29,7 +31,7 @@ public interface PagesSpatialIndex private final int[] emptyAddresses = new int[0]; @Override - public int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel) + public int[] findJoinPositions(int probePosition, Page probe, int probeGeometryChannel, Optional probePartitionChannel) { return emptyAddresses; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexFactory.java index afd4138121c95..f824e8dd25337 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexFactory.java @@ -138,7 +138,7 @@ public synchronized void probeOperatorFinished() /** * Called by {@link SpatialIndexBuilderOperator} to provide a * {@link Supplier} of spatial indexes for {@link SpatialJoinOperator}s to use. - * + *

* Returns a Future that completes once all the {@link SpatialJoinOperator}s have completed. */ public ListenableFuture lendPagesSpatialIndex(Supplier pagesSpatialIndex) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexSupplier.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexSupplier.java index 08e6a600fed78..e9fd87b57c56a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesSpatialIndexSupplier.java @@ -13,8 +13,13 @@ */ package com.facebook.presto.operator; +import com.esri.core.geometry.Geometry; +import com.esri.core.geometry.GeometryCursor; +import com.esri.core.geometry.Operator; +import com.esri.core.geometry.OperatorFactoryLocal; import com.esri.core.geometry.ogc.OGCGeometry; import com.facebook.presto.Session; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.operator.PagesRTreeIndex.GeometryWithPosition; import com.facebook.presto.operator.SpatialIndexBuilderOperator.SpatialPredicate; import com.facebook.presto.spi.block.Block; @@ -30,6 +35,7 @@ import org.openjdk.jol.info.ClassLayout; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Supplier; @@ -38,8 +44,10 @@ import static com.facebook.presto.operator.SyntheticAddress.decodePosition; import static com.facebook.presto.operator.SyntheticAddress.decodeSliceIndex; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.google.common.base.Verify.verify; import static io.airlift.units.DataSize.Unit.BYTE; +import static java.lang.Math.toIntExact; public class PagesSpatialIndexSupplier implements Supplier @@ -58,6 +66,7 @@ public class PagesSpatialIndexSupplier private final SpatialPredicate spatialRelationshipTest; private final Optional filterFunctionFactory; private final STRtree rtree; + private final Map partitions; private final long memorySizeInBytes; public PagesSpatialIndexSupplier( @@ -68,8 +77,10 @@ public PagesSpatialIndexSupplier( List> channels, int geometryChannel, Optional radiusChannel, + Optional partitionChannel, SpatialPredicate spatialRelationshipTest, - Optional filterFunctionFactory) + Optional filterFunctionFactory, + Map partitions) { this.session = session; this.addresses = addresses; @@ -78,16 +89,18 @@ public PagesSpatialIndexSupplier( this.channels = channels; this.spatialRelationshipTest = spatialRelationshipTest; this.filterFunctionFactory = filterFunctionFactory; + this.partitions = partitions; - this.rtree = buildRTree(addresses, channels, geometryChannel, radiusChannel); + this.rtree = buildRTree(addresses, channels, geometryChannel, radiusChannel, partitionChannel); this.radiusChannel = radiusChannel; this.memorySizeInBytes = INSTANCE_SIZE + (rtree.isEmpty() ? 0 : STRTREE_INSTANCE_SIZE + computeMemorySizeInBytes(rtree.getRoot())); } - private static STRtree buildRTree(LongArrayList addresses, List> channels, int geometryChannel, Optional radiusChannel) + private static STRtree buildRTree(LongArrayList addresses, List> channels, int geometryChannel, Optional radiusChannel, Optional partitionChannel) { STRtree rtree = new STRtree(); + Operator relateOperator = OperatorFactoryLocal.getInstance().getOperator(Operator.Type.Relate); for (int position = 0; position < addresses.size(); position++) { long pageAddress = addresses.getLong(position); @@ -112,7 +125,18 @@ private static STRtree buildRTree(LongArrayList addresses, List> cha continue; } - rtree.insert(getEnvelope(ogcGeometry, radius), new GeometryWithPosition(ogcGeometry, position)); + if (!radiusChannel.isPresent()) { + // If radiusChannel is supplied, this is a distance query, for which our acceleration won't help. + accelerateGeometry(ogcGeometry, relateOperator); + } + + int partition = -1; + if (partitionChannel.isPresent()) { + Block partitionBlock = channels.get(partitionChannel.get()).get(blockIndex); + partition = toIntExact(INTEGER.getLong(partitionBlock, blockPosition)); + } + + rtree.insert(getEnvelope(ogcGeometry, radius), new GeometryWithPosition(ogcGeometry, partition, position)); } rtree.build(); @@ -140,6 +164,19 @@ private long computeMemorySizeInBytes(ItemBoundable item) return ENVELOPE_INSTANCE_SIZE + ((GeometryWithPosition) item.getItem()).getEstimatedMemorySizeInBytes(); } + private static void accelerateGeometry(OGCGeometry ogcGeometry, Operator relateOperator) + { + // Recurse into GeometryCollections + GeometryCursor cursor = ogcGeometry.getEsriGeometryCursor(); + while (true) { + com.esri.core.geometry.Geometry esriGeometry = cursor.next(); + if (esriGeometry == null) { + break; + } + relateOperator.accelerateGeometry(esriGeometry, null, Geometry.GeometryAccelerationDegree.enumMild); + } + } + // doesn't include memory used by channels and addresses which are shared with PagesIndex public DataSize getEstimatedSize() { @@ -152,6 +189,6 @@ public PagesSpatialIndex get() if (rtree.isEmpty()) { return EMPTY_INDEX; } - return new PagesRTreeIndex(session, addresses, types, outputChannels, channels, rtree, radiusChannel, spatialRelationshipTest, filterFunctionFactory); + return new PagesRTreeIndex(session, addresses, types, outputChannels, channels, rtree, radiusChannel, spatialRelationshipTest, filterFunctionFactory, partitions); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementation.java b/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementation.java index 70b1e17910fac..9096469172c80 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementation.java @@ -18,5 +18,6 @@ public interface ParametricImplementation { Signature getSignature(); + boolean hasSpecializedTypeParameters(); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementationsGroup.java b/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementationsGroup.java index 77827e95e62c8..08cb36ad7435d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementationsGroup.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ParametricImplementationsGroup.java @@ -29,6 +29,7 @@ /** * This class represents set of three collections representing implementations of operators * similar to partial template specialization from C++ allowing more optimized implementations to be provided for specific types. + * * @param type of implementation details */ public class ParametricImplementationsGroup diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PartitionedLookupSourceFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/PartitionedLookupSourceFactory.java index 3ff1d0c9bf490..7a4073d17b8c0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PartitionedLookupSourceFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PartitionedLookupSourceFactory.java @@ -48,6 +48,8 @@ import static com.google.common.base.Verify.verify; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; +import static com.google.common.util.concurrent.Futures.transform; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; @@ -149,6 +151,7 @@ public ListenableFuture createLookupSourceProvider() { lock.writeLock().lock(); try { + checkState(!destroyed.isDone(), "already destroyed"); if (lookupSourceSupplier != null) { return immediateFuture(new SpillAwareLookupSourceProvider()); } @@ -163,6 +166,19 @@ public ListenableFuture createLookupSourceProvider() } @Override + public ListenableFuture whenBuildFinishes() + { + return transform( + this.createLookupSourceProvider(), + lookupSourceProvider -> { + // Close the lookupSourceProvider we just created. + // The only reason we created it is to wait until lookup source is ready. + lookupSourceProvider.close(); + return null; + }, + directExecutor()); + } + public ListenableFuture lendPartitionLookupSource(int partitionIndex, Supplier partitionLookupSource) { requireNonNull(partitionLookupSource, "partitionLookupSource is null"); @@ -192,7 +208,6 @@ public ListenableFuture lendPartitionLookupSource(int partitionIndex, Supplie return partitionsNoLongerNeeded; } - @Override public void setPartitionSpilledLookupSourceHandle(int partitionIndex, SpilledLookupSourceHandle spilledLookupSourceHandle) { requireNonNull(spilledLookupSourceHandle, "spilledLookupSourceHandle is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PartitionedOutputOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/PartitionedOutputOperator.java index 5dee012e6fa1f..0cc18722d0e5c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PartitionedOutputOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PartitionedOutputOperator.java @@ -190,7 +190,6 @@ public OperatorFactory duplicate() private final OperatorContext operatorContext; private final Function pagePreprocessor; private final PagePartitioner partitionFunction; - private final OutputBuffer outputBuffer; private final LocalMemoryContext systemMemoryContext; private final long partitionsInitialRetainedSize; private boolean finished; @@ -210,7 +209,6 @@ public PartitionedOutputOperator( { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.pagePreprocessor = requireNonNull(pagePreprocessor, "pagePreprocessor is null"); - this.outputBuffer = requireNonNull(outputBuffer, "outputBuffer is null"); this.partitionFunction = new PagePartitioner( partitionFunction, partitionChannels, @@ -277,7 +275,7 @@ public void addInput(Page page) page = pagePreprocessor.apply(page); partitionFunction.partitionPage(page); - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); // We use getSizeInBytes() here instead of getRetainedSizeInBytes() for an approximation of // the amount of memory used by the pageBuilders, because calculating the retained diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PipelineContext.java b/presto-main/src/main/java/com/facebook/presto/operator/PipelineContext.java index 644496a331e30..1079b61a41af8 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PipelineContext.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PipelineContext.java @@ -65,9 +65,11 @@ public class PipelineContext private final boolean inputPipeline; private final boolean outputPipeline; + private final boolean partitioned; private final List drivers = new CopyOnWriteArrayList<>(); + private final AtomicInteger totalSplits = new AtomicInteger(); private final AtomicInteger completedDrivers = new AtomicInteger(); private final AtomicReference executionStartTime = new AtomicReference<>(); @@ -79,7 +81,6 @@ public class PipelineContext private final AtomicLong totalScheduledTime = new AtomicLong(); private final AtomicLong totalCpuTime = new AtomicLong(); - private final AtomicLong totalUserTime = new AtomicLong(); private final AtomicLong totalBlockedTime = new AtomicLong(); private final CounterStat rawInputDataSize = new CounterStat(); @@ -97,11 +98,12 @@ public class PipelineContext private final MemoryTrackingContext pipelineMemoryContext; - public PipelineContext(int pipelineId, TaskContext taskContext, Executor notificationExecutor, ScheduledExecutorService yieldExecutor, MemoryTrackingContext pipelineMemoryContext, boolean inputPipeline, boolean outputPipeline) + public PipelineContext(int pipelineId, TaskContext taskContext, Executor notificationExecutor, ScheduledExecutorService yieldExecutor, MemoryTrackingContext pipelineMemoryContext, boolean inputPipeline, boolean outputPipeline, boolean partitioned) { this.pipelineId = pipelineId; this.inputPipeline = inputPipeline; this.outputPipeline = outputPipeline; + this.partitioned = partitioned; this.taskContext = requireNonNull(taskContext, "taskContext is null"); this.notificationExecutor = requireNonNull(notificationExecutor, "notificationExecutor is null"); this.yieldExecutor = requireNonNull(yieldExecutor, "yieldExecutor is null"); @@ -137,17 +139,16 @@ public boolean isOutputPipeline() public DriverContext addDriverContext() { - return addDriverContext(false, Lifespan.taskWide()); + return addDriverContext(Lifespan.taskWide()); } - public DriverContext addDriverContext(boolean partitioned, Lifespan lifespan) + public DriverContext addDriverContext(Lifespan lifespan) { DriverContext driverContext = new DriverContext( this, notificationExecutor, yieldExecutor, pipelineMemoryContext.newMemoryTrackingContext(), - partitioned, lifespan); drivers.add(driverContext); return driverContext; @@ -158,6 +159,12 @@ public Session getSession() return taskContext.getSession(); } + public void splitsAdded(int count) + { + checkArgument(count >= 0); + totalSplits.addAndGet(count); + } + public void driverFinished(DriverContext driverContext) { requireNonNull(driverContext, "driverContext is null"); @@ -178,7 +185,6 @@ public void driverFinished(DriverContext driverContext) totalScheduledTime.getAndAdd(driverStats.getTotalScheduledTime().roundTo(NANOSECONDS)); totalCpuTime.getAndAdd(driverStats.getTotalCpuTime().roundTo(NANOSECONDS)); - totalUserTime.getAndAdd(driverStats.getTotalUserTime().roundTo(NANOSECONDS)); totalBlockedTime.getAndAdd(driverStats.getTotalBlockedTime().roundTo(NANOSECONDS)); @@ -253,9 +259,9 @@ public void moreMemoryAvailable() drivers.forEach(DriverContext::moreMemoryAvailable); } - public boolean isVerboseStats() + public boolean isPerOperatorCpuTimerEnabled() { - return taskContext.isVerboseStats(); + return taskContext.isPerOperatorCpuTimerEnabled(); } public boolean isCpuTimerEnabled() @@ -312,7 +318,7 @@ public long getPhysicalWrittenDataSize() public PipelineStatus getPipelineStatus() { - return getPipelineStatus(drivers.iterator()); + return getPipelineStatus(drivers.iterator(), totalSplits.get(), completedDrivers.get(), partitioned); } public PipelineStats getPipelineStats() @@ -325,18 +331,18 @@ public PipelineStats getPipelineStats() lastExecutionEndTime.compareAndSet(null, now); } + int completedDrivers = this.completedDrivers.get(); List driverContexts = ImmutableList.copyOf(this.drivers); - PipelineStatus pipelineStatus = getPipelineStatus(driverContexts.iterator()); + int totalSplits = this.totalSplits.get(); + PipelineStatus pipelineStatus = getPipelineStatus(driverContexts.iterator(), totalSplits, completedDrivers, partitioned); - int totalDrivers = completedDrivers.get() + driverContexts.size(); - int completedDrivers = this.completedDrivers.get(); + int totalDrivers = completedDrivers + driverContexts.size(); Distribution queuedTime = new Distribution(this.queuedTime); Distribution elapsedTime = new Distribution(this.elapsedTime); long totalScheduledTime = this.totalScheduledTime.get(); long totalCpuTime = this.totalCpuTime.get(); - long totalUserTime = this.totalUserTime.get(); long totalBlockedTime = this.totalBlockedTime.get(); long rawInputDataSize = this.rawInputDataSize.getTotalCount(); @@ -362,7 +368,6 @@ public PipelineStats getPipelineStats() totalScheduledTime += driverStats.getTotalScheduledTime().roundTo(NANOSECONDS); totalCpuTime += driverStats.getTotalCpuTime().roundTo(NANOSECONDS); - totalUserTime += driverStats.getTotalUserTime().roundTo(NANOSECONDS); totalBlockedTime += driverStats.getTotalBlockedTime().roundTo(NANOSECONDS); List operators = ImmutableList.copyOf(transform(driverContext.getOperatorContexts(), OperatorContext::getOperatorStats)); @@ -431,7 +436,6 @@ public PipelineStats getPipelineStats() new Duration(totalScheduledTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalCpuTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(totalUserTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalBlockedTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), fullyBlocked, blockedReasons, @@ -478,32 +482,43 @@ public MemoryTrackingContext getPipelineMemoryContext() return pipelineMemoryContext; } - private static PipelineStatus getPipelineStatus(Iterator driverContextsIterator) + private static PipelineStatus getPipelineStatus(Iterator driverContextsIterator, int totalSplits, int completedDrivers, boolean partitioned) { - int queuedDrivers = 0; int runningDrivers = 0; int blockedDrivers = 0; - int queuedPartitionedDrivers = 0; - int runningPartitionedDrivers = 0; + // When a split for a partitioned pipeline is delivered to a worker, + // conceptually, the worker would have an additional driver. + // The queuedDrivers field in PipelineStatus is supposed to represent this. + // However, due to implementation details of SqlTaskExecution, it may defer instantiation of drivers. + // + // physically queued drivers: actual number of instantiated drivers whose execution hasn't started + // conceptually queued drivers: includes assigned splits that haven't been turned into a driver + int physicallyQueuedDrivers = 0; while (driverContextsIterator.hasNext()) { DriverContext driverContext = driverContextsIterator.next(); if (!driverContext.isExecutionStarted()) { - queuedDrivers++; - if (driverContext.isPartitioned()) { - queuedPartitionedDrivers++; - } + physicallyQueuedDrivers++; } else if (driverContext.isFullyBlocked()) { blockedDrivers++; } else { runningDrivers++; - if (driverContext.isPartitioned()) { - runningPartitionedDrivers++; - } } } - return new PipelineStatus(queuedDrivers, runningDrivers, blockedDrivers, queuedPartitionedDrivers, runningPartitionedDrivers); + int queuedDrivers; + if (partitioned) { + queuedDrivers = totalSplits - runningDrivers - blockedDrivers - completedDrivers; + if (queuedDrivers < 0) { + // It is possible to observe negative here because inputs to the above expression was not taken in a snapshot. + queuedDrivers = 0; + } + } + else { + queuedDrivers = physicallyQueuedDrivers; + } + + return new PipelineStatus(queuedDrivers, runningDrivers, blockedDrivers, partitioned ? queuedDrivers : 0, partitioned ? runningDrivers : 0); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PipelineStats.java b/presto-main/src/main/java/com/facebook/presto/operator/PipelineStats.java index 39c0823fc2f92..2d54eaf9fe09f 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/PipelineStats.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/PipelineStats.java @@ -61,7 +61,6 @@ public class PipelineStats private final Duration totalScheduledTime; private final Duration totalCpuTime; - private final Duration totalUserTime; private final Duration totalBlockedTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -108,7 +107,6 @@ public PipelineStats( @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("totalCpuTime") Duration totalCpuTime, - @JsonProperty("totalUserTime") Duration totalUserTime, @JsonProperty("totalBlockedTime") Duration totalBlockedTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @@ -160,7 +158,6 @@ public PipelineStats( this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); - this.totalUserTime = requireNonNull(totalUserTime, "totalUserTime is null"); this.totalBlockedTime = requireNonNull(totalBlockedTime, "totalBlockedTime is null"); this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -306,12 +303,6 @@ public Duration getTotalCpuTime() return totalCpuTime; } - @JsonProperty - public Duration getTotalUserTime() - { - return totalUserTime; - } - @JsonProperty public Duration getTotalBlockedTime() { @@ -407,7 +398,6 @@ public PipelineStats summarize() elapsedTime, totalScheduledTime, totalCpuTime, - totalUserTime, totalBlockedTime, fullyBlocked, blockedReasons, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ReferenceCount.java b/presto-main/src/main/java/com/facebook/presto/operator/ReferenceCount.java index f88ba30067dc7..d0168daf345fd 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ReferenceCount.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ReferenceCount.java @@ -28,12 +28,15 @@ public class ReferenceCount private final SettableFuture freeFuture = SettableFuture.create(); @GuardedBy("this") - private int count = 1; + private int count; public ReferenceCount(int initialCount) { - checkArgument(initialCount >= 1, "initialCount must be at least 1"); + checkArgument(initialCount >= 0, "initialCount must not be negative, got %s", initialCount); count = initialCount; + if (initialCount == 0) { + freeFuture.set(null); + } } public ListenableFuture getFreeFuture() diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ScanFilterAndProjectOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/ScanFilterAndProjectOperator.java index 6de43cc7cd196..1181c1479d4e6 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ScanFilterAndProjectOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ScanFilterAndProjectOperator.java @@ -19,7 +19,6 @@ import com.facebook.presto.operator.project.CursorProcessorOutput; import com.facebook.presto.operator.project.MergingPageOutput; import com.facebook.presto.operator.project.PageProcessor; -import com.facebook.presto.operator.project.PageProcessorOutput; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorPageSource; import com.facebook.presto.spi.Page; @@ -27,6 +26,8 @@ import com.facebook.presto.spi.RecordCursor; import com.facebook.presto.spi.RecordPageSource; import com.facebook.presto.spi.UpdatablePageSource; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.LazyBlock; import com.facebook.presto.spi.type.Type; import com.facebook.presto.split.EmptySplit; import com.facebook.presto.split.EmptySplitPageSource; @@ -40,11 +41,13 @@ import java.io.Closeable; import java.io.IOException; import java.io.UncheckedIOException; +import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.google.common.base.Preconditions.checkState; import static io.airlift.concurrent.MoreFutures.toListenableFuture; import static java.util.Objects.requireNonNull; @@ -60,7 +63,8 @@ public class ScanFilterAndProjectOperator private final CursorProcessor cursorProcessor; private final PageProcessor pageProcessor; private final LocalMemoryContext pageSourceMemoryContext; - private final LocalMemoryContext pageBuilderMemoryContext; + private final LocalMemoryContext pageProcessorMemoryContext; + private final LocalMemoryContext outputMemoryContext; private final SettableFuture blocked = SettableFuture.create(); private final MergingPageOutput mergingOutput; @@ -91,7 +95,8 @@ protected ScanFilterAndProjectOperator( this.pageSourceProvider = requireNonNull(pageSourceProvider, "pageSourceProvider is null"); this.columns = ImmutableList.copyOf(requireNonNull(columns, "columns is null")); this.pageSourceMemoryContext = operatorContext.newLocalSystemMemoryContext(ScanFilterAndProjectOperator.class.getSimpleName()); - this.pageBuilderMemoryContext = operatorContext.newLocalSystemMemoryContext(ScanFilterAndProjectOperator.class.getSimpleName()); + this.pageProcessorMemoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(ScanFilterAndProjectOperator.class.getSimpleName()); + this.outputMemoryContext = operatorContext.newLocalSystemMemoryContext(ScanFilterAndProjectOperator.class.getSimpleName()); this.mergingOutput = requireNonNull(mergingOutput, "mergingOutput is null"); this.pageBuilder = new PageBuilder(ImmutableList.copyOf(requireNonNull(types, "types is null"))); @@ -239,7 +244,9 @@ private Page processColumnSource() long bytesProcessed = cursor.getCompletedBytes() - completedBytes; long elapsedNanos = cursor.getReadTimeNanos() - readTimeNanos; - operatorContext.recordGeneratedInput(bytesProcessed, output.getProcessedRows(), elapsedNanos); + operatorContext.recordRawInputWithTiming(bytesProcessed, elapsedNanos); + // TODO: derive better values for cursors + operatorContext.recordProcessedInput(bytesProcessed, output.getProcessedRows()); completedBytes = cursor.getCompletedBytes(); readTimeNanos = cursor.getReadTimeNanos(); if (output.isNoMoreRows()) { @@ -254,7 +261,7 @@ private Page processColumnSource() page = pageBuilder.build(); pageBuilder.reset(); } - pageBuilderMemoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); + outputMemoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); return page; } @@ -268,14 +275,16 @@ private Page processPageSource() pageSourceMemoryContext.setBytes(pageSource.getSystemMemoryUsage()); if (page != null) { + page = recordProcessedInput(page); + // update operator stats long endCompletedBytes = pageSource.getCompletedBytes(); long endReadTimeNanos = pageSource.getReadTimeNanos(); - operatorContext.recordGeneratedInput(endCompletedBytes - completedBytes, page.getPositionCount(), endReadTimeNanos - readTimeNanos); + operatorContext.recordRawInputWithTiming(endCompletedBytes - completedBytes, endReadTimeNanos - readTimeNanos); completedBytes = endCompletedBytes; readTimeNanos = endReadTimeNanos; - PageProcessorOutput output = pageProcessor.process(operatorContext.getSession().toConnectorSession(), yieldSignal, page); + Iterator> output = pageProcessor.process(operatorContext.getSession().toConnectorSession(), yieldSignal, pageProcessorMemoryContext, page); mergingOutput.addInput(output); } @@ -285,10 +294,33 @@ private Page processPageSource() } Page result = mergingOutput.getOutput(); - pageBuilderMemoryContext.setBytes(mergingOutput.getRetainedSizeInBytes()); + outputMemoryContext.setBytes(mergingOutput.getRetainedSizeInBytes() + pageProcessorMemoryContext.getBytes()); return result; } + private Page recordProcessedInput(Page page) + { + operatorContext.recordProcessedInput(0, page.getPositionCount()); + // account processed bytes from lazy blocks only when they are loaded + Block[] blocks = new Block[page.getChannelCount()]; + for (int i = 0; i < page.getChannelCount(); ++i) { + Block block = page.getBlock(i); + if (block instanceof LazyBlock) { + LazyBlock delegateLazyBlock = (LazyBlock) block; + blocks[i] = new LazyBlock(page.getPositionCount(), lazyBlock -> { + Block loadedBlock = delegateLazyBlock.getLoadedBlock(); + operatorContext.recordProcessedInput(loadedBlock.getSizeInBytes(), 0L); + lazyBlock.setBlock(loadedBlock); + }); + } + else { + operatorContext.recordProcessedInput(block.getSizeInBytes(), 0L); + blocks[i] = block; + } + } + return new Page(page.getPositionCount(), blocks); + } + public static class ScanFilterAndProjectOperatorFactory implements SourceOperatorFactory { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SetBuilderOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/SetBuilderOperator.java index bef4833272090..94afc8badfa74 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/SetBuilderOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/SetBuilderOperator.java @@ -170,7 +170,7 @@ public void finish() ChannelSet channelSet = channelSetBuilder.build(); setSupplier.setChannelSet(channelSet); - operatorContext.recordGeneratedOutput(channelSet.getEstimatedSizeInBytes(), channelSet.size()); + operatorContext.recordOutput(channelSet.getEstimatedSizeInBytes(), channelSet.size()); finished = true; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SharedLookupSourceFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/SharedLookupSourceFactory.java deleted file mode 100644 index 80a4b6deb9764..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/SharedLookupSourceFactory.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.presto.operator; - -import com.facebook.presto.spi.type.Type; -import com.facebook.presto.sql.planner.Symbol; -import com.google.common.util.concurrent.ListenableFuture; - -import javax.annotation.concurrent.ThreadSafe; - -import java.util.List; -import java.util.Map; -import java.util.OptionalInt; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -import static java.util.Objects.requireNonNull; - -/** - * LookupSourceFactory wrapper that enables sharing a LookupSourceFactory - * across multiple probe factories. - *

- * When LookupSourceFactory is shared across multiple probe factories, each of them - * will call destroy on the LookupSourceFactory when they are done with it. - * This leads to pre-mature destroy because other probes may still be actively using it. - * This class makes it possible to destroy the underlying LookupSourceFactroy - * in a coordinated fashion. - */ -@ThreadSafe -public class SharedLookupSourceFactory - implements LookupSourceFactory -{ - private final LookupSourceFactory delegate; - private final Runnable onDestroy; - - private final AtomicBoolean destroyed = new AtomicBoolean(false); - - public SharedLookupSourceFactory(LookupSourceFactory delegate, Runnable onDestroy) - { - this.delegate = requireNonNull(delegate, "delegate is null"); - this.onDestroy = requireNonNull(onDestroy, "onDestroy is null"); - } - - @Override - public void destroy() - { - if (destroyed.compareAndSet(false, true)) { - onDestroy.run(); - } - } - - @Override - public List getTypes() - { - return delegate.getTypes(); - } - - @Override - public List getOutputTypes() - { - return delegate.getOutputTypes(); - } - - @Override - public ListenableFuture createLookupSourceProvider() - { - return delegate.createLookupSourceProvider(); - } - - @Override - public int partitions() - { - return delegate.partitions(); - } - - @Override - public ListenableFuture>> finishProbeOperator(OptionalInt lookupJoinsCount) - { - return delegate.finishProbeOperator(lookupJoinsCount); - } - - @Override - public OuterPositionIterator getOuterPositionIterator() - { - return delegate.getOuterPositionIterator(); - } - - @Override - public Map getLayout() - { - return delegate.getLayout(); - } - - @Override - public void setTaskContext(TaskContext taskContext) - { - delegate.setTaskContext(taskContext); - } - - @Override - public ListenableFuture lendPartitionLookupSource(int partitionIndex, Supplier partitionLookupSource) - { - return delegate.lendPartitionLookupSource(partitionIndex, partitionLookupSource); - } - - @Override - public void setPartitionSpilledLookupSourceHandle(int partitionIndex, SpilledLookupSourceHandle spilledLookupSourceHandle) - { - delegate.setPartitionSpilledLookupSourceHandle(partitionIndex, spilledLookupSourceHandle); - } - - @Override - public ListenableFuture isDestroyed() - { - return delegate.isDestroyed(); - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SharedNestedLoopJoinPagesBridge.java b/presto-main/src/main/java/com/facebook/presto/operator/SharedNestedLoopJoinPagesBridge.java deleted file mode 100644 index fa58cef9e71de..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/SharedNestedLoopJoinPagesBridge.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.operator; - -import com.google.common.util.concurrent.ListenableFuture; - -import javax.annotation.concurrent.ThreadSafe; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static java.util.Objects.requireNonNull; - -@ThreadSafe -public final class SharedNestedLoopJoinPagesBridge - implements NestedLoopJoinPagesBridge -{ - private final NestedLoopJoinPagesBridge delegate; - private final Runnable onDestroy; - - private final AtomicBoolean destroyed = new AtomicBoolean(false); - - public SharedNestedLoopJoinPagesBridge(NestedLoopJoinPagesBridge delegate, Runnable onDestroy) - { - this.delegate = requireNonNull(delegate, "delegate is null"); - this.onDestroy = requireNonNull(onDestroy, "onDestroy is null"); - } - - @Override - public ListenableFuture getPagesFuture() - { - return delegate.getPagesFuture(); - } - - @Override - public ListenableFuture setPages(NestedLoopJoinPages nestedLoopJoinPages) - { - return delegate.setPages(nestedLoopJoinPages); - } - - @Override - public void destroy() - { - if (destroyed.compareAndSet(false, true)) { - onDestroy.run(); - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SpatialIndexBuilderOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/SpatialIndexBuilderOperator.java index f21b9bc6ffd2f..99e790de097a7 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/SpatialIndexBuilderOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/SpatialIndexBuilderOperator.java @@ -14,6 +14,8 @@ package com.facebook.presto.operator; import com.esri.core.geometry.ogc.OGCGeometry; +import com.facebook.presto.geospatial.KdbTreeUtils; +import com.facebook.presto.geospatial.Rectangle; import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.type.Type; @@ -22,7 +24,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; @@ -48,9 +52,11 @@ public static final class SpatialIndexBuilderOperatorFactory private final List outputChannels; private final int indexChannel; private final Optional radiusChannel; + private final Optional partitionChannel; private final SpatialPredicate spatialRelationshipTest; private final Optional filterFunctionFactory; private final PagesIndex.Factory pagesIndexFactory; + private final Map spatialPartitions = new HashMap<>(); private final int expectedPositions; @@ -63,7 +69,9 @@ public SpatialIndexBuilderOperatorFactory( List outputChannels, int indexChannel, Optional radiusChannel, + Optional partitionChannel, SpatialPredicate spatialRelationshipTest, + Optional kdbTreeJson, Optional filterFunctionFactory, int expectedPositions, PagesIndex.Factory pagesIndexFactory) @@ -79,10 +87,12 @@ public SpatialIndexBuilderOperatorFactory( this.indexChannel = indexChannel; this.radiusChannel = radiusChannel; + this.partitionChannel = requireNonNull(partitionChannel, "partitionChannel is null"); this.spatialRelationshipTest = spatialRelationshipTest; this.filterFunctionFactory = requireNonNull(filterFunctionFactory, "filterFunctionFactory is null"); this.pagesIndexFactory = pagesIndexFactory; this.expectedPositions = expectedPositions; + kdbTreeJson.ifPresent(json -> this.spatialPartitions.putAll(KdbTreeUtils.fromJson(json).getLeaves())); } public PagesSpatialIndexFactory getPagesSpatialIndexFactory() @@ -101,10 +111,12 @@ public SpatialIndexBuilderOperator createOperator(DriverContext driverContext) outputChannels, indexChannel, radiusChannel, + partitionChannel, spatialRelationshipTest, filterFunctionFactory, expectedPositions, - pagesIndexFactory); + pagesIndexFactory, + spatialPartitions); } @Override @@ -127,8 +139,10 @@ public OperatorFactory duplicate() private final List outputChannels; private final int indexChannel; private final Optional radiusChannel; + private final Optional partitionChannel; private final SpatialPredicate spatialRelationshipTest; private final Optional filterFunctionFactory; + private final Map partitions; private final PagesIndex index; private ListenableFuture indexNotNeeded; @@ -142,10 +156,12 @@ private SpatialIndexBuilderOperator( List outputChannels, int indexChannel, Optional radiusChannel, + Optional partitionChannel, SpatialPredicate spatialRelationshipTest, Optional filterFunctionFactory, int expectedPositions, - PagesIndex.Factory pagesIndexFactory) + PagesIndex.Factory pagesIndexFactory, + Map partitions) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.localUserMemoryContext = operatorContext.localUserMemoryContext(); @@ -158,6 +174,9 @@ private SpatialIndexBuilderOperator( this.outputChannels = requireNonNull(outputChannels, "outputChannels is null"); this.indexChannel = indexChannel; this.radiusChannel = radiusChannel; + this.partitionChannel = requireNonNull(partitionChannel, "partitionChannel is null"); + + this.partitions = requireNonNull(partitions, "partitions is null"); } @Override @@ -185,7 +204,7 @@ public void addInput(Page page) localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes()); } - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override @@ -211,7 +230,7 @@ public void finish() } finishing = true; - PagesSpatialIndexSupplier spatialIndex = index.createPagesSpatialIndex(operatorContext.getSession(), indexChannel, radiusChannel, spatialRelationshipTest, filterFunctionFactory, outputChannels); + PagesSpatialIndexSupplier spatialIndex = index.createPagesSpatialIndex(operatorContext.getSession(), indexChannel, radiusChannel, partitionChannel, spatialRelationshipTest, filterFunctionFactory, outputChannels, partitions); localUserMemoryContext.setBytes(index.getEstimatedSize().toBytes() + spatialIndex.getEstimatedSize().toBytes()); indexNotNeeded = pagesSpatialIndexFactory.lendPagesSpatialIndex(spatialIndex); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SpatialJoinOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/SpatialJoinOperator.java index 74997e467573d..e2d2c95da2619 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/SpatialJoinOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/SpatialJoinOperator.java @@ -18,22 +18,24 @@ import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.type.Type; -import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.PlanNodeId; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; import javax.annotation.Nullable; import java.util.List; +import java.util.Optional; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; +import static com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type.INNER; +import static com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type.LEFT; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static io.airlift.concurrent.MoreFutures.getDone; import static io.airlift.slice.SizeOf.sizeOf; +import static java.util.Objects.requireNonNull; public class SpatialJoinOperator implements Operator @@ -43,10 +45,11 @@ public static final class SpatialJoinOperatorFactory { private final int operatorId; private final PlanNodeId planNodeId; - private final JoinNode.Type joinType; + private final SpatialJoinNode.Type joinType; private final List probeTypes; private final List probeOutputChannels; private final int probeGeometryChannel; + private final Optional partitionChannel; private final PagesSpatialIndexFactory pagesSpatialIndexFactory; private boolean closed; @@ -54,10 +57,11 @@ public static final class SpatialJoinOperatorFactory public SpatialJoinOperatorFactory( int operatorId, PlanNodeId planNodeId, - JoinNode.Type joinType, + SpatialJoinNode.Type joinType, List probeTypes, List probeOutputChannels, int probeGeometryChannel, + Optional partitionChannel, PagesSpatialIndexFactory pagesSpatialIndexFactory) { checkArgument(joinType == INNER || joinType == LEFT, "unsupported join type: %s", joinType); @@ -67,6 +71,7 @@ public SpatialJoinOperatorFactory( this.probeTypes = ImmutableList.copyOf(probeTypes); this.probeOutputChannels = ImmutableList.copyOf(probeOutputChannels); this.probeGeometryChannel = probeGeometryChannel; + this.partitionChannel = requireNonNull(partitionChannel, "partitionChannel is null"); this.pagesSpatialIndexFactory = pagesSpatialIndexFactory; } @@ -84,6 +89,7 @@ public Operator createOperator(DriverContext driverContext) probeTypes, probeOutputChannels, probeGeometryChannel, + partitionChannel, pagesSpatialIndexFactory); } @@ -101,16 +107,17 @@ public void noMoreOperators() @Override public OperatorFactory duplicate() { - return new SpatialJoinOperatorFactory(operatorId, planNodeId, joinType, probeTypes, probeOutputChannels, probeGeometryChannel, pagesSpatialIndexFactory); + return new SpatialJoinOperatorFactory(operatorId, planNodeId, joinType, probeTypes, probeOutputChannels, probeGeometryChannel, partitionChannel, pagesSpatialIndexFactory); } } private final OperatorContext operatorContext; private final LocalMemoryContext localUserMemoryContext; - private final JoinNode.Type joinType; + private final SpatialJoinNode.Type joinType; private final List probeTypes; private final List probeOutputChannels; private final int probeGeometryChannel; + private final Optional partitionChannel; private final PagesSpatialIndexFactory pagesSpatialIndexFactory; private ListenableFuture pagesSpatialIndexFuture; @@ -131,10 +138,11 @@ public OperatorFactory duplicate() public SpatialJoinOperator( OperatorContext operatorContext, - JoinNode.Type joinType, + SpatialJoinNode.Type joinType, List probeTypes, List probeOutputChannels, int probeGeometryChannel, + Optional partitionChannel, PagesSpatialIndexFactory pagesSpatialIndexFactory) { this.operatorContext = operatorContext; @@ -143,6 +151,7 @@ public SpatialJoinOperator( this.probeTypes = ImmutableList.copyOf(probeTypes); this.probeOutputChannels = ImmutableList.copyOf(probeOutputChannels); this.probeGeometryChannel = probeGeometryChannel; + this.partitionChannel = requireNonNull(partitionChannel, "partitionChannel is null"); this.pagesSpatialIndexFactory = pagesSpatialIndexFactory; this.pagesSpatialIndexFuture = pagesSpatialIndexFactory.createPagesSpatialIndex(); this.pageBuilder = new PageBuilder(ImmutableList.builder() @@ -212,7 +221,7 @@ private void processProbe() DriverYieldSignal yieldSignal = operatorContext.getDriverContext().getYieldSignal(); while (probePosition < probe.getPositionCount()) { if (joinPositions == null) { - joinPositions = pagesSpatialIndex.findJoinPositions(probePosition, probe, probeGeometryChannel); + joinPositions = pagesSpatialIndex.findJoinPositions(probePosition, probe, probeGeometryChannel, partitionChannel); localUserMemoryContext.setBytes(sizeOf(joinPositions)); nextJoinPositionIndex = 0; matchFound = false; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/StandardJoinFilterFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/StandardJoinFilterFunction.java index 57072896f4dd3..423c574b24493 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/StandardJoinFilterFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/StandardJoinFilterFunction.java @@ -14,7 +14,6 @@ package com.facebook.presto.operator; import com.facebook.presto.spi.Page; -import com.facebook.presto.spi.block.Block; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.longs.LongArrayList; @@ -33,33 +32,20 @@ public class StandardJoinFilterFunction private final LongArrayList addresses; private final List pages; - public StandardJoinFilterFunction(InternalJoinFilterFunction filterFunction, LongArrayList addresses, List> channels) + public StandardJoinFilterFunction(InternalJoinFilterFunction filterFunction, LongArrayList addresses, List pages) { this.filterFunction = requireNonNull(filterFunction, "filterFunction can not be null"); this.addresses = requireNonNull(addresses, "addresses is null"); - - requireNonNull(channels, "channels can not be null"); - ImmutableList.Builder pagesBuilder = ImmutableList.builder(); - if (!channels.isEmpty()) { - int pagesCount = channels.get(0).size(); - for (int pageIndex = 0; pageIndex < pagesCount; ++pageIndex) { - Block[] blocks = new Block[channels.size()]; - for (int channelIndex = 0; channelIndex < channels.size(); ++channelIndex) { - blocks[channelIndex] = channels.get(channelIndex).get(pageIndex); - } - pagesBuilder.add(new Page(blocks)); - } - } - this.pages = pagesBuilder.build(); + this.pages = ImmutableList.copyOf(requireNonNull(pages, "pages is null")); } @Override public boolean filter(int leftPosition, int rightPosition, Page rightPage) { long pageAddress = addresses.getLong(leftPosition); - int blockIndex = decodeSliceIndex(pageAddress); - int blockPosition = decodePosition(pageAddress); + int pageIndex = decodeSliceIndex(pageAddress); + int pagePosition = decodePosition(pageAddress); - return filterFunction.filter(blockPosition, pages.isEmpty() ? EMPTY_PAGE : pages.get(blockIndex), rightPosition, rightPage); + return filterFunction.filter(pagePosition, pages.isEmpty() ? EMPTY_PAGE : pages.get(pageIndex), rightPosition, rightPage); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TableFinishInfo.java b/presto-main/src/main/java/com/facebook/presto/operator/TableFinishInfo.java index 49766cb89a965..ea927160cb45b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TableFinishInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TableFinishInfo.java @@ -20,12 +20,14 @@ import com.fasterxml.jackson.databind.JsonNode; import io.airlift.json.JsonCodec; import io.airlift.units.DataSize; +import io.airlift.units.Duration; import java.util.Optional; import static io.airlift.json.JsonCodec.jsonCodec; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.lang.Math.toIntExact; +import static java.util.Objects.requireNonNull; public class TableFinishInfo implements OperatorInfo @@ -34,32 +36,43 @@ public class TableFinishInfo private static final JsonCodec INFO_CODEC = jsonCodec(Object.class); private static final JsonCodec JSON_NODE_CODEC = jsonCodec(JsonNode.class); - private String connectorOutputMetadata; - private boolean jsonLengthLimitExceeded; + private final String connectorOutputMetadata; + private final boolean jsonLengthLimitExceeded; + private final Duration statisticsWallTime; + private final Duration statisticsCpuTime; - public TableFinishInfo(Optional metadata) + public TableFinishInfo(Optional metadata, Duration statisticsWallTime, Duration statisticsCpuTime) { + String connectorOutputMetadata = null; + boolean jsonLengthLimitExceeded = false; if (metadata.isPresent()) { Optional serializedMetadata = INFO_CODEC.toJsonWithLengthLimit(metadata.get().getInfo(), JSON_LENGTH_LIMIT); if (!serializedMetadata.isPresent()) { + connectorOutputMetadata = null; jsonLengthLimitExceeded = true; } else { connectorOutputMetadata = serializedMetadata.get(); + jsonLengthLimitExceeded = false; } } + this.connectorOutputMetadata = connectorOutputMetadata; + this.jsonLengthLimitExceeded = jsonLengthLimitExceeded; + this.statisticsWallTime = requireNonNull(statisticsWallTime, "statisticsWallTime is null"); + this.statisticsCpuTime = requireNonNull(statisticsCpuTime, "statisticsCpuTime is null"); } @JsonCreator - public TableFinishInfo(@JsonProperty("connectorOutputMetadata") JsonNode connectorOutputMetadata) + public TableFinishInfo( + @JsonProperty("connectorOutputMetadata") JsonNode connectorOutputMetadata, + @JsonProperty("jsonLengthLimitExceeded") boolean jsonLengthLimitExceeded, + @JsonProperty("statisticsWallTime") Duration statisticsWallTime, + @JsonProperty("statisticsCpuTime") Duration statisticsCpuTime) { this.connectorOutputMetadata = JSON_NODE_CODEC.toJson(connectorOutputMetadata); - } - - @Override - public boolean isFinal() - { - return true; + this.jsonLengthLimitExceeded = jsonLengthLimitExceeded; + this.statisticsWallTime = requireNonNull(statisticsWallTime, "statisticsWallTime is null"); + this.statisticsCpuTime = requireNonNull(statisticsCpuTime, "statisticsCpuTime is null"); } @JsonProperty @@ -74,4 +87,22 @@ public boolean isJsonLengthLimitExceeded() { return jsonLengthLimitExceeded; } + + @JsonProperty + public Duration getStatisticsWallTime() + { + return statisticsWallTime; + } + + @JsonProperty + public Duration getStatisticsCpuTime() + { + return statisticsCpuTime; + } + + @Override + public boolean isFinal() + { + return true; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TableFinishOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/TableFinishOperator.java index c7da6ec87bf81..0c53b800cf7ec 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TableFinishOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TableFinishOperator.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.operator; +import com.facebook.presto.Session; +import com.facebook.presto.operator.OperationTimer.OperationTiming; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; @@ -21,14 +23,17 @@ import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.StatisticAggregationsDescriptor; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.slice.Slice; +import io.airlift.units.Duration; import java.util.Collection; import java.util.List; import java.util.Optional; +import static com.facebook.presto.SystemSessionProperties.isStatisticsCpuTimerEnabled; import static com.facebook.presto.operator.TableWriterOperator.FRAGMENT_CHANNEL; import static com.facebook.presto.operator.TableWriterOperator.ROW_COUNT_CHANNEL; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -36,6 +41,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; public class TableFinishOperator implements Operator @@ -50,6 +56,7 @@ public static class TableFinishOperatorFactory private final TableFinisher tableFinisher; private final OperatorFactory statisticsAggregationOperatorFactory; private final StatisticAggregationsDescriptor descriptor; + private final Session session; private boolean closed; public TableFinishOperatorFactory( @@ -57,13 +64,15 @@ public TableFinishOperatorFactory( PlanNodeId planNodeId, TableFinisher tableFinisher, OperatorFactory statisticsAggregationOperatorFactory, - StatisticAggregationsDescriptor descriptor) + StatisticAggregationsDescriptor descriptor, + Session session) { this.operatorId = operatorId; this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); - this.tableFinisher = requireNonNull(tableFinisher, "tableCommitter is null"); + this.tableFinisher = requireNonNull(tableFinisher, "tableFinisher is null"); this.statisticsAggregationOperatorFactory = requireNonNull(statisticsAggregationOperatorFactory, "statisticsAggregationOperatorFactory is null"); this.descriptor = requireNonNull(descriptor, "descriptor is null"); + this.session = requireNonNull(session, "session is null"); } @Override @@ -71,7 +80,9 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext context = driverContext.addOperatorContext(operatorId, planNodeId, TableFinishOperator.class.getSimpleName()); - return new TableFinishOperator(context, tableFinisher, statisticsAggregationOperatorFactory.createOperator(driverContext), descriptor); + Operator statisticsAggregationOperator = statisticsAggregationOperatorFactory.createOperator(driverContext); + boolean statisticsCpuTimerEnabled = !(statisticsAggregationOperator instanceof DevNullOperator) && isStatisticsCpuTimerEnabled(session); + return new TableFinishOperator(context, tableFinisher, statisticsAggregationOperator, descriptor, statisticsCpuTimerEnabled); } @Override @@ -83,7 +94,7 @@ public void noMoreOperators() @Override public OperatorFactory duplicate() { - return new TableFinishOperatorFactory(operatorId, planNodeId, tableFinisher, statisticsAggregationOperatorFactory, descriptor); + return new TableFinishOperatorFactory(operatorId, planNodeId, tableFinisher, statisticsAggregationOperatorFactory, descriptor, session); } } @@ -103,18 +114,23 @@ private enum State private final ImmutableList.Builder fragmentBuilder = ImmutableList.builder(); private final ImmutableList.Builder computedStatisticsBuilder = ImmutableList.builder(); + private final OperationTiming statisticsTiming = new OperationTiming(); + private final boolean statisticsCpuTimerEnabled; + public TableFinishOperator( OperatorContext operatorContext, TableFinisher tableFinisher, Operator statisticsAggregationOperator, - StatisticAggregationsDescriptor descriptor) + StatisticAggregationsDescriptor descriptor, + boolean statisticsCpuTimerEnabled) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.tableFinisher = requireNonNull(tableFinisher, "tableCommitter is null"); this.statisticsAggregationOperator = requireNonNull(statisticsAggregationOperator, "statisticsAggregationOperator is null"); this.descriptor = requireNonNull(descriptor, "descriptor is null"); + this.statisticsCpuTimerEnabled = statisticsCpuTimerEnabled; - operatorContext.setInfoSupplier(() -> new TableFinishInfo(outputMetadata)); + operatorContext.setInfoSupplier(this::getInfo); } @Override @@ -126,7 +142,10 @@ public OperatorContext getOperatorContext() @Override public void finish() { + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); statisticsAggregationOperator.finish(); + timer.end(statisticsTiming); + if (state == State.RUNNING) { state = State.FINISHING; } @@ -174,8 +193,11 @@ public void addInput(Page page) } } - Optional statisticsPage = extractStatisticsRows(page); - statisticsPage.ifPresent(statisticsAggregationOperator::addInput); + extractStatisticsRows(page).ifPresent(statisticsPage -> { + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); + statisticsAggregationOperator.addInput(statisticsPage); + timer.end(statisticsTiming); + }); } private static Optional extractStatisticsRows(Page page) @@ -253,7 +275,11 @@ public Page getOutput() if (!statisticsAggregationOperator.isFinished()) { verify(statisticsAggregationOperator.isBlocked().isDone(), "aggregation operator should not be blocked"); + + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); Page page = statisticsAggregationOperator.getOutput(); + timer.end(statisticsTiming); + if (page == null) { return null; } @@ -297,6 +323,15 @@ private ComputedStatistics getComputedStatistics(Page page, int position) return statistics.build(); } + @VisibleForTesting + TableFinishInfo getInfo() + { + return new TableFinishInfo( + outputMetadata, + new Duration(statisticsTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(statisticsTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit()); + } + @Override public void close() throws Exception diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TableScanOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/TableScanOperator.java index 04fd529d4b2c5..e92f228362b73 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TableScanOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TableScanOperator.java @@ -247,7 +247,8 @@ public Page getOutput() // update operator stats long endCompletedBytes = source.getCompletedBytes(); long endReadTimeNanos = source.getReadTimeNanos(); - operatorContext.recordGeneratedInput(endCompletedBytes - completedBytes, page.getPositionCount(), endReadTimeNanos - readTimeNanos); + operatorContext.recordRawInputWithTiming(endCompletedBytes - completedBytes, endReadTimeNanos - readTimeNanos); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); completedBytes = endCompletedBytes; readTimeNanos = endReadTimeNanos; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TableWriterOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/TableWriterOperator.java index 2e606466ff007..27d7cdb13e01c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TableWriterOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TableWriterOperator.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.memory.context.LocalMemoryContext; +import com.facebook.presto.operator.OperationTimer.OperationTiming; import com.facebook.presto.spi.ConnectorPageSink; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; @@ -33,12 +34,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.slice.Slice; +import io.airlift.units.Duration; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; +import static com.facebook.presto.SystemSessionProperties.isStatisticsCpuTimerEnabled; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.facebook.presto.sql.planner.plan.TableWriterNode.CreateHandle; @@ -50,13 +53,14 @@ import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.airlift.concurrent.MoreFutures.toListenableFuture; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; public class TableWriterOperator implements Operator { public static final int ROW_COUNT_CHANNEL = 0; public static final int FRAGMENT_CHANNEL = 1; - private static final int WRITER_CHANNELS = 2; + public static final int STATS_START_CHANNEL = 2; public static class TableWriterOperatorFactory implements OperatorFactory @@ -96,7 +100,9 @@ public Operator createOperator(DriverContext driverContext) { checkState(!closed, "Factory is already closed"); OperatorContext context = driverContext.addOperatorContext(operatorId, planNodeId, TableWriterOperator.class.getSimpleName()); - return new TableWriterOperator(context, createPageSink(), columnChannels, statisticsAggregationOperatorFactory.createOperator(driverContext), types); + Operator statisticsAggregationOperator = statisticsAggregationOperatorFactory.createOperator(driverContext); + boolean statisticsCpuTimerEnabled = !(statisticsAggregationOperator instanceof DevNullOperator) && isStatisticsCpuTimerEnabled(session); + return new TableWriterOperator(context, createPageSink(), columnChannels, statisticsAggregationOperator, types, statisticsCpuTimerEnabled); } private ConnectorPageSink createPageSink() @@ -144,12 +150,16 @@ private enum State private boolean closed; private long writtenBytes; + private final OperationTiming statisticsTiming = new OperationTiming(); + private final boolean statisticsCpuTimerEnabled; + public TableWriterOperator( OperatorContext operatorContext, ConnectorPageSink pageSink, List columnChannels, Operator statisticAggregationOperator, - List types) + List types, + boolean statisticsCpuTimerEnabled) { this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); this.pageSinkMemoryContext = operatorContext.newLocalSystemMemoryContext(TableWriterOperator.class.getSimpleName()); @@ -158,6 +168,7 @@ public TableWriterOperator( this.operatorContext.setInfoSupplier(this::getInfo); this.statisticAggregationOperator = requireNonNull(statisticAggregationOperator, "statisticAggregationOperator is null"); this.types = ImmutableList.copyOf(requireNonNull(types, "types is null")); + this.statisticsCpuTimerEnabled = statisticsCpuTimerEnabled; } @Override @@ -170,7 +181,11 @@ public OperatorContext getOperatorContext() public void finish() { ListenableFuture currentlyBlocked = blocked; + + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); statisticAggregationOperator.finish(); + timer.end(statisticsTiming); + ListenableFuture blockedOnAggregation = statisticAggregationOperator.isBlocked(); ListenableFuture blockedOnFinish = NOT_BLOCKED; if (state == State.RUNNING) { @@ -214,7 +229,10 @@ public void addInput(Page page) blocks[outputChannel] = page.getBlock(columnChannels.get(outputChannel)); } + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); statisticAggregationOperator.addInput(page); + timer.end(statisticsTiming); + ListenableFuture blockedOnAggregation = statisticAggregationOperator.isBlocked(); CompletableFuture future = pageSink.appendPage(new Page(blocks)); updateMemoryUsage(); @@ -232,7 +250,10 @@ public Page getOutput() } if (!statisticAggregationOperator.isFinished()) { + OperationTimer timer = new OperationTimer(statisticsCpuTimerEnabled); Page aggregationOutput = statisticAggregationOperator.getOutput(); + timer.end(statisticsTiming); + if (aggregationOutput == null) { return null; } @@ -247,7 +268,7 @@ public Page getOutput() int positionCount = fragmentsPage.getPositionCount(); Block[] outputBlocks = new Block[types.size()]; for (int channel = 0; channel < types.size(); channel++) { - if (channel < WRITER_CHANNELS) { + if (channel < STATS_START_CHANNEL) { outputBlocks[channel] = fragmentsPage.getBlock(channel); } else { @@ -264,7 +285,7 @@ private Page createStatisticsPage(Page aggregationOutput) int positionCount = aggregationOutput.getPositionCount(); Block[] outputBlocks = new Block[types.size()]; for (int channel = 0; channel < types.size(); channel++) { - if (channel < WRITER_CHANNELS) { + if (channel < STATS_START_CHANNEL) { outputBlocks[channel] = RunLengthEncodedBlock.create(types.get(channel), null, positionCount); } else { @@ -340,18 +361,32 @@ Operator getStatisticAggregationOperator() @VisibleForTesting TableWriterInfo getInfo() { - return new TableWriterInfo(pageSinkPeakMemoryUsage.get()); + return new TableWriterInfo( + pageSinkPeakMemoryUsage.get(), + new Duration(statisticsTiming.getWallNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(statisticsTiming.getCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(pageSink.getValidationCpuNanos(), NANOSECONDS).convertToMostSuccinctTimeUnit()); } public static class TableWriterInfo implements Mergeable, OperatorInfo { private final long pageSinkPeakMemoryUsage; + private final Duration statisticsWallTime; + private final Duration statisticsCpuTime; + private final Duration validationCpuTime; @JsonCreator - public TableWriterInfo(@JsonProperty("pageSinkPeakMemoryUsage") long pageSinkPeakMemoryUsage) + public TableWriterInfo( + @JsonProperty("pageSinkPeakMemoryUsage") long pageSinkPeakMemoryUsage, + @JsonProperty("statisticsWallTime") Duration statisticsWallTime, + @JsonProperty("statisticsCpuTime") Duration statisticsCpuTime, + @JsonProperty("validationCpuTime") Duration validationCpuTime) { this.pageSinkPeakMemoryUsage = pageSinkPeakMemoryUsage; + this.statisticsWallTime = requireNonNull(statisticsWallTime, "statisticsWallTime is null"); + this.statisticsCpuTime = requireNonNull(statisticsCpuTime, "statisticsCpuTime is null"); + this.validationCpuTime = requireNonNull(validationCpuTime, "validationCpuTime is null"); } @JsonProperty @@ -360,10 +395,32 @@ public long getPageSinkPeakMemoryUsage() return pageSinkPeakMemoryUsage; } + @JsonProperty + public Duration getStatisticsWallTime() + { + return statisticsWallTime; + } + + @JsonProperty + public Duration getStatisticsCpuTime() + { + return statisticsCpuTime; + } + + @JsonProperty + public Duration getValidationCpuTime() + { + return validationCpuTime; + } + @Override public TableWriterInfo mergeWith(TableWriterInfo other) { - return new TableWriterInfo(Math.max(pageSinkPeakMemoryUsage, other.pageSinkPeakMemoryUsage)); + return new TableWriterInfo( + Math.max(pageSinkPeakMemoryUsage, other.pageSinkPeakMemoryUsage), + new Duration(statisticsWallTime.getValue(NANOSECONDS) + other.statisticsWallTime.getValue(NANOSECONDS), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(statisticsCpuTime.getValue(NANOSECONDS) + other.statisticsCpuTime.getValue(NANOSECONDS), NANOSECONDS).convertToMostSuccinctTimeUnit(), + new Duration(validationCpuTime.getValue(NANOSECONDS) + other.validationCpuTime.getValue(NANOSECONDS), NANOSECONDS).convertToMostSuccinctTimeUnit()); } @Override @@ -377,6 +434,9 @@ public String toString() { return toStringHelper(this) .add("pageSinkPeakMemoryUsage", pageSinkPeakMemoryUsage) + .add("statisticsWallTime", statisticsWallTime) + .add("statisticsCpuTime", statisticsCpuTime) + .add("validationCpuTime", validationCpuTime) .toString(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TaskContext.java b/presto-main/src/main/java/com/facebook/presto/operator/TaskContext.java index 7152b2ca5dfd2..d0e42b29bbf6b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TaskContext.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TaskContext.java @@ -86,7 +86,7 @@ public class TaskContext private final List pipelineContexts = new CopyOnWriteArrayList<>(); - private final boolean verboseStats; + private final boolean perOperatorCpuTimerEnabled; private final boolean cpuTimerEnabled; private final OptionalInt totalPartitions; @@ -110,11 +110,11 @@ public static TaskContext createTaskContext( ScheduledExecutorService yieldExecutor, Session session, MemoryTrackingContext taskMemoryContext, - boolean verboseStats, + boolean perOperatorCpuTimerEnabled, boolean cpuTimerEnabled, OptionalInt totalPartitions) { - TaskContext taskContext = new TaskContext(queryContext, taskStateMachine, gcMonitor, notificationExecutor, yieldExecutor, session, taskMemoryContext, verboseStats, cpuTimerEnabled, totalPartitions); + TaskContext taskContext = new TaskContext(queryContext, taskStateMachine, gcMonitor, notificationExecutor, yieldExecutor, session, taskMemoryContext, perOperatorCpuTimerEnabled, cpuTimerEnabled, totalPartitions); taskContext.initialize(); return taskContext; } @@ -126,7 +126,7 @@ private TaskContext(QueryContext queryContext, ScheduledExecutorService yieldExecutor, Session session, MemoryTrackingContext taskMemoryContext, - boolean verboseStats, + boolean perOperatorCpuTimerEnabled, boolean cpuTimerEnabled, OptionalInt totalPartitions) { @@ -139,7 +139,7 @@ private TaskContext(QueryContext queryContext, this.taskMemoryContext = requireNonNull(taskMemoryContext, "taskMemoryContext is null"); // Initialize the local memory contexts with the LazyOutputBuffer tag as LazyOutputBuffer will do the local memory allocations taskMemoryContext.initializeLocalMemoryContexts(LazyOutputBuffer.class.getSimpleName()); - this.verboseStats = verboseStats; + this.perOperatorCpuTimerEnabled = perOperatorCpuTimerEnabled; this.cpuTimerEnabled = cpuTimerEnabled; this.totalPartitions = requireNonNull(totalPartitions, "totalPartitions is null"); } @@ -162,7 +162,7 @@ public OptionalInt getTotalPartitions() return totalPartitions; } - public PipelineContext addPipelineContext(int pipelineId, boolean inputPipeline, boolean outputPipeline) + public PipelineContext addPipelineContext(int pipelineId, boolean inputPipeline, boolean outputPipeline, boolean partitioned) { PipelineContext pipelineContext = new PipelineContext( pipelineId, @@ -171,7 +171,8 @@ public PipelineContext addPipelineContext(int pipelineId, boolean inputPipeline, yieldExecutor, taskMemoryContext.newMemoryTrackingContext(), inputPipeline, - outputPipeline); + outputPipeline, + partitioned); pipelineContexts.add(pipelineContext); return pipelineContext; } @@ -286,9 +287,9 @@ public void moreMemoryAvailable() pipelineContexts.forEach(PipelineContext::moreMemoryAvailable); } - public boolean isVerboseStats() + public boolean isPerOperatorCpuTimerEnabled() { - return verboseStats; + return perOperatorCpuTimerEnabled; } public boolean isCpuTimerEnabled() @@ -387,7 +388,6 @@ public TaskStats getTaskStats() long totalScheduledTime = 0; long totalCpuTime = 0; - long totalUserTime = 0; long totalBlockedTime = 0; long rawInputDataSize = 0; @@ -416,7 +416,6 @@ public TaskStats getTaskStats() totalScheduledTime += pipeline.getTotalScheduledTime().roundTo(NANOSECONDS); totalCpuTime += pipeline.getTotalCpuTime().roundTo(NANOSECONDS); - totalUserTime += pipeline.getTotalUserTime().roundTo(NANOSECONDS); totalBlockedTime += pipeline.getTotalBlockedTime().roundTo(NANOSECONDS); if (pipeline.isInputPipeline()) { @@ -494,7 +493,6 @@ public TaskStats getTaskStats() succinctBytes(taskMemoryContext.getSystemMemory()), new Duration(totalScheduledTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalCpuTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), - new Duration(totalUserTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), new Duration(totalBlockedTime, NANOSECONDS).convertToMostSuccinctTimeUnit(), fullyBlocked && (runningDrivers > 0 || runningPartitionedDrivers > 0), blockedReasons, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TaskOutputOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/TaskOutputOperator.java index 3ac85b565cb4f..9385a6aac01d5 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TaskOutputOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TaskOutputOperator.java @@ -147,7 +147,7 @@ public void addInput(Page page) .collect(toImmutableList()); outputBuffer.enqueue(serializedPages); - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/TaskStats.java b/presto-main/src/main/java/com/facebook/presto/operator/TaskStats.java index cba5793f5d6f0..88ce4423a5ae0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/TaskStats.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/TaskStats.java @@ -58,7 +58,6 @@ public class TaskStats private final Duration totalScheduledTime; private final Duration totalCpuTime; - private final Duration totalUserTime; private final Duration totalBlockedTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -102,7 +101,6 @@ public TaskStats(DateTime createTime, DateTime endTime) new Duration(0, MILLISECONDS), new Duration(0, MILLISECONDS), new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), false, ImmutableSet.of(), new DataSize(0, BYTE), @@ -142,7 +140,6 @@ public TaskStats( @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("totalCpuTime") Duration totalCpuTime, - @JsonProperty("totalUserTime") Duration totalUserTime, @JsonProperty("totalBlockedTime") Duration totalBlockedTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @@ -196,7 +193,6 @@ public TaskStats( this.totalScheduledTime = requireNonNull(totalScheduledTime, "totalScheduledTime is null"); this.totalCpuTime = requireNonNull(totalCpuTime, "totalCpuTime is null"); - this.totalUserTime = requireNonNull(totalUserTime, "totalUserTime is null"); this.totalBlockedTime = requireNonNull(totalBlockedTime, "totalBlockedTime is null"); this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -334,12 +330,6 @@ public Duration getTotalCpuTime() return totalCpuTime; } - @JsonProperty - public Duration getTotalUserTime() - { - return totalUserTime; - } - @JsonProperty public Duration getTotalBlockedTime() { @@ -453,7 +443,6 @@ public TaskStats summarize() systemMemoryReservation, totalScheduledTime, totalCpuTime, - totalUserTime, totalBlockedTime, fullyBlocked, blockedReasons, @@ -492,7 +481,6 @@ public TaskStats summarizeFinal() systemMemoryReservation, totalScheduledTime, totalCpuTime, - totalUserTime, totalBlockedTime, fullyBlocked, blockedReasons, diff --git a/presto-main/src/main/java/com/facebook/presto/operator/ValuesOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/ValuesOperator.java index c0df77fbd4136..9311e79c08930 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/ValuesOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/ValuesOperator.java @@ -113,7 +113,7 @@ public Page getOutput() } Page page = pages.next(); if (page != null) { - operatorContext.recordGeneratedInput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); } return page; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/WindowOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/WindowOperator.java index 3028534709f74..492eeeb95b434 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/WindowOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/WindowOperator.java @@ -474,51 +474,23 @@ private static int findGroupEnd(PagesIndex pagesIndex, PagesHashStrategy pagesHa static int findEndPosition(int startPosition, int endPosition, BiPredicate comparator) { checkArgument(startPosition >= 0, "startPosition must be greater or equal than zero: %s", startPosition); - checkArgument(endPosition > 0, "endPosition must be greater than zero: %s", endPosition); - checkArgument(startPosition < endPosition, "startPosition must be less than endPosition: %s < %s", startPosition, endPosition); + checkArgument(startPosition < endPosition, "startPosition (%s) must be less than endPosition (%s)", startPosition, endPosition); int left = startPosition; - int right = endPosition - 1; - for (int i = 0; i < endPosition - startPosition; i++) { - int distance = right - left; + int right = endPosition; - if (distance == 0) { - return right + 1; - } + while (left + 1 < right) { + int middle = (left + right) >>> 1; - if (distance == 1) { - if (comparator.test(left, right)) { - return right + 1; - } - return right; - } - - int mid = left + distance / 2; - if (comparator.test(left, mid)) { - // explore to the right - left = mid; + if (comparator.test(startPosition, middle)) { + left = middle; } else { - // explore to the left - right = mid; - } - } - - // hasn't managed to find a solution after N iteration. Probably the input is not sorted. Lets verify it. - for (int first = startPosition; first < endPosition; first++) { - boolean previousPairsWereEqual = true; - for (int second = first + 1; second < endPosition; second++) { - if (!comparator.test(first, second)) { - previousPairsWereEqual = false; - } - else if (!previousPairsWereEqual) { - throw new IllegalArgumentException("The input is not sorted"); - } + right = middle; } } - // the input is sorted, but the algorithm has still failed - throw new IllegalArgumentException("failed to find a group ending"); + return right; } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/Work.java b/presto-main/src/main/java/com/facebook/presto/operator/Work.java index b9637dc3b238b..40da0f2a88b3c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/Work.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/Work.java @@ -22,6 +22,7 @@ public interface Work /** * Call the method to do the work. * The caller can keep calling this method until it returns true (i.e., the work is done). + * * @return boolean to indicate if the work has finished. */ boolean process(); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessor.java b/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessor.java index 24f502339b9cb..e7c40697dd442 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessor.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessor.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.operator; +import com.google.common.collect.Iterators; import com.google.common.util.concurrent.ListenableFuture; import javax.annotation.concurrent.Immutable; @@ -20,14 +21,9 @@ import java.util.Comparator; import java.util.Iterator; import java.util.Optional; +import java.util.function.BooleanSupplier; import java.util.function.Function; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.BLOCKED; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.FINISHED; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.NEEDS_MORE_DATA; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.RESULT; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.YIELD; -import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; public interface WorkProcessor @@ -58,6 +54,16 @@ public interface WorkProcessor */ T getResult(); + /** + * Makes {@link WorkProcessor} yield when given {@code yieldSignal} is set. The processor is + * guaranteed to progress computations on subsequent {@link WorkProcessor#process()} calls + * even if {@code yieldSignal} is permanently on. + */ + default WorkProcessor yielding(BooleanSupplier yieldSignal) + { + return WorkProcessorUtils.yielding(this, yieldSignal); + } + default WorkProcessor flatMap(Function> mapper) { return WorkProcessorUtils.flatMap(this, mapper); @@ -77,14 +83,23 @@ default WorkProcessor flatTransform(Transformation> t return WorkProcessorUtils.flatTransform(this, transformation); } + /** + * Transforms {@link WorkProcessor} using {@link Transformation}. {@link Transformation} instance will be dereferenced immediately after + * {@link WorkProcessor} is exhausted. + */ default WorkProcessor transform(Transformation transformation) { return WorkProcessorUtils.transform(this, transformation); } + default WorkProcessor transformProcessor(Function, WorkProcessor> transformation) + { + return transformation.apply(this); + } + /** * Converts {@link WorkProcessor} into an {@link Iterator}. The iterator will throw {@link IllegalStateException} when underlying {@link WorkProcessor} - * yields or becomes blocked. + * yields or becomes blocked. {@link WorkProcessor} instance will be dereferenced immediately after iterator is finished. */ default Iterator iterator() { @@ -93,7 +108,7 @@ default Iterator iterator() /** * Converts {@link WorkProcessor} into an yielding {@link Iterator}. The iterator will throw {@link IllegalStateException} when underlying {@link WorkProcessor} - * becomes blocked. + * becomes blocked. {@link WorkProcessor} instance will be dereferenced immediately after iterator is exhausted. */ default Iterator> yieldingIterator() { @@ -105,6 +120,12 @@ static WorkProcessor flatten(WorkProcessor> processor) return WorkProcessorUtils.flatten(processor); } + @SafeVarargs + static WorkProcessor of(T... elements) + { + return fromIterator(Iterators.forArray(elements)); + } + static WorkProcessor fromIterable(Iterable iterable) { return WorkProcessorUtils.fromIterator(iterable.iterator()); @@ -115,6 +136,9 @@ static WorkProcessor fromIterator(Iterator iterator) return WorkProcessorUtils.fromIterator(iterator); } + /** + * Creates {@link WorkProcessor} from {@link Process}. {@link Process} instance will be dereferenced immediately after {@link WorkProcessor} is finished. + */ static WorkProcessor create(Process process) { return WorkProcessorUtils.create(process); @@ -133,40 +157,37 @@ interface Transformation * @param elementOptional an element to be transformed. Will be empty * when there are no more elements. In such case transformation should * finish processing and flush any remaining data. - * @return a current transformation state. - *
    - *
  • if state is {@link ProcessorState#finished()} then transformation has finished - * and the process method won't be called again;
  • - *
  • if state is {@link ProcessorState#needsMoreData()} then transformation requires - * more data in order to continue. The process method will be called with - * a new input element or with {@link Optional#empty()} if there are no more elements;
  • - *
  • if state is {@link ProcessorState#yield()} then transformation has yielded. - * The process method will be called again with the same input element;
  • - *
  • if state is {@link ProcessorState#blocked(ListenableFuture)} then transformation is blocked. - * The process method will be called again with the same input element after blocked - * future is done;
  • - *
  • if state is {@link ProcessorState#ofResult(Object, boolean)} then the transformation - * has produced a result. If needsMoreData {@link ProcessorState#ofResult(Object, boolean)} - * parameter is true then the process method will be called again with a new element - * (or with {@link Optional#empty()} if there are no more elements). - * If needsMoreData parameter is false then the process method - * will be called again with the same input element. - *
+ * @return the current transformation state, optionally bearing a result + * @see TransformationState#needsMoreData() + * @see TransformationState#blocked(ListenableFuture) + * @see TransformationState#yield() + * @see TransformationState#ofResult(Object) + * @see TransformationState#ofResult(Object, boolean) + * @see TransformationState#finished() */ - ProcessorState process(Optional elementOptional); + TransformationState process(Optional elementOptional); } interface Process { - ProcessorState process(); + /** + * Does some work and returns current state. + * + * @return the current state, optionally bearing a result + * @see ProcessState#blocked(ListenableFuture) + * @see ProcessState#yield() + * @see ProcessState#ofResult(Object) + * @see ProcessState#finished() + */ + ProcessState process(); } @Immutable - final class ProcessorState + final class TransformationState { - private static final ProcessorState NEEDS_MORE_DATE_STATE = new ProcessorState<>(NEEDS_MORE_DATA, true, Optional.empty(), Optional.empty()); - private static final ProcessorState YIELD_STATE = new ProcessorState<>(YIELD, false, Optional.empty(), Optional.empty()); - private static final ProcessorState FINISHED_STATE = new ProcessorState<>(FINISHED, false, Optional.empty(), Optional.empty()); + private static final TransformationState NEEDS_MORE_DATE_STATE = new TransformationState<>(Type.NEEDS_MORE_DATA, true, Optional.empty(), Optional.empty()); + private static final TransformationState YIELD_STATE = new TransformationState<>(Type.YIELD, false, Optional.empty(), Optional.empty()); + private static final TransformationState FINISHED_STATE = new TransformationState<>(Type.FINISHED, false, Optional.empty(), Optional.empty()); enum Type { @@ -177,51 +198,74 @@ enum Type FINISHED } - private final ProcessorState.Type type; + private final Type type; private final boolean needsMoreData; private final Optional result; private final Optional> blocked; - ProcessorState(Type type, boolean needsMoreData, Optional result, Optional> blocked) + private TransformationState(Type type, boolean needsMoreData, Optional result, Optional> blocked) { this.type = requireNonNull(type, "type is null"); this.needsMoreData = needsMoreData; this.result = requireNonNull(result, "result is null"); this.blocked = requireNonNull(blocked, "blocked is null"); - - checkArgument(!needsMoreData || type == NEEDS_MORE_DATA || type == RESULT); - checkArgument(!blocked.isPresent() || type == BLOCKED); - checkArgument(!result.isPresent() || type == RESULT); } - public static ProcessorState needsMoreData() + /** + * Signals that transformation requires more data in order to continue and no result has been produced. + * {@link #process()} will be called with a new input element or with {@link Optional#empty()} if there + * are no more elements. + */ + @SuppressWarnings("unchecked") + public static TransformationState needsMoreData() { - return NEEDS_MORE_DATE_STATE; + return (TransformationState) NEEDS_MORE_DATE_STATE; } - public static ProcessorState blocked(ListenableFuture blocked) + /** + * Signals that transformation is blocked. {@link #process()} will be called again with the same input + * element after {@code blocked} future is done. + */ + public static TransformationState blocked(ListenableFuture blocked) { - return new ProcessorState<>(Type.BLOCKED, false, Optional.empty(), Optional.of(blocked)); + return new TransformationState<>(Type.BLOCKED, false, Optional.empty(), Optional.of(blocked)); } - public static ProcessorState yield() + /** + * Signals that transformation has yielded. {@link #process()} will be called again with the same input element. + */ + @SuppressWarnings("unchecked") + public static TransformationState yield() { - return YIELD_STATE; + return (TransformationState) YIELD_STATE; } - public static ProcessorState ofResult(T result) + /** + * Signals that transformation has produced a result from its input. {@link #process()} will be called again with + * a new element or with {@link Optional#empty()} if there are no more elements. + */ + public static TransformationState ofResult(T result) { return ofResult(result, true); } - public static ProcessorState ofResult(T result, boolean needsMoreData) + /** + * Signals that transformation has produced a result. If {@code needsMoreData}, {@link #process()} will be called again + * with a new element (or with {@link Optional#empty()} if there are no more elements). If not @{code needsMoreData}, + * {@link #process()} will be called again with the same element. + */ + public static TransformationState ofResult(T result, boolean needsMoreData) { - return new ProcessorState<>(Type.RESULT, needsMoreData, Optional.of(result), Optional.empty()); + return new TransformationState<>(Type.RESULT, needsMoreData, Optional.of(result), Optional.empty()); } - public static ProcessorState finished() + /** + * Signals that transformation has finished. {@link #process()} method will not be called again. + */ + @SuppressWarnings("unchecked") + public static TransformationState finished() { - return FINISHED_STATE; + return (TransformationState) FINISHED_STATE; } Type getType() @@ -244,4 +288,79 @@ Optional> getBlocked() return blocked; } } + + @Immutable + final class ProcessState + { + private static final ProcessState YIELD_STATE = new ProcessState<>(Type.YIELD, Optional.empty(), Optional.empty()); + private static final ProcessState FINISHED_STATE = new ProcessState<>(Type.FINISHED, Optional.empty(), Optional.empty()); + + enum Type + { + BLOCKED, + YIELD, + RESULT, + FINISHED + } + + private final Type type; + private final Optional result; + private final Optional> blocked; + + private ProcessState(Type type, Optional result, Optional> blocked) + { + this.type = requireNonNull(type, "type is null"); + this.result = requireNonNull(result, "result is null"); + this.blocked = requireNonNull(blocked, "blocked is null"); + } + + /** + * Signals that process is blocked. {@link #process()} will be called again after {@code blocked} future is done. + */ + public static ProcessState blocked(ListenableFuture blocked) + { + return new ProcessState<>(Type.BLOCKED, Optional.empty(), Optional.of(blocked)); + } + + /** + * Signals that process has yielded. {@link #process()} will be called again later. + */ + @SuppressWarnings("unchecked") + public static ProcessState yield() + { + return (ProcessState) YIELD_STATE; + } + + /** + * Signals that process has produced a result. {@link #process()} will be called again. + */ + public static ProcessState ofResult(T result) + { + return new ProcessState<>(Type.RESULT, Optional.of(result), Optional.empty()); + } + + /** + * Signals that process has finished. {@link #process()} method will not be called again. + */ + @SuppressWarnings("unchecked") + public static ProcessState finished() + { + return (ProcessState) FINISHED_STATE; + } + + Type getType() + { + return type; + } + + Optional getResult() + { + return result; + } + + Optional> getBlocked() + { + return blocked; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessorUtils.java b/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessorUtils.java index af34c9d4f2263..749472513db2a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessorUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/WorkProcessorUtils.java @@ -13,21 +13,21 @@ */ package com.facebook.presto.operator; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; +import com.facebook.presto.operator.WorkProcessor.ProcessState; import com.facebook.presto.operator.WorkProcessor.Transformation; +import com.facebook.presto.operator.WorkProcessor.TransformationState; import com.google.common.collect.AbstractIterator; import com.google.common.util.concurrent.ListenableFuture; +import javax.annotation.Nullable; + import java.util.Comparator; import java.util.Iterator; import java.util.Optional; import java.util.PriorityQueue; +import java.util.function.BooleanSupplier; import java.util.function.Function; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.BLOCKED; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.FINISHED; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.NEEDS_MORE_DATA; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.Type.RESULT; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.util.Comparator.comparing; @@ -59,28 +59,39 @@ protected T computeNext() static Iterator> yieldingIteratorFrom(WorkProcessor processor) { - requireNonNull(processor, "processor is null"); - return new AbstractIterator>() + return new YieldingIterator<>(processor); + } + + private static class YieldingIterator + extends AbstractIterator> + { + @Nullable + WorkProcessor processor; + + YieldingIterator(WorkProcessor processor) { - @Override - protected Optional computeNext() - { - if (processor.process()) { - if (processor.isFinished()) { - return endOfData(); - } + this.processor = requireNonNull(processor, "processorParameter is null"); + } - return Optional.of(processor.getResult()); + @Override + protected Optional computeNext() + { + if (processor.process()) { + if (processor.isFinished()) { + processor = null; + return endOfData(); } - if (processor.isBlocked()) { - throw new IllegalStateException("Cannot iterate over blocking WorkProcessor"); - } + return Optional.of(processor.getResult()); + } - // yielded - return Optional.empty(); + if (processor.isBlocked()) { + throw new IllegalStateException("Cannot iterate over blocking WorkProcessor"); } - }; + + // yielded + return Optional.empty(); + } } static WorkProcessor fromIterator(Iterator iterator) @@ -88,10 +99,10 @@ static WorkProcessor fromIterator(Iterator iterator) requireNonNull(iterator, "iterator is null"); return create(() -> { if (!iterator.hasNext()) { - return ProcessorState.finished(); + return ProcessState.finished(); } - return ProcessorState.ofResult(iterator.next()); + return ProcessState.ofResult(iterator.next()); }); } @@ -107,7 +118,7 @@ static WorkProcessor mergeSorted(Iterable> processorIter WorkProcessor processor = requireNonNull(processorIterator.next()); @Override - public ProcessorState process() + public ProcessState process() { while (true) { if (processor.process()) { @@ -116,37 +127,68 @@ public ProcessorState process() } } else if (processor.isBlocked()) { - return ProcessorState.blocked(processor.getBlockedFuture()); + return ProcessState.blocked(processor.getBlockedFuture()); } else { - return ProcessorState.yield(); + return ProcessState.yield(); } if (processorIterator.hasNext()) { - processor = processorIterator.next(); + processor = requireNonNull(processorIterator.next()); continue; } if (queue.isEmpty()) { - return ProcessorState.finished(); + return ProcessState.finished(); } ElementAndProcessor elementAndProcessor = queue.poll(); processor = elementAndProcessor.getProcessor(); - return ProcessorState.ofResult(elementAndProcessor.getElement()); + return ProcessState.ofResult(elementAndProcessor.getElement()); } } }); } + static WorkProcessor yielding(WorkProcessor processor, BooleanSupplier yieldSignal) + { + return processor.transform(new YieldingTransformation<>(yieldSignal)); + } + + private static class YieldingTransformation + implements Transformation + { + final BooleanSupplier yieldSignal; + boolean lastProcessYielded; + + YieldingTransformation(BooleanSupplier yieldSignal) + { + this.yieldSignal = requireNonNull(yieldSignal, "yieldSignal is null"); + } + + @Override + public TransformationState process(Optional elementOptional) + { + if (!lastProcessYielded && yieldSignal.getAsBoolean()) { + lastProcessYielded = true; + return TransformationState.yield(); + } + lastProcessYielded = false; + + return elementOptional + .map(TransformationState::ofResult) + .orElseGet(TransformationState::finished); + } + } + static WorkProcessor flatMap(WorkProcessor processor, Function> mapper) { requireNonNull(processor, "processor is null"); requireNonNull(mapper, "mapper is null"); return processor.flatTransform(elementOptional -> elementOptional - .map(element -> ProcessorState.ofResult(mapper.apply(element))) - .orElse(ProcessorState.finished())); + .map(element -> TransformationState.ofResult(mapper.apply(element))) + .orElseGet(TransformationState::finished)); } static WorkProcessor map(WorkProcessor processor, Function mapper) @@ -155,15 +197,15 @@ static WorkProcessor map(WorkProcessor processor, Function ma requireNonNull(mapper, "mapper is null"); return processor.transform(elementOptional -> elementOptional - .map(element -> ProcessorState.ofResult(mapper.apply(element))) - .orElse(ProcessorState.finished())); + .map(element -> TransformationState.ofResult(mapper.apply(element))) + .orElseGet(TransformationState::finished)); } static WorkProcessor flatTransform(WorkProcessor processor, Transformation> transformation) { requireNonNull(processor, "processor is null"); requireNonNull(transformation, "transformation is null"); - return flatten(processor.transform(transformation)); + return processor.transform(transformation).transformProcessor(WorkProcessorUtils::flatten); } static WorkProcessor flatten(WorkProcessor> processor) @@ -171,23 +213,23 @@ static WorkProcessor flatten(WorkProcessor> processor) requireNonNull(processor, "processor is null"); return processor.transform(nestedProcessorOptional -> { if (!nestedProcessorOptional.isPresent()) { - return ProcessorState.finished(); + return TransformationState.finished(); } WorkProcessor nestedProcessor = nestedProcessorOptional.get(); if (nestedProcessor.process()) { if (nestedProcessor.isFinished()) { - return ProcessorState.needsMoreData(); + return TransformationState.needsMoreData(); } - return ProcessorState.ofResult(nestedProcessor.getResult(), false); + return TransformationState.ofResult(nestedProcessor.getResult(), false); } if (nestedProcessor.isBlocked()) { - return ProcessorState.blocked(nestedProcessor.getBlockedFuture()); + return TransformationState.blocked(nestedProcessor.getBlockedFuture()); } - return ProcessorState.yield(); + return TransformationState.yield(); }); } @@ -200,7 +242,7 @@ static WorkProcessor transform(WorkProcessor processor, Transformat Optional element = Optional.empty(); @Override - public ProcessorState process() + public ProcessState process() { while (true) { if (!element.isPresent() && !processor.isFinished()) { @@ -210,14 +252,14 @@ public ProcessorState process() } } else if (processor.isBlocked()) { - return ProcessorState.blocked(processor.getBlockedFuture()); + return ProcessState.blocked(processor.getBlockedFuture()); } else { - return ProcessorState.yield(); + return ProcessState.yield(); } } - ProcessorState state = requireNonNull(transformation.process(element), "state is null"); + TransformationState state = requireNonNull(transformation.process(element), "state is null"); if (state.isNeedsMoreData()) { checkState(!processor.isFinished(), "Cannot request more data when base processor is finished"); @@ -225,9 +267,18 @@ else if (processor.isBlocked()) { element = Optional.empty(); } - if (state.getType() != NEEDS_MORE_DATA) { - // passthrough transformation state if it doesn't require new data - return state; + // pass-through transformation state if it doesn't require new data + switch (state.getType()) { + case NEEDS_MORE_DATA: + break; + case BLOCKED: + return ProcessState.blocked(state.getBlocked().get()); + case YIELD: + return ProcessState.yield(); + case RESULT: + return ProcessState.ofResult(state.getResult().get()); + case FINISHED: + return ProcessState.finished(); } } } @@ -236,62 +287,76 @@ else if (processor.isBlocked()) { static WorkProcessor create(WorkProcessor.Process process) { - requireNonNull(process, "process is null"); - return new WorkProcessor() + return new ProcessWorkProcessor<>(process); + } + + private static class ProcessWorkProcessor + implements WorkProcessor + { + @Nullable + WorkProcessor.Process process; + @Nullable + ProcessState state; + + ProcessWorkProcessor(WorkProcessor.Process process) { - ProcessorState state; + this.process = requireNonNull(process, "process is null"); + } - @Override - public boolean process() - { - if (isBlocked()) { - return false; - } - if (isFinished()) { - return true; - } - state = requireNonNull(process.process()); - checkState(state.getType() != NEEDS_MORE_DATA, "Unexpected state: NEEDS_MORE_DATA"); - return state.getType() == RESULT || state.getType() == FINISHED; + @Override + public boolean process() + { + if (isBlocked()) { + return false; } - - @Override - public boolean isBlocked() - { - return state != null && state.getType() == BLOCKED && !state.getBlocked().get().isDone(); + if (isFinished()) { + return true; } + state = requireNonNull(process.process()); - @Override - public ListenableFuture getBlockedFuture() - { - checkState(state != null && state.getType() == BLOCKED, "Must be blocked to get blocked future"); - return state.getBlocked().get(); + if (state.getType() == ProcessState.Type.FINISHED) { + process = null; } - @Override - public boolean isFinished() - { - return state != null && state.getType() == FINISHED; - } + return state.getType() == ProcessState.Type.RESULT || state.getType() == ProcessState.Type.FINISHED; + } - @Override - public T getResult() - { - checkState(state != null && state.getType() == RESULT, "process() must return true and must not be finished"); - return state.getResult().get(); - } - }; + @Override + public boolean isBlocked() + { + return state != null && state.getType() == ProcessState.Type.BLOCKED && !state.getBlocked().get().isDone(); + } + + @Override + public ListenableFuture getBlockedFuture() + { + checkState(state != null && state.getType() == ProcessState.Type.BLOCKED, "Must be blocked to get blocked future"); + return state.getBlocked().get(); + } + + @Override + public boolean isFinished() + { + return state != null && state.getType() == ProcessState.Type.FINISHED; + } + + @Override + public T getResult() + { + checkState(state != null && state.getType() == ProcessState.Type.RESULT, "process() must return true and must not be finished"); + return state.getResult().get(); + } } private static class ElementAndProcessor { - final T element; + @Nullable final T element; final WorkProcessor processor; ElementAndProcessor(T element, WorkProcessor processor) { this.element = element; - this.processor = processor; + this.processor = requireNonNull(processor, "processor is null"); } T getElement() diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractGroupCollectionAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractGroupCollectionAggregationState.java new file mode 100644 index 0000000000000..095fb14a52f7d --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractGroupCollectionAggregationState.java @@ -0,0 +1,185 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.array.IntBigArray; +import com.facebook.presto.array.ShortBigArray; +import com.facebook.presto.operator.aggregation.state.AbstractGroupedAccumulatorState; +import com.facebook.presto.spi.PageBuilder; +import com.facebook.presto.spi.block.Block; +import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongList; +import org.openjdk.jol.info.ClassLayout; + +import java.util.ArrayList; +import java.util.List; + +import static com.google.common.base.Verify.verify; + +/** + * Instances of this state use a single PageBuilder for all groups. + */ +public abstract class AbstractGroupCollectionAggregationState + extends AbstractGroupedAccumulatorState +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(AbstractGroupCollectionAggregationState.class).instanceSize(); + private static final int MAX_NUM_BLOCKS = 30000; + private static final short NULL = -1; + + private final ShortBigArray headBlockIndex; + private final IntBigArray headPosition; + + private final ShortBigArray nextBlockIndex; + private final IntBigArray nextPosition; + + private final ShortBigArray tailBlockIndex; + private final IntBigArray tailPosition; + + private final List values; + private final LongList sumPositions; + private final IntBigArray groupEntryCount; + private PageBuilder currentPageBuilder; + + private long valueBlocksRetainedSizeInBytes; + private long totalPositions; + private long capacity; + + protected AbstractGroupCollectionAggregationState(PageBuilder pageBuilder) + { + this.headBlockIndex = new ShortBigArray(NULL); + this.headPosition = new IntBigArray(NULL); + this.nextBlockIndex = new ShortBigArray(NULL); + this.nextPosition = new IntBigArray(NULL); + this.tailBlockIndex = new ShortBigArray(NULL); + this.tailPosition = new IntBigArray(NULL); + + this.currentPageBuilder = pageBuilder; + this.values = new ArrayList<>(); + this.sumPositions = new LongArrayList(); + this.groupEntryCount = new IntBigArray(); + values.add(currentPageBuilder); + sumPositions.add(0L); + valueBlocksRetainedSizeInBytes = 0; + + totalPositions = 0; + capacity = 1024; + nextBlockIndex.ensureCapacity(capacity); + nextPosition.ensureCapacity(capacity); + groupEntryCount.ensureCapacity(capacity); + } + + @Override + public void ensureCapacity(long size) + { + headBlockIndex.ensureCapacity(size); + headPosition.ensureCapacity(size); + tailBlockIndex.ensureCapacity(size); + tailPosition.ensureCapacity(size); + groupEntryCount.ensureCapacity(size); + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + + headBlockIndex.sizeOf() + + headPosition.sizeOf() + + tailBlockIndex.sizeOf() + + tailPosition.sizeOf() + + nextBlockIndex.sizeOf() + + nextPosition.sizeOf() + + groupEntryCount.sizeOf() + + valueBlocksRetainedSizeInBytes + + // valueBlocksRetainedSizeInBytes doesn't contain the current block builder + currentPageBuilder.getRetainedSizeInBytes(); + } + + /** + * This method should be called before {@link #appendAtChannel(int, Block, int)} to update the internal linked list, where + * {@link #appendAtChannel(int, Block, int)} is called for each channel that has a new entry to be added. + */ + protected final void prepareAdd() + { + if (currentPageBuilder.isFull()) { + valueBlocksRetainedSizeInBytes += currentPageBuilder.getRetainedSizeInBytes(); + sumPositions.add(totalPositions); + currentPageBuilder = currentPageBuilder.newPageBuilderLike(); + values.add(currentPageBuilder); + + verify(values.size() <= MAX_NUM_BLOCKS); + } + + long currentGroupId = getGroupId(); + short insertedBlockIndex = (short) (values.size() - 1); + int insertedPosition = currentPageBuilder.getPositionCount(); + + if (totalPositions == capacity) { + capacity *= 1.5; + nextBlockIndex.ensureCapacity(capacity); + nextPosition.ensureCapacity(capacity); + } + + if (isEmpty()) { + // new linked list, set up the header pointer + headBlockIndex.set(currentGroupId, insertedBlockIndex); + headPosition.set(currentGroupId, insertedPosition); + } + else { + // existing linked list, link the new entry to the tail + long absoluteTailAddress = toAbsolutePosition(tailBlockIndex.get(currentGroupId), tailPosition.get(currentGroupId)); + nextBlockIndex.set(absoluteTailAddress, insertedBlockIndex); + nextPosition.set(absoluteTailAddress, insertedPosition); + } + tailBlockIndex.set(currentGroupId, insertedBlockIndex); + tailPosition.set(currentGroupId, insertedPosition); + groupEntryCount.increment(currentGroupId); + currentPageBuilder.declarePosition(); + totalPositions++; + } + + protected final void appendAtChannel(int channel, Block block, int position) + { + currentPageBuilder.getType(channel).appendTo(block, position, currentPageBuilder.getBlockBuilder(channel)); + } + + public void forEach(T consumer) + { + short currentBlockId = headBlockIndex.get(getGroupId()); + int currentPosition = headPosition.get(getGroupId()); + while (currentBlockId != NULL) { + accept(consumer, values.get(currentBlockId), currentPosition); + + long absoluteCurrentAddress = toAbsolutePosition(currentBlockId, currentPosition); + currentBlockId = nextBlockIndex.get(absoluteCurrentAddress); + currentPosition = nextPosition.get(absoluteCurrentAddress); + } + } + + public boolean isEmpty() + { + return headBlockIndex.get(getGroupId()) == NULL; + } + + public final int getEntryCount() + { + return groupEntryCount.get(getGroupId()); + } + + private long toAbsolutePosition(short blockId, int position) + { + return sumPositions.get(blockId) + position; + } + + protected abstract void accept(T consumer, PageBuilder pageBuilder, int currentPosition); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxAggregationFunction.java index 722b3697ee9aa..c0ffcd824bcb1 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.BlockPositionState; import com.facebook.presto.operator.aggregation.state.BlockPositionStateSerializer; import com.facebook.presto.operator.aggregation.state.NullableBooleanState; @@ -145,13 +146,14 @@ else if (type.getJavaType() == boolean.class) { inputFunction, combineFunction, outputFunction, - stateInterface, - stateSerializer, - stateFactory, + ImmutableList.of(new AccumulatorStateDescriptor( + stateInterface, + stateSerializer, + stateFactory)), type); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(getSignature().getName(), inputTypes, intermediateType, type, true, false, factory); + return new InternalAggregationFunction(getSignature().getName(), inputTypes, ImmutableList.of(intermediateType), type, true, false, factory); } private static List createParameterMetadata(Type type) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxNAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxNAggregationFunction.java index fad1b7fd28692..d904e56937b9e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxNAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AbstractMinMaxNAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.MinMaxNState; import com.facebook.presto.operator.aggregation.state.MinMaxNStateFactory; import com.facebook.presto.operator.aggregation.state.MinMaxNStateSerializer; @@ -99,13 +100,14 @@ protected InternalAggregationFunction generateAggregation(Type type) INPUT_FUNCTION.bindTo(comparator).bindTo(type), COMBINE_FUNCTION, OUTPUT_FUNCTION.bindTo(outputType), - MinMaxNState.class, - stateSerializer, - new MinMaxNStateFactory(), + ImmutableList.of(new AccumulatorStateDescriptor( + MinMaxNState.class, + stateSerializer, + new MinMaxNStateFactory())), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(getSignature().getName(), inputTypes, intermediateType, outputType, true, false, factory); + return new InternalAggregationFunction(getSignature().getName(), inputTypes, ImmutableList.of(intermediateType), outputType, true, false, factory); } public static void input(BlockComparator comparator, Type type, MinMaxNState state, Block block, long n, int blockIndex) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorCompiler.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorCompiler.java index fc57ba10e0901..ab550944e27ed 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorCompiler.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorCompiler.java @@ -14,12 +14,15 @@ package com.facebook.presto.operator.aggregation; import com.facebook.presto.operator.GroupByIdBlock; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.block.ColumnarRow; import com.facebook.presto.spi.function.AccumulatorStateFactory; import com.facebook.presto.spi.function.AccumulatorStateSerializer; import com.facebook.presto.spi.function.WindowIndex; +import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.gen.Binding; import com.facebook.presto.sql.gen.CallSiteBinder; @@ -55,6 +58,9 @@ import static com.facebook.presto.util.CompilerUtils.defineClass; import static com.facebook.presto.util.CompilerUtils.makeClassName; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.bytecode.Access.FINAL; import static io.airlift.bytecode.Access.PRIVATE; import static io.airlift.bytecode.Access.PUBLIC; @@ -63,11 +69,13 @@ import static io.airlift.bytecode.ParameterizedType.type; import static io.airlift.bytecode.expression.BytecodeExpressions.constantFalse; import static io.airlift.bytecode.expression.BytecodeExpressions.constantInt; +import static io.airlift.bytecode.expression.BytecodeExpressions.constantLong; import static io.airlift.bytecode.expression.BytecodeExpressions.constantString; import static io.airlift.bytecode.expression.BytecodeExpressions.invokeDynamic; import static io.airlift.bytecode.expression.BytecodeExpressions.invokeStatic; import static io.airlift.bytecode.expression.BytecodeExpressions.not; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; public class AccumulatorCompiler { @@ -88,8 +96,7 @@ public static GenericAccumulatorFactoryBinder generateAccumulatorFactoryBinder(A classLoader); return new GenericAccumulatorFactoryBinder( - metadata.getStateSerializer(), - metadata.getStateFactory(), + metadata.getAccumulatorStateDescriptors(), accumulatorClass, groupedAccumulatorClass); } @@ -109,46 +116,89 @@ private static Class generateAccumulatorClass( CallSiteBinder callSiteBinder = new CallSiteBinder(); - AccumulatorStateSerializer stateSerializer = metadata.getStateSerializer(); - AccumulatorStateFactory stateFactory = metadata.getStateFactory(); + List stateDescriptors = metadata.getAccumulatorStateDescriptors(); + List stateFieldAndDescriptors = new ArrayList<>(); + for (int i = 0; i < stateDescriptors.size(); i++) { + stateFieldAndDescriptors.add(new StateFieldAndDescriptor( + definition.declareField(a(PRIVATE, FINAL), "stateSerializer_" + i, AccumulatorStateSerializer.class), + definition.declareField(a(PRIVATE, FINAL), "stateFactory_" + i, AccumulatorStateFactory.class), + definition.declareField(a(PRIVATE, FINAL), "state_" + i, grouped ? stateDescriptors.get(i).getFactory().getGroupedStateClass() : stateDescriptors.get(i).getFactory().getSingleStateClass()), + stateDescriptors.get(i))); + } + List stateFileds = stateFieldAndDescriptors.stream() + .map(StateFieldAndDescriptor::getStateField) + .collect(toImmutableList()); + + int lambdaCount = metadata.getLambdaInterfaces().size(); + List lambdaProviderFields = new ArrayList<>(lambdaCount); + for (int i = 0; i < lambdaCount; i++) { + lambdaProviderFields.add(definition.declareField(a(PRIVATE, FINAL), "lambdaProvider_" + i, LambdaProvider.class)); + } - FieldDefinition stateSerializerField = definition.declareField(a(PRIVATE, FINAL), "stateSerializer", AccumulatorStateSerializer.class); - FieldDefinition stateFactoryField = definition.declareField(a(PRIVATE, FINAL), "stateFactory", AccumulatorStateFactory.class); FieldDefinition inputChannelsField = definition.declareField(a(PRIVATE, FINAL), "inputChannels", type(List.class, Integer.class)); FieldDefinition maskChannelField = definition.declareField(a(PRIVATE, FINAL), "maskChannel", type(Optional.class, Integer.class)); - FieldDefinition stateField = definition.declareField(a(PRIVATE, FINAL), "state", grouped ? stateFactory.getGroupedStateClass() : stateFactory.getSingleStateClass()); // Generate constructor generateConstructor( definition, - stateSerializerField, - stateFactoryField, + stateFieldAndDescriptors, inputChannelsField, maskChannelField, - stateField, + lambdaProviderFields, grouped); // Generate methods - generateAddInput(definition, stateField, inputChannelsField, maskChannelField, metadata.getInputMetadata(), metadata.getInputFunction(), callSiteBinder, grouped); - generateAddInputWindowIndex(definition, stateField, metadata.getInputMetadata(), metadata.getInputFunction(), callSiteBinder); - generateGetEstimatedSize(definition, stateField); - generateGetIntermediateType(definition, callSiteBinder, stateSerializer.getSerializedType()); + generateAddInput( + definition, + stateFileds, + inputChannelsField, + maskChannelField, + metadata.getValueInputMetadata(), + metadata.getLambdaInterfaces(), + lambdaProviderFields, + metadata.getInputFunction(), + callSiteBinder, + grouped); + generateAddInputWindowIndex( + definition, + stateFileds, + metadata.getValueInputMetadata(), + metadata.getLambdaInterfaces(), + lambdaProviderFields, + metadata.getInputFunction(), + callSiteBinder); + generateGetEstimatedSize(definition, stateFileds); + + generateGetIntermediateType( + definition, + callSiteBinder, + stateDescriptors.stream() + .map(stateDescriptor -> stateDescriptor.getSerializer().getSerializedType()) + .collect(toImmutableList())); + generateGetFinalType(definition, callSiteBinder, metadata.getOutputType()); - generateAddIntermediateAsCombine(definition, stateField, stateSerializerField, stateFactoryField, metadata.getCombineFunction(), stateFactory.getSingleStateClass(), callSiteBinder, grouped); + generateAddIntermediateAsCombine( + definition, + stateFieldAndDescriptors, + metadata.getLambdaInterfaces(), + lambdaProviderFields, + metadata.getCombineFunction(), + callSiteBinder, + grouped); if (grouped) { - generateGroupedEvaluateIntermediate(definition, stateSerializerField, stateField); + generateGroupedEvaluateIntermediate(definition, stateFieldAndDescriptors); } else { - generateEvaluateIntermediate(definition, stateSerializerField, stateField); + generateEvaluateIntermediate(definition, stateFieldAndDescriptors); } if (grouped) { - generateGroupedEvaluateFinal(definition, stateField, metadata.getOutputFunction(), callSiteBinder); + generateGroupedEvaluateFinal(definition, stateFileds, metadata.getOutputFunction(), callSiteBinder); } else { - generateEvaluateFinal(definition, stateField, metadata.getOutputFunction(), callSiteBinder); + generateEvaluateFinal(definition, stateFileds, metadata.getOutputFunction(), callSiteBinder); } if (grouped) { @@ -157,13 +207,20 @@ private static Class generateAccumulatorClass( return defineClass(definition, accumulatorInterface, callSiteBinder.getBindings(), classLoader); } - private static MethodDefinition generateGetIntermediateType(ClassDefinition definition, CallSiteBinder callSiteBinder, Type type) + private static MethodDefinition generateGetIntermediateType(ClassDefinition definition, CallSiteBinder callSiteBinder, List type) { MethodDefinition methodDefinition = definition.declareMethod(a(PUBLIC), "getIntermediateType", type(Type.class)); - methodDefinition.getBody() - .append(constantType(callSiteBinder, type)) - .retObject(); + if (type.size() == 1) { + methodDefinition.getBody() + .append(constantType(callSiteBinder, getOnlyElement(type))) + .retObject(); + } + else { + methodDefinition.getBody() + .append(constantType(callSiteBinder, RowType.anonymous(type))) + .retObject(); + } return methodDefinition; } @@ -179,20 +236,30 @@ private static MethodDefinition generateGetFinalType(ClassDefinition definition, return methodDefinition; } - private static void generateGetEstimatedSize(ClassDefinition definition, FieldDefinition stateField) + private static void generateGetEstimatedSize(ClassDefinition definition, List stateFields) { MethodDefinition method = definition.declareMethod(a(PUBLIC), "getEstimatedSize", type(long.class)); - BytecodeExpression state = method.getThis().getField(stateField); - method.getBody() - .append(state.invoke("getEstimatedSize", long.class).ret()); + Variable estimatedSize = method.getScope().declareVariable(long.class, "estimatedSize"); + method.getBody().append(estimatedSize.set(constantLong(0L))); + + for (FieldDefinition stateField : stateFields) { + method.getBody() + .append(estimatedSize.set( + BytecodeExpressions.add( + estimatedSize, + method.getThis().getField(stateField).invoke("getEstimatedSize", long.class)))); + } + method.getBody().append(estimatedSize.ret()); } private static void generateAddInput( ClassDefinition definition, - FieldDefinition stateField, + List stateField, FieldDefinition inputChannelsField, FieldDefinition maskChannelField, List parameterMetadatas, + List lambdaInterfaces, + List lambdaProviderFields, MethodHandle inputFunction, CallSiteBinder callSiteBinder, boolean grouped) @@ -240,7 +307,17 @@ private static void generateAddInput( .invokeVirtual(Page.class, "getBlock", Block.class, int.class) .putVariable(parameterVariables.get(i)); } - BytecodeBlock block = generateInputForLoop(stateField, parameterMetadatas, inputFunction, scope, parameterVariables, masksBlock, callSiteBinder, grouped); + BytecodeBlock block = generateInputForLoop( + stateField, + parameterMetadatas, + inputFunction, + scope, + parameterVariables, + lambdaInterfaces, + lambdaProviderFields, + masksBlock, + callSiteBinder, + grouped); body.append(block); body.ret(); @@ -248,8 +325,10 @@ private static void generateAddInput( private static void generateAddInputWindowIndex( ClassDefinition definition, - FieldDefinition stateField, + List stateField, List parameterMetadatas, + List lambdaInterfaces, + List lambdaProviderFields, MethodHandle inputFunction, CallSiteBinder callSiteBinder) { @@ -275,6 +354,8 @@ private static void generateAddInputWindowIndex( scope, inputFunction.type().parameterArray(), parameterMetadatas, + lambdaInterfaces, + lambdaProviderFields, stateField, index, channels, @@ -321,20 +402,26 @@ private static List getInvokeFunctionOnWindowIndexParameters Scope scope, Class[] parameterTypes, List parameterMetadatas, - FieldDefinition stateField, + List lambdaInterfaces, + List lambdaProviderFields, + List stateField, Variable index, Variable channels, Variable position) { int inputChannel = 0; + int stateIndex = 0; + verify(parameterTypes.length == parameterMetadatas.size() + lambdaInterfaces.size()); List expressions = new ArrayList<>(); - for (int i = 0; i < parameterTypes.length; i++) { + + for (int i = 0; i < parameterMetadatas.size(); i++) { ParameterMetadata parameterMetadata = parameterMetadatas.get(i); Class parameterType = parameterTypes[i]; BytecodeExpression getChannel = channels.invoke("get", Object.class, constantInt(inputChannel)).cast(int.class); switch (parameterMetadata.getParameterType()) { case STATE: - expressions.add(scope.getThis().getField(stateField)); + expressions.add(scope.getThis().getField(stateField.get(stateIndex))); + stateIndex++; break; case BLOCK_INDEX: // index.getSingleValueBlock(channel, position) generates always a page with only one position @@ -373,18 +460,28 @@ else if (parameterType == Block.class) { inputChannel++; break; + default: + throw new IllegalArgumentException("Unsupported parameter type: " + parameterMetadata.getParameterType()); } } + for (int i = 0; i < lambdaInterfaces.size(); i++) { + expressions.add(scope.getThis().getField(lambdaProviderFields.get(i)) + .invoke("getLambda", Object.class) + .cast(lambdaInterfaces.get(i))); + } + return expressions; } private static BytecodeBlock generateInputForLoop( - FieldDefinition stateField, + List stateField, List parameterMetadatas, MethodHandle inputFunction, Scope scope, List parameterVariables, + List lambdaInterfaces, + List lambdaProviderFields, Variable masksBlock, CallSiteBinder callSiteBinder, boolean grouped) @@ -400,7 +497,17 @@ private static BytecodeBlock generateInputForLoop( .putVariable(rowsVariable) .initializeVariable(positionVariable); - BytecodeNode loopBody = generateInvokeInputFunction(scope, stateField, positionVariable, parameterVariables, parameterMetadatas, inputFunction, callSiteBinder, grouped); + BytecodeNode loopBody = generateInvokeInputFunction( + scope, + stateField, + positionVariable, + parameterVariables, + parameterMetadatas, + lambdaInterfaces, + lambdaProviderFields, + inputFunction, + callSiteBinder, + grouped); // Wrap with null checks List nullable = new ArrayList<>(); @@ -450,10 +557,12 @@ private static BytecodeBlock generateInputForLoop( private static BytecodeBlock generateInvokeInputFunction( Scope scope, - FieldDefinition stateField, + List stateField, Variable position, List parameterVariables, List parameterMetadatas, + List lambdaInterfaces, + List lambdaProviderFields, MethodHandle inputFunction, CallSiteBinder callSiteBinder, boolean grouped) @@ -468,11 +577,14 @@ private static BytecodeBlock generateInvokeInputFunction( Class[] parameters = inputFunction.type().parameterArray(); int inputChannel = 0; - for (int i = 0; i < parameters.length; i++) { + int stateIndex = 0; + verify(parameters.length == parameterMetadatas.size() + lambdaInterfaces.size()); + for (int i = 0; i < parameterMetadatas.size(); i++) { ParameterMetadata parameterMetadata = parameterMetadatas.get(i); switch (parameterMetadata.getParameterType()) { case STATE: - block.append(scope.getThis().getField(stateField)); + block.append(scope.getThis().getField(stateField.get(stateIndex))); + stateIndex++; break; case BLOCK_INDEX: block.getVariable(position); @@ -492,6 +604,11 @@ private static BytecodeBlock generateInvokeInputFunction( throw new IllegalArgumentException("Unsupported parameter type: " + parameterMetadata.getParameterType()); } } + for (int i = 0; i < lambdaInterfaces.size(); i++) { + block.append(scope.getThis().getField(lambdaProviderFields.get(i)) + .invoke("getLambda", Object.class) + .cast(lambdaInterfaces.get(i))); + } block.append(invoke(callSiteBinder.bind(inputFunction), "input")); return block; @@ -540,11 +657,10 @@ else if (parameter == Slice.class) { private static void generateAddIntermediateAsCombine( ClassDefinition definition, - FieldDefinition stateField, - FieldDefinition stateSerializerField, - FieldDefinition stateFactoryField, + List stateFieldAndDescriptors, + List lambdaInterfaces, + List lambdaProviderFields, MethodHandle combineFunction, - Class singleStateClass, CallSiteBinder callSiteBinder, boolean grouped) { @@ -553,33 +669,73 @@ private static void generateAddIntermediateAsCombine( BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); - Variable block = scope.getVariable("block"); - Variable scratchState = scope.declareVariable(singleStateClass, "scratchState"); - Variable position = scope.declareVariable(int.class, "position"); + int stateCount = stateFieldAndDescriptors.size(); + List scratchStates = new ArrayList<>(); + for (int i = 0; i < stateCount; i++) { + Class scratchStateClass = stateFieldAndDescriptors.get(i).getStateDescriptor().getFactory().getSingleStateClass(); + scratchStates.add(scope.declareVariable(scratchStateClass, "scratchState_" + i)); + } - body.comment("scratchState = stateFactory.createSingleState();") - .append(thisVariable.getField(stateFactoryField)) - .invokeInterface(AccumulatorStateFactory.class, "createSingleState", Object.class) - .checkCast(scratchState.getType()) - .putVariable(scratchState); + List block; + if (stateCount == 1) { + block = ImmutableList.of(scope.getVariable("block")); + } + else { + // ColumnarRow is used to get the column blocks represents each state, this allows to + // 1. handle single state and multiple states in a unified way + // 2. avoid the cost of constructing SingleRowBlock for each group + Variable columnarRow = scope.declareVariable(ColumnarRow.class, "columnarRow"); + body.append(columnarRow.set( + invokeStatic(ColumnarRow.class, "toColumnarRow", ColumnarRow.class, scope.getVariable("block")))); + + block = new ArrayList<>(); + for (int i = 0; i < stateCount; i++) { + Variable columnBlock = scope.declareVariable(Block.class, "columnBlock_" + i); + body.append(columnBlock.set( + columnarRow.invoke("getField", Block.class, constantInt(i)))); + block.add(columnBlock); + } + } - if (grouped) { - generateEnsureCapacity(scope, stateField, body); + Variable position = scope.declareVariable(int.class, "position"); + for (int i = 0; i < stateCount; i++) { + FieldDefinition stateFactoryField = stateFieldAndDescriptors.get(i).getStateFactoryField(); + body.comment(format("scratchState_%s = stateFactory[%s].createSingleState();", i, i)) + .append(thisVariable.getField(stateFactoryField)) + .invokeInterface(AccumulatorStateFactory.class, "createSingleState", Object.class) + .checkCast(scratchStates.get(i).getType()) + .putVariable(scratchStates.get(i)); } - BytecodeBlock loopBody = new BytecodeBlock(); + List stateFields = stateFieldAndDescriptors.stream() + .map(StateFieldAndDescriptor::getStateField) + .collect(toImmutableList()); if (grouped) { - Variable groupIdsBlock = scope.getVariable("groupIdsBlock"); - loopBody.append(thisVariable.getField(stateField).invoke("setGroupId", void.class, groupIdsBlock.invoke("getGroupId", long.class, position))); + generateEnsureCapacity(scope, stateFields, body); } - loopBody.append(thisVariable.getField(stateSerializerField).invoke("deserialize", void.class, block, position, scratchState.cast(Object.class))); + BytecodeBlock loopBody = new BytecodeBlock(); - loopBody.comment("combine(state, scratchState)") - .append(thisVariable.getField(stateField)) - .append(scratchState) - .append(invoke(callSiteBinder.bind(combineFunction), "combine")); + loopBody.comment("combine(state_0, state_1, ... scratchState_0, scratchState_1, ... lambda_0, lambda_1, ...)"); + for (FieldDefinition stateField : stateFields) { + if (grouped) { + Variable groupIdsBlock = scope.getVariable("groupIdsBlock"); + loopBody.append(thisVariable.getField(stateField).invoke("setGroupId", void.class, groupIdsBlock.invoke("getGroupId", long.class, position))); + } + loopBody.append(thisVariable.getField(stateField)); + } + for (int i = 0; i < stateCount; i++) { + FieldDefinition stateSerializerField = stateFieldAndDescriptors.get(i).getStateSerializerField(); + loopBody.append(thisVariable.getField(stateSerializerField).invoke("deserialize", void.class, block.get(i), position, scratchStates.get(i).cast(Object.class))); + loopBody.append(scratchStates.get(i)); + } + for (int i = 0; i < lambdaInterfaces.size(); i++) { + loopBody.append(scope.getThis().getField(lambdaProviderFields.get(i)) + .invoke("getLambda", Object.class) + .cast(lambdaInterfaces.get(i))); + } + loopBody.append(invoke(callSiteBinder.bind(combineFunction), "combine")); if (grouped) { // skip rows with null group id @@ -594,19 +750,23 @@ private static void generateAddIntermediateAsCombine( .ret(); } - private static void generateSetGroupIdFromGroupIdsBlock(Scope scope, FieldDefinition stateField, BytecodeBlock block) + private static void generateSetGroupIdFromGroupIdsBlock(Scope scope, List stateFields, BytecodeBlock block) { Variable groupIdsBlock = scope.getVariable("groupIdsBlock"); Variable position = scope.getVariable("position"); - BytecodeExpression state = scope.getThis().getField(stateField); - block.append(state.invoke("setGroupId", void.class, groupIdsBlock.invoke("getGroupId", long.class, position))); + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = scope.getThis().getField(stateField); + block.append(state.invoke("setGroupId", void.class, groupIdsBlock.invoke("getGroupId", long.class, position))); + } } - private static void generateEnsureCapacity(Scope scope, FieldDefinition stateField, BytecodeBlock block) + private static void generateEnsureCapacity(Scope scope, List stateFields, BytecodeBlock block) { Variable groupIdsBlock = scope.getVariable("groupIdsBlock"); - BytecodeExpression state = scope.getThis().getField(stateField); - block.append(state.invoke("ensureCapacity", void.class, groupIdsBlock.invoke("getGroupCount", long.class))); + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = scope.getThis().getField(stateField); + block.append(state.invoke("ensureCapacity", void.class, groupIdsBlock.invoke("getGroupCount", long.class))); + } } private static MethodDefinition declareAddIntermediate(ClassDefinition definition, boolean grouped) @@ -655,23 +815,40 @@ private static BytecodeBlock generateBlockNonNullPositionForLoop(Scope scope, Va return block; } - private static void generateGroupedEvaluateIntermediate(ClassDefinition definition, FieldDefinition stateSerializerField, FieldDefinition stateField) + private static void generateGroupedEvaluateIntermediate(ClassDefinition definition, List stateFieldAndDescriptors) { Parameter groupId = arg("groupId", int.class); Parameter out = arg("out", BlockBuilder.class); MethodDefinition method = definition.declareMethod(a(PUBLIC), "evaluateIntermediate", type(void.class), groupId, out); Variable thisVariable = method.getThis(); - BytecodeExpression state = thisVariable.getField(stateField); - BytecodeExpression stateSerializer = thisVariable.getField(stateSerializerField); + BytecodeBlock body = method.getBody(); - method.getBody() - .append(state.invoke("setGroupId", void.class, groupId.cast(long.class))) - .append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), out)) - .ret(); + if (stateFieldAndDescriptors.size() == 1) { + BytecodeExpression stateSerializer = thisVariable.getField(getOnlyElement(stateFieldAndDescriptors).getStateSerializerField()); + BytecodeExpression state = thisVariable.getField(getOnlyElement(stateFieldAndDescriptors).getStateField()); + + body.append(state.invoke("setGroupId", void.class, groupId.cast(long.class))) + .append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), out)) + .ret(); + } + else { + Variable rowBuilder = method.getScope().declareVariable(BlockBuilder.class, "rowBuilder"); + body.append(rowBuilder.set(out.invoke("beginBlockEntry", BlockBuilder.class))); + + for (int i = 0; i < stateFieldAndDescriptors.size(); i++) { + BytecodeExpression stateSerializer = thisVariable.getField(stateFieldAndDescriptors.get(i).getStateSerializerField()); + BytecodeExpression state = thisVariable.getField(stateFieldAndDescriptors.get(i).getStateField()); + + body.append(state.invoke("setGroupId", void.class, groupId.cast(long.class))) + .append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), rowBuilder)); + } + body.append(out.invoke("closeEntry", BlockBuilder.class).pop()) + .ret(); + } } - private static void generateEvaluateIntermediate(ClassDefinition definition, FieldDefinition stateSerializerField, FieldDefinition stateField) + private static void generateEvaluateIntermediate(ClassDefinition definition, List stateFieldAndDescriptors) { Parameter out = arg("out", BlockBuilder.class); MethodDefinition method = definition.declareMethod( @@ -681,17 +858,32 @@ private static void generateEvaluateIntermediate(ClassDefinition definition, Fie out); Variable thisVariable = method.getThis(); - BytecodeExpression stateSerializer = thisVariable.getField(stateSerializerField); - BytecodeExpression state = thisVariable.getField(stateField); + BytecodeBlock body = method.getBody(); - method.getBody() - .append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), out)) - .ret(); + if (stateFieldAndDescriptors.size() == 1) { + BytecodeExpression stateSerializer = thisVariable.getField(getOnlyElement(stateFieldAndDescriptors).getStateSerializerField()); + BytecodeExpression state = thisVariable.getField(getOnlyElement(stateFieldAndDescriptors).getStateField()); + + body.append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), out)) + .ret(); + } + else { + Variable rowBuilder = method.getScope().declareVariable(BlockBuilder.class, "rowBuilder"); + body.append(rowBuilder.set(out.invoke("beginBlockEntry", BlockBuilder.class))); + + for (int i = 0; i < stateFieldAndDescriptors.size(); i++) { + BytecodeExpression stateSerializer = thisVariable.getField(stateFieldAndDescriptors.get(i).getStateSerializerField()); + BytecodeExpression state = thisVariable.getField(stateFieldAndDescriptors.get(i).getStateField()); + body.append(stateSerializer.invoke("serialize", void.class, state.cast(Object.class), rowBuilder)); + } + body.append(out.invoke("closeEntry", BlockBuilder.class).pop()) + .ret(); + } } private static void generateGroupedEvaluateFinal( ClassDefinition definition, - FieldDefinition stateField, + List stateFields, MethodHandle outputFunction, CallSiteBinder callSiteBinder) { @@ -702,12 +894,16 @@ private static void generateGroupedEvaluateFinal( BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); - BytecodeExpression state = thisVariable.getField(stateField); + List states = new ArrayList<>(); - body.append(state.invoke("setGroupId", void.class, groupId.cast(long.class))); + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = thisVariable.getField(stateField); + body.append(state.invoke("setGroupId", void.class, groupId.cast(long.class))); + states.add(state); + } - body.comment("output(state, out)"); - body.append(state); + body.comment("output(state_0, state_1, ..., out)"); + states.forEach(body::append); body.append(out); body.append(invoke(callSiteBinder.bind(outputFunction), "output")); @@ -716,7 +912,7 @@ private static void generateGroupedEvaluateFinal( private static void generateEvaluateFinal( ClassDefinition definition, - FieldDefinition stateField, + List stateFields, MethodHandle outputFunction, CallSiteBinder callSiteBinder) { @@ -730,10 +926,15 @@ private static void generateEvaluateFinal( BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); - BytecodeExpression state = thisVariable.getField(stateField); + List states = new ArrayList<>(); + + for (FieldDefinition stateField : stateFields) { + BytecodeExpression state = thisVariable.getField(stateField); + states.add(state); + } - body.comment("output(state, out)"); - body.append(state); + body.comment("output(state_0, state_1, ..., out)"); + states.forEach(body::append); body.append(out); body.append(invoke(callSiteBinder.bind(outputFunction), "output")); @@ -751,23 +952,22 @@ private static void generatePrepareFinal(ClassDefinition definition) private static void generateConstructor( ClassDefinition definition, - FieldDefinition stateSerializerField, - FieldDefinition stateFactoryField, + List stateFieldAndDescriptors, FieldDefinition inputChannelsField, FieldDefinition maskChannelField, - FieldDefinition stateField, + List lambdaProviderFields, boolean grouped) { - Parameter stateSerializer = arg("stateSerializer", AccumulatorStateSerializer.class); - Parameter stateFactory = arg("stateFactory", AccumulatorStateFactory.class); + Parameter stateDescriptors = arg("stateDescriptors", type(List.class, AccumulatorStateDescriptor.class)); Parameter inputChannels = arg("inputChannels", type(List.class, Integer.class)); Parameter maskChannel = arg("maskChannel", type(Optional.class, Integer.class)); + Parameter lambdaProviders = arg("lambdaProviders", type(List.class, LambdaProvider.class)); MethodDefinition method = definition.declareConstructor( a(PUBLIC), - stateSerializer, - stateFactory, + stateDescriptors, inputChannels, - maskChannel); + maskChannel, + lambdaProviders); BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); @@ -776,8 +976,25 @@ private static void generateConstructor( .append(thisVariable) .invokeConstructor(Object.class); - body.append(thisVariable.setField(stateSerializerField, generateRequireNotNull(stateSerializer))); - body.append(thisVariable.setField(stateFactoryField, generateRequireNotNull(stateFactory))); + for (int i = 0; i < stateFieldAndDescriptors.size(); i++) { + body.append(thisVariable.setField( + stateFieldAndDescriptors.get(i).getStateSerializerField(), + stateDescriptors.invoke("get", Object.class, constantInt(i)) + .cast(AccumulatorStateDescriptor.class) + .invoke("getSerializer", AccumulatorStateSerializer.class))); + body.append(thisVariable.setField( + stateFieldAndDescriptors.get(i).getStateFactoryField(), + stateDescriptors.invoke("get", Object.class, constantInt(i)) + .cast(AccumulatorStateDescriptor.class) + .invoke("getFactory", AccumulatorStateFactory.class))); + } + for (int i = 0; i < lambdaProviderFields.size(); i++) { + body.append(thisVariable.setField( + lambdaProviderFields.get(i), + lambdaProviders.invoke("get", Object.class, constantInt(i)) + .cast(LambdaProvider.class))); + } + body.append(thisVariable.setField(inputChannelsField, generateRequireNotNull(inputChannels))); body.append(thisVariable.setField(maskChannelField, generateRequireNotNull(maskChannel))); @@ -789,7 +1006,12 @@ private static void generateConstructor( createState = "createSingleState"; } - body.append(thisVariable.setField(stateField, stateFactory.invoke(createState, Object.class).cast(stateField.getType()))); + for (StateFieldAndDescriptor stateFieldAndDescriptor : stateFieldAndDescriptors) { + FieldDefinition stateField = stateFieldAndDescriptor.getStateField(); + BytecodeExpression stateFactory = thisVariable.getField(stateFieldAndDescriptor.getStateFactoryField()); + + body.append(thisVariable.setField(stateField, stateFactory.invoke(createState, Object.class).cast(stateField.getType()))); + } body.ret(); } @@ -798,4 +1020,41 @@ private static BytecodeExpression generateRequireNotNull(Variable variable) return invokeStatic(Objects.class, "requireNonNull", Object.class, variable.cast(Object.class), constantString(variable.getName() + " is null")) .cast(variable.getType()); } + + private static class StateFieldAndDescriptor + { + private final FieldDefinition stateSerializerField; + private final FieldDefinition stateFactoryField; + private final FieldDefinition stateField; + + private final AccumulatorStateDescriptor stateDescriptor; + + public StateFieldAndDescriptor(FieldDefinition stateSerializerField, FieldDefinition stateFactoryField, FieldDefinition stateField, AccumulatorStateDescriptor stateDescriptor) + { + this.stateSerializerField = requireNonNull(stateSerializerField, "stateSerializerField is null"); + this.stateFactoryField = requireNonNull(stateFactoryField, "stateFactoryField is null"); + this.stateField = requireNonNull(stateField, "stateField is null"); + this.stateDescriptor = requireNonNull(stateDescriptor, "stateDescriptor is null"); + } + + public FieldDefinition getStateSerializerField() + { + return stateSerializerField; + } + + public FieldDefinition getStateFactoryField() + { + return stateFactoryField; + } + + public FieldDefinition getStateField() + { + return stateField; + } + + public AccumulatorStateDescriptor getStateDescriptor() + { + return stateDescriptor; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorFactoryBinder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorFactoryBinder.java index 8b3cc77e0f44c..a7732d70018ce 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorFactoryBinder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AccumulatorFactoryBinder.java @@ -24,5 +24,15 @@ public interface AccumulatorFactoryBinder { - AccumulatorFactory bind(List argumentChannels, Optional maskChannel, List sourceTypes, List orderByChannels, List orderings, PagesIndex.Factory pagesIndexFactory, boolean distinct, JoinCompiler joinCompiler, Session session); + AccumulatorFactory bind( + List argumentChannels, + Optional maskChannel, + List sourceTypes, + List orderByChannels, + List orderings, + PagesIndex.Factory pagesIndexFactory, + boolean distinct, + JoinCompiler joinCompiler, + List lambdaProviders, + Session session); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationImplementation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationImplementation.java index 801b507e112a2..089527237f354 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationImplementation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationImplementation.java @@ -442,8 +442,8 @@ public List parseImplementationDependencies(Method inp inputFunction, annotation, typeParameters.stream() - .map(TypeParameter::value) - .collect(toImmutableSet()), + .map(TypeParameter::value) + .collect(toImmutableSet()), literalParameters); builder.add(createDependency(annotation, literalParameters)); }); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationMetadata.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationMetadata.java index 07e7725a21b06..e3bf994ab1833 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationMetadata.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/AggregationMetadata.java @@ -41,37 +41,56 @@ public class AggregationMetadata public static final Set> SUPPORTED_PARAMETER_TYPES = ImmutableSet.of(Block.class, long.class, double.class, boolean.class, Slice.class); private final String name; - private final List inputMetadata; + private final List valueInputMetadata; + private final List lambdaInterfaces; private final MethodHandle inputFunction; private final MethodHandle combineFunction; private final MethodHandle outputFunction; - private final AccumulatorStateSerializer stateSerializer; - private final AccumulatorStateFactory stateFactory; + private final List accumulatorStateDescriptors; private final Type outputType; public AggregationMetadata( String name, - List inputMetadata, + List valueInputMetadata, MethodHandle inputFunction, MethodHandle combineFunction, MethodHandle outputFunction, - Class stateInterface, - AccumulatorStateSerializer stateSerializer, - AccumulatorStateFactory stateFactory, + List accumulatorStateDescriptors, Type outputType) + { + this( + name, + valueInputMetadata, + inputFunction, + combineFunction, + outputFunction, + accumulatorStateDescriptors, + outputType, + ImmutableList.of()); + } + + public AggregationMetadata( + String name, + List valueInputMetadata, + MethodHandle inputFunction, + MethodHandle combineFunction, + MethodHandle outputFunction, + List accumulatorStateDescriptors, + Type outputType, + List lambdaInterfaces) { this.outputType = requireNonNull(outputType); - this.inputMetadata = ImmutableList.copyOf(requireNonNull(inputMetadata, "inputMetadata is null")); + this.valueInputMetadata = ImmutableList.copyOf(requireNonNull(valueInputMetadata, "valueInputMetadata is null")); this.name = requireNonNull(name, "name is null"); this.inputFunction = requireNonNull(inputFunction, "inputFunction is null"); this.combineFunction = requireNonNull(combineFunction, "combineFunction is null"); this.outputFunction = requireNonNull(outputFunction, "outputFunction is null"); - this.stateSerializer = requireNonNull(stateSerializer, "stateSerializer is null"); - this.stateFactory = requireNonNull(stateFactory, "stateFactory is null"); + this.accumulatorStateDescriptors = requireNonNull(accumulatorStateDescriptors, "accumulatorStateDescriptors is null"); + this.lambdaInterfaces = ImmutableList.copyOf(requireNonNull(lambdaInterfaces, "lambdaInterfaces is null")); - verifyInputFunctionSignature(inputFunction, inputMetadata, stateInterface); - verifyCombineFunction(combineFunction, stateInterface); - verifyExactOutputFunction(outputFunction, stateInterface); + verifyInputFunctionSignature(inputFunction, valueInputMetadata, lambdaInterfaces, accumulatorStateDescriptors); + verifyCombineFunction(combineFunction, lambdaInterfaces, accumulatorStateDescriptors); + verifyExactOutputFunction(outputFunction, accumulatorStateDescriptors); } public Type getOutputType() @@ -79,9 +98,14 @@ public Type getOutputType() return outputType; } - public List getInputMetadata() + public List getValueInputMetadata() + { + return valueInputMetadata; + } + + public List getLambdaInterfaces() { - return inputMetadata; + return lambdaInterfaces; } public String getName() @@ -104,27 +128,27 @@ public MethodHandle getOutputFunction() return outputFunction; } - public AccumulatorStateSerializer getStateSerializer() + public List getAccumulatorStateDescriptors() { - return stateSerializer; + return accumulatorStateDescriptors; } - public AccumulatorStateFactory getStateFactory() - { - return stateFactory; - } - - private static void verifyInputFunctionSignature(MethodHandle method, List parameterMetadatas, Class stateInterface) + private static void verifyInputFunctionSignature(MethodHandle method, List dataChannelMetadata, List lambdaInterfaces, List stateDescriptors) { Class[] parameters = method.type().parameterArray(); checkArgument(parameters.length > 0, "Aggregation input function must have at least one parameter"); - checkArgument(parameterMetadatas.stream().filter(m -> m.getParameterType() == STATE).count() == 1, "There must be exactly one state parameter in input function"); - checkArgument(parameterMetadatas.get(0).getParameterType() == STATE, "First parameter must be state"); - for (int i = 0; i < parameters.length; i++) { - ParameterMetadata metadata = parameterMetadatas.get(i); + checkArgument(parameters.length == dataChannelMetadata.size() + lambdaInterfaces.size(), "Wenlei TODO..."); + checkArgument(dataChannelMetadata.stream().filter(m -> m.getParameterType() == STATE).count() == stateDescriptors.size(), "Number of state parameter in input function must be the same as size of stateDescriptors"); + checkArgument(dataChannelMetadata.get(0).getParameterType() == STATE, "First parameter must be state"); + + // verify data channels + int stateIndex = 0; + for (int i = 0; i < dataChannelMetadata.size(); i++) { + ParameterMetadata metadata = dataChannelMetadata.get(i); switch (metadata.getParameterType()) { case STATE: - checkArgument(stateInterface == parameters[i], String.format("State argument must be of type %s", stateInterface)); + checkArgument(stateDescriptors.get(stateIndex).getStateInterface() == parameters[i], String.format("State argument must be of type %s", stateDescriptors.get(stateIndex).getStateInterface())); + stateIndex++; break; case BLOCK_INPUT_CHANNEL: case NULLABLE_BLOCK_INPUT_CHANNEL: @@ -132,12 +156,7 @@ private static void verifyInputFunctionSignature(MethodHandle method, List stateInterface) + private static void verifyCombineFunction(MethodHandle method, List lambdaInterfaces, List stateDescriptors) { Class[] parameterTypes = method.type().parameterArray(); - checkArgument(parameterTypes.length == 2, "Combine function must take exactly 2 arguments."); - checkArgument(Arrays.stream(parameterTypes).filter(type -> type.equals(stateInterface)).count() == 2, "Combine function must take exactly two arguments of type %s annotated as @AggregationState", stateInterface.getSimpleName()); + checkArgument( + parameterTypes.length == stateDescriptors.size() * 2 + lambdaInterfaces.size(), + "Number of arguments for combine function must be 2 times the size of states plus number of lambda channels."); + + for (int i = 0; i < stateDescriptors.size() * 2; i++) { + checkArgument( + parameterTypes[i].equals(stateDescriptors.get(i % stateDescriptors.size()).getStateInterface()), + String.format("Type for Parameter index %d is unexpected. Arguments for combine function must appear in the order of state1, state2, ..., otherState1, otherState2, ...", i)); + } + + for (int i = 0; i < lambdaInterfaces.size(); i++) { + verifyMethodParameterType(method, i + stateDescriptors.size() * 2, lambdaInterfaces.get(i), "function"); + } } - private static void verifyExactOutputFunction(MethodHandle method, Class stateInterface) + private static void verifyExactOutputFunction(MethodHandle method, List stateDescriptors) { if (method == null) { return; } Class[] parameterTypes = method.type().parameterArray(); - checkArgument(parameterTypes.length == 2, "Output function must take at exactly 2 arguments."); - checkArgument(Arrays.stream(parameterTypes).filter(type -> type.equals(stateInterface)).count() == 1, "Output function must take exactly one @AggregationState of type %s", stateInterface.getSimpleName()); + checkArgument(parameterTypes.length == stateDescriptors.size() + 1, "Number of arguments for combine function must be exactly one plus than number of states."); + for (int i = 0; i < stateDescriptors.size(); i++) { + checkArgument(parameterTypes[i].equals(stateDescriptors.get(i).getStateInterface()), String.format("Type for Parameter index %d is unexpected", i)); + } checkArgument(Arrays.stream(parameterTypes).filter(type -> type.equals(BlockBuilder.class)).count() == 1, "Output function must take exactly one BlockBuilder parameter"); } + private static void verifyMethodParameterType(MethodHandle method, int index, Class javaType, String sqlTypeDisplayName) + { + checkArgument(method.type().parameterArray()[index] == javaType, + "Expected method %s parameter %s type to be %s (%s)", + method, + index, + javaType.getName(), + sqlTypeDisplayName); + } + public static int countInputChannels(List metadatas) { int parameters = 0; @@ -252,4 +300,33 @@ static ParameterType inputChannelParameterType(boolean isNullable, boolean isBlo } } } + + public static class AccumulatorStateDescriptor + { + private final Class stateInterface; + private final AccumulatorStateSerializer serializer; + private final AccumulatorStateFactory factory; + + public AccumulatorStateDescriptor(Class stateInterface, AccumulatorStateSerializer serializer, AccumulatorStateFactory factory) + { + this.stateInterface = requireNonNull(stateInterface, "stateInterface is null"); + this.serializer = requireNonNull(serializer, "serializer is null"); + this.factory = requireNonNull(factory, "factory is null"); + } + + public Class getStateInterface() + { + return stateInterface; + } + + public AccumulatorStateSerializer getSerializer() + { + return serializer; + } + + public AccumulatorStateFactory getFactory() + { + return factory; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ArbitraryAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ArbitraryAggregationFunction.java index 26d2c6f2ae451..6e81e343ff26d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ArbitraryAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ArbitraryAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.BlockPositionState; import com.facebook.presto.operator.aggregation.state.BlockPositionStateSerializer; import com.facebook.presto.operator.aggregation.state.NullableBooleanState; @@ -138,13 +139,14 @@ else if (type.getJavaType() == boolean.class) { inputFunction, combineFunction, outputFunction.bindTo(type), - stateInterface, - stateSerializer, - StateCompiler.generateStateFactory(stateInterface, classLoader), + ImmutableList.of(new AccumulatorStateDescriptor( + stateInterface, + stateSerializer, + StateCompiler.generateStateFactory(stateInterface, classLoader))), type); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, type, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), type, true, false, factory); } private static List createInputParameterMetadata(Type value) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ChecksumAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ChecksumAggregationFunction.java index 6010faffca276..69f5ff45dc5d5 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ChecksumAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ChecksumAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.NullableLongState; import com.facebook.presto.operator.aggregation.state.StateCompiler; import com.facebook.presto.spi.block.Block; @@ -88,13 +89,14 @@ private static InternalAggregationFunction generateAggregation(Type type) INPUT_FUNCTION.bindTo(type), COMBINE_FUNCTION, OUTPUT_FUNCTION, - NullableLongState.class, - StateCompiler.generateStateSerializer(NullableLongState.class, classLoader), - StateCompiler.generateStateFactory(NullableLongState.class, classLoader), + ImmutableList.of(new AccumulatorStateDescriptor( + NullableLongState.class, + StateCompiler.generateStateSerializer(NullableLongState.class, classLoader), + StateCompiler.generateStateFactory(NullableLongState.class, classLoader))), VARBINARY); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, BIGINT, VARBINARY, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(BIGINT), VARBINARY, true, false, factory); } private static List createInputParameterMetadata(Type type) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/CountColumn.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/CountColumn.java index 7c16c228ba90a..7f7e42feae9d3 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/CountColumn.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/CountColumn.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.LongState; import com.facebook.presto.operator.aggregation.state.StateCompiler; import com.facebook.presto.spi.block.Block; @@ -89,13 +90,14 @@ private static InternalAggregationFunction generateAggregation(Type type) INPUT_FUNCTION, COMBINE_FUNCTION, OUTPUT_FUNCTION, - LongState.class, - stateSerializer, - stateFactory, + ImmutableList.of(new AccumulatorStateDescriptor( + LongState.class, + stateSerializer, + stateFactory)), BIGINT); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, BIGINT, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), BIGINT, true, false, factory); } private static List createInputParameterMetadata(Type type) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalAverageAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalAverageAggregation.java index 79675b3bcae85..a033eff105f4b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalAverageAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalAverageAggregation.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowAndLongState; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowAndLongStateFactory; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowAndLongStateSerializer; @@ -120,14 +121,15 @@ private static InternalAggregationFunction generateAggregation(Type type) inputFunction, COMBINE_FUNCTION, outputFunction, - stateInterface, - stateSerializer, - new LongDecimalWithOverflowAndLongStateFactory(), + ImmutableList.of(new AccumulatorStateDescriptor( + stateInterface, + stateSerializer, + new LongDecimalWithOverflowAndLongStateFactory())), type); Type intermediateType = stateSerializer.getSerializedType(); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, type, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), type, true, false, factory); } private static List createInputParameterMetadata(Type type) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalSumAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalSumAggregation.java index fe09d398f550f..93e3b5cdc8b0d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalSumAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DecimalSumAggregation.java @@ -17,6 +17,7 @@ import com.facebook.presto.metadata.FunctionKind; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowState; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowStateFactory; import com.facebook.presto.operator.aggregation.state.LongDecimalWithOverflowStateSerializer; @@ -109,14 +110,15 @@ private static InternalAggregationFunction generateAggregation(Type inputType, T inputFunction.bindTo(inputType), COMBINE_FUNCTION, LONG_DECIMAL_OUTPUT_FUNCTION.bindTo(outputType), - stateInterface, - stateSerializer, - new LongDecimalWithOverflowStateFactory(), + ImmutableList.of(new AccumulatorStateDescriptor( + stateInterface, + stateSerializer, + new LongDecimalWithOverflowStateFactory())), outputType); Type intermediateType = stateSerializer.getSerializedType(); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, outputType, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, false, factory); } private static List createInputParameterMetadata(Type type) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DoubleHistogramAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DoubleHistogramAggregation.java index 9542466fd4557..5711d6b4c5688 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DoubleHistogramAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/DoubleHistogramAggregation.java @@ -25,8 +25,6 @@ import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.DoubleType; -import javax.validation.constraints.NotNull; - import java.util.Map; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; @@ -48,7 +46,6 @@ private DoubleHistogramAggregation() public interface State extends AccumulatorState { - @NotNull NumericHistogram get(); void set(NumericHistogram value); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/FloatingPointBitsConverterUtil.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/FloatingPointBitsConverterUtil.java index c0186d4a31a66..e43d16ccfa624 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/FloatingPointBitsConverterUtil.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/FloatingPointBitsConverterUtil.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.operator.aggregation; -final class FloatingPointBitsConverterUtil +public final class FloatingPointBitsConverterUtil { private FloatingPointBitsConverterUtil() {} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactory.java index 5de9b86b19c04..3d590c8e56a07 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactory.java @@ -19,12 +19,11 @@ import com.facebook.presto.operator.PagesIndex; import com.facebook.presto.operator.UpdateMemory; import com.facebook.presto.operator.Work; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.block.SortOrder; -import com.facebook.presto.spi.function.AccumulatorStateFactory; -import com.facebook.presto.spi.function.AccumulatorStateSerializer; import com.facebook.presto.spi.function.WindowIndex; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.gen.JoinCompiler; @@ -52,10 +51,10 @@ public class GenericAccumulatorFactory implements AccumulatorFactory { - private final AccumulatorStateSerializer stateSerializer; - private final AccumulatorStateFactory stateFactory; + private final List stateDescriptors; private final Constructor accumulatorConstructor; private final Constructor groupedAccumulatorConstructor; + private final List lambdaProviders; private final Optional maskChannel; private final List inputChannels; private final List sourceTypes; @@ -71,10 +70,10 @@ public class GenericAccumulatorFactory private final PagesIndex.Factory pagesIndexFactory; public GenericAccumulatorFactory( - AccumulatorStateSerializer stateSerializer, - AccumulatorStateFactory stateFactory, + List stateDescriptors, Constructor accumulatorConstructor, Constructor groupedAccumulatorConstructor, + List lambdaProviders, List inputChannels, Optional maskChannel, List sourceTypes, @@ -85,10 +84,10 @@ public GenericAccumulatorFactory( Session session, boolean distinct) { - this.stateSerializer = requireNonNull(stateSerializer, "stateSerializer is null"); - this.stateFactory = requireNonNull(stateFactory, "stateFactory is null"); + this.stateDescriptors = requireNonNull(stateDescriptors, "stateDescriptors is null"); this.accumulatorConstructor = requireNonNull(accumulatorConstructor, "accumulatorConstructor is null"); this.groupedAccumulatorConstructor = requireNonNull(groupedAccumulatorConstructor, "groupedAccumulatorConstructor is null"); + this.lambdaProviders = ImmutableList.copyOf(requireNonNull(lambdaProviders, "lambdaProviders is null")); this.maskChannel = requireNonNull(maskChannel, "maskChannel is null"); this.inputChannels = ImmutableList.copyOf(requireNonNull(inputChannels, "inputChannels is null")); this.sourceTypes = ImmutableList.copyOf(requireNonNull(sourceTypes, "sourceTypes is null")); @@ -143,7 +142,7 @@ public Accumulator createAccumulator() public Accumulator createIntermediateAccumulator() { try { - return accumulatorConstructor.newInstance(stateSerializer, stateFactory, ImmutableList.of(), Optional.empty()); + return accumulatorConstructor.newInstance(stateDescriptors, ImmutableList.of(), Optional.empty(), lambdaProviders); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); @@ -185,7 +184,7 @@ public GroupedAccumulator createGroupedAccumulator() public GroupedAccumulator createGroupedIntermediateAccumulator() { try { - return groupedAccumulatorConstructor.newInstance(stateSerializer, stateFactory, ImmutableList.of(), maskChannel); + return groupedAccumulatorConstructor.newInstance(stateDescriptors, ImmutableList.of(), Optional.empty(), lambdaProviders); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); @@ -207,7 +206,7 @@ public boolean hasDistinct() private Accumulator instantiateAccumulator(List inputs, Optional mask) { try { - return accumulatorConstructor.newInstance(stateSerializer, stateFactory, inputs, mask); + return accumulatorConstructor.newInstance(stateDescriptors, inputs, mask, lambdaProviders); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); @@ -217,7 +216,7 @@ private Accumulator instantiateAccumulator(List inputs, Optional inputs, Optional mask) { try { - return groupedAccumulatorConstructor.newInstance(stateSerializer, stateFactory, inputs, mask); + return groupedAccumulatorConstructor.newInstance(stateDescriptors, inputs, mask, lambdaProviders); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new RuntimeException(e); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactoryBinder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactoryBinder.java index e04721cd78d62..7edb6582a3126 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactoryBinder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/GenericAccumulatorFactoryBinder.java @@ -15,9 +15,8 @@ import com.facebook.presto.Session; import com.facebook.presto.operator.PagesIndex; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.spi.block.SortOrder; -import com.facebook.presto.spi.function.AccumulatorStateFactory; -import com.facebook.presto.spi.function.AccumulatorStateSerializer; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.gen.JoinCompiler; import com.google.common.annotations.VisibleForTesting; @@ -31,32 +30,29 @@ public class GenericAccumulatorFactoryBinder implements AccumulatorFactoryBinder { - private final AccumulatorStateSerializer stateSerializer; - private final AccumulatorStateFactory stateFactory; + private final List stateDescriptors; private final Constructor accumulatorConstructor; private final Constructor groupedAccumulatorConstructor; public GenericAccumulatorFactoryBinder( - AccumulatorStateSerializer stateSerializer, - AccumulatorStateFactory stateFactory, + List stateDescriptors, Class accumulatorClass, Class groupedAccumulatorClass) { - this.stateSerializer = requireNonNull(stateSerializer, "stateSerializer is null"); - this.stateFactory = requireNonNull(stateFactory, "stateFactory is null"); + this.stateDescriptors = requireNonNull(stateDescriptors, "stateDescriptors is null"); try { accumulatorConstructor = accumulatorClass.getConstructor( - AccumulatorStateSerializer.class, - AccumulatorStateFactory.class, - List.class, - Optional.class); + List.class, /* List stateDescriptors */ + List.class, /* List inputChannel */ + Optional.class, /* Optional maskChannel */ + List.class /* List lambdaProviders */); groupedAccumulatorConstructor = groupedAccumulatorClass.getConstructor( - AccumulatorStateSerializer.class, - AccumulatorStateFactory.class, - List.class, - Optional.class); + List.class, /* List stateDescriptors */ + List.class, /* List inputChannel */ + Optional.class, /* Optional maskChannel */ + List.class /* List lambdaProviders */); } catch (NoSuchMethodException e) { throw new RuntimeException(e); @@ -64,14 +60,37 @@ public GenericAccumulatorFactoryBinder( } @Override - public AccumulatorFactory bind(List argumentChannels, Optional maskChannel, List sourceTypes, List orderByChannels, List orderings, PagesIndex.Factory pagesIndexFactory, boolean distinct, JoinCompiler joinCompiler, Session session) + public AccumulatorFactory bind( + List argumentChannels, + Optional maskChannel, + List sourceTypes, + List orderByChannels, + List orderings, + PagesIndex.Factory pagesIndexFactory, + boolean distinct, + JoinCompiler joinCompiler, + List lambdaProviders, + Session session) { - return new GenericAccumulatorFactory(stateSerializer, stateFactory, accumulatorConstructor, groupedAccumulatorConstructor, argumentChannels, maskChannel, sourceTypes, orderByChannels, orderings, pagesIndexFactory, joinCompiler, session, distinct); + return new GenericAccumulatorFactory( + stateDescriptors, + accumulatorConstructor, + groupedAccumulatorConstructor, + lambdaProviders, + argumentChannels, + maskChannel, + sourceTypes, + orderByChannels, + orderings, + pagesIndexFactory, + joinCompiler, + session, + distinct); } @VisibleForTesting - public AccumulatorStateSerializer getStateSerializer() + public List getStateDescriptors() { - return stateSerializer; + return stateDescriptors; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/InternalAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/InternalAggregationFunction.java index e86f7990e4211..f2485704a23f2 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/InternalAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/InternalAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.Session; import com.facebook.presto.operator.PagesIndex; import com.facebook.presto.spi.block.SortOrder; +import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.gen.JoinCompiler; import com.google.common.annotations.VisibleForTesting; @@ -25,19 +26,49 @@ import java.util.Optional; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.getOnlyElement; import static java.util.Objects.requireNonNull; public final class InternalAggregationFunction { private final String name; private final List parameterTypes; - private final Type intermediateType; + private final List intermediateType; private final Type finalType; + private final List lambdaInterfaces; private final boolean decomposable; private final boolean orderSensitive; private final AccumulatorFactoryBinder factory; - public InternalAggregationFunction(String name, List parameterTypes, Type intermediateType, Type finalType, boolean decomposable, boolean orderSensitive, AccumulatorFactoryBinder factory) + public InternalAggregationFunction( + String name, + List parameterTypes, + List intermediateType, + Type finalType, + boolean decomposable, + boolean orderSensitive, + AccumulatorFactoryBinder factory) + { + this( + name, + parameterTypes, + intermediateType, + finalType, + decomposable, + orderSensitive, + factory, + ImmutableList.of()); + } + + public InternalAggregationFunction( + String name, + List parameterTypes, + List intermediateType, + Type finalType, + boolean decomposable, + boolean orderSensitive, + AccumulatorFactoryBinder factory, + List lambdaInterfaces) { this.name = requireNonNull(name, "name is null"); checkArgument(!name.isEmpty(), "name is empty"); @@ -47,6 +78,7 @@ public InternalAggregationFunction(String name, List parameterTypes, Type this.decomposable = decomposable; this.orderSensitive = orderSensitive; this.factory = requireNonNull(factory, "factory is null"); + this.lambdaInterfaces = ImmutableList.copyOf(lambdaInterfaces); } public String name() @@ -66,7 +98,17 @@ public Type getFinalType() public Type getIntermediateType() { - return intermediateType; + if (intermediateType.size() == 1) { + return getOnlyElement(intermediateType); + } + else { + return RowType.anonymous(intermediateType); + } + } + + public List getLambdaInterfaces() + { + return lambdaInterfaces; } /** @@ -87,7 +129,17 @@ public boolean isOrderSensitive() public AccumulatorFactory bind(List inputChannels, Optional maskChannel) { - return factory.bind(inputChannels, maskChannel, ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), null, false, null, null); + return factory.bind( + inputChannels, + maskChannel, + ImmutableList.of(), + ImmutableList.of(), + ImmutableList.of(), + null, + false, + null, + ImmutableList.of(), + null); } public AccumulatorFactory bind( @@ -99,9 +151,10 @@ public AccumulatorFactory bind( PagesIndex.Factory pagesIndexFactory, boolean distinct, JoinCompiler joinCompiler, + List lambdaProviders, Session session) { - return factory.bind(inputChannels, maskChannel, sourceTypes, orderByChannels, orderings, pagesIndexFactory, distinct, joinCompiler, session); + return factory.bind(inputChannels, maskChannel, sourceTypes, orderByChannels, orderings, pagesIndexFactory, distinct, joinCompiler, lambdaProviders, session); } @VisibleForTesting diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LambdaProvider.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LambdaProvider.java new file mode 100644 index 0000000000000..da83c0a803874 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LambdaProvider.java @@ -0,0 +1,25 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +// Lambda has to be compiled into a dedicated class, as functions might be stateful (e.g. use CachedInstanceBinder) +public interface LambdaProvider +{ + // To support capture, we can enrich the interface into + // getLambda(Object[] capturedValues) + + // The lambda capture is done through invokedynamic, and the CallSite will be cached after + // the first call. Thus separate classes have to be generated for different captures. + Object getLambda(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LazyAccumulatorFactoryBinder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LazyAccumulatorFactoryBinder.java index 0152d6b8bade0..97461e08b9dce 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LazyAccumulatorFactoryBinder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/LazyAccumulatorFactoryBinder.java @@ -43,8 +43,18 @@ public GenericAccumulatorFactoryBinder getGenericAccumulatorFactoryBinder() } @Override - public AccumulatorFactory bind(List argumentChannels, Optional maskChannel, List sourceTypes, List orderByChannels, List orderings, PagesIndex.Factory pagesIndexFactory, boolean distinct, JoinCompiler joinCompiler, Session session) + public AccumulatorFactory bind( + List argumentChannels, + Optional maskChannel, + List sourceTypes, + List orderByChannels, + List orderings, + PagesIndex.Factory pagesIndexFactory, + boolean distinct, + JoinCompiler joinCompiler, + List lambdaProviders, + Session session) { - return binder.get().bind(argumentChannels, maskChannel, sourceTypes, orderByChannels, orderings, pagesIndexFactory, distinct, joinCompiler, session); + return binder.get().bind(argumentChannels, maskChannel, sourceTypes, orderByChannels, orderings, pagesIndexFactory, distinct, joinCompiler, lambdaProviders, session); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapAggregationFunction.java index 45418bf50d9a9..5736e4d46f8ae 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.KeyValuePairStateSerializer; import com.facebook.presto.operator.aggregation.state.KeyValuePairsState; import com.facebook.presto.operator.aggregation.state.KeyValuePairsStateFactory; @@ -92,13 +93,14 @@ private static InternalAggregationFunction generateAggregation(Type keyType, Typ INPUT_FUNCTION.bindTo(keyType).bindTo(valueType), COMBINE_FUNCTION, OUTPUT_FUNCTION, - KeyValuePairsState.class, - stateSerializer, - new KeyValuePairsStateFactory(keyType, valueType), + ImmutableList.of(new AccumulatorStateDescriptor( + KeyValuePairsState.class, + stateSerializer, + new KeyValuePairsStateFactory(keyType, valueType))), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, outputType, true, true, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, true, factory); } private static List createInputParameterMetadata(Type keyType, Type valueType) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapUnionAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapUnionAggregation.java index e82ccd2fc645f..97395ee304688 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapUnionAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MapUnionAggregation.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.state.KeyValuePairStateSerializer; import com.facebook.presto.operator.aggregation.state.KeyValuePairsState; import com.facebook.presto.operator.aggregation.state.KeyValuePairsStateFactory; @@ -86,13 +87,14 @@ private static InternalAggregationFunction generateAggregation(Type keyType, Typ INPUT_FUNCTION.bindTo(keyType).bindTo(valueType), COMBINE_FUNCTION, OUTPUT_FUNCTION, - KeyValuePairsState.class, - stateSerializer, - new KeyValuePairsStateFactory(keyType, valueType), + ImmutableList.of(new AccumulatorStateDescriptor( + KeyValuePairsState.class, + stateSerializer, + new KeyValuePairsStateFactory(keyType, valueType))), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, outputType, true, false, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, false, factory); } private static List createInputParameterMetadata(Type inputType) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MergeQuantileDigestFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MergeQuantileDigestFunction.java new file mode 100644 index 0000000000000..193204349c9c3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MergeQuantileDigestFunction.java @@ -0,0 +1,153 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.metadata.BoundVariables; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.state.QuantileDigestState; +import com.facebook.presto.operator.aggregation.state.QuantileDigestStateFactory; +import com.facebook.presto.operator.aggregation.state.QuantileDigestStateSerializer; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.AggregationFunction; +import com.facebook.presto.spi.function.CombineFunction; +import com.facebook.presto.spi.function.InputFunction; +import com.facebook.presto.spi.type.QuantileDigestType; +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.spi.type.TypeSignatureParameter; +import com.google.common.collect.ImmutableList; +import io.airlift.bytecode.DynamicClassLoader; +import io.airlift.stats.QuantileDigest; + +import java.lang.invoke.MethodHandle; +import java.util.List; + +import static com.facebook.presto.metadata.Signature.comparableTypeParameter; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.BLOCK_INDEX; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.BLOCK_INPUT_CHANNEL; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.STATE; +import static com.facebook.presto.operator.aggregation.AggregationUtils.generateAggregationName; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.util.MoreMath.nearlyEqual; +import static com.facebook.presto.util.Reflection.methodHandle; +import static com.google.common.base.Preconditions.checkArgument; + +@AggregationFunction("merge") +public final class MergeQuantileDigestFunction + extends SqlAggregationFunction +{ + public static final MergeQuantileDigestFunction MERGE = new MergeQuantileDigestFunction(); + public static final String NAME = "merge"; + private static final MethodHandle INPUT_FUNCTION = methodHandle(MergeQuantileDigestFunction.class, "input", Type.class, QuantileDigestState.class, Block.class, int.class); + private static final MethodHandle COMBINE_FUNCTION = methodHandle(MergeQuantileDigestFunction.class, "combine", QuantileDigestState.class, QuantileDigestState.class); + private static final MethodHandle OUTPUT_FUNCTION = methodHandle(MergeQuantileDigestFunction.class, "output", QuantileDigestStateSerializer.class, QuantileDigestState.class, BlockBuilder.class); + private static final double COMPARISON_EPSILON = 1E-6; + + public MergeQuantileDigestFunction() + { + super(NAME, + ImmutableList.of(comparableTypeParameter("T")), + ImmutableList.of(), + parseTypeSignature("qdigest(T)"), + ImmutableList.of(parseTypeSignature("qdigest(T)"))); + } + + @Override + public String getDescription() + { + return "Merges the input quantile digests into a single quantile digest"; + } + + @Override + public InternalAggregationFunction specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) + { + Type valueType = boundVariables.getTypeVariable("T"); + QuantileDigestType outputType = (QuantileDigestType) typeManager.getParameterizedType(StandardTypes.QDIGEST, + ImmutableList.of(TypeSignatureParameter.of(valueType.getTypeSignature()))); + return generateAggregation(valueType, outputType); + } + + private static InternalAggregationFunction generateAggregation(Type valueType, QuantileDigestType type) + { + DynamicClassLoader classLoader = new DynamicClassLoader(MapAggregationFunction.class.getClassLoader()); + QuantileDigestStateSerializer stateSerializer = new QuantileDigestStateSerializer(valueType); + Type intermediateType = stateSerializer.getSerializedType(); + + AggregationMetadata metadata = new AggregationMetadata( + generateAggregationName(NAME, type.getTypeSignature(), ImmutableList.of(type.getTypeSignature())), + createInputParameterMetadata(type), + INPUT_FUNCTION.bindTo(type), + COMBINE_FUNCTION, + OUTPUT_FUNCTION.bindTo(stateSerializer), + ImmutableList.of(new AccumulatorStateDescriptor( + QuantileDigestState.class, + stateSerializer, + new QuantileDigestStateFactory())), + type); + + GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); + return new InternalAggregationFunction(NAME, ImmutableList.of(type), ImmutableList.of(intermediateType), type, true, true, factory); + } + + private static List createInputParameterMetadata(Type valueType) + { + return ImmutableList.of( + new ParameterMetadata(STATE), + new ParameterMetadata(BLOCK_INPUT_CHANNEL, valueType), + new ParameterMetadata(BLOCK_INDEX)); + } + + @InputFunction + public static void input(Type type, QuantileDigestState state, Block value, int index) + { + merge(state, new QuantileDigest(type.getSlice(value, index))); + } + + @CombineFunction + public static void combine(QuantileDigestState state, QuantileDigestState otherState) + { + merge(state, otherState.getQuantileDigest()); + } + + private static void merge(QuantileDigestState state, QuantileDigest input) + { + if (input == null) { + return; + } + QuantileDigest previous = state.getQuantileDigest(); + if (previous == null) { + state.setQuantileDigest(input); + state.addMemoryUsage(input.estimatedInMemorySizeInBytes()); + } + else { + checkArgument(nearlyEqual(previous.getMaxError(), input.getMaxError(), COMPARISON_EPSILON), + "Cannot merge qdigests with different accuracies (%s vs. %s)", state.getQuantileDigest().getMaxError(), input.getMaxError()); + checkArgument(nearlyEqual(previous.getAlpha(), input.getAlpha(), COMPARISON_EPSILON), + "Cannot merge qdigests with different alpha values (%s vs. %s)", state.getQuantileDigest().getAlpha(), input.getAlpha()); + state.addMemoryUsage(-previous.estimatedInMemorySizeInBytes()); + previous.merge(input); + state.addMemoryUsage(previous.estimatedInMemorySizeInBytes()); + } + } + + public static void output(QuantileDigestStateSerializer serializer, QuantileDigestState state, BlockBuilder out) + { + serializer.serialize(state, out); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultiKeyValuePairs.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultiKeyValuePairs.java deleted file mode 100644 index d2612f27cf021..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultiKeyValuePairs.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.operator.aggregation; - -import com.facebook.presto.array.ObjectBigArray; -import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.block.BlockBuilder; -import com.facebook.presto.spi.type.ArrayType; -import com.facebook.presto.spi.type.Type; -import org.openjdk.jol.info.ClassLayout; - -import static com.facebook.presto.type.TypeUtils.expectedValueSize; -import static java.util.Objects.requireNonNull; - -public class MultiKeyValuePairs -{ - private static final int INSTANCE_SIZE = ClassLayout.parseClass(MultiKeyValuePairs.class).instanceSize(); - private static final int EXPECTED_ENTRIES = 10; - private static final int EXPECTED_ENTRY_SIZE = 16; - - private final BlockBuilder keyBlockBuilder; - private final Type keyType; - - private final BlockBuilder valueBlockBuilder; - private final Type valueType; - - public MultiKeyValuePairs(Type keyType, Type valueType) - { - this.keyType = requireNonNull(keyType, "keyType is null"); - this.valueType = requireNonNull(valueType, "valueType is null"); - keyBlockBuilder = this.keyType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(keyType, EXPECTED_ENTRY_SIZE)); - valueBlockBuilder = this.valueType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(valueType, EXPECTED_ENTRY_SIZE)); - } - - public MultiKeyValuePairs(Block serialized, Type keyType, Type valueType) - { - this(keyType, valueType); - deserialize(requireNonNull(serialized, "serialized is null")); - } - - public Block getKeys() - { - return keyBlockBuilder.build(); - } - - public Block getValues() - { - return valueBlockBuilder.build(); - } - - private void deserialize(Block block) - { - for (int i = 0; i < block.getPositionCount(); i++) { - Block entryBlock = block.getObject(i, Block.class); - add(entryBlock, entryBlock, 0, 1); - } - } - - public void serialize(BlockBuilder out) - { - BlockBuilder arrayBlockBuilder = out.beginBlockEntry(); - for (int i = 0; i < keyBlockBuilder.getPositionCount(); i++) { - BlockBuilder rowBlockBuilder = arrayBlockBuilder.beginBlockEntry(); - keyType.appendTo(keyBlockBuilder, i, rowBlockBuilder); - valueType.appendTo(valueBlockBuilder, i, rowBlockBuilder); - arrayBlockBuilder.closeEntry(); - } - out.closeEntry(); - } - - /** - * Serialize as a multimap: map(key, array(value)), each key can be associated with multiple values - */ - public void toMultimapNativeEncoding(BlockBuilder blockBuilder) - { - Block keys = keyBlockBuilder.build(); - Block values = valueBlockBuilder.build(); - - // Merge values of the same key into an array - BlockBuilder distinctKeyBlockBuilder = keyType.createBlockBuilder(null, keys.getPositionCount(), expectedValueSize(keyType, EXPECTED_ENTRY_SIZE)); - ObjectBigArray valueArrayBlockBuilders = new ObjectBigArray<>(); - valueArrayBlockBuilders.ensureCapacity(keys.getPositionCount()); - TypedSet keySet = new TypedSet(keyType, keys.getPositionCount(), MultimapAggregationFunction.NAME); - for (int keyValueIndex = 0; keyValueIndex < keys.getPositionCount(); keyValueIndex++) { - if (!keySet.contains(keys, keyValueIndex)) { - keySet.add(keys, keyValueIndex); - keyType.appendTo(keys, keyValueIndex, distinctKeyBlockBuilder); - BlockBuilder valueArrayBuilder = valueType.createBlockBuilder(null, 10, expectedValueSize(valueType, EXPECTED_ENTRY_SIZE)); - valueArrayBlockBuilders.set(keySet.positionOf(keys, keyValueIndex), valueArrayBuilder); - } - valueType.appendTo(values, keyValueIndex, valueArrayBlockBuilders.get(keySet.positionOf(keys, keyValueIndex))); - } - - // Write keys and value arrays into one Block - Block distinctKeys = distinctKeyBlockBuilder.build(); - Type valueArrayType = new ArrayType(valueType); - BlockBuilder multimapBlockBuilder = blockBuilder.beginBlockEntry(); - for (int i = 0; i < distinctKeys.getPositionCount(); i++) { - keyType.appendTo(distinctKeys, i, multimapBlockBuilder); - valueArrayType.writeObject(multimapBlockBuilder, valueArrayBlockBuilders.get(i).build()); - } - blockBuilder.closeEntry(); - } - - public long estimatedInMemorySize() - { - long size = INSTANCE_SIZE; - size += keyBlockBuilder.getRetainedSizeInBytes(); - size += valueBlockBuilder.getRetainedSizeInBytes(); - return size; - } - - public void add(Block key, Block value, int keyPosition, int valuePosition) - { - keyType.appendTo(key, keyPosition, keyBlockBuilder); - if (value.isNull(valuePosition)) { - valueBlockBuilder.appendNull(); - } - else { - valueType.appendTo(value, valuePosition, valueBlockBuilder); - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ParametricAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ParametricAggregation.java index 80a30b7a39dc2..2f766698aec51 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ParametricAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ParametricAggregation.java @@ -18,6 +18,7 @@ import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlAggregationFunction; import com.facebook.presto.operator.ParametricImplementationsGroup; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; import com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType; import com.facebook.presto.operator.aggregation.state.StateCompiler; @@ -102,15 +103,16 @@ public InternalAggregationFunction specialize(BoundVariables variables, int arit inputHandle, combineHandle, outputHandle, - stateClass, - stateSerializer, - stateFactory, + ImmutableList.of(new AccumulatorStateDescriptor( + stateClass, + stateSerializer, + stateFactory)), outputType); // Create specialized InternalAggregregationFunction for Presto return new InternalAggregationFunction(getSignature().getName(), inputTypes, - stateSerializer.getSerializedType(), + ImmutableList.of(stateSerializer.getSerializedType()), outputType, details.isDecomposable(), details.isOrderSensitive(), diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/QuantileDigestAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/QuantileDigestAggregationFunction.java new file mode 100644 index 0000000000000..558ad10f8c8a3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/QuantileDigestAggregationFunction.java @@ -0,0 +1,227 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.metadata.BoundVariables; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.state.QuantileDigestState; +import com.facebook.presto.operator.aggregation.state.QuantileDigestStateFactory; +import com.facebook.presto.operator.aggregation.state.QuantileDigestStateSerializer; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.QuantileDigestType; +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.spi.type.TypeSignatureParameter; +import com.google.common.collect.ImmutableList; +import io.airlift.bytecode.DynamicClassLoader; +import io.airlift.stats.QuantileDigest; + +import java.lang.invoke.MethodHandle; +import java.util.List; +import java.util.stream.Collectors; + +import static com.facebook.presto.metadata.Signature.comparableTypeParameter; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.INPUT_CHANNEL; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.STATE; +import static com.facebook.presto.operator.aggregation.AggregationUtils.generateAggregationName; +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.doubleToSortableLong; +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.floatToSortableInt; +import static com.facebook.presto.operator.scalar.QuantileDigestFunctions.DEFAULT_ACCURACY; +import static com.facebook.presto.operator.scalar.QuantileDigestFunctions.DEFAULT_WEIGHT; +import static com.facebook.presto.operator.scalar.QuantileDigestFunctions.verifyAccuracy; +import static com.facebook.presto.operator.scalar.QuantileDigestFunctions.verifyWeight; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.util.Reflection.methodHandle; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.lang.Float.intBitsToFloat; +import static java.lang.String.format; +import static java.lang.invoke.MethodHandles.insertArguments; + +public final class QuantileDigestAggregationFunction + extends SqlAggregationFunction +{ + public static final QuantileDigestAggregationFunction QDIGEST_AGG = new QuantileDigestAggregationFunction(parseTypeSignature("V")); + public static final QuantileDigestAggregationFunction QDIGEST_AGG_WITH_WEIGHT = new QuantileDigestAggregationFunction(parseTypeSignature("V"), parseTypeSignature(StandardTypes.BIGINT)); + public static final QuantileDigestAggregationFunction QDIGEST_AGG_WITH_WEIGHT_AND_ERROR = new QuantileDigestAggregationFunction(parseTypeSignature("V"), parseTypeSignature(StandardTypes.BIGINT), parseTypeSignature(StandardTypes.DOUBLE)); + public static final String NAME = "qdigest_agg"; + + private static final MethodHandle INPUT_DOUBLE = methodHandle(QuantileDigestAggregationFunction.class, "inputDouble", QuantileDigestState.class, double.class, long.class, double.class); + private static final MethodHandle INPUT_REAL = methodHandle(QuantileDigestAggregationFunction.class, "inputReal", QuantileDigestState.class, long.class, long.class, double.class); + private static final MethodHandle INPUT_BIGINT = methodHandle(QuantileDigestAggregationFunction.class, "inputBigint", QuantileDigestState.class, long.class, long.class, double.class); + private static final MethodHandle COMBINE_FUNCTION = methodHandle(QuantileDigestAggregationFunction.class, "combineState", QuantileDigestState.class, QuantileDigestState.class); + private static final MethodHandle OUTPUT_FUNCTION = methodHandle(QuantileDigestAggregationFunction.class, "evaluateFinal", QuantileDigestStateSerializer.class, QuantileDigestState.class, BlockBuilder.class); + + private QuantileDigestAggregationFunction(TypeSignature... typeSignatures) + { + super( + NAME, + ImmutableList.of(comparableTypeParameter("V")), + ImmutableList.of(), + parseTypeSignature("qdigest(V)"), + ImmutableList.copyOf(typeSignatures)); + } + + @Override + public String getDescription() + { + return "Returns a qdigest from the set of reals, bigints or doubles"; + } + + @Override + public InternalAggregationFunction specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) + { + Type valueType = boundVariables.getTypeVariable("V"); + QuantileDigestType outputType = (QuantileDigestType) typeManager.getParameterizedType( + StandardTypes.QDIGEST, + ImmutableList.of(TypeSignatureParameter.of(valueType.getTypeSignature()))); + return generateAggregation(valueType, outputType, arity); + } + + private static InternalAggregationFunction generateAggregation(Type valueType, QuantileDigestType outputType, int arity) + { + DynamicClassLoader classLoader = new DynamicClassLoader(QuantileDigestAggregationFunction.class.getClassLoader()); + List inputTypes = getInputTypes(valueType, arity); + QuantileDigestStateSerializer stateSerializer = new QuantileDigestStateSerializer(valueType); + Type intermediateType = stateSerializer.getSerializedType(); + + AggregationMetadata metadata = new AggregationMetadata( + generateAggregationName(NAME, outputType.getTypeSignature(), inputTypes.stream().map(Type::getTypeSignature).collect(toImmutableList())), + createInputParameterMetadata(inputTypes), + getMethodHandle(valueType, arity), + COMBINE_FUNCTION, + OUTPUT_FUNCTION.bindTo(stateSerializer), + ImmutableList.of(new AccumulatorStateDescriptor( + QuantileDigestState.class, + stateSerializer, + new QuantileDigestStateFactory())), + outputType); + + GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, true, factory); + } + + private static List getInputTypes(Type valueType, int arity) + { + switch (arity) { + case 1: + // weight and accuracy unspecified + return ImmutableList.of(valueType); + case 2: + // weight specified, accuracy unspecified + return ImmutableList.of(valueType, BIGINT); + case 3: + // weight and accuracy specified + return ImmutableList.of(valueType, BIGINT, DOUBLE); + default: + throw new IllegalArgumentException(format("Unsupported number of arguments: %s", arity)); + } + } + + private static MethodHandle getMethodHandle(Type valueType, int arity) + { + final MethodHandle inputFunction; + switch (valueType.getDisplayName()) { + case StandardTypes.DOUBLE: + inputFunction = INPUT_DOUBLE; + break; + case StandardTypes.REAL: + inputFunction = INPUT_REAL; + break; + case StandardTypes.BIGINT: + inputFunction = INPUT_BIGINT; + break; + default: + throw new IllegalArgumentException(format("Unsupported type %s supplied", valueType.getDisplayName())); + } + + switch (arity) { + case 1: + // weight and accuracy unspecified + return insertArguments(inputFunction, 2, DEFAULT_WEIGHT, DEFAULT_ACCURACY); + case 2: + // weight specified, accuracy unspecified + return insertArguments(inputFunction, 3, DEFAULT_ACCURACY); + case 3: + // weight and accuracy specified + return inputFunction; + default: + throw new IllegalArgumentException(format("Unsupported number of arguments: %s", arity)); + } + } + + private static List createInputParameterMetadata(List valueTypes) + { + return ImmutableList.builder() + .add(new ParameterMetadata(STATE)) + .addAll(valueTypes.stream().map(valueType -> new ParameterMetadata(INPUT_CHANNEL, valueType)).collect(Collectors.toList())) + .build(); + } + + public static void inputDouble(QuantileDigestState state, double value, long weight, double accuracy) + { + inputBigint(state, doubleToSortableLong(value), weight, accuracy); + } + + public static void inputReal(QuantileDigestState state, long value, long weight, double accuracy) + { + inputBigint(state, floatToSortableInt(intBitsToFloat((int) value)), weight, accuracy); + } + + public static void inputBigint(QuantileDigestState state, long value, long weight, double accuracy) + { + QuantileDigest qdigest = getOrCreateQuantileDigest(state, verifyAccuracy(accuracy)); + state.addMemoryUsage(-qdigest.estimatedInMemorySizeInBytes()); + qdigest.add(value, verifyWeight(weight)); + state.addMemoryUsage(qdigest.estimatedInMemorySizeInBytes()); + } + + private static QuantileDigest getOrCreateQuantileDigest(QuantileDigestState state, double accuracy) + { + QuantileDigest qdigest = state.getQuantileDigest(); + if (qdigest == null) { + qdigest = new QuantileDigest(accuracy); + state.setQuantileDigest(qdigest); + state.addMemoryUsage(qdigest.estimatedInMemorySizeInBytes()); + } + return qdigest; + } + + public static void combineState(QuantileDigestState state, QuantileDigestState otherState) + { + QuantileDigest input = otherState.getQuantileDigest(); + + QuantileDigest previous = state.getQuantileDigest(); + if (previous == null) { + state.setQuantileDigest(input); + state.addMemoryUsage(input.estimatedInMemorySizeInBytes()); + } + else { + state.addMemoryUsage(-previous.estimatedInMemorySizeInBytes()); + previous.merge(input); + state.addMemoryUsage(previous.estimatedInMemorySizeInBytes()); + } + } + + public static void evaluateFinal(QuantileDigestStateSerializer serializer, QuantileDigestState state, BlockBuilder out) + { + serializer.serialize(state, out); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/RealAverageAggregation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/RealAverageAggregation.java index 3fa216906a6fe..c991205f61a63 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/RealAverageAggregation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/RealAverageAggregation.java @@ -13,49 +13,125 @@ */ package com.facebook.presto.operator.aggregation; -import com.facebook.presto.operator.aggregation.state.LongAndDoubleState; +import com.facebook.presto.metadata.BoundVariables; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; +import com.facebook.presto.operator.aggregation.state.DoubleState; +import com.facebook.presto.operator.aggregation.state.LongState; +import com.facebook.presto.operator.aggregation.state.StateCompiler; import com.facebook.presto.spi.block.BlockBuilder; -import com.facebook.presto.spi.function.AggregationFunction; -import com.facebook.presto.spi.function.AggregationState; -import com.facebook.presto.spi.function.CombineFunction; -import com.facebook.presto.spi.function.InputFunction; -import com.facebook.presto.spi.function.OutputFunction; -import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.function.AccumulatorState; +import com.facebook.presto.spi.function.AccumulatorStateSerializer; import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.google.common.collect.ImmutableList; +import io.airlift.bytecode.DynamicClassLoader; +import java.lang.invoke.MethodHandle; +import java.util.List; + +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.BLOCK_INDEX; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.BLOCK_INPUT_CHANNEL; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.INPUT_CHANNEL; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.STATE; +import static com.facebook.presto.operator.aggregation.AggregationUtils.generateAggregationName; import static com.facebook.presto.spi.type.RealType.REAL; -import static java.lang.Float.floatToRawIntBits; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.util.Reflection.methodHandle; +import static java.lang.Float.floatToIntBits; import static java.lang.Float.intBitsToFloat; -@AggregationFunction("avg") -public final class RealAverageAggregation +public class RealAverageAggregation + extends SqlAggregationFunction { - private RealAverageAggregation() {} + public static final RealAverageAggregation REAL_AVERAGE_AGGREGATION = new RealAverageAggregation(); + private static final String NAME = "avg"; + + private static final MethodHandle INPUT_FUNCTION = methodHandle(RealAverageAggregation.class, "input", LongState.class, DoubleState.class, long.class); + private static final MethodHandle COMBINE_FUNCTION = methodHandle(RealAverageAggregation.class, "combine", LongState.class, DoubleState.class, LongState.class, DoubleState.class); + private static final MethodHandle OUTPUT_FUNCTION = methodHandle(RealAverageAggregation.class, "output", LongState.class, DoubleState.class, BlockBuilder.class); + + protected RealAverageAggregation() + { + super(NAME, + ImmutableList.of(), + ImmutableList.of(), + parseTypeSignature(StandardTypes.REAL), + ImmutableList.of(parseTypeSignature(StandardTypes.REAL))); + } + + @Override + public String getDescription() + { + return "Returns the average value of the argument"; + } + + @Override + public InternalAggregationFunction specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) + { + DynamicClassLoader classLoader = new DynamicClassLoader(AverageAggregations.class.getClassLoader()); + Class longStateInterface = LongState.class; + Class doubleStateInterface = DoubleState.class; + AccumulatorStateSerializer longStateSerializer = StateCompiler.generateStateSerializer(longStateInterface, classLoader); + AccumulatorStateSerializer doubleStateSerializer = StateCompiler.generateStateSerializer(doubleStateInterface, classLoader); + + AggregationMetadata metadata = new AggregationMetadata( + generateAggregationName(NAME, parseTypeSignature(StandardTypes.REAL), ImmutableList.of(parseTypeSignature(StandardTypes.REAL))), + ImmutableList.of(new ParameterMetadata(STATE), new ParameterMetadata(STATE), new ParameterMetadata(INPUT_CHANNEL, REAL)), + INPUT_FUNCTION, + COMBINE_FUNCTION, + OUTPUT_FUNCTION, + ImmutableList.of( + new AccumulatorStateDescriptor( + longStateInterface, + longStateSerializer, + StateCompiler.generateStateFactory(longStateInterface, classLoader)), + new AccumulatorStateDescriptor( + doubleStateInterface, + doubleStateSerializer, + StateCompiler.generateStateFactory(doubleStateInterface, classLoader))), + REAL); + + GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); + return new InternalAggregationFunction( + NAME, + ImmutableList.of(REAL), + ImmutableList.of( + longStateSerializer.getSerializedType(), + doubleStateSerializer.getSerializedType()), + REAL, + true, + false, + factory); + } + + private static List createInputParameterMetadata(Type value) + { + return ImmutableList.of(new ParameterMetadata(STATE), new ParameterMetadata(BLOCK_INPUT_CHANNEL, value), new ParameterMetadata(BLOCK_INDEX)); + } - @InputFunction - public static void input(@AggregationState LongAndDoubleState state, @SqlType(StandardTypes.REAL) long value) + public static void input(LongState count, DoubleState sum, long value) { - state.setLong(state.getLong() + 1); - state.setDouble(state.getDouble() + intBitsToFloat((int) value)); + count.setLong(count.getLong() + 1); + sum.setDouble(sum.getDouble() + intBitsToFloat((int) value)); } - @CombineFunction - public static void combine(@AggregationState LongAndDoubleState state, LongAndDoubleState otherState) + public static void combine(LongState count, DoubleState sum, LongState otherCount, DoubleState otherSum) { - state.setLong(state.getLong() + otherState.getLong()); - state.setDouble(state.getDouble() + otherState.getDouble()); + count.setLong(count.getLong() + otherCount.getLong()); + sum.setDouble(sum.getDouble() + otherSum.getDouble()); } - @OutputFunction(StandardTypes.REAL) - public static void output(@AggregationState LongAndDoubleState state, BlockBuilder out) + public static void output(LongState count, DoubleState sum, BlockBuilder out) { - long count = state.getLong(); - if (count == 0) { + if (count.getLong() == 0) { out.appendNull(); } else { - double average = state.getDouble() / count; - REAL.writeLong(out, floatToRawIntBits((float) average)); + REAL.writeLong(out, floatToIntBits((float) (sum.getDouble() / count.getLong()))); } } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ReduceAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ReduceAggregationFunction.java new file mode 100644 index 0000000000000..a251357155c07 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/ReduceAggregationFunction.java @@ -0,0 +1,218 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.metadata.BoundVariables; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.SqlAggregationFunction; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; +import com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; +import com.facebook.presto.operator.aggregation.state.NullableBooleanState; +import com.facebook.presto.operator.aggregation.state.NullableDoubleState; +import com.facebook.presto.operator.aggregation.state.NullableLongState; +import com.facebook.presto.operator.aggregation.state.StateCompiler; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.sql.gen.lambda.BinaryFunctionInterface; +import com.google.common.collect.ImmutableList; +import io.airlift.bytecode.DynamicClassLoader; + +import java.lang.invoke.MethodHandle; +import java.util.List; + +import static com.facebook.presto.metadata.Signature.typeVariable; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.INPUT_CHANNEL; +import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.STATE; +import static com.facebook.presto.operator.aggregation.AggregationUtils.generateAggregationName; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.util.Reflection.methodHandle; + +public class ReduceAggregationFunction + extends SqlAggregationFunction +{ + public static final ReduceAggregationFunction REDUCE_AGG = new ReduceAggregationFunction(); + private static final String NAME = "reduce_agg"; + + private static final MethodHandle LONG_STATE_INPUT_FUNCTION = methodHandle(ReduceAggregationFunction.class, "input", NullableLongState.class, Object.class, long.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + private static final MethodHandle DOUBLE_STATE_INPUT_FUNCTION = methodHandle(ReduceAggregationFunction.class, "input", NullableDoubleState.class, Object.class, double.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + private static final MethodHandle BOOLEAN_STATE_INPUT_FUNCTION = methodHandle(ReduceAggregationFunction.class, "input", NullableBooleanState.class, Object.class, boolean.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + + private static final MethodHandle LONG_STATE_COMBINE_FUNCTION = methodHandle(ReduceAggregationFunction.class, "combine", NullableLongState.class, NullableLongState.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + private static final MethodHandle DOUBLE_STATE_COMBINE_FUNCTION = methodHandle(ReduceAggregationFunction.class, "combine", NullableDoubleState.class, NullableDoubleState.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + private static final MethodHandle BOOLEAN_STATE_COMBINE_FUNCTION = methodHandle(ReduceAggregationFunction.class, "combine", NullableBooleanState.class, NullableBooleanState.class, BinaryFunctionInterface.class, BinaryFunctionInterface.class); + + private static final MethodHandle LONG_STATE_OUTPUT_FUNCTION = methodHandle(NullableLongState.class, "write", Type.class, NullableLongState.class, BlockBuilder.class); + private static final MethodHandle DOUBLE_STATE_OUTPUT_FUNCTION = methodHandle(NullableDoubleState.class, "write", Type.class, NullableDoubleState.class, BlockBuilder.class); + private static final MethodHandle BOOLEAN_STATE_OUTPUT_FUNCTION = methodHandle(NullableBooleanState.class, "write", Type.class, NullableBooleanState.class, BlockBuilder.class); + + public ReduceAggregationFunction() + { + super(NAME, + ImmutableList.of(typeVariable("T"), typeVariable("S")), + ImmutableList.of(), + parseTypeSignature("S"), + ImmutableList.of( + parseTypeSignature("T"), + parseTypeSignature("S"), + parseTypeSignature("function(S,T,S)"), + parseTypeSignature("function(S,S,S)"))); + } + + @Override + public String getDescription() + { + return "Reduce input elements into a single value"; + } + + @Override + public InternalAggregationFunction specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) + { + Type inputType = boundVariables.getTypeVariable("T"); + Type stateType = boundVariables.getTypeVariable("S"); + return generateAggregation(inputType, stateType); + } + + private InternalAggregationFunction generateAggregation(Type inputType, Type stateType) + { + DynamicClassLoader classLoader = new DynamicClassLoader(ReduceAggregationFunction.class.getClassLoader()); + + MethodHandle inputMethodHandle; + MethodHandle combineMethodHandle; + MethodHandle outputMethodHandle; + AccumulatorStateDescriptor stateDescriptor; + + if (stateType.getJavaType() == long.class) { + inputMethodHandle = LONG_STATE_INPUT_FUNCTION; + combineMethodHandle = LONG_STATE_COMBINE_FUNCTION; + outputMethodHandle = LONG_STATE_OUTPUT_FUNCTION.bindTo(stateType); + stateDescriptor = new AccumulatorStateDescriptor( + NullableLongState.class, + StateCompiler.generateStateSerializer(NullableLongState.class, classLoader), + StateCompiler.generateStateFactory(NullableLongState.class, classLoader)); + } + else if (stateType.getJavaType() == double.class) { + inputMethodHandle = DOUBLE_STATE_INPUT_FUNCTION; + combineMethodHandle = DOUBLE_STATE_COMBINE_FUNCTION; + outputMethodHandle = DOUBLE_STATE_OUTPUT_FUNCTION.bindTo(stateType); + stateDescriptor = new AccumulatorStateDescriptor( + NullableDoubleState.class, + StateCompiler.generateStateSerializer(NullableDoubleState.class, classLoader), + StateCompiler.generateStateFactory(NullableDoubleState.class, classLoader)); + } + else if (stateType.getJavaType() == boolean.class) { + inputMethodHandle = BOOLEAN_STATE_INPUT_FUNCTION; + combineMethodHandle = BOOLEAN_STATE_COMBINE_FUNCTION; + outputMethodHandle = BOOLEAN_STATE_OUTPUT_FUNCTION.bindTo(stateType); + stateDescriptor = new AccumulatorStateDescriptor( + NullableBooleanState.class, + StateCompiler.generateStateSerializer(NullableBooleanState.class, classLoader), + StateCompiler.generateStateFactory(NullableBooleanState.class, classLoader)); + } + else { + // State with Slice or Block as native container type is intentionally not supported yet, + // as it may result in excessive JVM memory usage of remembered set. + // See JDK-8017163. + throw new UnsupportedOperationException(); + } + + AggregationMetadata metadata = new AggregationMetadata( + generateAggregationName(getSignature().getName(), inputType.getTypeSignature(), ImmutableList.of(inputType.getTypeSignature())), + createInputParameterMetadata(inputType, stateType), + inputMethodHandle.asType( + inputMethodHandle.type() + .changeParameterType(1, inputType.getJavaType())), + combineMethodHandle, + outputMethodHandle, + ImmutableList.of(stateDescriptor), + inputType, + ImmutableList.of(BinaryFunctionInterface.class, BinaryFunctionInterface.class)); + + GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); + return new InternalAggregationFunction( + getSignature().getName(), + ImmutableList.of(inputType), + ImmutableList.of(stateType), + stateType, + true, + false, + factory, + ImmutableList.of(BinaryFunctionInterface.class, BinaryFunctionInterface.class)); + } + + private static List createInputParameterMetadata(Type inputType, Type stateType) + { + return ImmutableList.of( + new ParameterMetadata(STATE), + new ParameterMetadata(INPUT_CHANNEL, inputType), + new ParameterMetadata(INPUT_CHANNEL, stateType)); + } + + public static void input(NullableLongState state, Object value, long initialStateValue, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setLong(initialStateValue); + } + state.setLong((long) inputFunction.apply(state.getLong(), value)); + } + + public static void input(NullableDoubleState state, Object value, double initialStateValue, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setDouble(initialStateValue); + } + state.setDouble((double) inputFunction.apply(state.getDouble(), value)); + } + + public static void input(NullableBooleanState state, Object value, boolean initialStateValue, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setBoolean(initialStateValue); + } + state.setBoolean((boolean) inputFunction.apply(state.getBoolean(), value)); + } + + public static void combine(NullableLongState state, NullableLongState otherState, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setLong(otherState.getLong()); + return; + } + state.setLong((long) combineFunction.apply(state.getLong(), otherState.getLong())); + } + + public static void combine(NullableDoubleState state, NullableDoubleState otherState, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setDouble(otherState.getDouble()); + return; + } + state.setDouble((double) combineFunction.apply(state.getDouble(), otherState.getDouble())); + } + + public static void combine(NullableBooleanState state, NullableBooleanState otherState, BinaryFunctionInterface inputFunction, BinaryFunctionInterface combineFunction) + { + if (state.isNull()) { + state.setNull(false); + state.setBoolean(otherState.getBoolean()); + return; + } + state.setBoolean((boolean) combineFunction.apply(state.getBoolean(), otherState.getBoolean())); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/TypedSet.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/TypedSet.java index ac5eec5bf886e..5135afb062ed5 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/TypedSet.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/TypedSet.java @@ -47,6 +47,11 @@ public class TypedSet private final BlockBuilder elementBlock; private final String functionName; + private int initialElementBlockOffset; + private long initialElementBlockSizeInBytes; + // size is the number of elements added to the TypedSet (including null). + // It equals to elementBlock.size() - initialElementBlockOffset + private int size; private int hashCapacity; private int maxFill; private int hashMask; @@ -55,13 +60,22 @@ public class TypedSet private boolean containsNullElement; public TypedSet(Type elementType, int expectedSize, String functionName) + { + this(elementType, elementType.createBlockBuilder(null, expectedSize), expectedSize, functionName); + } + + public TypedSet(Type elementType, BlockBuilder blockBuilder, int expectedSize, String functionName) { checkArgument(expectedSize >= 0, "expectedSize must not be negative"); this.elementType = requireNonNull(elementType, "elementType must not be null"); - this.elementBlock = elementType.createBlockBuilder(null, expectedSize); + this.elementBlock = requireNonNull(blockBuilder, "blockBuilder must not be null"); this.functionName = functionName; - hashCapacity = arraySize(expectedSize, FILL_RATIO); + initialElementBlockOffset = elementBlock.getPositionCount(); + initialElementBlockSizeInBytes = elementBlock.getSizeInBytes(); + + this.size = 0; + this.hashCapacity = arraySize(expectedSize, FILL_RATIO); this.maxFill = calculateMaxFill(hashCapacity); this.hashMask = hashCapacity - 1; @@ -97,20 +111,20 @@ public void add(Block block, int position) requireNonNull(block, "block must not be null"); checkArgument(position >= 0, "position must be >= 0"); + // containsNullElement flag is maintained so contains() method can have shortcut for null value if (block.isNull(position)) { containsNullElement = true; } - else { - int hashPosition = getHashPositionOfElement(block, position); - if (blockPositionByHash.get(hashPosition) == EMPTY_SLOT) { - addNewElement(hashPosition, block, position); - } + + int hashPosition = getHashPositionOfElement(block, position); + if (blockPositionByHash.get(hashPosition) == EMPTY_SLOT) { + addNewElement(hashPosition, block, position); } } public int size() { - return elementBlock.getPositionCount() + (containsNullElement ? 1 : 0); + return size; } public int positionOf(Block block, int position) @@ -142,7 +156,7 @@ else if (positionEqualsPosition(elementType, elementBlock, blockPosition, block, private void addNewElement(int hashPosition, Block block, int position) { elementType.appendTo(block, position, elementBlock); - if (elementBlock.getSizeInBytes() > FOUR_MEGABYTES) { + if (elementBlock.getSizeInBytes() - initialElementBlockSizeInBytes > FOUR_MEGABYTES) { throw new PrestoException( EXCEEDED_FUNCTION_MEMORY_LIMIT, format("The input to %s is too large. More than %s of memory is needed to hold the intermediate hash set.\n", @@ -152,7 +166,8 @@ private void addNewElement(int hashPosition, Block block, int position) blockPositionByHash.set(hashPosition, elementBlock.getPositionCount() - 1); // increase capacity, if necessary - if (elementBlock.getPositionCount() >= maxFill) { + size++; + if (size >= maxFill) { rehash(); } } @@ -173,13 +188,8 @@ private void rehash() blockPositionByHash.set(i, EMPTY_SLOT); } - rehashBlock(elementBlock); - } - - private void rehashBlock(Block block) - { - for (int blockPosition = 0; blockPosition < block.getPositionCount(); blockPosition++) { - blockPositionByHash.set(getHashPositionOfElement(block, blockPosition), blockPosition); + for (int blockPosition = initialElementBlockOffset; blockPosition < elementBlock.getPositionCount(); blockPosition++) { + blockPositionByHash.set(getHashPositionOfElement(elementBlock, blockPosition), blockPosition); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationFunction.java index fde37518c3f3f..ccfca03b937d8 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationFunction.java @@ -18,6 +18,7 @@ import com.facebook.presto.metadata.SqlAggregationFunction; import com.facebook.presto.operator.aggregation.AccumulatorCompiler; import com.facebook.presto.operator.aggregation.AggregationMetadata; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata; import com.facebook.presto.operator.aggregation.GenericAccumulatorFactoryBinder; import com.facebook.presto.operator.aggregation.InternalAggregationFunction; @@ -104,13 +105,14 @@ private static InternalAggregationFunction generateAggregation(Type type, boolea inputFunction, combineFunction, outputFunction, - stateInterface, - stateSerializer, - stateFactory, + ImmutableList.of(new AccumulatorStateDescriptor( + stateInterface, + stateSerializer, + stateFactory)), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, outputType, true, true, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, true, factory); } private static List createInputParameterMetadata(Type value, boolean legacyArrayAgg) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationStateFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationStateFactory.java index 23c74ac57c53e..c682fd4ea615f 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationStateFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/ArrayAggregationStateFactory.java @@ -17,8 +17,6 @@ import com.facebook.presto.spi.function.AccumulatorStateFactory; import com.facebook.presto.spi.type.Type; -import static com.facebook.presto.operator.aggregation.arrayagg.ArrayAggGroupImplementation.LEGACY; -import static com.facebook.presto.operator.aggregation.arrayagg.ArrayAggGroupImplementation.NEW; import static com.facebook.presto.spi.StandardErrorCode.FUNCTION_IMPLEMENTATION_ERROR; import static java.lang.String.format; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/GroupArrayAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/GroupArrayAggregationState.java index a46bb8937bf40..1c1fc307a410c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/GroupArrayAggregationState.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/GroupArrayAggregationState.java @@ -13,165 +13,34 @@ */ package com.facebook.presto.operator.aggregation.arrayagg; -import com.facebook.presto.array.IntBigArray; -import com.facebook.presto.array.ShortBigArray; -import com.facebook.presto.operator.aggregation.state.AbstractGroupedAccumulatorState; +import com.facebook.presto.operator.aggregation.AbstractGroupCollectionAggregationState; +import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.block.BlockBuilder; -import com.facebook.presto.spi.block.PageBuilderStatus; import com.facebook.presto.spi.type.Type; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.longs.LongList; -import org.openjdk.jol.info.ClassLayout; +import com.google.common.collect.ImmutableList; -import java.util.ArrayList; -import java.util.List; - -import static com.google.common.base.Verify.verify; - -/** - * state object that uses a single BlockBuilder for all groups. - */ -public class GroupArrayAggregationState - extends AbstractGroupedAccumulatorState +public final class GroupArrayAggregationState + extends AbstractGroupCollectionAggregationState implements ArrayAggregationState { - private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupArrayAggregationState.class).instanceSize(); private static final int MAX_BLOCK_SIZE = 1024 * 1024; - private static final int MAX_NUM_BLOCKS = 30000; - private static final short NULL = -1; - - private final Type type; - - private final ShortBigArray headBlockIndex; - private final IntBigArray headPosition; - - private final ShortBigArray nextBlockIndex; - private final IntBigArray nextPosition; - - private final ShortBigArray tailBlockIndex; - private final IntBigArray tailPosition; - - private final List values; - private final LongList sumPositions; - private BlockBuilder currentBlockBuilder; - private PageBuilderStatus pageBuilderStatus; - - private long valueBlocksRetainedSizeInBytes; - private long totalPositions; - private long capacity; - - public GroupArrayAggregationState(Type type) - { - this.type = type; - this.headBlockIndex = new ShortBigArray(NULL); - this.headPosition = new IntBigArray(NULL); - this.nextBlockIndex = new ShortBigArray(NULL); - this.nextPosition = new IntBigArray(NULL); - this.tailBlockIndex = new ShortBigArray(NULL); - this.tailPosition = new IntBigArray(NULL); - - this.pageBuilderStatus = new PageBuilderStatus(MAX_BLOCK_SIZE); - this.currentBlockBuilder = type.createBlockBuilder(pageBuilderStatus.createBlockBuilderStatus(), 16); - this.values = new ArrayList<>(); - this.sumPositions = new LongArrayList(); - values.add(currentBlockBuilder); - sumPositions.add(0L); - valueBlocksRetainedSizeInBytes = 0; - - totalPositions = 0; - capacity = 1024; - nextBlockIndex.ensureCapacity(capacity); - nextPosition.ensureCapacity(capacity); - } - - @Override - public void ensureCapacity(long size) - { - headBlockIndex.ensureCapacity(size); - headPosition.ensureCapacity(size); - tailBlockIndex.ensureCapacity(size); - tailPosition.ensureCapacity(size); - } - - @Override - public long getEstimatedSize() - { - return INSTANCE_SIZE + - headBlockIndex.sizeOf() + - headPosition.sizeOf() + - tailBlockIndex.sizeOf() + - tailPosition.sizeOf() + - nextBlockIndex.sizeOf() + - nextPosition.sizeOf() + - valueBlocksRetainedSizeInBytes + - // valueBlocksRetainedSizeInBytes doesn't contain the current block builder - currentBlockBuilder.getRetainedSizeInBytes(); - } + private static final int VALUE_CHANNEL = 0; - @Override - public void add(Block block, int position) + GroupArrayAggregationState(Type valueType) { - long currentGroupId = getGroupId(); - short insertedBlockIndex = (short) (values.size() - 1); - int insertedPosition = currentBlockBuilder.getPositionCount(); - - if (totalPositions == capacity) { - capacity *= 1.5; - nextBlockIndex.ensureCapacity(capacity); - nextPosition.ensureCapacity(capacity); - } - - if (isEmpty()) { - // new linked list, set up the header pointer - headBlockIndex.set(currentGroupId, insertedBlockIndex); - headPosition.set(currentGroupId, insertedPosition); - } - else { - // existing linked list, link the new entry to the tail - long absoluteTailAddress = toAbsolutePosition(tailBlockIndex.get(currentGroupId), tailPosition.get(currentGroupId)); - nextBlockIndex.set(absoluteTailAddress, insertedBlockIndex); - nextPosition.set(absoluteTailAddress, insertedPosition); - } - tailBlockIndex.set(currentGroupId, insertedBlockIndex); - tailPosition.set(currentGroupId, insertedPosition); - - type.appendTo(block, position, currentBlockBuilder); - totalPositions++; - - if (pageBuilderStatus.isFull()) { - valueBlocksRetainedSizeInBytes += currentBlockBuilder.getRetainedSizeInBytes(); - sumPositions.add(totalPositions); - pageBuilderStatus = new PageBuilderStatus(MAX_BLOCK_SIZE); - currentBlockBuilder = currentBlockBuilder.newBlockBuilderLike(pageBuilderStatus.createBlockBuilderStatus()); - values.add(currentBlockBuilder); - - verify(values.size() <= MAX_NUM_BLOCKS); - } + super(PageBuilder.withMaxPageSize(MAX_BLOCK_SIZE, ImmutableList.of(valueType))); } @Override - public void forEach(ArrayAggregationStateConsumer consumer) + public final void add(Block block, int position) { - short currentBlockId = headBlockIndex.get(getGroupId()); - int currentPosition = headPosition.get(getGroupId()); - while (currentBlockId != NULL) { - consumer.accept(values.get(currentBlockId), currentPosition); - - long absoluteCurrentAddress = toAbsolutePosition(currentBlockId, currentPosition); - currentBlockId = nextBlockIndex.get(absoluteCurrentAddress); - currentPosition = nextPosition.get(absoluteCurrentAddress); - } + prepareAdd(); + appendAtChannel(VALUE_CHANNEL, block, position); } @Override - public boolean isEmpty() - { - return headBlockIndex.get(getGroupId()) == NULL; - } - - private long toAbsolutePosition(short blockId, int position) + protected final void accept(ArrayAggregationStateConsumer consumer, PageBuilder pageBuilder, int currentPosition) { - return sumPositions.get(blockId) + position; + consumer.accept(pageBuilder.getBlockBuilder(VALUE_CHANNEL), currentPosition); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java index d3806c200f37d..773ccc3ce836f 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/arrayagg/LegacyArrayAggregationGroupState.java @@ -28,7 +28,7 @@ public class LegacyArrayAggregationGroupState extends AbstractGroupedAccumulatorState implements ArrayAggregationState { - private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupArrayAggregationState.class).instanceSize(); + private static final int INSTANCE_SIZE = ClassLayout.parseClass(LegacyArrayAggregationGroupState.class).instanceSize(); private final ObjectBigArray blockBuilders; private final Type type; private long size; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/InMemoryHashAggregationBuilder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/InMemoryHashAggregationBuilder.java index cb521940f192f..cdd7ad23517d1 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/InMemoryHashAggregationBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/InMemoryHashAggregationBuilder.java @@ -23,7 +23,7 @@ import com.facebook.presto.operator.UpdateMemory; import com.facebook.presto.operator.Work; import com.facebook.presto.operator.WorkProcessor; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; +import com.facebook.presto.operator.WorkProcessor.ProcessState; import com.facebook.presto.operator.aggregation.AccumulatorFactory; import com.facebook.presto.operator.aggregation.GroupedAccumulator; import com.facebook.presto.spi.Page; @@ -45,6 +45,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.OptionalLong; import static com.facebook.presto.SystemSessionProperties.isDictionaryAggregationEnabled; import static com.facebook.presto.operator.GroupByHash.createGroupByHash; @@ -59,9 +60,10 @@ public class InMemoryHashAggregationBuilder private final List aggregators; private final OperatorContext operatorContext; private final boolean partial; - private final long maxPartialMemory; + private final OptionalLong maxPartialMemory; private final LocalMemoryContext systemMemoryContext; private final LocalMemoryContext localUserMemoryContext; + private final boolean useSystemMemory; private boolean full; @@ -73,9 +75,10 @@ public InMemoryHashAggregationBuilder( List groupByChannels, Optional hashChannel, OperatorContext operatorContext, - DataSize maxPartialMemory, + Optional maxPartialMemory, JoinCompiler joinCompiler, - boolean yieldForMemoryReservation) + boolean yieldForMemoryReservation, + boolean useSystemMemory) { this(accumulatorFactories, step, @@ -87,21 +90,23 @@ public InMemoryHashAggregationBuilder( maxPartialMemory, Optional.empty(), joinCompiler, - yieldForMemoryReservation); + yieldForMemoryReservation, + useSystemMemory); } public InMemoryHashAggregationBuilder( List accumulatorFactories, - AggregationNode.Step step, + Step step, int expectedGroups, List groupByTypes, List groupByChannels, Optional hashChannel, OperatorContext operatorContext, - DataSize maxPartialMemory, + Optional maxPartialMemory, Optional overwriteIntermediateChannelOffset, JoinCompiler joinCompiler, - boolean yieldForMemoryReservation) + boolean yieldForMemoryReservation, + boolean useSystemMemory) { UpdateMemory updateMemory; if (yieldForMemoryReservation) { @@ -126,9 +131,10 @@ public InMemoryHashAggregationBuilder( updateMemory); this.operatorContext = operatorContext; this.partial = step.isOutputPartial(); - this.maxPartialMemory = maxPartialMemory.toBytes(); + this.maxPartialMemory = maxPartialMemory.map(dataSize -> OptionalLong.of(dataSize.toBytes())).orElseGet(OptionalLong::empty); this.systemMemoryContext = operatorContext.newLocalSystemMemoryContext(InMemoryHashAggregationBuilder.class.getSimpleName()); this.localUserMemoryContext = operatorContext.localUserMemoryContext(); + this.useSystemMemory = useSystemMemory; // wrapper each function with an aggregator ImmutableList.Builder builder = ImmutableList.builder(); @@ -147,12 +153,7 @@ public InMemoryHashAggregationBuilder( @Override public void close() { - if (partial) { - systemMemoryContext.setBytes(0); - } - else { - localUserMemoryContext.setBytes(0); - } + updateMemory(0); } @Override @@ -283,7 +284,7 @@ private WorkProcessor buildResult(IntIterator groupIds) final PageBuilder pageBuilder = new PageBuilder(buildTypes()); return WorkProcessor.create(() -> { if (!groupIds.hasNext()) { - return ProcessorState.finished(); + return ProcessState.finished(); } pageBuilder.reset(); @@ -302,7 +303,7 @@ private WorkProcessor buildResult(IntIterator groupIds) } } - return ProcessorState.ofResult(pageBuilder.build()); + return ProcessState.ofResult(pageBuilder.build()); }); } @@ -326,18 +327,28 @@ public List buildTypes() private boolean updateMemoryWithYieldInfo() { long memorySize = getSizeInMemory(); - if (partial) { - systemMemoryContext.setBytes(memorySize); - full = (memorySize > maxPartialMemory); + if (partial && maxPartialMemory.isPresent()) { + updateMemory(memorySize); + full = (memorySize > maxPartialMemory.getAsLong()); return true; } // Operator/driver will be blocked on memory after we call setBytes. // If memory is not available, once we return, this operator will be blocked until memory is available. - localUserMemoryContext.setBytes(memorySize); + updateMemory(memorySize); // If memory is not available, inform the caller that we cannot proceed for allocation. return operatorContext.isWaitingForMemory().isDone(); } + private void updateMemory(long memorySize) + { + if (useSystemMemory) { + systemMemoryContext.setBytes(memorySize); + } + else { + localUserMemoryContext.setBytes(memorySize); + } + } + private IntIterator consecutiveGroupIds() { return IntIterators.fromTo(0, groupByHash.getGroupCount()); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/MergingHashAggregationBuilder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/MergingHashAggregationBuilder.java index ebe705845f4f2..f54c3ceb576db 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/MergingHashAggregationBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/MergingHashAggregationBuilder.java @@ -16,8 +16,8 @@ import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.operator.OperatorContext; import com.facebook.presto.operator.WorkProcessor; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; import com.facebook.presto.operator.WorkProcessor.Transformation; +import com.facebook.presto.operator.WorkProcessor.TransformationState; import com.facebook.presto.operator.aggregation.AccumulatorFactory; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.type.Type; @@ -90,7 +90,7 @@ public WorkProcessor buildResult() boolean reset = true; long memorySize; - public ProcessorState> process(Optional inputPageOptional) + public TransformationState> process(Optional inputPageOptional) { if (reset) { rebuildHashAggregationBuilder(); @@ -101,7 +101,7 @@ public ProcessorState> process(Optional inputPageOptio boolean inputFinished = !inputPageOptional.isPresent(); if (inputFinished && memorySize == 0) { // no more pages and aggregation builder is empty - return ProcessorState.finished(); + return TransformationState.finished(); } if (!inputFinished) { @@ -113,12 +113,14 @@ public ProcessorState> process(Optional inputPageOptio systemMemoryContext.setBytes(memorySize); if (!shouldProduceOutput(memorySize)) { - return ProcessorState.needsMoreData(); + return TransformationState.needsMoreData(); } } reset = true; - return ProcessorState.ofResult(hashAggregationBuilder.buildResult(), !inputFinished); + // we can produce output after every input page, because input pages do not have + // hash values that span multiple pages (guaranteed by MergeHashSort) + return TransformationState.ofResult(hashAggregationBuilder.buildResult(), !inputFinished); } }); } @@ -144,9 +146,10 @@ private void rebuildHashAggregationBuilder() groupByPartialChannels, hashChannel, operatorContext, - DataSize.succinctBytes(0), + Optional.of(DataSize.succinctBytes(0)), Optional.of(overwriteIntermediateChannelOffset), joinCompiler, + false, false); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/SpillableHashAggregationBuilder.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/SpillableHashAggregationBuilder.java index 2e24b715bade3..900302cdd610d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/SpillableHashAggregationBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/builder/SpillableHashAggregationBuilder.java @@ -299,8 +299,9 @@ private void rebuildHashAggregationBuilder() groupByChannels, hashChannel, operatorContext, - DataSize.succinctBytes(0), + Optional.of(DataSize.succinctBytes(0)), joinCompiler, + false, false); emptyHashAggregationBuilderSize = hashAggregationBuilder.getSizeInMemory(); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/Histogram.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/Histogram.java index 19efa5b7ce0ee..7245b01553d0c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/Histogram.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/Histogram.java @@ -18,6 +18,7 @@ import com.facebook.presto.metadata.SqlAggregationFunction; import com.facebook.presto.operator.aggregation.AccumulatorCompiler; import com.facebook.presto.operator.aggregation.AggregationMetadata; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.GenericAccumulatorFactoryBinder; import com.facebook.presto.operator.aggregation.InternalAggregationFunction; import com.facebook.presto.spi.block.Block; @@ -100,13 +101,14 @@ private static InternalAggregationFunction generateAggregation( inputFunction, COMBINE_FUNCTION, outputFunction, - HistogramState.class, - stateSerializer, - new HistogramStateFactory(keyType, EXPECTED_SIZE_FOR_HASHING, groupMode), + ImmutableList.of(new AccumulatorStateDescriptor( + HistogramState.class, + stateSerializer, + new HistogramStateFactory(keyType, EXPECTED_SIZE_FOR_HASHING, groupMode))), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(functionName, inputTypes, intermediateType, outputType, true, false, factory); + return new InternalAggregationFunction(functionName, inputTypes, ImmutableList.of(intermediateType), outputType, true, false, factory); } private static List createInputParameterMetadata(Type keyType) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramGroupImplementation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramGroupImplementation.java index b2039b17f94db..7af33df10e483 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramGroupImplementation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramGroupImplementation.java @@ -15,6 +15,5 @@ public enum HistogramGroupImplementation { - LEGACY, - NEW,; + LEGACY, NEW } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramState.java index 24597d745718d..8fdc6db11db97 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramState.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/histogram/HistogramState.java @@ -24,6 +24,7 @@ public interface HistogramState { /** * will create an empty histogram if none exists + * * @return histogram based on the type of state (single, grouped). Note that empty histograms will serialize to null as required */ TypedHistogram get(); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxBy.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxBy.java index da601833c142a..9a4e41c10b118 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxBy.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxBy.java @@ -18,6 +18,7 @@ import com.facebook.presto.metadata.SqlAggregationFunction; import com.facebook.presto.operator.aggregation.AccumulatorCompiler; import com.facebook.presto.operator.aggregation.AggregationMetadata; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.GenericAccumulatorFactoryBinder; import com.facebook.presto.operator.aggregation.InternalAggregationFunction; import com.facebook.presto.operator.aggregation.state.StateCompiler; @@ -154,12 +155,13 @@ private InternalAggregationFunction generateAggregation(Type valueType, Type key inputMethod, combineMethod, outputMethod, - stateClazz, - stateSerializer, - stateFactory, + ImmutableList.of(new AccumulatorStateDescriptor( + stateClazz, + stateSerializer, + stateFactory)), valueType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(getSignature().getName(), inputTypes, intermediateType, valueType, true, false, factory); + return new InternalAggregationFunction(getSignature().getName(), inputTypes, ImmutableList.of(intermediateType), valueType, true, false, factory); } private static List createInputParameterMetadata(Type value, Type key) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxByNAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxByNAggregationFunction.java index f4855e6c90e49..181705d951302 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxByNAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/minmaxby/AbstractMinMaxByNAggregationFunction.java @@ -19,6 +19,7 @@ import com.facebook.presto.operator.aggregation.AbstractMinMaxNAggregationFunction; import com.facebook.presto.operator.aggregation.AccumulatorCompiler; import com.facebook.presto.operator.aggregation.AggregationMetadata; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; import com.facebook.presto.operator.aggregation.BlockComparator; import com.facebook.presto.operator.aggregation.GenericAccumulatorFactoryBinder; import com.facebook.presto.operator.aggregation.InternalAggregationFunction; @@ -164,12 +165,13 @@ protected InternalAggregationFunction generateAggregation(Type valueType, Type k INPUT_FUNCTION.bindTo(comparator).bindTo(valueType).bindTo(keyType), COMBINE_FUNCTION, OUTPUT_FUNCTION.bindTo(outputType), - MinMaxByNState.class, - stateSerializer, - new MinMaxByNStateFactory(), + ImmutableList.of(new AccumulatorStateDescriptor( + MinMaxByNState.class, + stateSerializer, + new MinMaxByNStateFactory())), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(name, inputTypes, intermediateType, outputType, true, false, factory); + return new InternalAggregationFunction(name, inputTypes, ImmutableList.of(intermediateType), outputType, true, false, factory); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java new file mode 100644 index 0000000000000..25ed471e47dfc --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/GroupedMultimapAggregationState.java @@ -0,0 +1,48 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.operator.aggregation.AbstractGroupCollectionAggregationState; +import com.facebook.presto.spi.PageBuilder; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.Type; +import com.google.common.collect.ImmutableList; + +public final class GroupedMultimapAggregationState + extends AbstractGroupCollectionAggregationState + implements MultimapAggregationState +{ + private static final int MAX_BLOCK_SIZE = 1024 * 1024; + static final int VALUE_CHANNEL = 0; + static final int KEY_CHANNEL = 1; + + public GroupedMultimapAggregationState(Type keyType, Type valueType) + { + super(PageBuilder.withMaxPageSize(MAX_BLOCK_SIZE, ImmutableList.of(valueType, keyType))); + } + + @Override + public final void add(Block keyBlock, Block valueBlock, int position) + { + prepareAdd(); + appendAtChannel(VALUE_CHANNEL, valueBlock, position); + appendAtChannel(KEY_CHANNEL, keyBlock, position); + } + + @Override + protected final void accept(MultimapAggregationStateConsumer consumer, PageBuilder pageBuilder, int currentPosition) + { + consumer.accept(pageBuilder.getBlockBuilder(KEY_CHANNEL), pageBuilder.getBlockBuilder(VALUE_CHANNEL), currentPosition); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java new file mode 100644 index 0000000000000..4868780e149c3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/LegacyGroupedMultimapAggregationState.java @@ -0,0 +1,103 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.array.ObjectBigArray; +import com.facebook.presto.operator.aggregation.state.AbstractGroupedAccumulatorState; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.Type; +import org.openjdk.jol.info.ClassLayout; + +import static com.facebook.presto.type.TypeUtils.expectedValueSize; + +@Deprecated +public class LegacyGroupedMultimapAggregationState + extends AbstractGroupedAccumulatorState + implements MultimapAggregationState +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(LegacyGroupedMultimapAggregationState.class).instanceSize(); + private static final int EXPECTED_ENTRIES = 10; + private static final int EXPECTED_ENTRY_SIZE = 16; + private final Type keyType; + private final Type valueType; + private final ObjectBigArray keyBlockBuilders = new ObjectBigArray<>(); + private final ObjectBigArray valueBlockBuilders = new ObjectBigArray<>(); + private long size; + + public LegacyGroupedMultimapAggregationState(Type keyType, Type valueType) + { + this.keyType = keyType; + this.valueType = valueType; + } + + @Override + public void ensureCapacity(long size) + { + keyBlockBuilders.ensureCapacity(size); + valueBlockBuilders.ensureCapacity(size); + } + + @Override + public void add(Block key, Block value, int position) + { + BlockBuilder keyBlockBuilder = keyBlockBuilders.get(getGroupId()); + BlockBuilder valueBlockBuilder = valueBlockBuilders.get(getGroupId()); + if (keyBlockBuilder != null) { + size -= keyBlockBuilder.getRetainedSizeInBytes(); + size -= valueBlockBuilder.getRetainedSizeInBytes(); + } + else { + keyBlockBuilder = keyType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(keyType, EXPECTED_ENTRY_SIZE)); + valueBlockBuilder = valueType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(valueType, EXPECTED_ENTRY_SIZE)); + keyBlockBuilders.set(getGroupId(), keyBlockBuilder); + valueBlockBuilders.set(getGroupId(), valueBlockBuilder); + } + keyType.appendTo(key, position, keyBlockBuilder); + valueType.appendTo(value, position, valueBlockBuilder); + size += keyBlockBuilder.getRetainedSizeInBytes(); + size += valueBlockBuilder.getRetainedSizeInBytes(); + } + + @Override + public void forEach(MultimapAggregationStateConsumer consumer) + { + BlockBuilder keyBlockBuilder = keyBlockBuilders.get(getGroupId()); + BlockBuilder valueBlockBuilder = valueBlockBuilders.get(getGroupId()); + if (keyBlockBuilder == null) { + return; + } + for (int i = 0; i < keyBlockBuilder.getPositionCount(); i++) { + consumer.accept(keyBlockBuilder, valueBlockBuilder, i); + } + } + + @Override + public boolean isEmpty() + { + return keyBlockBuilders.get(getGroupId()) == null; + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + keyBlockBuilders.sizeOf() + valueBlockBuilders.sizeOf() + size; + } + + @Override + public int getEntryCount() + { + return keyBlockBuilders.get(getGroupId()).getPositionCount(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggGroupImplementation.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggGroupImplementation.java new file mode 100644 index 0000000000000..8973ccb4ece94 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggGroupImplementation.java @@ -0,0 +1,20 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +public enum MultimapAggGroupImplementation +{ + LEGACY, + NEW, +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultimapAggregationFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationFunction.java similarity index 55% rename from presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultimapAggregationFunction.java rename to presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationFunction.java index 86809420951f9..9630fa559555c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/MultimapAggregationFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationFunction.java @@ -11,14 +11,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.operator.aggregation; +package com.facebook.presto.operator.aggregation.multimapagg; +import com.facebook.presto.array.ObjectBigArray; import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlAggregationFunction; -import com.facebook.presto.operator.aggregation.state.MultiKeyValuePairStateSerializer; -import com.facebook.presto.operator.aggregation.state.MultiKeyValuePairsState; -import com.facebook.presto.operator.aggregation.state.MultiKeyValuePairsStateFactory; +import com.facebook.presto.operator.aggregation.AccumulatorCompiler; +import com.facebook.presto.operator.aggregation.AggregationMetadata; +import com.facebook.presto.operator.aggregation.AggregationMetadata.AccumulatorStateDescriptor; +import com.facebook.presto.operator.aggregation.GenericAccumulatorFactoryBinder; +import com.facebook.presto.operator.aggregation.InternalAggregationFunction; +import com.facebook.presto.operator.aggregation.TypedSet; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.ArrayType; @@ -41,25 +45,28 @@ import static com.facebook.presto.operator.aggregation.AggregationMetadata.ParameterMetadata.ParameterType.STATE; import static com.facebook.presto.operator.aggregation.AggregationUtils.generateAggregationName; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.type.TypeUtils.expectedValueSize; import static com.facebook.presto.util.Reflection.methodHandle; import static com.google.common.collect.ImmutableList.toImmutableList; public class MultimapAggregationFunction extends SqlAggregationFunction { - public static final MultimapAggregationFunction MULTIMAP_AGG = new MultimapAggregationFunction(); public static final String NAME = "multimap_agg"; - private static final MethodHandle OUTPUT_FUNCTION = methodHandle(MultimapAggregationFunction.class, "output", MultiKeyValuePairsState.class, BlockBuilder.class); - private static final MethodHandle INPUT_FUNCTION = methodHandle(MultimapAggregationFunction.class, "input", MultiKeyValuePairsState.class, Block.class, Block.class, int.class); - private static final MethodHandle COMBINE_FUNCTION = methodHandle(MultimapAggregationFunction.class, "combine", MultiKeyValuePairsState.class, MultiKeyValuePairsState.class); + private static final MethodHandle OUTPUT_FUNCTION = methodHandle(MultimapAggregationFunction.class, "output", Type.class, Type.class, MultimapAggregationState.class, BlockBuilder.class); + private static final MethodHandle COMBINE_FUNCTION = methodHandle(MultimapAggregationFunction.class, "combine", MultimapAggregationState.class, MultimapAggregationState.class); + private static final MethodHandle INPUT_FUNCTION = methodHandle(MultimapAggregationFunction.class, "input", MultimapAggregationState.class, Block.class, Block.class, int.class); + private static final int EXPECTED_ENTRY_SIZE = 100; + private final MultimapAggGroupImplementation groupMode; - public MultimapAggregationFunction() + public MultimapAggregationFunction(MultimapAggGroupImplementation groupMode) { super(NAME, ImmutableList.of(comparableTypeParameter("K"), typeVariable("V")), ImmutableList.of(), parseTypeSignature("map(K,array(V))"), ImmutableList.of(parseTypeSignature("K"), parseTypeSignature("V"))); + this.groupMode = groupMode; } @Override @@ -79,11 +86,11 @@ public InternalAggregationFunction specialize(BoundVariables boundVariables, int return generateAggregation(keyType, valueType, outputType); } - private static InternalAggregationFunction generateAggregation(Type keyType, Type valueType, Type outputType) + private InternalAggregationFunction generateAggregation(Type keyType, Type valueType, Type outputType) { DynamicClassLoader classLoader = new DynamicClassLoader(MultimapAggregationFunction.class.getClassLoader()); List inputTypes = ImmutableList.of(keyType, valueType); - MultiKeyValuePairStateSerializer stateSerializer = new MultiKeyValuePairStateSerializer(keyType, valueType); + MultimapAggregationStateSerializer stateSerializer = new MultimapAggregationStateSerializer(keyType, valueType); Type intermediateType = stateSerializer.getSerializedType(); AggregationMetadata metadata = new AggregationMetadata( @@ -91,14 +98,15 @@ private static InternalAggregationFunction generateAggregation(Type keyType, Typ createInputParameterMetadata(keyType, valueType), INPUT_FUNCTION, COMBINE_FUNCTION, - OUTPUT_FUNCTION, - MultiKeyValuePairsState.class, - stateSerializer, - new MultiKeyValuePairsStateFactory(keyType, valueType), + OUTPUT_FUNCTION.bindTo(keyType).bindTo(valueType), + ImmutableList.of(new AccumulatorStateDescriptor( + MultimapAggregationState.class, + stateSerializer, + new MultimapAggregationStateFactory(keyType, valueType, groupMode))), outputType); GenericAccumulatorFactoryBinder factory = AccumulatorCompiler.generateAccumulatorFactoryBinder(metadata, classLoader); - return new InternalAggregationFunction(NAME, inputTypes, intermediateType, outputType, true, true, factory); + return new InternalAggregationFunction(NAME, inputTypes, ImmutableList.of(intermediateType), outputType, true, true, factory); } private static List createInputParameterMetadata(Type keyType, Type valueType) @@ -109,44 +117,47 @@ private static List createInputParameterMetadata(Type keyType new ParameterMetadata(BLOCK_INDEX)); } - public static void input(MultiKeyValuePairsState state, Block key, Block value, int position) + public static void input(MultimapAggregationState state, Block key, Block value, int position) { - MultiKeyValuePairs pairs = state.get(); - if (pairs == null) { - pairs = new MultiKeyValuePairs(state.getKeyType(), state.getValueType()); - state.set(pairs); - } - - long startSize = pairs.estimatedInMemorySize(); - pairs.add(key, value, position, position); - state.addMemoryUsage(pairs.estimatedInMemorySize() - startSize); + state.add(key, value, position); } - public static void combine(MultiKeyValuePairsState state, MultiKeyValuePairsState otherState) + public static void combine(MultimapAggregationState state, MultimapAggregationState otherState) { - if (state.get() != null && otherState.get() != null) { - Block keys = otherState.get().getKeys(); - Block values = otherState.get().getValues(); - MultiKeyValuePairs pairs = state.get(); - long startSize = pairs.estimatedInMemorySize(); - for (int i = 0; i < keys.getPositionCount(); i++) { - pairs.add(keys, values, i, i); - } - state.addMemoryUsage(pairs.estimatedInMemorySize() - startSize); - } - else if (state.get() == null) { - state.set(otherState.get()); - } + state.merge(otherState); } - public static void output(MultiKeyValuePairsState state, BlockBuilder out) + public static void output(Type keyType, Type valueType, MultimapAggregationState state, BlockBuilder out) { - MultiKeyValuePairs pairs = state.get(); - if (pairs == null) { + if (state.isEmpty()) { out.appendNull(); } else { - pairs.toMultimapNativeEncoding(out); + // TODO: Avoid copy value block associated with the same key by using strategy similar to multimap_from_entries + ObjectBigArray valueArrayBlockBuilders = new ObjectBigArray<>(); + valueArrayBlockBuilders.ensureCapacity(state.getEntryCount()); + BlockBuilder distinctKeyBlockBuilder = keyType.createBlockBuilder(null, state.getEntryCount(), expectedValueSize(keyType, 100)); + TypedSet keySet = new TypedSet(keyType, state.getEntryCount(), MultimapAggregationFunction.NAME); + + state.forEach((key, value, keyValueIndex) -> { + // Merge values of the same key into an array + if (!keySet.contains(key, keyValueIndex)) { + keySet.add(key, keyValueIndex); + keyType.appendTo(key, keyValueIndex, distinctKeyBlockBuilder); + BlockBuilder valueArrayBuilder = valueType.createBlockBuilder(null, 10, expectedValueSize(valueType, EXPECTED_ENTRY_SIZE)); + valueArrayBlockBuilders.set(keySet.positionOf(key, keyValueIndex), valueArrayBuilder); + } + valueType.appendTo(value, keyValueIndex, valueArrayBlockBuilders.get(keySet.positionOf(key, keyValueIndex))); + }); + + // Write keys and value arrays into one Block + Type valueArrayType = new ArrayType(valueType); + BlockBuilder multimapBlockBuilder = out.beginBlockEntry(); + for (int i = 0; i < distinctKeyBlockBuilder.getPositionCount(); i++) { + keyType.appendTo(distinctKeyBlockBuilder, i, multimapBlockBuilder); + valueArrayType.writeObject(multimapBlockBuilder, valueArrayBlockBuilders.get(i).build()); + } + out.closeEntry(); } } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationState.java new file mode 100644 index 0000000000000..39dc38567e698 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationState.java @@ -0,0 +1,43 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.AccumulatorState; +import com.facebook.presto.spi.function.AccumulatorStateMetadata; + +@AccumulatorStateMetadata(stateFactoryClass = MultimapAggregationStateFactory.class, stateSerializerClass = MultimapAggregationStateSerializer.class) +public interface MultimapAggregationState + extends AccumulatorState +{ + void add(Block keyBlock, Block valueBlock, int position); + + void forEach(MultimapAggregationStateConsumer consumer); + + default void merge(MultimapAggregationState otherState) + { + otherState.forEach(this::add); + } + + boolean isEmpty(); + + default void reset() + { + throw new UnsupportedOperationException(); + } + + long getEstimatedSize(); + + int getEntryCount(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateConsumer.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateConsumer.java new file mode 100644 index 0000000000000..4b45c26c721e2 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateConsumer.java @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.spi.block.Block; + +public interface MultimapAggregationStateConsumer +{ + void accept(Block keyBlock, Block valueBlock, int position); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateFactory.java new file mode 100644 index 0000000000000..acb3e73feb4ae --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateFactory.java @@ -0,0 +1,75 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.AccumulatorStateFactory; +import com.facebook.presto.spi.type.Type; + +import static com.facebook.presto.spi.StandardErrorCode.FUNCTION_IMPLEMENTATION_ERROR; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class MultimapAggregationStateFactory + implements AccumulatorStateFactory +{ + private final Type keyType; + private final Type valueType; + private final MultimapAggGroupImplementation implementation; + + public MultimapAggregationStateFactory(Type keyType, Type valueType, MultimapAggGroupImplementation implementation) + { + this.keyType = requireNonNull(keyType); + this.valueType = requireNonNull(valueType); + this.implementation = requireNonNull(implementation); + } + + @Override + public MultimapAggregationState createSingleState() + { + return new SingleMultimapAggregationState(keyType, valueType); + } + + @Override + public Class getSingleStateClass() + { + return SingleMultimapAggregationState.class; + } + + @Override + public MultimapAggregationState createGroupedState() + { + switch (implementation) { + case NEW: + return new GroupedMultimapAggregationState(keyType, valueType); + case LEGACY: + return new LegacyGroupedMultimapAggregationState(keyType, valueType); + default: + throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, format("Unexpected group enum type %s", implementation)); + } + } + + @Override + public Class getGroupedStateClass() + { + switch (implementation) { + case NEW: + return GroupedMultimapAggregationState.class; + case LEGACY: + return LegacyGroupedMultimapAggregationState.class; + default: + throw new PrestoException(FUNCTION_IMPLEMENTATION_ERROR, format("Unexpected group enum type %s", implementation)); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateSerializer.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateSerializer.java new file mode 100644 index 0000000000000..3719bfc6081ed --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/MultimapAggregationStateSerializer.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.block.ColumnarRow; +import com.facebook.presto.spi.function.AccumulatorStateSerializer; +import com.facebook.presto.spi.type.ArrayType; +import com.facebook.presto.spi.type.RowType; +import com.facebook.presto.spi.type.Type; +import com.google.common.collect.ImmutableList; + +import static com.facebook.presto.operator.aggregation.multimapagg.GroupedMultimapAggregationState.KEY_CHANNEL; +import static com.facebook.presto.operator.aggregation.multimapagg.GroupedMultimapAggregationState.VALUE_CHANNEL; +import static com.facebook.presto.spi.block.ColumnarRow.toColumnarRow; +import static java.util.Objects.requireNonNull; + +public class MultimapAggregationStateSerializer + implements AccumulatorStateSerializer +{ + private final Type keyType; + private final Type valueType; + private final ArrayType arrayType; + + public MultimapAggregationStateSerializer(Type keyType, Type valueType) + { + this.keyType = requireNonNull(keyType); + this.valueType = requireNonNull(valueType); + this.arrayType = new ArrayType(RowType.anonymous(ImmutableList.of(valueType, keyType))); + } + + @Override + public Type getSerializedType() + { + return arrayType; + } + + @Override + public void serialize(MultimapAggregationState state, BlockBuilder out) + { + if (state.isEmpty()) { + out.appendNull(); + return; + } + BlockBuilder entryBuilder = out.beginBlockEntry(); + state.forEach((keyBlock, valueBlock, position) -> { + BlockBuilder rowBlockBuilder = entryBuilder.beginBlockEntry(); + valueType.appendTo(valueBlock, position, rowBlockBuilder); + keyType.appendTo(keyBlock, position, rowBlockBuilder); + entryBuilder.closeEntry(); + }); + out.closeEntry(); + } + + @Override + public void deserialize(Block block, int index, MultimapAggregationState state) + { + state.reset(); + ColumnarRow columnarRow = toColumnarRow(arrayType.getObject(block, index)); + Block keys = columnarRow.getField(KEY_CHANNEL); + Block values = columnarRow.getField(VALUE_CHANNEL); + for (int i = 0; i < columnarRow.getPositionCount(); i++) { + state.add(keys, values, i); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/SingleMultimapAggregationState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/SingleMultimapAggregationState.java new file mode 100644 index 0000000000000..b2188de14d883 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/multimapagg/SingleMultimapAggregationState.java @@ -0,0 +1,84 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.multimapagg; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.Type; +import org.openjdk.jol.info.ClassLayout; + +import static com.facebook.presto.type.TypeUtils.expectedValueSize; +import static java.util.Objects.requireNonNull; + +public class SingleMultimapAggregationState + implements MultimapAggregationState +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(SingleMultimapAggregationState.class).instanceSize(); + private static final int EXPECTED_ENTRIES = 10; + private static final int EXPECTED_ENTRY_SIZE = 16; + private final Type keyType; + private final Type valueType; + private BlockBuilder keyBlockBuilder; + private BlockBuilder valueBlockBuilder; + + public SingleMultimapAggregationState(Type keyType, Type valueType) + { + this.keyType = requireNonNull(keyType); + this.valueType = requireNonNull(valueType); + keyBlockBuilder = keyType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(keyType, EXPECTED_ENTRY_SIZE)); + valueBlockBuilder = valueType.createBlockBuilder(null, EXPECTED_ENTRIES, expectedValueSize(valueType, EXPECTED_ENTRY_SIZE)); + } + + @Override + public void add(Block key, Block value, int position) + { + keyType.appendTo(key, position, keyBlockBuilder); + valueType.appendTo(value, position, valueBlockBuilder); + } + + @Override + public void forEach(MultimapAggregationStateConsumer consumer) + { + for (int i = 0; i < keyBlockBuilder.getPositionCount(); i++) { + consumer.accept(keyBlockBuilder, valueBlockBuilder, i); + } + } + + @Override + public boolean isEmpty() + { + return keyBlockBuilder.getPositionCount() == 0; + } + + @Override + public int getEntryCount() + { + return keyBlockBuilder.getPositionCount(); + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + keyBlockBuilder.getRetainedSizeInBytes() + valueBlockBuilder.getRetainedSizeInBytes(); + } + + @Override + public void reset() + { + // Single aggregation state is used as scratch state in group accumulator. + // Thus reset() will be called for each group (via MultimapAggregationStateSerializer#deserialize) + keyBlockBuilder = keyBlockBuilder.newBlockBuilderLike(null); + valueBlockBuilder = valueBlockBuilder.newBlockBuilderLike(null); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/DoubleState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/DoubleState.java new file mode 100644 index 0000000000000..6dbbf00f4e123 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/DoubleState.java @@ -0,0 +1,24 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.state; + +import com.facebook.presto.spi.function.AccumulatorState; + +public interface DoubleState + extends AccumulatorState +{ + double getDouble(); + + void setDouble(double value); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/HyperLogLogState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/HyperLogLogState.java index e8bc0e02a92e7..e5464d6307d27 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/HyperLogLogState.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/HyperLogLogState.java @@ -17,15 +17,12 @@ import com.facebook.presto.spi.function.AccumulatorStateMetadata; import io.airlift.stats.cardinality.HyperLogLog; -import javax.validation.constraints.NotNull; - @AccumulatorStateMetadata(stateSerializerClass = HyperLogLogStateSerializer.class, stateFactoryClass = HyperLogLogStateFactory.class) public interface HyperLogLogState extends AccumulatorState { int NUMBER_OF_BUCKETS = 4096; - @NotNull HyperLogLog getHyperLogLog(); void setHyperLogLog(HyperLogLog value); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsStateFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsStateFactory.java deleted file mode 100644 index 559ff72eb0d63..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairsStateFactory.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.operator.aggregation.state; - -import com.facebook.presto.array.ObjectBigArray; -import com.facebook.presto.operator.aggregation.MultiKeyValuePairs; -import com.facebook.presto.spi.function.AccumulatorStateFactory; -import com.facebook.presto.spi.type.Type; -import org.openjdk.jol.info.ClassLayout; - -import static java.util.Objects.requireNonNull; - -public class MultiKeyValuePairsStateFactory - implements AccumulatorStateFactory -{ - private final Type keyType; - private final Type valueType; - - public MultiKeyValuePairsStateFactory(Type keyType, Type valueType) - { - this.keyType = keyType; - this.valueType = valueType; - } - - @Override - public MultiKeyValuePairsState createSingleState() - { - return new SingleState(keyType, valueType); - } - - @Override - public Class getSingleStateClass() - { - return SingleState.class; - } - - @Override - public MultiKeyValuePairsState createGroupedState() - { - return new GroupedState(keyType, valueType); - } - - @Override - public Class getGroupedStateClass() - { - return GroupedState.class; - } - - public static class GroupedState - extends AbstractGroupedAccumulatorState - implements MultiKeyValuePairsState - { - private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupedState.class).instanceSize(); - private final Type keyType; - private final Type valueType; - private final ObjectBigArray pairs = new ObjectBigArray<>(); - private long size; - - public GroupedState(Type keyType, Type valueType) - { - this.keyType = keyType; - this.valueType = valueType; - } - - @Override - public void ensureCapacity(long size) - { - pairs.ensureCapacity(size); - } - - @Override - public MultiKeyValuePairs get() - { - return pairs.get(getGroupId()); - } - - @Override - public void set(MultiKeyValuePairs value) - { - requireNonNull(value, "value is null"); - - MultiKeyValuePairs previous = get(); - if (previous != null) { - size -= previous.estimatedInMemorySize(); - } - - pairs.set(getGroupId(), value); - size += value.estimatedInMemorySize(); - } - - @Override - public void addMemoryUsage(long memory) - { - size += memory; - } - - @Override - public Type getKeyType() - { - return keyType; - } - - @Override - public Type getValueType() - { - return valueType; - } - - @Override - public long getEstimatedSize() - { - return INSTANCE_SIZE + size + pairs.sizeOf(); - } - } - - public static class SingleState - implements MultiKeyValuePairsState - { - private static final int INSTANCE_SIZE = ClassLayout.parseClass(SingleState.class).instanceSize(); - private final Type keyType; - private final Type valueType; - private MultiKeyValuePairs pair; - - public SingleState(Type keyType, Type valueType) - { - this.keyType = keyType; - this.valueType = valueType; - } - - @Override - public MultiKeyValuePairs get() - { - return pair; - } - - @Override - public void set(MultiKeyValuePairs value) - { - pair = value; - } - - @Override - public void addMemoryUsage(long memory) - { - } - - @Override - public Type getKeyType() - { - return keyType; - } - - @Override - public Type getValueType() - { - return valueType; - } - - @Override - public long getEstimatedSize() - { - long estimatedSize = INSTANCE_SIZE; - if (pair != null) { - estimatedSize += pair.estimatedInMemorySize(); - } - return estimatedSize; - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestState.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestState.java new file mode 100644 index 0000000000000..da3488bb9d8a2 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestState.java @@ -0,0 +1,27 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.state; + +import com.facebook.presto.spi.function.AccumulatorState; +import io.airlift.stats.QuantileDigest; + +public interface QuantileDigestState + extends AccumulatorState +{ + QuantileDigest getQuantileDigest(); + + void setQuantileDigest(QuantileDigest value); + + void addMemoryUsage(int value); +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateFactory.java new file mode 100644 index 0000000000000..e9e00efe31b8d --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateFactory.java @@ -0,0 +1,124 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation.state; + +import com.facebook.presto.array.ObjectBigArray; +import com.facebook.presto.spi.function.AccumulatorStateFactory; +import io.airlift.stats.QuantileDigest; +import org.openjdk.jol.info.ClassLayout; + +import static java.util.Objects.requireNonNull; + +public class QuantileDigestStateFactory + implements AccumulatorStateFactory +{ + @Override + public QuantileDigestState createSingleState() + { + return new SingleQuantileDigestState(); + } + + @Override + public Class getSingleStateClass() + { + return SingleQuantileDigestState.class; + } + + @Override + public QuantileDigestState createGroupedState() + { + return new GroupedQuantileDigestState(); + } + + @Override + public Class getGroupedStateClass() + { + return GroupedQuantileDigestState.class; + } + + public static class GroupedQuantileDigestState + extends AbstractGroupedAccumulatorState + implements QuantileDigestState + { + private static final int INSTANCE_SIZE = ClassLayout.parseClass(GroupedQuantileDigestState.class).instanceSize(); + private final ObjectBigArray qdigests = new ObjectBigArray<>(); + private long size; + + @Override + public void ensureCapacity(long size) + { + qdigests.ensureCapacity(size); + } + + @Override + public QuantileDigest getQuantileDigest() + { + return qdigests.get(getGroupId()); + } + + @Override + public void setQuantileDigest(QuantileDigest value) + { + requireNonNull(value, "value is null"); + qdigests.set(getGroupId(), value); + } + + @Override + public void addMemoryUsage(int value) + { + size += value; + } + + @Override + public long getEstimatedSize() + { + return INSTANCE_SIZE + size + qdigests.sizeOf(); + } + } + + public static class SingleQuantileDigestState + implements QuantileDigestState + { + private static final int INSTANCE_SIZE = ClassLayout.parseClass(SingleQuantileDigestState.class).instanceSize(); + private QuantileDigest qdigest; + + @Override + public QuantileDigest getQuantileDigest() + { + return qdigest; + } + + @Override + public void setQuantileDigest(QuantileDigest value) + { + qdigest = value; + } + + @Override + public void addMemoryUsage(int value) + { + // noop + } + + @Override + public long getEstimatedSize() + { + long estimatedSize = INSTANCE_SIZE; + if (qdigest != null) { + estimatedSize += qdigest.estimatedInMemorySizeInBytes(); + } + return estimatedSize; + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairStateSerializer.java b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateSerializer.java similarity index 52% rename from presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairStateSerializer.java rename to presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateSerializer.java index 5eff4617a07bc..e51fb4d60ac4e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/MultiKeyValuePairStateSerializer.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/aggregation/state/QuantileDigestStateSerializer.java @@ -13,45 +13,43 @@ */ package com.facebook.presto.operator.aggregation.state; -import com.facebook.presto.operator.aggregation.MultiKeyValuePairs; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.function.AccumulatorStateSerializer; -import com.facebook.presto.spi.type.ArrayType; -import com.facebook.presto.spi.type.RowType; +import com.facebook.presto.spi.type.QuantileDigestType; import com.facebook.presto.spi.type.Type; -import com.google.common.collect.ImmutableList; +import io.airlift.stats.QuantileDigest; -public class MultiKeyValuePairStateSerializer - implements AccumulatorStateSerializer +public class QuantileDigestStateSerializer + implements AccumulatorStateSerializer { - private final ArrayType serializedType; + private final QuantileDigestType type; - public MultiKeyValuePairStateSerializer(Type keyType, Type valueType) + public QuantileDigestStateSerializer(Type elementType) { - this.serializedType = new ArrayType(RowType.anonymous(ImmutableList.of(keyType, valueType))); + this.type = new QuantileDigestType(elementType); } @Override public Type getSerializedType() { - return serializedType; + return type; } @Override - public void serialize(MultiKeyValuePairsState state, BlockBuilder out) + public void serialize(QuantileDigestState state, BlockBuilder out) { - if (state.get() == null) { + if (state.getQuantileDigest() == null) { out.appendNull(); } else { - state.get().serialize(out); + type.writeSlice(out, state.getQuantileDigest().serialize()); } } @Override - public void deserialize(Block block, int index, MultiKeyValuePairsState state) + public void deserialize(Block block, int index, QuantileDigestState state) { - state.set(new MultiKeyValuePairs(serializedType.getObject(block, index), state.getKeyType(), state.getValueType())); + state.setQuantileDigest(new QuantileDigest(type.getSlice(block, index))); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/annotations/FunctionImplementationDependency.java b/presto-main/src/main/java/com/facebook/presto/operator/annotations/FunctionImplementationDependency.java index 298216f4499a3..0ae06a61e2889 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/annotations/FunctionImplementationDependency.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/annotations/FunctionImplementationDependency.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.operator.annotations; -import com.facebook.presto.spi.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention; import com.facebook.presto.spi.type.TypeSignature; import java.util.List; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/annotations/ImplementationDependency.java b/presto-main/src/main/java/com/facebook/presto/operator/annotations/ImplementationDependency.java index 5d2ffffc44d6f..11415e81e9fa3 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/annotations/ImplementationDependency.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/annotations/ImplementationDependency.java @@ -15,9 +15,9 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; -import com.facebook.presto.spi.InvocationConvention; import com.facebook.presto.spi.function.Convention; import com.facebook.presto.spi.function.FunctionDependency; +import com.facebook.presto.spi.function.InvocationConvention; import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.TypeParameter; import com.facebook.presto.spi.type.TypeManager; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/annotations/OperatorImplementationDependency.java b/presto-main/src/main/java/com/facebook/presto/operator/annotations/OperatorImplementationDependency.java index d63d1ca20d7dc..cf8b67cee7d11 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/annotations/OperatorImplementationDependency.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/annotations/OperatorImplementationDependency.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.operator.annotations; -import com.facebook.presto.spi.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.type.TypeSignature; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/annotations/ScalarImplementationDependency.java b/presto-main/src/main/java/com/facebook/presto/operator/annotations/ScalarImplementationDependency.java index 31fac896db4e9..f7644f9b15cab 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/annotations/ScalarImplementationDependency.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/annotations/ScalarImplementationDependency.java @@ -16,7 +16,7 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; -import com.facebook.presto.spi.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention; import com.facebook.presto.spi.type.TypeManager; import java.lang.invoke.MethodHandle; diff --git a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSinkOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSinkOperator.java index 6995a2041a07e..dd5ae846a1817 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSinkOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSinkOperator.java @@ -143,7 +143,7 @@ public void addInput(Page page) requireNonNull(page, "page is null"); page = pagePreprocessor.apply(page); sink.addPage(page); - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSource.java b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSource.java index 7e66dcf28b68a..0852f5eab8a75 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSource.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSource.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.exchange; import com.facebook.presto.operator.WorkProcessor; +import com.facebook.presto.operator.WorkProcessor.ProcessState; import com.facebook.presto.spi.Page; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; @@ -28,10 +29,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.blocked; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.finished; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.ofResult; -import static com.facebook.presto.operator.WorkProcessor.ProcessorState.yield; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; @@ -106,18 +103,18 @@ public WorkProcessor pages() Page page = removePage(); if (page == null) { if (isFinished()) { - return finished(); + return ProcessState.finished(); } ListenableFuture blocked = waitForReading(); if (!blocked.isDone()) { - return blocked(blocked); + return ProcessState.blocked(blocked); } - return yield(); + return ProcessState.yield(); } - return ofResult(page); + return ProcessState.ofResult(page); }); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSourceOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSourceOperator.java index 7fe1130b350fe..2c3f54dcf1ba8 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSourceOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalExchangeSourceOperator.java @@ -123,7 +123,7 @@ public Page getOutput() { Page page = source.removePage(); if (page != null) { - operatorContext.recordGeneratedInput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); } return page; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalMergeSourceOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalMergeSourceOperator.java index 11ce4d27dadfa..52afcba097eb1 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalMergeSourceOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/exchange/LocalMergeSourceOperator.java @@ -166,7 +166,7 @@ public Page getOutput() } Page page = mergedPages.getResult(); - operatorContext.recordGeneratedInput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordProcessedInput(page.getSizeInBytes(), page.getPositionCount()); return page; } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/DynamicTupleFilterFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/index/DynamicTupleFilterFactory.java index 4d4ba051ccb49..fceb47e8eaae0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/DynamicTupleFilterFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/DynamicTupleFilterFactory.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Supplier; import java.util.stream.IntStream; @@ -85,19 +86,19 @@ public DynamicTupleFilterFactory( public OperatorFactory filterWithTuple(Page tuplePage) { Page filterTuple = getFilterTuple(tuplePage); - Supplier processor = createPageProcessor(filterTuple); + Supplier processor = createPageProcessor(filterTuple, OptionalInt.empty()); return new FilterAndProjectOperatorFactory(filterOperatorId, planNodeId, processor, outputTypes, new DataSize(0, BYTE), 0); } @VisibleForTesting - public Supplier createPageProcessor(Page filterTuple) + public Supplier createPageProcessor(Page filterTuple, OptionalInt initialBatchSize) { TuplePageFilter filter = new TuplePageFilter(filterTuple, filterTypes, outputFilterChannels); return () -> new PageProcessor( Optional.of(filter), outputProjections.stream() .map(Supplier::get) - .collect(toImmutableList())); + .collect(toImmutableList()), initialBatchSize); } private Page getFilterTuple(Page tuplePage) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/FieldSetFilteringRecordSet.java b/presto-main/src/main/java/com/facebook/presto/operator/index/FieldSetFilteringRecordSet.java index 64fb30c9344b0..3477908c1dab1 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/FieldSetFilteringRecordSet.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/FieldSetFilteringRecordSet.java @@ -31,6 +31,7 @@ import static com.facebook.presto.metadata.Signature.internalOperator; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfUnchecked; +import static java.lang.Boolean.TRUE; import static java.util.Objects.requireNonNull; /** @@ -172,19 +173,19 @@ private static boolean fieldEquals(RecordCursor cursor, Field field1, Field fiel Class javaType = cursor.getType(field1.getField()).getJavaType(); try { if (javaType == long.class) { - return (boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getLong(field1.getField()), cursor.getLong(field2.getField())); + return TRUE.equals((Boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getLong(field1.getField()), cursor.getLong(field2.getField()))); } else if (javaType == double.class) { - return (boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getDouble(field1.getField()), cursor.getDouble(field2.getField())); + return TRUE.equals((Boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getDouble(field1.getField()), cursor.getDouble(field2.getField()))); } else if (javaType == boolean.class) { - return (boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getBoolean(field1.getField()), cursor.getBoolean(field2.getField())); + return TRUE.equals((Boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getBoolean(field1.getField()), cursor.getBoolean(field2.getField()))); } else if (javaType == Slice.class) { - return (boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getSlice(field1.getField()), cursor.getSlice(field2.getField())); + return TRUE.equals((Boolean) field1.getEqualsMethodHandle().invokeExact(cursor.getSlice(field1.getField()), cursor.getSlice(field2.getField()))); } else { - return (boolean) field1.getEqualsMethodHandle().invoke(cursor.getObject(field1.getField()), cursor.getObject(field2.getField())); + return TRUE.equals((Boolean) field1.getEqualsMethodHandle().invoke(cursor.getObject(field1.getField()), cursor.getObject(field2.getField()))); } } catch (Throwable t) { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLoader.java b/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLoader.java index 5825439d06d6b..064b682275f81 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLoader.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLoader.java @@ -249,7 +249,7 @@ private synchronized void initializeStateIfNecessary() if (pipelineContext == null) { TaskContext taskContext = taskContextReference.get(); checkState(taskContext != null, "Task context must be set before index can be built"); - pipelineContext = taskContext.addPipelineContext(indexBuildDriverFactoryProvider.getPipelineId(), false, false); + pipelineContext = taskContext.addPipelineContext(indexBuildDriverFactoryProvider.getPipelineId(), true, true, false); } if (indexSnapshotLoader == null) { indexSnapshotLoader = new IndexSnapshotLoader( diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLookupSourceFactory.java b/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLookupSourceFactory.java index 716822fb4c750..8cd0510859c7a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLookupSourceFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/IndexLookupSourceFactory.java @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import io.airlift.units.DataSize; import java.util.List; @@ -35,6 +36,8 @@ import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.util.concurrent.Futures.transform; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Objects.requireNonNull; public class IndexLookupSourceFactory @@ -44,6 +47,7 @@ public class IndexLookupSourceFactory private final Map layout; private final Supplier indexLoaderSupplier; private TaskContext taskContext; + private final SettableFuture whenTaskContextSet = SettableFuture.create(); public IndexLookupSourceFactory( Set lookupSourceInputChannels, @@ -92,6 +96,7 @@ public Map getLayout() public void setTaskContext(TaskContext taskContext) { this.taskContext = taskContext; + whenTaskContextSet.set(null); } @Override @@ -104,6 +109,23 @@ public ListenableFuture createLookupSourceProvider() return Futures.immediateFuture(new StaticLookupSourceProvider(new IndexLookupSource(indexLoader))); } + @Override + public ListenableFuture whenBuildFinishes() + { + return Futures.transformAsync( + whenTaskContextSet, + ignored -> transform( + this.createLookupSourceProvider(), + lookupSourceProvider -> { + // Close the lookupSourceProvider we just created. + // The only reason we created it is to wait until lookup source is ready. + lookupSourceProvider.close(); + return null; + }, + directExecutor()), + directExecutor()); + } + @Override public int partitions() { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/PageBufferOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/index/PageBufferOperator.java index b409ba4b7ecc5..0bc0cb2fcafd6 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/PageBufferOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/PageBufferOperator.java @@ -120,7 +120,7 @@ public void addInput(Page page) if (!future.isDone()) { this.blocked = future; } - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/index/PagesIndexBuilderOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/index/PagesIndexBuilderOperator.java index 3b6f545da290b..57723f13a6eae 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/index/PagesIndexBuilderOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/index/PagesIndexBuilderOperator.java @@ -111,7 +111,7 @@ public void addInput(Page page) finish(); return; } - operatorContext.recordGeneratedOutput(page.getSizeInBytes(), page.getPositionCount()); + operatorContext.recordOutput(page.getSizeInBytes(), page.getPositionCount()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/GeneratedPageProjection.java b/presto-main/src/main/java/com/facebook/presto/operator/project/GeneratedPageProjection.java index da2e8b6aa1880..516fc4dbedcb5 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/GeneratedPageProjection.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/project/GeneratedPageProjection.java @@ -69,7 +69,7 @@ public Work project(ConnectorSession session, DriverYieldSignal yieldSign { blockBuilder = blockBuilder.newBlockBuilderLike(null); try { - return (Work) pageProjectionWorkFactory.invoke(blockBuilder, session, yieldSignal, page, selectedPositions); + return (Work) pageProjectionWorkFactory.invoke(blockBuilder, session, page, selectedPositions); } catch (Throwable e) { throw new RuntimeException(e); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageFilter.java b/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageFilter.java index f8291e0758602..ae6b81a4ed48a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageFilter.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageFilter.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.project; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; @@ -66,7 +67,7 @@ public InterpretedPageFilter( Type type = inputTypes.get(parameter); parameterTypes.put(parameter, type); } - Map, Type> expressionTypes = getExpressionTypesFromInput(session, metadata, sqlParser, parameterTypes.build(), rewritten, emptyList()); + Map, Type> expressionTypes = getExpressionTypesFromInput(session, metadata, sqlParser, parameterTypes.build(), rewritten, emptyList(), WarningCollector.NOOP); this.evaluator = ExpressionInterpreter.expressionInterpreter(rewritten, metadata, session, expressionTypes); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageProjection.java b/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageProjection.java index d72c5a053cac2..39f49b8f80e17 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageProjection.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/project/InterpretedPageProjection.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.project; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.Work; @@ -69,7 +70,7 @@ public InterpretedPageProjection( Type type = inputTypes.get(parameter); parameterTypes.put(parameter, type); } - Map, Type> expressionTypes = getExpressionTypesFromInput(session, metadata, sqlParser, parameterTypes.build(), rewritten, emptyList()); + Map, Type> expressionTypes = getExpressionTypesFromInput(session, metadata, sqlParser, parameterTypes.build(), rewritten, emptyList(), WarningCollector.NOOP); this.evaluator = ExpressionInterpreter.expressionInterpreter(rewritten, metadata, session, expressionTypes); blockBuilder = evaluator.getType().createBlockBuilder(null, 1); diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/MergingPageOutput.java b/presto-main/src/main/java/com/facebook/presto/operator/project/MergingPageOutput.java index 3eba38e81f886..6e73527347602 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/MergingPageOutput.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/project/MergingPageOutput.java @@ -22,6 +22,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -65,7 +66,7 @@ public class MergingPageOutput private final int minRowCount; @Nullable - private PageProcessorOutput currentInput; + private Iterator> currentInput; private boolean finishing; public MergingPageOutput(Iterable types, long minPageSizeInBytes, int minRowCount) @@ -91,7 +92,7 @@ public boolean needsInput() return currentInput == null && !finishing && outputQueue.isEmpty(); } - public void addInput(PageProcessorOutput input) + public void addInput(Iterator> input) { requireNonNull(input, "input is null"); checkState(!finishing, "output is in finishing state"); @@ -183,9 +184,6 @@ public long getRetainedSizeInBytes() { long retainedSizeInBytes = INSTANCE_SIZE; retainedSizeInBytes += pageBuilder.getRetainedSizeInBytes(); - if (currentInput != null) { - retainedSizeInBytes += currentInput.getRetainedSizeInBytes(); - } for (Page page : outputQueue) { retainedSizeInBytes += page.getRetainedSizeInBytes(); } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessor.java b/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessor.java index a0219266c1d2b..ca20985048410 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessor.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessor.java @@ -14,48 +14,63 @@ package com.facebook.presto.operator.project; import com.facebook.presto.array.ReferenceCountMap; +import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.Work; +import com.facebook.presto.operator.WorkProcessor; +import com.facebook.presto.operator.WorkProcessor.ProcessState; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.DictionaryBlock; import com.facebook.presto.spi.block.DictionaryId; import com.facebook.presto.spi.block.LazyBlock; +import com.facebook.presto.sql.gen.ExpressionProfiler; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.AbstractIterator; import io.airlift.slice.SizeOf; import javax.annotation.concurrent.NotThreadSafe; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Function; -import static com.facebook.presto.operator.project.PageProcessorOutput.EMPTY_PAGE_PROCESSOR_OUTPUT; +import static com.facebook.presto.operator.WorkProcessor.ProcessState.finished; +import static com.facebook.presto.operator.WorkProcessor.ProcessState.ofResult; +import static com.facebook.presto.operator.WorkProcessor.ProcessState.yield; import static com.facebook.presto.operator.project.SelectedPositions.positionsRange; import static com.facebook.presto.spi.block.DictionaryId.randomDictionaryId; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.Iterators.singletonIterator; import static java.util.Objects.requireNonNull; @NotThreadSafe public class PageProcessor { - static final int MAX_BATCH_SIZE = 8 * 1024; + public static final int MAX_BATCH_SIZE = 8 * 1024; static final int MAX_PAGE_SIZE_IN_BYTES = 4 * 1024 * 1024; static final int MIN_PAGE_SIZE_IN_BYTES = 1024 * 1024; + private final ExpressionProfiler expressionProfiler; private final DictionarySourceIdFunction dictionarySourceIdFunction = new DictionarySourceIdFunction(); private final Optional filter; private final List projections; - private int projectBatchSize = MAX_BATCH_SIZE; + private int projectBatchSize; - public PageProcessor(Optional filter, List projections) + @VisibleForTesting + public PageProcessor(Optional filter, List projections, OptionalInt initialBatchSize) + { + this(filter, projections, initialBatchSize, new ExpressionProfiler()); + } + + @VisibleForTesting + public PageProcessor(Optional filter, List projections, OptionalInt initialBatchSize, ExpressionProfiler expressionProfiler) { this.filter = requireNonNull(filter, "filter is null") .map(pageFilter -> { @@ -72,69 +87,59 @@ public PageProcessor(Optional filter, List return projection; }) .collect(toImmutableList()); + this.projectBatchSize = initialBatchSize.orElse(1); + this.expressionProfiler = requireNonNull(expressionProfiler, "expressionProfiler is null"); + } + + public PageProcessor(Optional filter, List projections) + { + this(filter, projections, OptionalInt.of(1)); } - public PageProcessorOutput process(ConnectorSession session, DriverYieldSignal yieldSignal, Page page) + public Iterator> process(ConnectorSession session, DriverYieldSignal yieldSignal, LocalMemoryContext memoryContext, Page page) + { + WorkProcessor processor = createWorkProcessor(session, yieldSignal, memoryContext, page); + return processor.yieldingIterator(); + } + + private WorkProcessor createWorkProcessor(ConnectorSession session, DriverYieldSignal yieldSignal, LocalMemoryContext memoryContext, Page page) { // limit the scope of the dictionary ids to just one page dictionarySourceIdFunction.reset(); if (page.getPositionCount() == 0) { - return EMPTY_PAGE_PROCESSOR_OUTPUT; + return WorkProcessor.of(); } if (filter.isPresent()) { SelectedPositions selectedPositions = filter.get().filter(session, filter.get().getInputChannels().getInputChannels(page)); if (selectedPositions.isEmpty()) { - return EMPTY_PAGE_PROCESSOR_OUTPUT; + return WorkProcessor.of(); } if (projections.isEmpty()) { - return new PageProcessorOutput(() -> calculateRetainedSizeWithoutLoading(page), singletonIterator(Optional.of(new Page(selectedPositions.size())))); + // retained memory for empty page is negligible + return WorkProcessor.of(new Page(selectedPositions.size())); } if (selectedPositions.size() != page.getPositionCount()) { - PositionsPageProcessorIterator pages = new PositionsPageProcessorIterator(session, yieldSignal, page, selectedPositions); - return new PageProcessorOutput(pages::getRetainedSizeInBytes, pages); + return WorkProcessor.create(new ProjectSelectedPositions(session, yieldSignal, memoryContext, page, selectedPositions)); } } - PositionsPageProcessorIterator pages = new PositionsPageProcessorIterator(session, yieldSignal, page, positionsRange(0, page.getPositionCount())); - return new PageProcessorOutput(pages::getRetainedSizeInBytes, pages); + return WorkProcessor.create(new ProjectSelectedPositions(session, yieldSignal, memoryContext, page, positionsRange(0, page.getPositionCount()))); } - @VisibleForTesting - public List getProjections() - { - return projections; - } - - private static boolean isUnloadedLazyBlock(Block block) - { - return (block instanceof LazyBlock) && !((LazyBlock) block).isLoaded(); - } - - private static long calculateRetainedSizeWithoutLoading(Page page) - { - long retainedSizeInBytes = Page.INSTANCE_SIZE + SizeOf.sizeOfObjectArray(page.getChannelCount()); - for (int channel = 0; channel < page.getChannelCount(); channel++) { - Block block = page.getBlock(channel); - if (!isUnloadedLazyBlock(block)) { - retainedSizeInBytes += block.getRetainedSizeInBytes(); - } - } - return retainedSizeInBytes; - } - - private class PositionsPageProcessorIterator - extends AbstractIterator> + private class ProjectSelectedPositions + implements WorkProcessor.Process { private final ConnectorSession session; private final DriverYieldSignal yieldSignal; - private final Page page; + private final LocalMemoryContext memoryContext; + private Page page; + private Block[] previouslyComputedResults; private SelectedPositions selectedPositions; - private final Block[] previouslyComputedResults; private long retainedSizeInBytes; // remember if we need to re-use the same batch size if we yield last time @@ -142,30 +147,26 @@ private class PositionsPageProcessorIterator private int lastComputeBatchSize; private Work pageProjectWork; - public PositionsPageProcessorIterator(ConnectorSession session, DriverYieldSignal yieldSignal, Page page, SelectedPositions selectedPositions) + private ProjectSelectedPositions(ConnectorSession session, DriverYieldSignal yieldSignal, LocalMemoryContext memoryContext, Page page, SelectedPositions selectedPositions) { + checkArgument(!selectedPositions.isEmpty(), "selectedPositions is empty"); + this.session = session; this.yieldSignal = yieldSignal; this.page = page; + this.memoryContext = memoryContext; this.selectedPositions = selectedPositions; this.previouslyComputedResults = new Block[projections.size()]; - updateRetainedSize(); - } - - public long getRetainedSizeInBytes() - { - return retainedSizeInBytes; } @Override - protected Optional computeNext() + public ProcessState process() { int batchSize; while (true) { if (selectedPositions.isEmpty()) { - updateRetainedSize(); verify(!lastComputeYielded); - return endOfData(); + return finished(); } // we always process one chunk @@ -185,7 +186,8 @@ protected Optional computeNext() // if we are running out of time, save the batch size and continue next time lastComputeYielded = true; lastComputeBatchSize = batchSize; - return Optional.empty(); + updateRetainedSize(); + return yield(); } if (result.isPageTooLarge()) { @@ -196,16 +198,16 @@ protected Optional computeNext() } verify(result.isSuccess()); - Page page = result.getPage(); + Page resultPage = result.getPage(); - // if we produced a large page, halve the batch size for the next call - long pageSize = page.getSizeInBytes(); - if (page.getPositionCount() > 1 && pageSize > MAX_PAGE_SIZE_IN_BYTES) { + // if we produced a large page or if the expression is expensive, halve the batch size for the next call + long pageSize = resultPage.getSizeInBytes(); + if (resultPage.getPositionCount() > 1 && (pageSize > MAX_PAGE_SIZE_IN_BYTES || expressionProfiler.isExpressionExpensive())) { projectBatchSize = projectBatchSize / 2; } // if we produced a small page, double the batch size for the next call - if (pageSize < MIN_PAGE_SIZE_IN_BYTES && projectBatchSize < MAX_BATCH_SIZE) { + if (pageSize < MIN_PAGE_SIZE_IN_BYTES && projectBatchSize < MAX_BATCH_SIZE && !expressionProfiler.isExpressionExpensive()) { projectBatchSize = projectBatchSize * 2; } @@ -220,8 +222,19 @@ protected Optional computeNext() } } - updateRetainedSize(); - return Optional.of(page); + if (!selectedPositions.isEmpty()) { + // there are still some positions to process therefore we need to retain page and account its memory + updateRetainedSize(); + } + else { + page = null; + for (int i = 0; i < previouslyComputedResults.length; i++) { + previouslyComputedResults[i] = null; + } + memoryContext.setBytes(0); + } + + return ofResult(resultPage); } } @@ -232,7 +245,7 @@ private void updateRetainedSize() ReferenceCountMap referenceCountMap = new ReferenceCountMap(); for (int channel = 0; channel < page.getChannelCount(); channel++) { Block block = page.getBlock(channel); - if (!isUnloadedLazyBlock(block)) { + if (!isNotLoadedLazyBlock(block)) { block.retainedBytesForEachPart((object, size) -> { if (referenceCountMap.incrementAndGet(object) == 1) { retainedSizeInBytes += size; @@ -249,6 +262,8 @@ private void updateRetainedSize() }); } } + + memoryContext.setBytes(retainedSizeInBytes); } private ProcessBatchResult processBatch(int batchSize) @@ -273,7 +288,9 @@ private ProcessBatchResult processBatch(int batchSize) } else { if (pageProjectWork == null) { + expressionProfiler.start(); pageProjectWork = projection.project(session, yieldSignal, projection.getInputChannels().getInputChannels(page), positionsBatch); + expressionProfiler.stop(positionsBatch.size()); } if (!pageProjectWork.process()) { return ProcessBatchResult.processBatchYield(); @@ -289,6 +306,17 @@ private ProcessBatchResult processBatch(int batchSize) } } + @VisibleForTesting + public List getProjections() + { + return projections; + } + + private static boolean isNotLoadedLazyBlock(Block block) + { + return (block instanceof LazyBlock) && !((LazyBlock) block).isLoaded(); + } + @NotThreadSafe private static class DictionarySourceIdFunction implements Function diff --git a/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessorOutput.java b/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessorOutput.java deleted file mode 100644 index 3286465dd294d..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/operator/project/PageProcessorOutput.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.operator.project; - -import com.facebook.presto.spi.Page; - -import java.util.Iterator; -import java.util.Optional; -import java.util.function.LongSupplier; - -import static java.util.Collections.emptyIterator; -import static java.util.Objects.requireNonNull; - -public class PageProcessorOutput - implements Iterator> -{ - public static final PageProcessorOutput EMPTY_PAGE_PROCESSOR_OUTPUT = new PageProcessorOutput(() -> 0, emptyIterator()); - - private final LongSupplier retainedSizeInBytesSupplier; - private final Iterator> pages; - private long retainedSizeInBytes; - - public PageProcessorOutput(LongSupplier retainedSizeInBytesSupplier, Iterator> pages) - { - this.retainedSizeInBytesSupplier = requireNonNull(retainedSizeInBytesSupplier, "retainedSizeInBytesSupplier is null"); - this.pages = requireNonNull(pages, "pages is null"); - this.retainedSizeInBytes = retainedSizeInBytesSupplier.getAsLong(); - } - - public long getRetainedSizeInBytes() - { - return retainedSizeInBytes; - } - - @Override - public boolean hasNext() - { - boolean result = pages.hasNext(); - retainedSizeInBytes = retainedSizeInBytesSupplier.getAsLong(); - return result; - } - - @Override - public Optional next() - { - return pages.next(); - } - - @Override - public void remove() - { - throw new UnsupportedOperationException(); - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayContains.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayContains.java index 6fb0062334e09..adc685ed65f65 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayContains.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayContains.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.operator.scalar; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.Description; import com.facebook.presto.spi.function.OperatorDependency; @@ -26,6 +27,7 @@ import java.lang.invoke.MethodHandle; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.util.Failures.internalError; @@ -51,7 +53,9 @@ public static Boolean contains( continue; } try { - if ((boolean) equals.invokeExact((Block) elementType.getObject(arrayBlock, i), value)) { + Boolean result = (Boolean) equals.invokeExact((Block) elementType.getObject(arrayBlock, i), value); + checkNotIndeterminate(result); + if (result) { return true; } } @@ -81,7 +85,9 @@ public static Boolean contains( continue; } try { - if ((boolean) equals.invokeExact(elementType.getSlice(arrayBlock, i), value)) { + Boolean result = (Boolean) equals.invokeExact(elementType.getSlice(arrayBlock, i), value); + checkNotIndeterminate(result); + if (result) { return true; } } @@ -111,7 +117,9 @@ public static Boolean contains( continue; } try { - if ((boolean) equals.invokeExact(elementType.getLong(arrayBlock, i), value)) { + Boolean result = (Boolean) equals.invokeExact(elementType.getLong(arrayBlock, i), value); + checkNotIndeterminate(result); + if (result) { return true; } } @@ -141,7 +149,9 @@ public static Boolean contains( continue; } try { - if ((boolean) equals.invokeExact(elementType.getBoolean(arrayBlock, i), value)) { + Boolean result = (Boolean) equals.invokeExact(elementType.getBoolean(arrayBlock, i), value); + checkNotIndeterminate(result); + if (result) { return true; } } @@ -171,7 +181,9 @@ public static Boolean contains( continue; } try { - if ((boolean) equals.invokeExact(elementType.getDouble(arrayBlock, i), value)) { + Boolean result = (Boolean) equals.invokeExact(elementType.getDouble(arrayBlock, i), value); + checkNotIndeterminate(result); + if (result) { return true; } } @@ -184,4 +196,11 @@ public static Boolean contains( } return false; } + + private static void checkNotIndeterminate(Boolean equalsResult) + { + if (equalsResult == null) { + throw new PrestoException(NOT_SUPPORTED, "contains does not support arrays with elements that are null or contain null"); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFromOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFromOperator.java index c8df51d57794a..99ae365bfa932 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFromOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFromOperator.java @@ -14,6 +14,8 @@ */ import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.Convention; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.OperatorDependency; @@ -25,12 +27,10 @@ import java.lang.invoke.MethodHandle; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.NULL_FLAG; -import static com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static com.facebook.presto.spi.function.OperatorType.IS_DISTINCT_FROM; -import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; import static com.facebook.presto.util.Failures.internalError; -import static com.google.common.base.Defaults.defaultValue; @ScalarOperator(IS_DISTINCT_FROM) public final class ArrayDistinctFromOperator @@ -44,8 +44,8 @@ public static boolean isDistinctFrom( operator = IS_DISTINCT_FROM, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}, - convention = @Convention(arguments = {NULL_FLAG, NULL_FLAG}, result = FAIL_ON_NULL)) MethodHandle function, - @TypeParameter("E") Type type, + convention = @Convention(arguments = {BLOCK_POSITION, BLOCK_POSITION}, result = FAIL_ON_NULL)) MethodHandle function, + @TypeParameter("array(E)") Type type, @SqlType("array(E)") Block left, @IsNull boolean leftNull, @SqlType("array(E)") Block right, @@ -61,22 +61,12 @@ public static boolean isDistinctFrom( return true; } for (int i = 0; i < left.getPositionCount(); i++) { - Object leftValue = readNativeValue(type, left, i); - boolean leftValueNull = leftValue == null; - if (leftValueNull) { - leftValue = defaultValue(type.getJavaType()); - } - Object rightValue = readNativeValue(type, right, i); - boolean rightValueNull = rightValue == null; - if (rightValueNull) { - rightValue = defaultValue(type.getJavaType()); - } try { - if ((boolean) function.invoke( - leftValue, - leftValueNull, - rightValue, - rightValueNull)) { + if ((boolean) function.invokeExact( + left, + i, + right, + i)) { return true; } } @@ -86,4 +76,27 @@ public static boolean isDistinctFrom( } return false; } + + @TypeParameter("E") + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @OperatorDependency( + operator = IS_DISTINCT_FROM, + returnType = StandardTypes.BOOLEAN, + argumentTypes = {"E", "E"}, + convention = @Convention(arguments = {BLOCK_POSITION, BLOCK_POSITION}, result = FAIL_ON_NULL)) MethodHandle elementIsDistinctFrom, + @TypeParameter("array(E)") Type type, + @BlockPosition @SqlType(value = "array(E)", nativeContainerType = Block.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = "array(E)", nativeContainerType = Block.class) Block right, + @BlockIndex int rightPosition) + { + return isDistinctFrom( + elementIsDistinctFrom, + type, + (Block) type.getObject(left, leftPosition), + left.isNull(leftPosition), + (Block) type.getObject(right, rightPosition), + right.isNull(rightPosition)); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFunction.java index a5e6a8d4ce9fe..18aa15f8e83c2 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayDistinctFunction.java @@ -22,6 +22,7 @@ import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.function.TypeParameter; import com.facebook.presto.spi.type.Type; +import com.facebook.presto.type.TypeUtils; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; @@ -49,7 +50,7 @@ public Block distinct(@TypeParameter("E") Type type, @SqlType("array(E)") Block } if (array.getPositionCount() == 2) { - if (type.equalTo(array, 0, array, 1)) { + if (TypeUtils.positionEqualsPosition(type, array, 0, array, 1)) { return array.getSingleValueBlock(0); } else { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayEqualOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayEqualOperator.java index 14145291d8648..628b3b2fa7117 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayEqualOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayEqualOperator.java @@ -16,6 +16,7 @@ import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.function.TypeParameter; import com.facebook.presto.spi.function.TypeParameterSpecialization; @@ -25,9 +26,7 @@ import java.lang.invoke.MethodHandle; import static com.facebook.presto.spi.function.OperatorType.EQUAL; -import static com.facebook.presto.spi.type.ArrayType.ARRAY_NULL_ELEMENT_MSG; import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; -import static com.facebook.presto.type.TypeUtils.checkElementNotNull; import static com.facebook.presto.util.Failures.internalError; @ScalarOperator(EQUAL) @@ -37,7 +36,8 @@ private ArrayEqualOperator() {} @TypeParameter("E") @SqlType(StandardTypes.BOOLEAN) - public static boolean equals( + @SqlNullable + public static Boolean equals( @OperatorDependency(operator = EQUAL, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}) MethodHandle equalsFunction, @TypeParameter("E") Type type, @SqlType("array(E)") Block leftArray, @@ -46,13 +46,21 @@ public static boolean equals( if (leftArray.getPositionCount() != rightArray.getPositionCount()) { return false; } + + boolean indeterminate = false; for (int i = 0; i < leftArray.getPositionCount(); i++) { - checkElementNotNull(leftArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); - checkElementNotNull(rightArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); + if (leftArray.isNull(i) || rightArray.isNull(i)) { + indeterminate = true; + continue; + } Object leftElement = readNativeValue(type, leftArray, i); Object rightElement = readNativeValue(type, rightArray, i); try { - if (!(boolean) equalsFunction.invoke(leftElement, rightElement)) { + Boolean result = (Boolean) equalsFunction.invoke(leftElement, rightElement); + if (result == null) { + indeterminate = true; + } + else if (!result) { return false; } } @@ -60,13 +68,18 @@ public static boolean equals( throw internalError(t); } } + + if (indeterminate) { + return null; + } return true; } @TypeParameter("E") @TypeParameterSpecialization(name = "E", nativeContainerType = long.class) @SqlType(StandardTypes.BOOLEAN) - public static boolean equalsLong( + @SqlNullable + public static Boolean equalsLong( @OperatorDependency(operator = EQUAL, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}) MethodHandle equalsFunction, @TypeParameter("E") Type type, @SqlType("array(E)") Block leftArray, @@ -76,13 +89,20 @@ public static boolean equalsLong( return false; } + boolean indeterminate = false; for (int i = 0; i < leftArray.getPositionCount(); i++) { - checkElementNotNull(leftArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); - checkElementNotNull(rightArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); + if (leftArray.isNull(i) || rightArray.isNull(i)) { + indeterminate = true; + continue; + } long leftElement = type.getLong(leftArray, i); long rightElement = type.getLong(rightArray, i); try { - if (!(boolean) equalsFunction.invokeExact(leftElement, rightElement)) { + Boolean result = (Boolean) equalsFunction.invokeExact(leftElement, rightElement); + if (result == null) { + indeterminate = true; + } + else if (!result) { return false; } } @@ -90,13 +110,18 @@ public static boolean equalsLong( throw internalError(t); } } + + if (indeterminate) { + return null; + } return true; } @TypeParameter("E") @TypeParameterSpecialization(name = "E", nativeContainerType = double.class) @SqlType(StandardTypes.BOOLEAN) - public static boolean equalsDouble( + @SqlNullable + public static Boolean equalsDouble( @OperatorDependency(operator = EQUAL, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}) MethodHandle equalsFunction, @TypeParameter("E") Type type, @SqlType("array(E)") Block leftArray, @@ -106,13 +131,20 @@ public static boolean equalsDouble( return false; } + boolean indeterminate = false; for (int i = 0; i < leftArray.getPositionCount(); i++) { - checkElementNotNull(leftArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); - checkElementNotNull(rightArray.isNull(i), ARRAY_NULL_ELEMENT_MSG); + if (leftArray.isNull(i) || rightArray.isNull(i)) { + indeterminate = true; + continue; + } double leftElement = type.getDouble(leftArray, i); double rightElement = type.getDouble(rightArray, i); try { - if (!(boolean) equalsFunction.invokeExact(leftElement, rightElement)) { + Boolean result = (Boolean) equalsFunction.invokeExact(leftElement, rightElement); + if (result == null) { + indeterminate = true; + } + else if (!result) { return false; } } @@ -120,6 +152,10 @@ public static boolean equalsDouble( throw internalError(t); } } + + if (indeterminate) { + return null; + } return true; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayIntersectFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayIntersectFunction.java index 4664e9cf1c2c6..7bf89dbb6b99a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayIntersectFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayIntersectFunction.java @@ -13,33 +13,21 @@ */ package com.facebook.presto.operator.scalar; +import com.facebook.presto.operator.aggregation.TypedSet; import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.function.Description; -import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.function.TypeParameter; -import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; -import it.unimi.dsi.fastutil.ints.AbstractIntComparator; -import it.unimi.dsi.fastutil.ints.IntArrays; -import it.unimi.dsi.fastutil.ints.IntComparator; - -import java.lang.invoke.MethodHandle; - -import static com.facebook.presto.spi.function.OperatorType.LESS_THAN; @ScalarFunction("array_intersect") @Description("Intersects elements of the two given arrays") public final class ArrayIntersectFunction { - private static final int INITIAL_SIZE = 128; - - private int[] leftPositions = new int[INITIAL_SIZE]; - private int[] rightPositions = new int[INITIAL_SIZE]; private final PageBuilder pageBuilder; @TypeParameter("E") @@ -48,98 +36,47 @@ public ArrayIntersectFunction(@TypeParameter("E") Type elementType) pageBuilder = new PageBuilder(ImmutableList.of(elementType)); } - private static IntComparator intBlockCompare(Type type, Block block) - { - return new AbstractIntComparator() - { - @Override - public int compare(int left, int right) - { - if (block.isNull(left) && block.isNull(right)) { - return 0; - } - if (block.isNull(left)) { - return -1; - } - if (block.isNull(right)) { - return 1; - } - return type.compareTo(block, left, block, right); - } - }; - } - @TypeParameter("E") @SqlType("array(E)") public Block intersect( - @OperatorDependency(operator = LESS_THAN, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}) MethodHandle lessThanFunction, @TypeParameter("E") Type type, @SqlType("array(E)") Block leftArray, @SqlType("array(E)") Block rightArray) { + if (leftArray.getPositionCount() < rightArray.getPositionCount()) { + Block tempArray = leftArray; + leftArray = rightArray; + rightArray = tempArray; + } + int leftPositionCount = leftArray.getPositionCount(); int rightPositionCount = rightArray.getPositionCount(); - if (leftPositionCount == 0) { - return leftArray; - } if (rightPositionCount == 0) { return rightArray; } - if (leftPositions.length < leftPositionCount) { - leftPositions = new int[leftPositionCount]; - } - - if (rightPositions.length < rightPositionCount) { - rightPositions = new int[rightPositionCount]; - } - if (pageBuilder.isFull()) { pageBuilder.reset(); } - for (int i = 0; i < leftPositionCount; i++) { - leftPositions[i] = i; - } + TypedSet rightTypedSet = new TypedSet(type, rightPositionCount, "array_intersect"); for (int i = 0; i < rightPositionCount; i++) { - rightPositions[i] = i; + rightTypedSet.add(rightArray, i); } - IntArrays.quickSort(leftPositions, 0, leftPositionCount, intBlockCompare(type, leftArray)); - IntArrays.quickSort(rightPositions, 0, rightPositionCount, intBlockCompare(type, rightArray)); - - BlockBuilder resultBlockBuilder = pageBuilder.getBlockBuilder(0); - int leftCurrentPosition = 0; - int rightCurrentPosition = 0; - int leftBasePosition; - int rightBasePosition; - int totalCount = 0; + BlockBuilder blockBuilder = pageBuilder.getBlockBuilder(0); - while (leftCurrentPosition < leftPositionCount && rightCurrentPosition < rightPositionCount) { - leftBasePosition = leftCurrentPosition; - rightBasePosition = rightCurrentPosition; - int compareValue = type.compareTo(leftArray, leftPositions[leftCurrentPosition], rightArray, rightPositions[rightCurrentPosition]); - if (compareValue > 0) { - rightCurrentPosition++; - } - else if (compareValue < 0) { - leftCurrentPosition++; - } - else { - type.appendTo(leftArray, leftPositions[leftCurrentPosition], resultBlockBuilder); - leftCurrentPosition++; - rightCurrentPosition++; - totalCount++; - while (leftCurrentPosition < leftPositionCount && type.equalTo(leftArray, leftPositions[leftBasePosition], leftArray, leftPositions[leftCurrentPosition])) { - leftCurrentPosition++; - } - while (rightCurrentPosition < rightPositionCount && type.equalTo(rightArray, rightPositions[rightBasePosition], rightArray, rightPositions[rightCurrentPosition])) { - rightCurrentPosition++; - } + // The intersected set can have at most rightPositionCount elements + TypedSet intersectTypedSet = new TypedSet(type, blockBuilder, rightPositionCount, "array_intersect"); + for (int i = 0; i < leftPositionCount; i++) { + if (rightTypedSet.contains(leftArray, i)) { + intersectTypedSet.add(leftArray, i); } } - pageBuilder.declarePositions(totalCount); - return resultBlockBuilder.getRegion(resultBlockBuilder.getPositionCount() - totalCount, totalCount); + + pageBuilder.declarePositions(intersectTypedSet.size()); + + return blockBuilder.getRegion(blockBuilder.getPositionCount() - intersectTypedSet.size(), intersectTypedSet.size()); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNgramsFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNgramsFunction.java new file mode 100644 index 0000000000000..52ace9bb1d45e --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNgramsFunction.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.spi.block.ArrayBlock; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.Description; +import com.facebook.presto.spi.function.ScalarFunction; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.function.TypeParameter; + +import java.util.Optional; + +import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.type.StandardTypes.INTEGER; +import static com.facebook.presto.util.Failures.checkCondition; +import static java.lang.Math.min; +import static java.lang.StrictMath.toIntExact; + +@ScalarFunction("ngrams") +@Description("Return N-grams for the input") +public final class ArrayNgramsFunction +{ + private ArrayNgramsFunction(){} + + @TypeParameter("T") + @SqlType("array(array(T))") + public static Block ngrams(@SqlType("array(T)") Block array, @SqlType(INTEGER) long n) + { + checkCondition(n > 0, INVALID_FUNCTION_ARGUMENT, "N must be positive"); + + // n should not be larger than the array length + int elementsPerRecord = toIntExact(min(array.getPositionCount(), n)); + int totalRecords = array.getPositionCount() - elementsPerRecord + 1; + int[] ids = new int[totalRecords * elementsPerRecord]; + int[] offset = new int[totalRecords + 1]; + for (int recordIndex = 0; recordIndex < totalRecords; recordIndex++) { + for (int elementIndex = 0; elementIndex < elementsPerRecord; elementIndex++) { + ids[recordIndex * elementsPerRecord + elementIndex] = recordIndex + elementIndex; + } + offset[recordIndex + 1] = (recordIndex + 1) * elementsPerRecord; + } + + return ArrayBlock.fromElementBlock(totalRecords, Optional.empty(), offset, array.getPositions(ids, 0, totalRecords * elementsPerRecord)); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNotEqualOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNotEqualOperator.java index 7808b38a55ab0..5c949b1a4d126 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNotEqualOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayNotEqualOperator.java @@ -16,6 +16,7 @@ import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.function.TypeParameter; import com.facebook.presto.spi.type.StandardTypes; @@ -33,12 +34,17 @@ private ArrayNotEqualOperator() {} @TypeParameter("E") @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual( + @SqlNullable + public static Boolean notEqual( @OperatorDependency(operator = EQUAL, returnType = StandardTypes.BOOLEAN, argumentTypes = {"E", "E"}) MethodHandle equalsFunction, @TypeParameter("E") Type type, @SqlType("array(E)") Block left, @SqlType("array(E)") Block right) { - return !ArrayEqualOperator.equals(equalsFunction, type, left, right); + Boolean result = ArrayEqualOperator.equals(equalsFunction, type, left, right); + if (result == null) { + return null; + } + return !result; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java index 94fa7f955589a..5563ab563549c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayPositionFunction.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.operator.scalar; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.Description; import com.facebook.presto.spi.function.OperatorDependency; @@ -25,6 +26,7 @@ import java.lang.invoke.MethodHandle; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.util.Failures.internalError; @@ -47,7 +49,9 @@ public static long arrayPosition( if (!array.isNull(i)) { boolean arrayValue = type.getBoolean(array, i); try { - if ((boolean) equalMethodHandle.invokeExact(arrayValue, element)) { + Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); + checkNotIndeterminate(result); + if (result) { return i + 1; // result is 1-based (instead of 0) } } @@ -72,7 +76,9 @@ public static long arrayPosition( if (!array.isNull(i)) { long arrayValue = type.getLong(array, i); try { - if ((boolean) equalMethodHandle.invokeExact(arrayValue, element)) { + Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); + checkNotIndeterminate(result); + if (result) { return i + 1; // result is 1-based (instead of 0) } } @@ -97,7 +103,9 @@ public static long arrayPosition( if (!array.isNull(i)) { double arrayValue = type.getDouble(array, i); try { - if ((boolean) equalMethodHandle.invokeExact(arrayValue, element)) { + Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); + checkNotIndeterminate(result); + if (result) { return i + 1; // result is 1-based (instead of 0) } } @@ -122,7 +130,9 @@ public static long arrayPosition( if (!array.isNull(i)) { Slice arrayValue = type.getSlice(array, i); try { - if ((boolean) equalMethodHandle.invokeExact(arrayValue, element)) { + Boolean result = (Boolean) equalMethodHandle.invokeExact(arrayValue, element); + checkNotIndeterminate(result); + if (result) { return i + 1; // result is 1-based (instead of 0) } } @@ -147,7 +157,9 @@ public static long arrayPosition( if (!array.isNull(i)) { Object arrayValue = type.getObject(array, i); try { - if ((boolean) equalMethodHandle.invoke(arrayValue, element)) { + Boolean result = (Boolean) equalMethodHandle.invoke(arrayValue, element); + checkNotIndeterminate(result); + if (result) { return i + 1; // result is 1-based (instead of 0) } } @@ -158,4 +170,11 @@ public static long arrayPosition( } return 0; } + + private static void checkNotIndeterminate(Boolean equalsResult) + { + if (equalsResult == null) { + throw new PrestoException(NOT_SUPPORTED, "array_position does not support arrays with elements that are null or contain null"); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayRemoveFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayRemoveFunction.java index 4c80ea3967798..f9f51995e4d72 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayRemoveFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArrayRemoveFunction.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.scalar; import com.facebook.presto.spi.PageBuilder; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.function.Description; @@ -29,6 +30,7 @@ import java.util.ArrayList; import java.util.List; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; import static com.facebook.presto.util.Failures.internalError; @@ -92,7 +94,15 @@ public Block remove( Object element = readNativeValue(type, array, i); try { - if (element == null || !(boolean) equalsFunction.invoke(element, value)) { + if (element == null) { + positions.add(i); + continue; + } + Boolean result = (Boolean) equalsFunction.invoke(element, value); + if (result == null) { + throw new PrestoException(NOT_SUPPORTED, "array_remove does not support arrays with elements that are null or contain null"); + } + if (!result) { positions.add(i); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArraySortComparatorFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArraySortComparatorFunction.java index 48714e128479e..343b0170739a4 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArraySortComparatorFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ArraySortComparatorFunction.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator.scalar; import com.facebook.presto.spi.PageBuilder; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.function.Description; @@ -27,6 +28,7 @@ import com.google.common.primitives.Ints; import io.airlift.slice.Slice; +import java.util.Comparator; import java.util.List; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; @@ -38,7 +40,6 @@ public final class ArraySortComparatorFunction { private final PageBuilder pageBuilder; private static final int INITIAL_LENGTH = 128; - private static final String COMPARATOR_RETURN_ERROR = "Lambda comparator must return either -1, 0, or 1"; private List positions = Ints.asList(new int[INITIAL_LENGTH]); @TypeParameter("T") @@ -58,16 +59,11 @@ public Block sortLong( int arrayLength = block.getPositionCount(); initPositionsList(arrayLength); - positions.subList(0, arrayLength).sort((p1, p2) -> { - Long lambdaResult = function.apply( - block.isNull(p1) ? null : type.getLong(block, p1), - block.isNull(p2) ? null : type.getLong(block, p2)); - checkCondition( - lambdaResult != null && (lambdaResult == -1 || lambdaResult == 0 || lambdaResult == 1), - INVALID_FUNCTION_ARGUMENT, - COMPARATOR_RETURN_ERROR); - return lambdaResult.intValue(); - }); + Comparator comparator = (x, y) -> comparatorResult(function.apply( + block.isNull(x) ? null : type.getLong(block, x), + block.isNull(y) ? null : type.getLong(block, y))); + + sortPositions(arrayLength, comparator); return computeResultBlock(type, block, arrayLength); } @@ -83,16 +79,11 @@ public Block sortDouble( int arrayLength = block.getPositionCount(); initPositionsList(arrayLength); - positions.subList(0, arrayLength).sort((p1, p2) -> { - Long lambdaResult = function.apply( - block.isNull(p1) ? null : type.getDouble(block, p1), - block.isNull(p2) ? null : type.getDouble(block, p2)); - checkCondition( - lambdaResult != null && (lambdaResult == -1 || lambdaResult == 0 || lambdaResult == 1), - INVALID_FUNCTION_ARGUMENT, - COMPARATOR_RETURN_ERROR); - return lambdaResult.intValue(); - }); + Comparator comparator = (x, y) -> comparatorResult(function.apply( + block.isNull(x) ? null : type.getDouble(block, x), + block.isNull(y) ? null : type.getDouble(block, y))); + + sortPositions(arrayLength, comparator); return computeResultBlock(type, block, arrayLength); } @@ -108,16 +99,11 @@ public Block sortBoolean( int arrayLength = block.getPositionCount(); initPositionsList(arrayLength); - positions.subList(0, arrayLength).sort((p1, p2) -> { - Long lambdaResult = function.apply( - block.isNull(p1) ? null : type.getBoolean(block, p1), - block.isNull(p2) ? null : type.getBoolean(block, p2)); - checkCondition( - lambdaResult != null && (lambdaResult == -1 || lambdaResult == 0 || lambdaResult == 1), - INVALID_FUNCTION_ARGUMENT, - COMPARATOR_RETURN_ERROR); - return lambdaResult.intValue(); - }); + Comparator comparator = (x, y) -> comparatorResult(function.apply( + block.isNull(x) ? null : type.getBoolean(block, x), + block.isNull(y) ? null : type.getBoolean(block, y))); + + sortPositions(arrayLength, comparator); return computeResultBlock(type, block, arrayLength); } @@ -133,16 +119,11 @@ public Block sortSlice( int arrayLength = block.getPositionCount(); initPositionsList(arrayLength); - positions.subList(0, arrayLength).sort((p1, p2) -> { - Long lambdaResult = function.apply( - block.isNull(p1) ? null : type.getSlice(block, p1), - block.isNull(p2) ? null : type.getSlice(block, p2)); - checkCondition( - lambdaResult != null && (lambdaResult == -1 || lambdaResult == 0 || lambdaResult == 1), - INVALID_FUNCTION_ARGUMENT, - COMPARATOR_RETURN_ERROR); - return lambdaResult.intValue(); - }); + Comparator comparator = (x, y) -> comparatorResult(function.apply( + block.isNull(x) ? null : type.getSlice(block, x), + block.isNull(y) ? null : type.getSlice(block, y))); + + sortPositions(arrayLength, comparator); return computeResultBlock(type, block, arrayLength); } @@ -158,16 +139,11 @@ public Block sortObject( int arrayLength = block.getPositionCount(); initPositionsList(arrayLength); - positions.subList(0, arrayLength).sort((p1, p2) -> { - Long lambdaResult = function.apply( - block.isNull(p1) ? null : (Block) type.getObject(block, p1), - block.isNull(p2) ? null : (Block) type.getObject(block, p2)); - checkCondition( - lambdaResult != null && (lambdaResult == -1 || lambdaResult == 0 || lambdaResult == 1), - INVALID_FUNCTION_ARGUMENT, - COMPARATOR_RETURN_ERROR); - return lambdaResult.intValue(); - }); + Comparator comparator = (x, y) -> comparatorResult(function.apply( + block.isNull(x) ? null : (Block) type.getObject(block, x), + block.isNull(y) ? null : (Block) type.getObject(block, y))); + + sortPositions(arrayLength, comparator); return computeResultBlock(type, block, arrayLength); } @@ -182,6 +158,18 @@ private void initPositionsList(int arrayLength) } } + private void sortPositions(int arrayLength, Comparator comparator) + { + List list = positions.subList(0, arrayLength); + + try { + list.sort(comparator); + } + catch (IllegalArgumentException e) { + throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Lambda comparator violates the comparator contract", e); + } + } + private Block computeResultBlock(Type type, Block block, int arrayLength) { if (pageBuilder.isFull()) { @@ -198,6 +186,15 @@ private Block computeResultBlock(Type type, Block block, int arrayLength) return blockBuilder.getRegion(blockBuilder.getPositionCount() - arrayLength, arrayLength); } + private static int comparatorResult(Long result) + { + checkCondition( + (result != null) && ((result == -1) || (result == 0) || (result == 1)), + INVALID_FUNCTION_ARGUMENT, + "Lambda comparator must return either -1, 0, or 1"); + return result.intValue(); + } + @FunctionalInterface public interface ComparatorLongLambda extends LambdaFunctionInterface diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/DataSizeFunctions.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/DataSizeFunctions.java new file mode 100644 index 0000000000000..f3252d0f296be --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/DataSizeFunctions.java @@ -0,0 +1,135 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.function.Description; +import com.facebook.presto.spi.function.LiteralParameters; +import com.facebook.presto.spi.function.ScalarFunction; +import com.facebook.presto.spi.function.SqlType; +import io.airlift.slice.Slice; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.StandardErrorCode.NUMERIC_VALUE_OUT_OF_RANGE; +import static com.facebook.presto.spi.type.Decimals.encodeUnscaledValue; +import static java.lang.Character.isDigit; +import static java.lang.String.format; + +public final class DataSizeFunctions +{ + private DataSizeFunctions() {} + + @Description("converts data size string to bytes") + @ScalarFunction("parse_presto_data_size") + @LiteralParameters("x") + @SqlType("decimal(38,0)") + public static Slice parsePrestoDataSize(@SqlType("varchar(x)") Slice input) + { + String dataSize = input.toStringUtf8(); + + int valueLength = 0; + for (int i = 0; i < dataSize.length(); i++) { + char c = dataSize.charAt(i); + if (isDigit(c) || c == '.') { + valueLength++; + } + else { + break; + } + } + + if (valueLength == 0) { + throw invalidDataSize(dataSize); + } + + BigDecimal value = parseValue(dataSize.substring(0, valueLength), dataSize); + Unit unit = Unit.parse(dataSize.substring(valueLength), dataSize); + BigInteger bytes = value.multiply(unit.getFactor()).toBigInteger(); + try { + return encodeUnscaledValue(bytes); + } + catch (ArithmeticException e) { + throw new PrestoException(NUMERIC_VALUE_OUT_OF_RANGE, format("Value out of range: '%s' ('%sB')", dataSize, bytes)); + } + } + + private static BigDecimal parseValue(String value, String dataSize) + { + try { + return new BigDecimal(value); + } + catch (NumberFormatException e) { + throw invalidDataSize(dataSize); + } + } + + private static PrestoException invalidDataSize(String dataSize) + { + return new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Invalid data size: '%s'", dataSize)); + } + + private enum Unit + { + BYTE(BigDecimal.ONE), + KILOBYTE(new BigDecimal(1L << 10)), + MEGABYTE(new BigDecimal(1L << 20)), + GIGABYTE(new BigDecimal(1L << 30)), + TERABYTE(new BigDecimal(1L << 40)), + PETABYTE(new BigDecimal(1L << 50)), + EXABYTE(new BigDecimal(1L << 60)), + ZETTABYTE(new BigDecimal(1L << 60).multiply(new BigDecimal(1L << 10))), + YOTTABYTE(new BigDecimal(1L << 60).multiply(new BigDecimal(1L << 20))); + + private final BigDecimal factor; + + Unit(BigDecimal factor) + { + this.factor = factor; + } + + public BigDecimal getFactor() + { + return factor; + } + + public static Unit parse(String unitString, String dataSize) + { + switch (unitString) { + case "B": + return BYTE; + case "kB": + return KILOBYTE; + case "MB": + return MEGABYTE; + case "GB": + return GIGABYTE; + case "TB": + return TERABYTE; + case "PB": + return PETABYTE; + case "EB": + return EXABYTE; + case "ZB": + return ZETTABYTE; + case "YB": + return YOTTABYTE; + default: + throw invalidDataSize(dataSize); + } + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/DateTimeFunctions.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/DateTimeFunctions.java index 6ae58e4c3a999..3cc994f613ec0 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/DateTimeFunctions.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/DateTimeFunctions.java @@ -66,6 +66,7 @@ public final class DateTimeFunctions private static final ISOChronology UTC_CHRONOLOGY = ISOChronology.getInstanceUTC(); private static final DateTimeField SECOND_OF_MINUTE = UTC_CHRONOLOGY.secondOfMinute(); + private static final DateTimeField MILLISECOND_OF_SECOND = UTC_CHRONOLOGY.millisOfSecond(); private static final DateTimeField MINUTE_OF_HOUR = UTC_CHRONOLOGY.minuteOfHour(); private static final DateTimeField HOUR_OF_DAY = UTC_CHRONOLOGY.hourOfDay(); private static final DateTimeField DAY_OF_WEEK = UTC_CHRONOLOGY.dayOfWeek(); @@ -700,6 +701,54 @@ public static long dateParse(ConnectorSession session, @SqlType("varchar(x)") Sl } } + @Description("millisecond of the second of the given timestamp") + @ScalarFunction("millisecond") + @SqlType(StandardTypes.BIGINT) + public static long millisecondFromTimestamp(@SqlType(StandardTypes.TIMESTAMP) long timestamp) + { + // No need to check isLegacyTimestamp: + // * Under legacy semantics, the session zone matters. But a zone always has offset of whole minutes. + // * Under new semantics, timestamp is agnostic to the session zone. + return MILLISECOND_OF_SECOND.get(timestamp); + } + + @Description("millisecond of the second of the given timestamp") + @ScalarFunction("millisecond") + @SqlType(StandardTypes.BIGINT) + public static long millisecondFromTimestampWithTimeZone(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long timestampWithTimeZone) + { + // No need to check the associated zone here. A zone always has offset of whole minutes. + return MILLISECOND_OF_SECOND.get(unpackMillisUtc(timestampWithTimeZone)); + } + + @Description("millisecond of the second of the given time") + @ScalarFunction("millisecond") + @SqlType(StandardTypes.BIGINT) + public static long millisecondFromTime(@SqlType(StandardTypes.TIME) long time) + { + // No need to check isLegacyTimestamp: + // * Under legacy semantics, the session zone matters. But a zone always has offset of whole minutes. + // * Under new semantics, time is agnostic to the session zone. + return MILLISECOND_OF_SECOND.get(time); + } + + @Description("millisecond of the second of the given time") + @ScalarFunction("millisecond") + @SqlType(StandardTypes.BIGINT) + public static long millisecondFromTimeWithTimeZone(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long time) + { + // No need to check the associated zone here. A zone always has offset of whole minutes. + return MILLISECOND_OF_SECOND.get(unpackMillisUtc(time)); + } + + @Description("millisecond of the second of the given interval") + @ScalarFunction("millisecond") + @SqlType(StandardTypes.BIGINT) + public static long millisecondFromInterval(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long milliseconds) + { + return milliseconds % MILLISECONDS_IN_SECOND; + } + @Description("second of the minute of the given timestamp") @ScalarFunction("second") @SqlType(StandardTypes.BIGINT) diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/JsonOperators.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/JsonOperators.java index 850db1e190a5a..0d41b08af3f8f 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/JsonOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/JsonOperators.java @@ -15,6 +15,9 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; @@ -376,28 +379,54 @@ public static boolean indeterminate(@SqlType(JSON) Slice value, @IsNull boolean @ScalarOperator(EQUAL) @SqlType(BOOLEAN) - public static boolean equals(@SqlType(JSON) Slice leftJson, @SqlType(JSON) Slice rightJson) + @SqlNullable + public static Boolean equals(@SqlType(JSON) Slice leftJson, @SqlType(JSON) Slice rightJson) { return leftJson.equals(rightJson); } @ScalarOperator(NOT_EQUAL) @SqlType(BOOLEAN) - public static boolean notEqual(@SqlType(JSON) Slice leftJson, @SqlType(JSON) Slice rightJson) + @SqlNullable + public static Boolean notEqual(@SqlType(JSON) Slice leftJson, @SqlType(JSON) Slice rightJson) { return !leftJson.equals(rightJson); } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(BOOLEAN) - public static boolean isDistinctFrom(@SqlType(JSON) Slice leftJson, @IsNull boolean leftNull, @SqlType(JSON) Slice rightJson, @IsNull boolean rightNull) + public static class JsonDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(BOOLEAN) + public static boolean isDistinctFrom(@SqlType(JSON) Slice leftJson, @IsNull boolean leftNull, @SqlType(JSON) Slice rightJson, @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(leftJson, rightJson); } - if (leftNull) { - return false; + + @SqlType(BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = JSON, nativeContainerType = Slice.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = JSON, nativeContainerType = Slice.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + int leftLength = left.getSliceLength(leftPosition); + int rightLength = right.getSliceLength(rightPosition); + if (leftLength != rightLength) { + return true; + } + return !left.equals(leftPosition, 0, right, rightPosition, 0, leftLength); } - return notEqual(leftJson, rightJson); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapDistinctFromOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapDistinctFromOperator.java index 1174446d33afa..85da781a7d55a 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapDistinctFromOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapDistinctFromOperator.java @@ -14,20 +14,23 @@ */ import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; +import com.facebook.presto.spi.function.Convention; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.OperatorDependency; import com.facebook.presto.spi.function.ScalarOperator; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.function.TypeParameter; +import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import java.lang.invoke.MethodHandle; -import static com.facebook.presto.spi.function.OperatorType.EQUAL; -import static com.facebook.presto.spi.function.OperatorType.HASH_CODE; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static com.facebook.presto.spi.function.OperatorType.IS_DISTINCT_FROM; -import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; @ScalarOperator(IS_DISTINCT_FROM) public final class MapDistinctFromOperator @@ -38,14 +41,9 @@ private MapDistinctFromOperator() {} @TypeParameter("V") @SqlType(StandardTypes.BOOLEAN) public static boolean isDistinctFrom( - @OperatorDependency(operator = EQUAL, returnType = StandardTypes.BOOLEAN, argumentTypes = {"K", "K"}) - MethodHandle keyEqualsFunction, - @OperatorDependency(operator = HASH_CODE, returnType = StandardTypes.BIGINT, argumentTypes = {"K"}) - MethodHandle keyHashcodeFunction, - @OperatorDependency(operator = IS_DISTINCT_FROM, returnType = StandardTypes.BOOLEAN, argumentTypes = {"V", "V"}) + @OperatorDependency(operator = IS_DISTINCT_FROM, returnType = StandardTypes.BOOLEAN, argumentTypes = {"V", "V"}, convention = @Convention(arguments = {BLOCK_POSITION, BLOCK_POSITION}, result = FAIL_ON_NULL)) MethodHandle valueDistinctFromFunction, - @TypeParameter("K") Type keyType, - @TypeParameter("V") Type valueType, + @TypeParameter("map(K, V)") Type mapType, @SqlType("map(K,V)") Block leftMapBlock, @IsNull boolean leftMapNull, @SqlType("map(K,V)") Block rightMapBlock, @@ -59,18 +57,30 @@ public static boolean isDistinctFrom( } // Note that we compare to NOT distinct here and so negate the result. return !MapGenericEquality.genericEqual( - keyType, + ((MapType) mapType).getKeyType(), leftMapBlock, rightMapBlock, - (leftMapIndex, rightMapIndex) -> { - Object leftValue = readNativeValue(valueType, leftMapBlock, leftMapIndex); - Object rightValue = readNativeValue(valueType, rightMapBlock, rightMapIndex); - boolean leftNull = leftValue == null; - boolean rightNull = rightValue == null; - if (leftNull || rightNull) { - return leftNull == rightNull; - } - return !(boolean) valueDistinctFromFunction.invoke(leftValue, leftNull, rightValue, rightNull); - }); + (leftMapIndex, rightMapIndex) -> !(boolean) valueDistinctFromFunction.invokeExact(leftMapBlock, leftMapIndex, rightMapBlock, rightMapIndex)); + } + + @TypeParameter("K") + @TypeParameter("V") + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @OperatorDependency(operator = IS_DISTINCT_FROM, returnType = StandardTypes.BOOLEAN, argumentTypes = {"V", "V"}, convention = @Convention(arguments = {BLOCK_POSITION, BLOCK_POSITION}, result = FAIL_ON_NULL)) + MethodHandle valueDistinctFromFunction, + @TypeParameter("map(K, V)") Type mapType, + @BlockPosition @SqlType(value = "map(K,V)", nativeContainerType = Block.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = "map(K,V)", nativeContainerType = Block.class) Block right, + @BlockIndex int rightPosition) + { + return isDistinctFrom( + valueDistinctFromFunction, + mapType, + (Block) mapType.getObject(left, leftPosition), + left.isNull(leftPosition), + (Block) mapType.getObject(right, rightPosition), + right.isNull(rightPosition)); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapGenericEquality.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapGenericEquality.java index c40ec3b61d6d0..3e1da18560c2e 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapGenericEquality.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapGenericEquality.java @@ -43,6 +43,7 @@ public static Boolean genericEqual( SingleMapBlock leftSingleMapLeftBlock = (SingleMapBlock) leftBlock; SingleMapBlock rightSingleMapBlock = (SingleMapBlock) rightBlock; + boolean indeterminate = false; for (int position = 0; position < leftSingleMapLeftBlock.getPositionCount(); position += 2) { Object key = readNativeValue(keyType, leftBlock, position); int leftPosition = position + 1; @@ -54,7 +55,7 @@ public static Boolean genericEqual( try { Boolean result = predicate.equals(leftPosition, rightPosition); if (result == null) { - return null; + indeterminate = true; } else if (!result) { return false; @@ -64,6 +65,10 @@ else if (!result) { throw internalError(t); } } + + if (indeterminate) { + return null; + } return true; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapZipWithFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapZipWithFunction.java index 19595ca73dfaa..072d27e14ab78 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapZipWithFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MapZipWithFunction.java @@ -91,8 +91,8 @@ public ScalarFunctionImplementation specialize(BoundVariables boundVariables, in Type outputMapType = typeManager.getParameterizedType( StandardTypes.MAP, ImmutableList.of( - TypeSignatureParameter.of(keyType.getTypeSignature()), - TypeSignatureParameter.of(outputValueType.getTypeSignature()))); + TypeSignatureParameter.of(keyType.getTypeSignature()), + TypeSignatureParameter.of(outputValueType.getTypeSignature()))); return new ScalarFunctionImplementation( false, ImmutableList.of( diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MathFunctions.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MathFunctions.java index c93e431fff318..746eb1f7d2f97 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/MathFunctions.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/MathFunctions.java @@ -16,7 +16,6 @@ import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlScalarFunction; import com.facebook.presto.operator.aggregation.TypedSet; -import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.Description; @@ -31,6 +30,7 @@ import com.facebook.presto.type.LiteralParameter; import com.google.common.primitives.Doubles; import io.airlift.slice.Slice; +import org.apache.commons.math3.distribution.BetaDistribution; import org.apache.commons.math3.special.Erf; import java.math.BigInteger; @@ -60,7 +60,6 @@ import static java.lang.Character.MIN_RADIX; import static java.lang.Float.floatToRawIntBits; import static java.lang.Float.intBitsToFloat; -import static java.lang.Math.toIntExact; import static java.lang.String.format; public final class MathFunctions @@ -631,6 +630,34 @@ public static double normalCdf( return 0.5 * (1 + Erf.erf((value - mean) / (standardDeviation * Math.sqrt(2)))); } + @Description("inverse of Beta cdf given a, b parameters and probability") + @ScalarFunction + @SqlType(StandardTypes.DOUBLE) + public static double inverseBetaCdf( + @SqlType(StandardTypes.DOUBLE) double a, + @SqlType(StandardTypes.DOUBLE) double b, + @SqlType(StandardTypes.DOUBLE) double p) + { + checkCondition(p >= 0 && p <= 1, INVALID_FUNCTION_ARGUMENT, "p must be 0 >= p >= 1"); + checkCondition(a > 0 && b > 0, INVALID_FUNCTION_ARGUMENT, "a, b must be > 0"); + BetaDistribution distribution = new BetaDistribution(null, a, b, BetaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY); + return distribution.inverseCumulativeProbability(p); + } + + @Description("Beta cdf given the a, b parameters and value") + @ScalarFunction + @SqlType(StandardTypes.DOUBLE) + public static double betaCdf( + @SqlType(StandardTypes.DOUBLE) double a, + @SqlType(StandardTypes.DOUBLE) double b, + @SqlType(StandardTypes.DOUBLE) double value) + { + checkCondition(value >= 0 && value <= 1, INVALID_FUNCTION_ARGUMENT, "value must be 0 >= v >= 1"); + checkCondition(a > 0 && b > 0, INVALID_FUNCTION_ARGUMENT, "a, b must be > 0"); + BetaDistribution distribution = new BetaDistribution(null, a, b, BetaDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY); + return distribution.cumulativeProbability(value); + } + @Description("round to nearest integer") @ScalarFunction("round") @SqlType(StandardTypes.TINYINT) @@ -672,15 +699,6 @@ public static long roundTinyint(@SqlType(StandardTypes.TINYINT) long num, @SqlTy return num; } - @Description("round to nearest integer") - @ScalarFunction("round") - @SqlType(StandardTypes.TINYINT) - public static long roundTinyintBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.TINYINT) long num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return roundTinyint(num, toIntExact(decimals)); - } - @Description("round to nearest integer") @ScalarFunction("round") @SqlType(StandardTypes.SMALLINT) @@ -690,15 +708,6 @@ public static long roundSmallint(@SqlType(StandardTypes.SMALLINT) long num, @Sql return num; } - @Description("round to nearest integer") - @ScalarFunction("round") - @SqlType(StandardTypes.SMALLINT) - public static long roundSmallintBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.SMALLINT) long num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return roundSmallint(num, toIntExact(decimals)); - } - @Description("round to nearest integer") @ScalarFunction("round") @SqlType(StandardTypes.INTEGER) @@ -708,15 +717,6 @@ public static long roundInteger(@SqlType(StandardTypes.INTEGER) long num, @SqlTy return num; } - @Description("round to nearest integer") - @ScalarFunction("round") - @SqlType(StandardTypes.INTEGER) - public static long roundIntegerBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.INTEGER) long num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return roundInteger(num, toIntExact(decimals)); - } - @Description("round to nearest integer") @ScalarFunction @SqlType(StandardTypes.BIGINT) @@ -726,15 +726,6 @@ public static long round(@SqlType(StandardTypes.BIGINT) long num, @SqlType(Stand return num; } - @Description("round to nearest integer") - @ScalarFunction - @SqlType(StandardTypes.BIGINT) - public static long roundBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.BIGINT) long num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return round(num, toIntExact(decimals)); - } - @Description("round to nearest integer") @ScalarFunction @SqlType(StandardTypes.DOUBLE) @@ -768,14 +759,6 @@ public static double round(@SqlType(StandardTypes.DOUBLE) double num, @SqlType(S return Math.round(num * factor) / factor; } - @ScalarFunction - @SqlType(StandardTypes.DOUBLE) - public static double roundBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.DOUBLE) double num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return round(num, toIntExact(decimals)); - } - @Description("round to given number of decimal places") @ScalarFunction("round") @SqlType(StandardTypes.REAL) @@ -794,15 +777,6 @@ public static long roundFloat(@SqlType(StandardTypes.REAL) long num, @SqlType(St return floatToRawIntBits((float) (Math.round(numInFloat * factor) / factor)); } - @Description("round to given number of decimal places") - @ScalarFunction("round") - @SqlType(StandardTypes.REAL) - public static long roundFloatBigintDecimals(ConnectorSession session, @SqlType(StandardTypes.REAL) long num, @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return roundFloat(num, toIntExact(decimals)); - } - @ScalarFunction("round") @Description("round to nearest integer") public static final class Round @@ -919,55 +893,6 @@ public static Slice roundNShortLong( } } - @ScalarFunction("round") - @Description("round to given number of decimal places") - public static final class RoundNBigintDecimals - { - private RoundNBigintDecimals() {} - - @LiteralParameters({"p", "s", "rp"}) - @SqlType("decimal(rp, s)") - @Constraint(variable = "rp", expression = "min(38, p + 1)") - public static long roundNShortBigintDecimals( - @LiteralParameter("p") long numPrecision, - @LiteralParameter("s") long numScale, - ConnectorSession session, - @SqlType("decimal(p, s)") long num, - @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return RoundN.roundNShort(numPrecision, numScale, num, toIntExact(decimals)); - } - - @LiteralParameters({"p", "s", "rp"}) - @SqlType("decimal(rp, s)") - @Constraint(variable = "rp", expression = "min(38, p + 1)") - public static Slice roundNLongBigintDecimals( - @LiteralParameter("s") long numScale, - @LiteralParameter("rp") long resultPrecision, - ConnectorSession session, - @SqlType("decimal(p, s)") Slice num, - @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return RoundN.roundNLong(numScale, resultPrecision, num, toIntExact(decimals)); - } - - @LiteralParameters({"p", "s", "rp"}) - @SqlType("decimal(rp, s)") - @Constraint(variable = "rp", expression = "min(38, p + 1)") - public static Slice roundNShortLongBigintDecimals( - @LiteralParameter("s") long numScale, - @LiteralParameter("rp") long resultPrecision, - ConnectorSession session, - @SqlType("decimal(p, s)") long num, - @SqlType(StandardTypes.BIGINT) long decimals) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return RoundN.roundNShortLong(numScale, resultPrecision, num, toIntExact(decimals)); - } - } - @ScalarFunction("truncate") @Description("round to integer by dropping digits after decimal point") public static final class Truncate @@ -1053,39 +978,6 @@ public static Slice truncateLong( } } - @ScalarFunction("truncate") - @Description("round to integer by dropping given number of digits after decimal point") - public static final class TruncateNBigintDecimals - { - private TruncateNBigintDecimals() {} - - @LiteralParameters({"p", "s"}) - @SqlType("decimal(p, s)") - public static long truncateShortBigintDecimals( - @LiteralParameter("p") long numPrecision, - @LiteralParameter("s") long numScale, - ConnectorSession session, - @SqlType("decimal(p, s)") long num, - @SqlType(StandardTypes.BIGINT) long roundScale) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "TRUNCATE(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return TruncateN.truncateShort(numPrecision, numScale, num, toIntExact(roundScale)); - } - - @LiteralParameters({"p", "s"}) - @SqlType("decimal(p, s)") - public static Slice truncateLongBigintDecimals( - @LiteralParameter("p") long numPrecision, - @LiteralParameter("s") long numScale, - ConnectorSession session, - @SqlType("decimal(p, s)") Slice num, - @SqlType(StandardTypes.BIGINT) long roundScale) - { - checkCondition(session.isLegacyRoundNBigint(), INVALID_FUNCTION_ARGUMENT, "TRUNCATE(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - return TruncateN.truncateLong(numPrecision, numScale, num, toIntExact(roundScale)); - } - } - @Description("signum") @ScalarFunction("sign") public static final class Sign diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/QuantileDigestFunctions.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/QuantileDigestFunctions.java new file mode 100644 index 0000000000000..a5e0d1c2a2619 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/QuantileDigestFunctions.java @@ -0,0 +1,115 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.Description; +import com.facebook.presto.spi.function.ScalarFunction; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.type.StandardTypes; +import io.airlift.slice.Slice; +import io.airlift.stats.QuantileDigest; + +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.sortableIntToFloat; +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.sortableLongToDouble; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.util.Failures.checkCondition; +import static java.lang.Float.floatToRawIntBits; + +public final class QuantileDigestFunctions +{ + public static final double DEFAULT_ACCURACY = 0.01; + public static final long DEFAULT_WEIGHT = 1L; + + private QuantileDigestFunctions() {} + + @ScalarFunction("value_at_quantile") + @Description("Given an input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType(StandardTypes.DOUBLE) + public static double valueAtQuantileDouble(@SqlType("qdigest(double)") Slice input, @SqlType(StandardTypes.DOUBLE) double quantile) + { + return sortableLongToDouble(valueAtQuantileBigint(input, quantile)); + } + + @ScalarFunction("value_at_quantile") + @Description("Given an input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType(StandardTypes.REAL) + public static long valueAtQuantileReal(@SqlType("qdigest(real)") Slice input, @SqlType(StandardTypes.DOUBLE) double quantile) + { + return floatToRawIntBits(sortableIntToFloat((int) valueAtQuantileBigint(input, quantile))); + } + + @ScalarFunction("value_at_quantile") + @Description("Given an input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType(StandardTypes.BIGINT) + public static long valueAtQuantileBigint(@SqlType("qdigest(bigint)") Slice input, @SqlType(StandardTypes.DOUBLE) double quantile) + { + return new QuantileDigest(input).getQuantile(quantile); + } + + @ScalarFunction("values_at_quantiles") + @Description("For each input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType("array(double)") + public static Block valuesAtQuantilesDouble(@SqlType("qdigest(double)") Slice input, @SqlType("array(double)") Block percentilesArrayBlock) + { + QuantileDigest digest = new QuantileDigest(input); + BlockBuilder output = DOUBLE.createBlockBuilder(null, percentilesArrayBlock.getPositionCount()); + for (int i = 0; i < percentilesArrayBlock.getPositionCount(); i++) { + DOUBLE.writeDouble(output, sortableLongToDouble(digest.getQuantile(DOUBLE.getDouble(percentilesArrayBlock, i)))); + } + return output.build(); + } + + @ScalarFunction("values_at_quantiles") + @Description("For each input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType("array(real)") + public static Block valuesAtQuantilesReal(@SqlType("qdigest(real)") Slice input, @SqlType("array(double)") Block percentilesArrayBlock) + { + QuantileDigest digest = new QuantileDigest(input); + BlockBuilder output = REAL.createBlockBuilder(null, percentilesArrayBlock.getPositionCount()); + for (int i = 0; i < percentilesArrayBlock.getPositionCount(); i++) { + REAL.writeLong(output, floatToRawIntBits(sortableIntToFloat((int) digest.getQuantile(DOUBLE.getDouble(percentilesArrayBlock, i))))); + } + return output.build(); + } + + @ScalarFunction("values_at_quantiles") + @Description("For each input q between [0, 1], find the value whose rank in the sorted sequence of the n values represented by the qdigest is qn.") + @SqlType("array(bigint)") + public static Block valuesAtQuantilesBigint(@SqlType("qdigest(bigint)") Slice input, @SqlType("array(double)") Block percentilesArrayBlock) + { + QuantileDigest digest = new QuantileDigest(input); + BlockBuilder output = BIGINT.createBlockBuilder(null, percentilesArrayBlock.getPositionCount()); + for (int i = 0; i < percentilesArrayBlock.getPositionCount(); i++) { + BIGINT.writeLong(output, digest.getQuantile(DOUBLE.getDouble(percentilesArrayBlock, i))); + } + return output.build(); + } + + public static double verifyAccuracy(double accuracy) + { + checkCondition(accuracy > 0 && accuracy < 1, INVALID_FUNCTION_ARGUMENT, "Percentile accuracy must be exclusively between 0 and 1, was %s", accuracy); + return accuracy; + } + + public static long verifyWeight(long weight) + { + checkCondition(weight > 0, INVALID_FUNCTION_ARGUMENT, "Percentile weight must be > 0, was %s", weight); + return weight; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowDistinctFromOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowDistinctFromOperator.java index 6bf0895a9acda..26b473829d69c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowDistinctFromOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowDistinctFromOperator.java @@ -18,8 +18,9 @@ import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlOperator; -import com.facebook.presto.spi.InvocationConvention; +import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ScalarImplementationChoice; import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.InvocationConvention; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; @@ -31,8 +32,9 @@ import static com.facebook.presto.metadata.Signature.comparableWithVariadicBound; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.BLOCK_AND_POSITION; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_NULL_FLAG; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.NULL_FLAG; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.NULL_FLAG; import static com.facebook.presto.spi.function.OperatorType.IS_DISTINCT_FROM; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; @@ -44,7 +46,8 @@ public class RowDistinctFromOperator extends SqlOperator { public static final RowDistinctFromOperator ROW_DISTINCT_FROM = new RowDistinctFromOperator(); - private static final MethodHandle METHOD_HANDLE = methodHandle(RowDistinctFromOperator.class, "isDistinctFrom", Type.class, List.class, Block.class, boolean.class, Block.class, boolean.class); + private static final MethodHandle METHOD_HANDLE_NULL_FLAG = methodHandle(RowDistinctFromOperator.class, "isDistinctFrom", Type.class, List.class, Block.class, boolean.class, Block.class, boolean.class); + private static final MethodHandle METHOD_HANDLE_BLOCK_POSITION = methodHandle(RowDistinctFromOperator.class, "isDistinctFrom", Type.class, List.class, Block.class, int.class, Block.class, int.class); private RowDistinctFromOperator() { @@ -71,11 +74,17 @@ public ScalarFunctionImplementation specialize(BoundVariables boundVariables, in argumentMethods.add(functionInvoker.methodHandle()); } return new ScalarFunctionImplementation( - false, ImmutableList.of( - valueTypeArgumentProperty(USE_NULL_FLAG), - valueTypeArgumentProperty(USE_NULL_FLAG)), - METHOD_HANDLE.bindTo(type).bindTo(argumentMethods.build()), + new ScalarImplementationChoice( + false, + ImmutableList.of(valueTypeArgumentProperty(USE_NULL_FLAG), valueTypeArgumentProperty(USE_NULL_FLAG)), + METHOD_HANDLE_NULL_FLAG.bindTo(type).bindTo(argumentMethods.build()), + Optional.empty()), + new ScalarImplementationChoice( + false, + ImmutableList.of(valueTypeArgumentProperty(BLOCK_AND_POSITION), valueTypeArgumentProperty(BLOCK_AND_POSITION)), + METHOD_HANDLE_BLOCK_POSITION.bindTo(type).bindTo(argumentMethods.build()), + Optional.empty())), isDeterministic()); } @@ -115,4 +124,15 @@ public static boolean isDistinctFrom(Type rowType, List argumentMe } return false; } + + public static boolean isDistinctFrom(Type rowType, List argumentMethods, Block leftRow, int leftPosition, Block rightRow, int rightPosition) + { + return isDistinctFrom( + rowType, + argumentMethods, + (Block) rowType.getObject(leftRow, leftPosition), + leftRow.isNull(leftPosition), + (Block) rowType.getObject(rightRow, rightPosition), + rightRow.isNull(rightPosition)); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowEqualOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowEqualOperator.java index e2ca54f0cc7df..f54a16289bee6 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowEqualOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowEqualOperator.java @@ -15,28 +15,33 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlOperator; import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.google.common.collect.ImmutableList; import java.lang.invoke.MethodHandle; +import java.util.List; import static com.facebook.presto.metadata.Signature.comparableWithVariadicBound; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.RETURN_NULL_ON_NULL; import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.spi.type.TypeUtils.readNativeValue; +import static com.facebook.presto.util.Failures.internalError; import static com.facebook.presto.util.Reflection.methodHandle; +import static com.google.common.collect.ImmutableList.toImmutableList; public class RowEqualOperator extends SqlOperator { public static final RowEqualOperator ROW_EQUAL = new RowEqualOperator(); - private static final MethodHandle METHOD_HANDLE = methodHandle(RowEqualOperator.class, "equals", Type.class, Block.class, Block.class); + private static final MethodHandle METHOD_HANDLE = methodHandle(RowEqualOperator.class, "equals", RowType.class, List.class, Block.class, Block.class); private RowEqualOperator() { @@ -50,23 +55,61 @@ private RowEqualOperator() @Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { - Type type = boundVariables.getTypeVariable("T"); + RowType type = (RowType) boundVariables.getTypeVariable("T"); return new ScalarFunctionImplementation( - false, + true, ImmutableList.of( valueTypeArgumentProperty(RETURN_NULL_ON_NULL), valueTypeArgumentProperty(RETURN_NULL_ON_NULL)), - METHOD_HANDLE.bindTo(type), + METHOD_HANDLE + .bindTo(type) + .bindTo(resolveFieldEqualOperators(type, functionRegistry)), isDeterministic()); } - public static boolean equals(Type rowType, Block leftRow, Block rightRow) + public static List resolveFieldEqualOperators(RowType rowType, FunctionRegistry functionRegistry) { - // TODO: Fix this. It feels very inefficient and unnecessary to wrap and unwrap with Block - BlockBuilder leftBlockBuilder = rowType.createBlockBuilder(null, 1); - BlockBuilder rightBlockBuilder = rowType.createBlockBuilder(null, 1); - rowType.writeObject(leftBlockBuilder, leftRow); - rowType.writeObject(rightBlockBuilder, rightRow); - return rowType.equalTo(leftBlockBuilder.build(), 0, rightBlockBuilder.build(), 0); + return rowType.getTypeParameters().stream() + .map(type -> resolveEqualOperator(type, functionRegistry)) + .collect(toImmutableList()); + } + + private static MethodHandle resolveEqualOperator(Type type, FunctionRegistry functionRegistry) + { + Signature operator = functionRegistry.resolveOperator(EQUAL, ImmutableList.of(type, type)); + ScalarFunctionImplementation implementation = functionRegistry.getScalarFunctionImplementation(operator); + return implementation.getMethodHandle(); + } + + public static Boolean equals(RowType rowType, List fieldEqualOperators, Block leftRow, Block rightRow) + { + boolean indeterminate = false; + for (int fieldIndex = 0; fieldIndex < leftRow.getPositionCount(); fieldIndex++) { + if (leftRow.isNull(fieldIndex) || rightRow.isNull(fieldIndex)) { + indeterminate = true; + continue; + } + Type fieldType = rowType.getTypeParameters().get(fieldIndex); + Object leftField = readNativeValue(fieldType, leftRow, fieldIndex); + Object rightField = readNativeValue(fieldType, rightRow, fieldIndex); + try { + MethodHandle equalOperator = fieldEqualOperators.get(fieldIndex); + Boolean result = (Boolean) equalOperator.invoke(leftField, rightField); + if (result == null) { + indeterminate = true; + } + else if (!result) { + return false; + } + } + catch (Throwable t) { + throw internalError(t); + } + } + + if (indeterminate) { + return null; + } + return true; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowNotEqualOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowNotEqualOperator.java index 5975234d0963c..482d149b7bc6d 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowNotEqualOperator.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowNotEqualOperator.java @@ -18,12 +18,13 @@ import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlOperator; import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.StandardTypes; -import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.google.common.collect.ImmutableList; import java.lang.invoke.MethodHandle; +import java.util.List; import static com.facebook.presto.metadata.Signature.comparableWithVariadicBound; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; @@ -36,7 +37,7 @@ public class RowNotEqualOperator extends SqlOperator { public static final RowNotEqualOperator ROW_NOT_EQUAL = new RowNotEqualOperator(); - private static final MethodHandle METHOD_HANDLE = methodHandle(RowNotEqualOperator.class, "notEqual", Type.class, Block.class, Block.class); + private static final MethodHandle METHOD_HANDLE = methodHandle(RowNotEqualOperator.class, "notEqual", RowType.class, List.class, Block.class, Block.class); private RowNotEqualOperator() { @@ -50,19 +51,25 @@ private RowNotEqualOperator() @Override public ScalarFunctionImplementation specialize(BoundVariables boundVariables, int arity, TypeManager typeManager, FunctionRegistry functionRegistry) { - Type type = boundVariables.getTypeVariable("T"); + RowType type = (RowType) boundVariables.getTypeVariable("T"); return new ScalarFunctionImplementation( - false, + true, ImmutableList.of( valueTypeArgumentProperty(RETURN_NULL_ON_NULL), valueTypeArgumentProperty(RETURN_NULL_ON_NULL)), - METHOD_HANDLE.bindTo(type), + METHOD_HANDLE + .bindTo(type) + .bindTo(RowEqualOperator.resolveFieldEqualOperators(type, functionRegistry)), isDeterministic()); } @UsedByGeneratedCode - public static boolean notEqual(Type rowType, Block leftRow, Block rightRow) + public static Boolean notEqual(RowType rowType, List fieldEqualOperators, Block leftRow, Block rightRow) { - return !RowEqualOperator.equals(rowType, leftRow, rightRow); + Boolean result = RowEqualOperator.equals(rowType, fieldEqualOperators, leftRow, rightRow); + if (result == null) { + return null; + } + return !result; } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowToJsonCast.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowToJsonCast.java index 796cc6767ef29..58161d4ac1c30 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowToJsonCast.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/RowToJsonCast.java @@ -17,7 +17,6 @@ import com.facebook.presto.metadata.BoundVariables; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.SqlOperator; -import com.facebook.presto.server.SliceSerializer; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.OperatorType; @@ -26,12 +25,7 @@ import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.util.JsonUtil.JsonGeneratorWriter; import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.google.common.base.Supplier; -import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; -import io.airlift.json.ObjectMapperProvider; import io.airlift.slice.DynamicSliceOutput; import io.airlift.slice.Slice; import io.airlift.slice.SliceOutput; @@ -59,7 +53,6 @@ public class RowToJsonCast extends SqlOperator { public static final RowToJsonCast ROW_TO_JSON = new RowToJsonCast(); - private static final Supplier OBJECT_MAPPER = Suppliers.memoize(() -> new ObjectMapperProvider().get().registerModule(new SimpleModule().addSerializer(Slice.class, new SliceSerializer()))); private static final MethodHandle METHOD_HANDLE = methodHandle(RowToJsonCast.class, "toJson", List.class, ConnectorSession.class, Block.class); private RowToJsonCast() diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ScalarFunctionImplementation.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ScalarFunctionImplementation.java index 164618bc746cd..876b31b7205ef 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/ScalarFunctionImplementation.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/ScalarFunctionImplementation.java @@ -54,7 +54,7 @@ public ScalarFunctionImplementation( Optional instanceFactory, boolean deterministic) { - this (ImmutableList.of(new ScalarImplementationChoice(nullable, argumentProperties, methodHandle, instanceFactory)), deterministic); + this(ImmutableList.of(new ScalarImplementationChoice(nullable, argumentProperties, methodHandle, instanceFactory)), deterministic); } /** @@ -64,7 +64,7 @@ public ScalarFunctionImplementation( * The first choice is the default choice, which is the one used for legacy access methods. * The default choice must be usable under any context. (e.g. it must not use BLOCK_POSITION convention.) * - * @param choices the list of choices, ordered from generic to specific + * @param choices the list of choices, ordered from generic to specific */ public ScalarFunctionImplementation(List choices, boolean deterministic) { diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/SessionFunctions.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/SessionFunctions.java index 11b6b0b97eca5..0e4151577901b 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/SessionFunctions.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/SessionFunctions.java @@ -13,15 +13,14 @@ */ package com.facebook.presto.operator.scalar; +import com.facebook.presto.FullConnectorSession; import com.facebook.presto.spi.ConnectorSession; -import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.function.Description; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import io.airlift.slice.Slice; -import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static io.airlift.slice.Slices.utf8Slice; public final class SessionFunctions @@ -41,9 +40,7 @@ public static Slice currentUser(ConnectorSession session) @SqlType(StandardTypes.VARCHAR) public static Slice currentPath(ConnectorSession session) { - if (session.getPath() == null) { - throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Session path is null"); - } - return utf8Slice(session.getPath()); + // this function is a language construct and has special access to internals + return utf8Slice(((FullConnectorSession) session).getSession().getPath().toString()); } } diff --git a/presto-main/src/main/java/com/facebook/presto/operator/scalar/TryFunction.java b/presto-main/src/main/java/com/facebook/presto/operator/scalar/TryFunction.java index 87df5165ce67a..aefe0629b687c 100644 --- a/presto-main/src/main/java/com/facebook/presto/operator/scalar/TryFunction.java +++ b/presto-main/src/main/java/com/facebook/presto/operator/scalar/TryFunction.java @@ -24,6 +24,8 @@ import com.facebook.presto.sql.gen.lambda.LambdaFunctionInterface; import io.airlift.slice.Slice; +import java.util.function.Supplier; + import static com.facebook.presto.spi.StandardErrorCode.DIVISION_BY_ZERO; import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; @@ -145,6 +147,17 @@ public interface TryBlockLambda Block apply(); } + public static T evaluate(Supplier supplier, T defaultValue) + { + try { + return supplier.get(); + } + catch (PrestoException e) { + propagateIfUnhandled(e); + return defaultValue; + } + } + private static void propagateIfUnhandled(PrestoException e) throws PrestoException { diff --git a/presto-main/src/main/java/com/facebook/presto/security/AccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/AccessControl.java index a80b7e2168556..1d6af6c372cec 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/AccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/AccessControl.java @@ -21,6 +21,7 @@ import com.facebook.presto.transaction.TransactionId; import java.security.Principal; +import java.util.Optional; import java.util.Set; public interface AccessControl @@ -30,7 +31,7 @@ public interface AccessControl * * @throws com.facebook.presto.spi.security.AccessDeniedException if not allowed */ - void checkCanSetUser(Principal principal, String userName); + void checkCanSetUser(Optional principal, String userName); /** * Filter the list of catalogs to those visible to the identity. diff --git a/presto-main/src/main/java/com/facebook/presto/security/AccessControlManager.java b/presto-main/src/main/java/com/facebook/presto/security/AccessControlManager.java index 013380d8cc681..ae1255e63840b 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/AccessControlManager.java +++ b/presto-main/src/main/java/com/facebook/presto/security/AccessControlManager.java @@ -40,6 +40,7 @@ import java.security.Principal; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -140,8 +141,9 @@ protected void setSystemAccessControl(String name, Map propertie } @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { + requireNonNull(principal, "principal is null"); requireNonNull(userName, "userName is null"); authenticationCheck(() -> systemAccessControl.get().checkCanSetUser(principal, userName)); @@ -518,7 +520,7 @@ public void checkCanSetCatalogSessionProperty(TransactionId transactionId, Ident CatalogAccessControlEntry entry = getConnectorAccessControl(transactionId, catalogName); if (entry != null) { - authorizationCheck(() -> entry.getAccessControl().checkCanSetCatalogSessionProperty(identity, propertyName)); + authorizationCheck(() -> entry.getAccessControl().checkCanSetCatalogSessionProperty(entry.getTransactionHandle(transactionId), identity, propertyName)); } } @@ -629,7 +631,7 @@ private static class InitializingSystemAccessControl implements SystemAccessControl { @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { throw new PrestoException(SERVER_STARTING_UP, "Presto server is still initializing"); } diff --git a/presto-main/src/main/java/com/facebook/presto/security/AllowAllAccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/AllowAllAccessControl.java index dc18f967633e1..4c229c67e77d4 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/AllowAllAccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/AllowAllAccessControl.java @@ -21,13 +21,14 @@ import com.facebook.presto.transaction.TransactionId; import java.security.Principal; +import java.util.Optional; import java.util.Set; public class AllowAllAccessControl implements AccessControl { @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { } diff --git a/presto-main/src/main/java/com/facebook/presto/security/AllowAllSystemAccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/AllowAllSystemAccessControl.java index c54c2e406fed1..60d0866c0d052 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/AllowAllSystemAccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/AllowAllSystemAccessControl.java @@ -23,6 +23,7 @@ import java.security.Principal; import java.util.Map; +import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -54,7 +55,7 @@ public SystemAccessControl create(Map config) } @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { } diff --git a/presto-main/src/main/java/com/facebook/presto/security/DenyAllAccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/DenyAllAccessControl.java index 39535ee9b309d..f80b8d5783276 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/DenyAllAccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/DenyAllAccessControl.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableSet; import java.security.Principal; +import java.util.Optional; import java.util.Set; import static com.facebook.presto.spi.security.AccessDeniedException.denyAddColumn; @@ -52,7 +53,7 @@ public class DenyAllAccessControl implements AccessControl { @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { denySetUser(principal, userName); } diff --git a/presto-main/src/main/java/com/facebook/presto/security/FileBasedSystemAccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/FileBasedSystemAccessControl.java index 653edaaaf91a5..488f3041cb149 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/FileBasedSystemAccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/FileBasedSystemAccessControl.java @@ -13,23 +13,20 @@ */ package com.facebook.presto.security; +import com.facebook.presto.plugin.base.security.ForwardingSystemAccessControl; import com.facebook.presto.spi.CatalogSchemaName; import com.facebook.presto.spi.CatalogSchemaTableName; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.security.Identity; import com.facebook.presto.spi.security.Privilege; import com.facebook.presto.spi.security.SystemAccessControl; import com.facebook.presto.spi.security.SystemAccessControlFactory; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import io.airlift.json.ObjectMapperProvider; +import io.airlift.log.Logger; +import io.airlift.units.Duration; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; import java.nio.file.Paths; import java.security.Principal; import java.util.List; @@ -38,15 +35,23 @@ import java.util.Set; import java.util.regex.Pattern; +import static com.facebook.presto.plugin.base.JsonUtils.parseJson; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_CONFIG_FILE; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_REFRESH_PERIOD; +import static com.facebook.presto.spi.StandardErrorCode.CONFIGURATION_INVALID; import static com.facebook.presto.spi.security.AccessDeniedException.denyCatalogAccess; import static com.facebook.presto.spi.security.AccessDeniedException.denySetUser; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Suppliers.memoizeWithExpiration; import static java.lang.String.format; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; public class FileBasedSystemAccessControl implements SystemAccessControl { + private static final Logger log = Logger.get(FileBasedSystemAccessControl.class); + public static final String NAME = "file"; private final List catalogRules; @@ -61,8 +66,6 @@ private FileBasedSystemAccessControl(List catalogRules public static class Factory implements SystemAccessControlFactory { - static final String CONFIG_FILE_NAME = "security.config-file"; - @Override public String getName() { @@ -74,63 +77,71 @@ public SystemAccessControl create(Map config) { requireNonNull(config, "config is null"); - String configFileName = config.get(CONFIG_FILE_NAME); - checkState( - configFileName != null, - "Security configuration must contain the '%s' property", CONFIG_FILE_NAME); + String configFileName = config.get(SECURITY_CONFIG_FILE); + checkState(configFileName != null, "Security configuration must contain the '%s' property", SECURITY_CONFIG_FILE); - try { - Path path = Paths.get(configFileName); - if (!path.isAbsolute()) { - path = path.toAbsolutePath(); + if (config.containsKey(SECURITY_REFRESH_PERIOD)) { + Duration refreshPeriod; + try { + refreshPeriod = Duration.valueOf(config.get(SECURITY_REFRESH_PERIOD)); + } + catch (IllegalArgumentException e) { + throw invalidRefreshPeriodException(config, configFileName); + } + if (refreshPeriod.toMillis() == 0) { + throw invalidRefreshPeriodException(config, configFileName); } - path.toFile().canRead(); + return ForwardingSystemAccessControl.of(memoizeWithExpiration( + () -> { + log.info("Refreshing system access control from %s", configFileName); + return create(configFileName); + }, + refreshPeriod.toMillis(), + MILLISECONDS)); + } + return create(configFileName); + } - FileBasedSystemAccessControlRules rules = parse(Files.readAllBytes(path)); + private PrestoException invalidRefreshPeriodException(Map config, String configFileName) + { + return new PrestoException( + CONFIGURATION_INVALID, + format("Invalid duration value '%s' for property '%s' in '%s'", config.get(SECURITY_REFRESH_PERIOD), SECURITY_REFRESH_PERIOD, configFileName)); + } - ImmutableList.Builder catalogRulesBuilder = ImmutableList.builder(); - catalogRulesBuilder.addAll(rules.getCatalogRules()); + private SystemAccessControl create(String configFileName) + { + FileBasedSystemAccessControlRules rules = parseJson(Paths.get(configFileName), FileBasedSystemAccessControlRules.class); - // Hack to allow Presto Admin to access the "system" catalog for retrieving server status. - // todo Change userRegex from ".*" to one particular user that Presto Admin will be restricted to run as - catalogRulesBuilder.add(new CatalogAccessControlRule( - true, - Optional.of(Pattern.compile(".*")), - Optional.of(Pattern.compile("system")))); + ImmutableList.Builder catalogRulesBuilder = ImmutableList.builder(); + catalogRulesBuilder.addAll(rules.getCatalogRules()); - return new FileBasedSystemAccessControl(catalogRulesBuilder.build(), rules.getPrincipalUserMatchRules()); - } - catch (SecurityException | IOException | InvalidPathException e) { - throw new RuntimeException(e); - } - } - } + // Hack to allow Presto Admin to access the "system" catalog for retrieving server status. + // todo Change userRegex from ".*" to one particular user that Presto Admin will be restricted to run as + catalogRulesBuilder.add(new CatalogAccessControlRule( + true, + Optional.of(Pattern.compile(".*")), + Optional.of(Pattern.compile("system")))); - private static FileBasedSystemAccessControlRules parse(byte[] json) - { - ObjectMapper mapper = new ObjectMapperProvider().get() - .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - Class javaType = FileBasedSystemAccessControlRules.class; - try { - return mapper.readValue(json, javaType); - } - catch (IOException e) { - throw new IllegalArgumentException(format("Invalid JSON string for %s", javaType), e); + return new FileBasedSystemAccessControl(catalogRulesBuilder.build(), rules.getPrincipalUserMatchRules()); } } @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { + requireNonNull(principal, "principal is null"); + requireNonNull(userName, "userName is null"); + if (!principalUserMatchRules.isPresent()) { return; } - if (principal == null) { + if (!principal.isPresent()) { denySetUser(principal, userName); } - String principalName = principal.getName(); + String principalName = principal.get().getName(); for (PrincipalUserMatchRule rule : principalUserMatchRules.get()) { Optional allowed = rule.match(principalName, userName); diff --git a/presto-main/src/main/java/com/facebook/presto/security/ReadOnlySystemAccessControl.java b/presto-main/src/main/java/com/facebook/presto/security/ReadOnlySystemAccessControl.java index 6838498fb618c..901d833242bce 100644 --- a/presto-main/src/main/java/com/facebook/presto/security/ReadOnlySystemAccessControl.java +++ b/presto-main/src/main/java/com/facebook/presto/security/ReadOnlySystemAccessControl.java @@ -22,6 +22,7 @@ import java.security.Principal; import java.util.Map; +import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -53,7 +54,7 @@ public SystemAccessControl create(Map config) } @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { } diff --git a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java index b392785a5a4e2..0e2c9bc215bd3 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java @@ -20,6 +20,7 @@ import com.facebook.presto.spi.ErrorType; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.memory.MemoryPoolId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -27,6 +28,7 @@ import javax.annotation.concurrent.Immutable; import java.net.URI; +import java.util.Optional; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -40,6 +42,7 @@ public class BasicQueryInfo { private final QueryId queryId; private final SessionRepresentation session; + private final Optional resourceGroupId; private final QueryState state; private final MemoryPoolId memoryPool; private final boolean scheduled; @@ -53,6 +56,7 @@ public class BasicQueryInfo public BasicQueryInfo( @JsonProperty("queryId") QueryId queryId, @JsonProperty("session") SessionRepresentation session, + @JsonProperty("resourceGroupId") Optional resourceGroupId, @JsonProperty("state") QueryState state, @JsonProperty("memoryPool") MemoryPoolId memoryPool, @JsonProperty("scheduled") boolean scheduled, @@ -64,6 +68,7 @@ public BasicQueryInfo( { this.queryId = requireNonNull(queryId, "queryId is null"); this.session = requireNonNull(session, "session is null"); + this.resourceGroupId = requireNonNull(resourceGroupId, "resourceGroupId is null"); this.state = requireNonNull(state, "state is null"); this.memoryPool = memoryPool; this.errorType = errorType; @@ -78,6 +83,7 @@ public BasicQueryInfo(QueryInfo queryInfo) { this(queryInfo.getQueryId(), queryInfo.getSession(), + queryInfo.getResourceGroupId(), queryInfo.getState(), queryInfo.getMemoryPool(), queryInfo.isScheduled(), @@ -100,6 +106,12 @@ public SessionRepresentation getSession() return session; } + @JsonProperty + public Optional getResourceGroupId() + { + return resourceGroupId; + } + @JsonProperty public QueryState getState() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java index 47895b8b54416..a395f7f8bf093 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java @@ -40,6 +40,7 @@ public class BasicQueryStats private final DateTime createTime; private final DateTime endTime; + private final Duration queuedTime; private final Duration elapsedTime; private final Duration executionTime; @@ -48,10 +49,15 @@ public class BasicQueryStats private final int runningDrivers; private final int completedDrivers; + private final DataSize rawInputDataSize; + private final long rawInputPositions; + private final double cumulativeUserMemory; private final DataSize userMemoryReservation; + private final DataSize totalMemoryReservation; private final DataSize peakUserMemoryReservation; private final Duration totalCpuTime; + private final Duration totalScheduledTime; private final boolean fullyBlocked; private final Set blockedReasons; @@ -62,16 +68,21 @@ public class BasicQueryStats public BasicQueryStats( @JsonProperty("createTime") DateTime createTime, @JsonProperty("endTime") DateTime endTime, + @JsonProperty("queuedTime") Duration queuedTime, @JsonProperty("elapsedTime") Duration elapsedTime, @JsonProperty("executionTime") Duration executionTime, @JsonProperty("totalDrivers") int totalDrivers, @JsonProperty("queuedDrivers") int queuedDrivers, @JsonProperty("runningDrivers") int runningDrivers, @JsonProperty("completedDrivers") int completedDrivers, + @JsonProperty("rawInputDataSize") DataSize rawInputDataSize, + @JsonProperty("rawInputPositions") long rawInputPositions, @JsonProperty("cumulativeUserMemory") double cumulativeUserMemory, @JsonProperty("userMemoryReservation") DataSize userMemoryReservation, + @JsonProperty("totalMemoryReservation") DataSize totalMemoryReservation, @JsonProperty("peakUserMemoryReservation") DataSize peakUserMemoryReservation, @JsonProperty("totalCpuTime") Duration totalCpuTime, + @JsonProperty("totalScheduledTime") Duration totalScheduledTime, @JsonProperty("fullyBlocked") boolean fullyBlocked, @JsonProperty("blockedReasons") Set blockedReasons, @JsonProperty("progressPercentage") OptionalDouble progressPercentage) @@ -79,8 +90,9 @@ public BasicQueryStats( this.createTime = createTime; this.endTime = endTime; - this.elapsedTime = elapsedTime; - this.executionTime = executionTime; + this.queuedTime = requireNonNull(queuedTime, "queuedTime is null"); + this.elapsedTime = requireNonNull(elapsedTime, "elapsedTime is null"); + this.executionTime = requireNonNull(executionTime, "executionTime is null"); checkArgument(totalDrivers >= 0, "totalDrivers is negative"); this.totalDrivers = totalDrivers; @@ -91,10 +103,15 @@ public BasicQueryStats( checkArgument(completedDrivers >= 0, "completedDrivers is negative"); this.completedDrivers = completedDrivers; + this.rawInputDataSize = requireNonNull(rawInputDataSize); + this.rawInputPositions = rawInputPositions; + this.cumulativeUserMemory = cumulativeUserMemory; this.userMemoryReservation = userMemoryReservation; + this.totalMemoryReservation = totalMemoryReservation; this.peakUserMemoryReservation = peakUserMemoryReservation; this.totalCpuTime = totalCpuTime; + this.totalScheduledTime = totalScheduledTime; this.fullyBlocked = fullyBlocked; this.blockedReasons = ImmutableSet.copyOf(requireNonNull(blockedReasons, "blockedReasons is null")); @@ -106,16 +123,21 @@ public BasicQueryStats(QueryStats queryStats) { this(queryStats.getCreateTime(), queryStats.getEndTime(), + queryStats.getQueuedTime(), queryStats.getElapsedTime(), queryStats.getExecutionTime(), queryStats.getTotalDrivers(), queryStats.getQueuedDrivers(), queryStats.getRunningDrivers(), queryStats.getCompletedDrivers(), + queryStats.getRawInputDataSize(), + queryStats.getRawInputPositions(), queryStats.getCumulativeUserMemory(), queryStats.getUserMemoryReservation(), + queryStats.getTotalMemoryReservation(), queryStats.getPeakUserMemoryReservation(), queryStats.getTotalCpuTime(), + queryStats.getTotalScheduledTime(), queryStats.isFullyBlocked(), queryStats.getBlockedReasons(), queryStats.getProgressPercentage()); @@ -133,6 +155,12 @@ public DateTime getEndTime() return endTime; } + @JsonProperty + public Duration getQueuedTime() + { + return queuedTime; + } + @JsonProperty public Duration getElapsedTime() { @@ -169,6 +197,18 @@ public int getCompletedDrivers() return completedDrivers; } + @JsonProperty + public DataSize getRawInputDataSize() + { + return rawInputDataSize; + } + + @JsonProperty + public long getRawInputPositions() + { + return rawInputPositions; + } + @JsonProperty public double getCumulativeUserMemory() { @@ -181,6 +221,12 @@ public DataSize getUserMemoryReservation() return userMemoryReservation; } + @JsonProperty + public DataSize getTotalMemoryReservation() + { + return totalMemoryReservation; + } + @JsonProperty public DataSize getPeakUserMemoryReservation() { @@ -193,6 +239,12 @@ public Duration getTotalCpuTime() return totalCpuTime; } + @JsonProperty + public Duration getTotalScheduledTime() + { + return totalScheduledTime; + } + @JsonProperty public boolean isFullyBlocked() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/ClusterStatsResource.java b/presto-main/src/main/java/com/facebook/presto/server/ClusterStatsResource.java index fe0b84f540d9d..1a5da4d8a61eb 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ClusterStatsResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ClusterStatsResource.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.server; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryState; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; @@ -70,7 +69,7 @@ public ClusterStats getClusterStats() long totalInputBytes = queryManager.getStats().getConsumedInputBytes().getTotalCount(); long totalCpuTimeSecs = queryManager.getStats().getConsumedCpuTimeSecs().getTotalCount(); - for (QueryInfo query : queryManager.getAllQueryInfo()) { + for (BasicQueryInfo query : queryManager.getQueries()) { if (query.getState() == QueryState.QUEUED) { queuedQueries++; } diff --git a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java index b19134d37f65d..b90b2a2b91ede 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java @@ -14,8 +14,18 @@ package com.facebook.presto.server; import com.facebook.presto.client.QueryResults; +import com.facebook.presto.cost.CostCalculator; +import com.facebook.presto.cost.CostCalculator.EstimatedExchanges; +import com.facebook.presto.cost.CostCalculatorUsingExchanges; +import com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges; +import com.facebook.presto.cost.CostComparator; +import com.facebook.presto.cost.StatsCalculatorModule; +import com.facebook.presto.cost.TaskCountEstimator; +import com.facebook.presto.event.QueryMonitor; +import com.facebook.presto.event.QueryMonitorConfig; import com.facebook.presto.execution.AddColumnTask; import com.facebook.presto.execution.CallTask; +import com.facebook.presto.execution.ClusterSizeMonitor; import com.facebook.presto.execution.CommitTask; import com.facebook.presto.execution.CreateSchemaTask; import com.facebook.presto.execution.CreateTableTask; @@ -36,6 +46,7 @@ import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryPerformanceFetcher; +import com.facebook.presto.execution.QueryPreparer; import com.facebook.presto.execution.RemoteTaskFactory; import com.facebook.presto.execution.RenameColumnTask; import com.facebook.presto.execution.RenameSchemaTask; @@ -131,6 +142,7 @@ import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static io.airlift.concurrent.Threads.threadsNamed; import static io.airlift.configuration.ConditionalModule.installModuleIf; +import static io.airlift.configuration.ConfigBinder.configBinder; import static io.airlift.discovery.client.DiscoveryBinder.discoveryBinder; import static io.airlift.http.client.HttpClientBinder.httpClientBinder; import static io.airlift.http.server.HttpServerBinder.httpServerBinder; @@ -175,6 +187,10 @@ protected void setup(Binder binder) jaxrsBinder(binder).bind(WorkerResource.class); httpClientBinder(binder).bindHttpClient("workerInfo", ForWorkerInfo.class); + // query monitor + configBinder(binder).bindConfig(QueryMonitorConfig.class); + binder.bind(QueryMonitor.class).in(Scopes.SINGLETON); + // query manager jaxrsBinder(binder).bind(QueryResource.class); jaxrsBinder(binder).bind(StageResource.class); @@ -182,6 +198,7 @@ protected void setup(Binder binder) jaxrsBinder(binder).bind(ResourceGroupStateInfoResource.class); binder.bind(QueryIdGenerator.class).in(Scopes.SINGLETON); binder.bind(QueryManager.class).to(SqlQueryManager.class).in(Scopes.SINGLETON); + binder.bind(QueryPreparer.class).in(Scopes.SINGLETON); binder.bind(SessionSupplier.class).to(QuerySessionSupplier.class).in(Scopes.SINGLETON); binder.bind(InternalResourceGroupManager.class).in(Scopes.SINGLETON); newExporter(binder).export(InternalResourceGroupManager.class).withGeneratedName(); @@ -203,6 +220,18 @@ protected void setup(Binder binder) bindLowMemoryKiller(LowMemoryKillerPolicy.TOTAL_RESERVATION_ON_BLOCKED_NODES, TotalReservationOnBlockedNodesLowMemoryKiller.class); newExporter(binder).export(ClusterMemoryManager.class).withGeneratedName(); + // node monitor + binder.bind(ClusterSizeMonitor.class).in(Scopes.SINGLETON); + + // statistics calculator + binder.install(new StatsCalculatorModule()); + + // cost calculator + binder.bind(TaskCountEstimator.class).in(Scopes.SINGLETON); + binder.bind(CostCalculator.class).to(CostCalculatorUsingExchanges.class).in(Scopes.SINGLETON); + binder.bind(CostCalculator.class).annotatedWith(EstimatedExchanges.class).to(CostCalculatorWithEstimatedExchanges.class).in(Scopes.SINGLETON); + binder.bind(CostComparator.class).in(Scopes.SINGLETON); + // cluster statistics jaxrsBinder(binder).bind(ClusterStatsResource.class); @@ -284,11 +313,18 @@ protected void setup(Binder binder) binder.bind(ExecutorCleanup.class).in(Scopes.SINGLETON); } + @Provides + @Singleton + public static ResourceGroupManager getResourceGroupManager(@SuppressWarnings("rawtypes") ResourceGroupManager manager) + { + return manager; + } + @Provides @Singleton public static QueryPerformanceFetcher createQueryPerformanceFetcher(QueryManager queryManager) { - return queryManager::getQueryInfo; + return queryManager::getFullQueryInfo; } @Provides diff --git a/presto-main/src/main/java/com/facebook/presto/server/NoOpSessionSupplier.java b/presto-main/src/main/java/com/facebook/presto/server/NoOpSessionSupplier.java index 88b0f88b8084d..7648552f64a2c 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/NoOpSessionSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/server/NoOpSessionSupplier.java @@ -15,10 +15,6 @@ import com.facebook.presto.Session; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; - -import java.util.Optional; /** * Used on workers. @@ -27,20 +23,8 @@ public class NoOpSessionSupplier implements SessionSupplier { @Override - public Session createSession(QueryId queryId, SessionContext context, Optional queryType, ResourceGroupId resourceGroupId) + public Session createSession(QueryId queryId, SessionContext context) { throw new UnsupportedOperationException(); } - - @Override - public void addConfigurationManager(SessionPropertyConfigurationManagerFactory sessionConfigFactory) - { - // no-op - } - - @Override - public void loadConfigurationManager() - { - // no-op - } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java index f2a7948bf40d9..7d3335142d393 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PluginManager.java @@ -75,12 +75,12 @@ public class PluginManager private final ConnectorManager connectorManager; private final Metadata metadata; - private final ResourceGroupManager resourceGroupManager; + private final ResourceGroupManager resourceGroupManager; private final AccessControlManager accessControlManager; private final PasswordAuthenticatorManager passwordAuthenticatorManager; private final EventListenerManager eventListenerManager; private final BlockEncodingManager blockEncodingManager; - private final SessionSupplier sessionSupplier; + private final SessionPropertyDefaults sessionPropertyDefaults; private final TypeRegistry typeRegistry; private final ArtifactResolver resolver; private final File installedPluginsDir; @@ -94,12 +94,12 @@ public PluginManager( PluginManagerConfig config, ConnectorManager connectorManager, Metadata metadata, - ResourceGroupManager resourceGroupManager, + ResourceGroupManager resourceGroupManager, AccessControlManager accessControlManager, PasswordAuthenticatorManager passwordAuthenticatorManager, EventListenerManager eventListenerManager, BlockEncodingManager blockEncodingManager, - SessionSupplier sessionSupplier, + SessionPropertyDefaults sessionPropertyDefaults, TypeRegistry typeRegistry) { requireNonNull(nodeInfo, "nodeInfo is null"); @@ -121,7 +121,7 @@ public PluginManager( this.passwordAuthenticatorManager = requireNonNull(passwordAuthenticatorManager, "passwordAuthenticatorManager is null"); this.eventListenerManager = requireNonNull(eventListenerManager, "eventListenerManager is null"); this.blockEncodingManager = requireNonNull(blockEncodingManager, "blockEncodingManager is null"); - this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); + this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); this.typeRegistry = requireNonNull(typeRegistry, "typeRegistry is null"); } @@ -202,7 +202,7 @@ public void installPlugin(Plugin plugin) for (SessionPropertyConfigurationManagerFactory sessionConfigFactory : plugin.getSessionPropertyConfigurationManagerFactories()) { log.info("Registering session property configuration manager %s", sessionConfigFactory.getName()); - sessionSupplier.addConfigurationManager(sessionConfigFactory); + sessionPropertyDefaults.addConfigurationManagerFactory(sessionConfigFactory); } for (ResourceGroupConfigurationManagerFactory configurationManagerFactory : plugin.getResourceGroupConfigurationManagerFactories()) { diff --git a/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java b/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java index bd5916c4b323f..4664d7e0488eb 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PrestoServer.java @@ -17,6 +17,7 @@ import com.facebook.presto.eventlistener.EventListenerModule; import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.execution.warnings.WarningCollectorModule; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.StaticCatalogStore; @@ -105,7 +106,8 @@ public void run() new AccessControlModule(), new EventListenerModule(), new ServerMainModule(sqlParserOptions), - new GracefulShutdownModule()); + new GracefulShutdownModule(), + new WarningCollectorModule()); modules.addAll(getAdditionalModules()); @@ -125,7 +127,7 @@ public void run() injector.getInstance(ServerConfig.class), injector.getInstance(NodeSchedulerConfig.class)); - injector.getInstance(SessionSupplier.class).loadConfigurationManager(); + injector.getInstance(SessionPropertyDefaults.class).loadConfigurationManager(); injector.getInstance(ResourceGroupManager.class).loadConfigurationManager(); injector.getInstance(AccessControlManager.class).loadSystemAccessControl(); injector.getInstance(PasswordAuthenticatorManager.class).loadPasswordAuthenticator(); diff --git a/presto-main/src/main/java/com/facebook/presto/server/PrestoSystemRequirements.java b/presto-main/src/main/java/com/facebook/presto/server/PrestoSystemRequirements.java index 001c420453f0c..06dbdd4daf06f 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/PrestoSystemRequirements.java +++ b/presto-main/src/main/java/com/facebook/presto/server/PrestoSystemRequirements.java @@ -40,7 +40,6 @@ private PrestoSystemRequirements() {} public static void verifyJvmRequirements() { - verifyJvmVendor(); verifyJavaVersion(); verify64BitJvm(); verifyOsArchitecture(); @@ -50,14 +49,6 @@ public static void verifyJvmRequirements() verifySlice(); } - private static void verifyJvmVendor() - { - String vendor = StandardSystemProperty.JAVA_VENDOR.value(); - if (!"Oracle Corporation".equals(vendor)) { - failRequirement("Presto requires an Oracle or OpenJDK JVM (found %s)", vendor); - } - } - private static void verify64BitJvm() { String dataModel = System.getProperty("sun.arch.data.model"); @@ -104,7 +95,7 @@ private static void verifyJavaVersion() } JavaVersion version = JavaVersion.parse(javaVersion); - if (version.getMajor() == 8 && version.getUpdate().isPresent() && version.getUpdate().getAsInt() >= 92) { + if (version.getMajor() == 8 && version.getUpdate().isPresent() && version.getUpdate().getAsInt() >= 151) { return; } @@ -112,7 +103,7 @@ private static void verifyJavaVersion() return; } - failRequirement("Presto requires Java 8u92+ (found %s)", javaVersion); + failRequirement("Presto requires Java 8u151+ (found %s)", javaVersion); } private static void verifyUsingG1Gc() @@ -178,7 +169,7 @@ private static void verifySlice() public static void verifySystemTimeIsReasonable() { int currentYear = DateTime.now().year().get(); - if (currentYear < 2015) { + if (currentYear < 2019) { failRequirement("Presto requires the system time to be current (found year %s)", currentYear); } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryProgressStats.java b/presto-main/src/main/java/com/facebook/presto/server/QueryProgressStats.java index 7bf10f9cbf679..3cefbd303c922 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryProgressStats.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryProgressStats.java @@ -14,24 +14,19 @@ package com.facebook.presto.server; -import com.facebook.presto.execution.QueryStats; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.joda.time.DateTime; -import java.util.Optional; import java.util.OptionalDouble; import static java.util.Objects.requireNonNull; public class QueryProgressStats { - private final Optional executionStartTime; private final long elapsedTimeMillis; private final long queuedTimeMillis; private final long cpuTimeMillis; private final long scheduledTimeMillis; - private final long blockedTimeMillis; private final long currentMemoryBytes; private final long peakMemoryBytes; private final long inputRows; @@ -41,12 +36,10 @@ public class QueryProgressStats @JsonCreator public QueryProgressStats( - @JsonProperty("executionStartTime") Optional executionStartTime, @JsonProperty("elapsedTimeMillis") long elapsedTimeMillis, @JsonProperty("queuedTimeMillis") long queuedTimeMillis, @JsonProperty("cpuTimeMillis") long cpuTimeMillis, @JsonProperty("scheduledTimeMillis") long scheduledTimeMillis, - @JsonProperty("blockedTimeMillis") long blockedTimeMillis, @JsonProperty("currentMemoryBytes") long currentMemoryBytes, @JsonProperty("peakMemoryBytes") long peakMemoryBytes, @JsonProperty("inputRows") long inputRows, @@ -54,12 +47,10 @@ public QueryProgressStats( @JsonProperty("blocked") boolean blocked, @JsonProperty("progressPercentage") OptionalDouble progressPercentage) { - this.executionStartTime = requireNonNull(executionStartTime, "executionStartTime is null"); this.elapsedTimeMillis = elapsedTimeMillis; this.queuedTimeMillis = queuedTimeMillis; this.cpuTimeMillis = cpuTimeMillis; this.scheduledTimeMillis = scheduledTimeMillis; - this.blockedTimeMillis = blockedTimeMillis; this.currentMemoryBytes = currentMemoryBytes; this.peakMemoryBytes = peakMemoryBytes; this.inputRows = inputRows; @@ -68,15 +59,13 @@ public QueryProgressStats( this.progressPercentage = requireNonNull(progressPercentage, "progressPercentage is null"); } - public static QueryProgressStats createQueryProgressStats(QueryStats queryStats) + public static QueryProgressStats createQueryProgressStats(BasicQueryStats queryStats) { return new QueryProgressStats( - Optional.ofNullable(queryStats.getExecutionStartTime()), queryStats.getElapsedTime().toMillis(), queryStats.getQueuedTime().toMillis(), queryStats.getTotalCpuTime().toMillis(), queryStats.getTotalScheduledTime().toMillis(), - queryStats.getTotalBlockedTime().toMillis(), queryStats.getUserMemoryReservation().toBytes(), queryStats.getPeakUserMemoryReservation().toBytes(), queryStats.getRawInputPositions(), @@ -85,12 +74,6 @@ public static QueryProgressStats createQueryProgressStats(QueryStats queryStats) queryStats.getProgressPercentage()); } - @JsonProperty - public Optional getExecutionStartTime() - { - return executionStartTime; - } - @JsonProperty public long getElapsedTimeMillis() { @@ -115,12 +98,6 @@ public long getScheduledTimeMillis() return scheduledTimeMillis; } - @JsonProperty - public long getBlockedTimeMillis() - { - return blockedTimeMillis; - } - @JsonProperty public long getCurrentMemoryBytes() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java b/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java index abc9f3235985c..adc0ff78f2969 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java @@ -17,6 +17,7 @@ import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryState; import com.facebook.presto.execution.StageId; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.google.common.collect.ImmutableList; @@ -35,7 +36,7 @@ import java.util.NoSuchElementException; import static com.facebook.presto.connector.system.KillQueryProcedure.createKillQueryException; -import static com.facebook.presto.spi.StandardErrorCode.ADMINISTRATIVELY_KILLED; +import static com.facebook.presto.connector.system.KillQueryProcedure.createPreemptQueryException; import static java.util.Objects.requireNonNull; /** @@ -53,12 +54,13 @@ public QueryResource(QueryManager queryManager) } @GET - public List getAllQueryInfo(@QueryParam("state") String queryState) + public List getAllQueryInfo(@QueryParam("state") String stateFilter) { + QueryState expectedState = stateFilter == null ? null : QueryState.valueOf(stateFilter.toUpperCase(Locale.ENGLISH)); ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (QueryInfo queryInfo : queryManager.getAllQueryInfo()) { - if (queryState == null || queryInfo.getState().equals(QueryState.valueOf(queryState.toUpperCase(Locale.ENGLISH)))) { - builder.add(new BasicQueryInfo(queryInfo)); + for (BasicQueryInfo queryInfo : queryManager.getQueries()) { + if (stateFilter == null || queryInfo.getState() == expectedState) { + builder.add(queryInfo); } } return builder.build(); @@ -71,7 +73,7 @@ public Response getQueryInfo(@PathParam("queryId") QueryId queryId) requireNonNull(queryId, "queryId is null"); try { - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); return Response.ok(queryInfo).build(); } catch (NoSuchElementException e) { @@ -90,23 +92,33 @@ public void cancelQuery(@PathParam("queryId") QueryId queryId) @PUT @Path("{queryId}/killed") public Response killQuery(@PathParam("queryId") QueryId queryId, String message) + { + return failQuery(queryId, createKillQueryException(message)); + } + + @PUT + @Path("{queryId}/preempted") + public Response preemptQuery(@PathParam("queryId") QueryId queryId, String message) + { + return failQuery(queryId, createPreemptQueryException(message)); + } + + private Response failQuery(QueryId queryId, PrestoException queryException) { requireNonNull(queryId, "queryId is null"); try { - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + QueryState state = queryManager.getQueryState(queryId); // check before killing to provide the proper error code (this is racy) - if (queryInfo.getState().isDone()) { + if (state.isDone()) { return Response.status(Status.CONFLICT).build(); } - queryManager.failQuery(queryId, createKillQueryException(message)); + queryManager.failQuery(queryId, queryException); - // verify if the query was killed (if not, we lost the race) - queryInfo = queryManager.getQueryInfo(queryId); - if ((queryInfo.getState() != QueryState.FAILED) || - !ADMINISTRATIVELY_KILLED.toErrorCode().equals(queryInfo.getErrorCode())) { + // verify if the query was failed (if not, we lost the race) + if (!queryException.getErrorCode().equals(queryManager.getQueryInfo(queryId).getErrorCode())) { return Response.status(Status.CONFLICT).build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/QuerySessionSupplier.java b/presto-main/src/main/java/com/facebook/presto/server/QuerySessionSupplier.java index df476590f7269..b9c9462c9b7e2 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QuerySessionSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QuerySessionSupplier.java @@ -17,37 +17,20 @@ import com.facebook.presto.metadata.SessionPropertyManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.spi.resourceGroups.SessionPropertyConfigurationManagerContext; import com.facebook.presto.spi.security.Identity; -import com.facebook.presto.spi.session.SessionConfigurationContext; -import com.facebook.presto.spi.session.SessionPropertyConfigurationManager; -import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; import com.facebook.presto.sql.SqlEnvironmentConfig; import com.facebook.presto.sql.SqlPath; import com.facebook.presto.transaction.TransactionManager; -import com.google.common.annotations.VisibleForTesting; -import io.airlift.log.Logger; -import io.airlift.node.NodeInfo; import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; -import java.io.File; -import java.io.IOException; -import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; import static com.facebook.presto.Session.SessionBuilder; import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKey; -import static com.facebook.presto.util.PropertiesUtil.loadProperties; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; -import static java.lang.String.format; import static java.util.Map.Entry; import static java.util.Objects.requireNonNull; @@ -55,27 +38,18 @@ public class QuerySessionSupplier implements SessionSupplier { - private static final Logger log = Logger.get(QuerySessionSupplier.class); - private static final File SESSION_PROPERTY_CONFIGURATION = new File("etc/session-property-config.properties"); - private static final String SESSION_PROPERTY_MANAGER_NAME = "session-property-config.configuration-manager"; - - private final SessionPropertyConfigurationManagerContext configurationManagerContext; private final TransactionManager transactionManager; private final AccessControl accessControl; private final SessionPropertyManager sessionPropertyManager; - private final Map sessionPropertyConfigurationManagerFactories = new ConcurrentHashMap<>(); - private final AtomicReference sessionPropertyConfigurationManager = new AtomicReference<>(); private final Optional path; @Inject public QuerySessionSupplier( - NodeInfo nodeInfo, TransactionManager transactionManager, AccessControl accessControl, SessionPropertyManager sessionPropertyManager, SqlEnvironmentConfig config) { - this.configurationManagerContext = new SessionPropertyConfigurationManagerContextInstance(nodeInfo.getEnvironment()); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); @@ -83,48 +57,10 @@ public QuerySessionSupplier( } @Override - public void addConfigurationManager(SessionPropertyConfigurationManagerFactory sessionConfigFactory) - { - if (sessionPropertyConfigurationManagerFactories.putIfAbsent(sessionConfigFactory.getName(), sessionConfigFactory) != null) { - throw new IllegalArgumentException(format("Session property configuration manager '%s' is already registered", sessionConfigFactory.getName())); - } - } - - @Override - public void loadConfigurationManager() - throws IOException - { - if (!SESSION_PROPERTY_CONFIGURATION.exists()) { - return; - } - - Map propertyMap = new HashMap<>(loadProperties(SESSION_PROPERTY_CONFIGURATION)); - - log.info("-- Loading session property configuration manager --"); - - String configManagerName = propertyMap.remove(SESSION_PROPERTY_MANAGER_NAME); - checkArgument(configManagerName != null, "Session property configuration %s does not contain %s", SESSION_PROPERTY_CONFIGURATION, SESSION_PROPERTY_MANAGER_NAME); - - setConfigurationManager(configManagerName, propertyMap); - - log.info("-- Loaded session property configuration manager %s --", configManagerName); - } - - @VisibleForTesting - public void setConfigurationManager(String name, Map properties) - { - SessionPropertyConfigurationManagerFactory factory = sessionPropertyConfigurationManagerFactories.get(name); - checkState(factory != null, "Session property configuration manager %s is not registered"); - - SessionPropertyConfigurationManager manager = factory.create(properties, configurationManagerContext); - checkState(sessionPropertyConfigurationManager.compareAndSet(null, manager), "sessionPropertyConfigurationManager is already set"); - } - - @Override - public Session createSession(QueryId queryId, SessionContext context, Optional queryType, ResourceGroupId resourceGroupId) + public Session createSession(QueryId queryId, SessionContext context) { Identity identity = context.getIdentity(); - accessControl.checkCanSetUser(identity.getPrincipal().orElse(null), identity.getUser()); + accessControl.checkCanSetUser(identity.getPrincipal(), identity.getUser()); SessionBuilder sessionBuilder = Session.builder(sessionPropertyManager) .setQueryId(queryId) @@ -153,24 +89,6 @@ public Session createSession(QueryId queryId, SessionContext context, Optional entry : sessionPropertyConfigurationManager.get().getSystemSessionProperties(configContext).entrySet()) { - sessionBuilder.setSystemProperty(entry.getKey(), entry.getValue()); - } - for (Entry> catalogProperties : sessionPropertyConfigurationManager.get().getCatalogSessionProperties(configContext).entrySet()) { - String catalog = catalogProperties.getKey(); - for (Entry entry : catalogProperties.getValue().entrySet()) { - sessionBuilder.setCatalogSessionProperty(catalog, entry.getKey(), entry.getValue()); - } - } - } - for (Entry entry : context.getSystemProperties().entrySet()) { sessionBuilder.setSystemProperty(entry.getKey(), entry.getValue()); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfo.java b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfo.java index 8243fee29616c..f4352cd27f753 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfo.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.server; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryState; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -74,12 +73,12 @@ public QueryStateInfo( this.progress = requireNonNull(progress, "progress is null"); } - public static QueryStateInfo createQueuedQueryStateInfo(QueryInfo queryInfo, Optional group, Optional> pathToRoot) + public static QueryStateInfo createQueuedQueryStateInfo(BasicQueryInfo queryInfo, Optional group, Optional> pathToRoot) { return createQueryStateInfo(queryInfo, group, pathToRoot, Optional.empty()); } - public static QueryStateInfo createQueryStateInfo(QueryInfo queryInfo, Optional group) + public static QueryStateInfo createQueryStateInfo(BasicQueryInfo queryInfo, Optional group) { Optional progress = Optional.empty(); if (!queryInfo.getState().isDone() && queryInfo.getState() != QUEUED) { @@ -89,7 +88,7 @@ public static QueryStateInfo createQueryStateInfo(QueryInfo queryInfo, Optional< } private static QueryStateInfo createQueryStateInfo( - QueryInfo queryInfo, + BasicQueryInfo queryInfo, Optional groupId, Optional> pathToRoot, Optional progress) diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java index 6065029676f42..1f38c8d932b62 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.server; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.spi.QueryId; @@ -45,12 +44,12 @@ public class QueryStateInfoResource { private final QueryManager queryManager; - private final ResourceGroupManager resourceGroupManager; + private final ResourceGroupManager resourceGroupManager; @Inject public QueryStateInfoResource( QueryManager queryManager, - ResourceGroupManager resourceGroupManager) + ResourceGroupManager resourceGroupManager) { this.queryManager = requireNonNull(queryManager, "queryManager is null"); this.resourceGroupManager = requireNonNull(resourceGroupManager, "resourceGroupManager is null"); @@ -60,7 +59,7 @@ public QueryStateInfoResource( @Produces(MediaType.APPLICATION_JSON) public List getQueryStateInfos(@QueryParam("user") String user) { - List queryInfos = queryManager.getAllQueryInfo(); + List queryInfos = queryManager.getQueries(); if (!isNullOrEmpty(user)) { queryInfos = queryInfos.stream() @@ -74,9 +73,9 @@ public List getQueryStateInfos(@QueryParam("user") String user) .collect(toImmutableList()); } - private QueryStateInfo getQueryStateInfo(QueryInfo queryInfo) + private QueryStateInfo getQueryStateInfo(BasicQueryInfo queryInfo) { - Optional groupId = queryManager.getQueryResourceGroup(queryInfo.getQueryId()); + Optional groupId = queryInfo.getResourceGroupId(); if (queryInfo.getState() == QUEUED) { return createQueuedQueryStateInfo( queryInfo, diff --git a/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupInfo.java b/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupInfo.java index e99772ac3b44a..44d05a6b2803c 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupInfo.java @@ -18,7 +18,6 @@ import com.facebook.presto.spi.resourceGroups.SchedulingPolicy; import com.fasterxml.jackson.annotation.JsonProperty; import io.airlift.units.DataSize; -import io.airlift.units.Duration; import javax.annotation.Nullable; @@ -42,8 +41,6 @@ public class ResourceGroupInfo private final int softConcurrencyLimit; private final int hardConcurrencyLimit; private final int maxQueuedQueries; - private final Duration runningTimeLimit; - private final Duration queuedTimeLimit; private final DataSize memoryUsage; private final int numQueuedQueries; @@ -65,8 +62,6 @@ public ResourceGroupInfo( int softConcurrencyLimit, int hardConcurrencyLimit, int maxQueuedQueries, - Duration runningTimeLimit, - Duration queuedTimeLimit, DataSize memoryUsage, int numQueuedQueries, @@ -88,8 +83,6 @@ public ResourceGroupInfo( this.softConcurrencyLimit = softConcurrencyLimit; this.hardConcurrencyLimit = hardConcurrencyLimit; this.maxQueuedQueries = maxQueuedQueries; - this.runningTimeLimit = requireNonNull(runningTimeLimit, "runningTimeLimit is null"); - this.queuedTimeLimit = requireNonNull(queuedTimeLimit, "queuedTimeLimit is null"); this.memoryUsage = requireNonNull(memoryUsage, "memoryUsage is null"); this.numQueuedQueries = numQueuedQueries; @@ -155,18 +148,6 @@ public int getMaxQueuedQueries() return maxQueuedQueries; } - @JsonProperty - public Duration getQueuedTimeLimit() - { - return queuedTimeLimit; - } - - @JsonProperty - public Duration getRunningTimeLimit() - { - return runningTimeLimit; - } - @JsonProperty public int getNumQueuedQueries() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupStateInfoResource.java b/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupStateInfoResource.java index d7517efe5b3b8..e6590155d431e 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupStateInfoResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ResourceGroupStateInfoResource.java @@ -39,10 +39,10 @@ @Path("/v1/resourceGroupState") public class ResourceGroupStateInfoResource { - private final ResourceGroupManager resourceGroupManager; + private final ResourceGroupManager resourceGroupManager; @Inject - public ResourceGroupStateInfoResource(ResourceGroupManager resourceGroupManager) + public ResourceGroupStateInfoResource(ResourceGroupManager resourceGroupManager) { this.resourceGroupManager = requireNonNull(resourceGroupManager, "resourceGroupManager is null"); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java index bcbffc7ba53ea..d7a897f55776f 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ServerMainModule.java @@ -22,19 +22,12 @@ import com.facebook.presto.client.ServerInfo; import com.facebook.presto.connector.ConnectorManager; import com.facebook.presto.connector.system.SystemConnectorModule; -import com.facebook.presto.cost.CostCalculator; -import com.facebook.presto.cost.CostCalculator.EstimatedExchanges; -import com.facebook.presto.cost.CostCalculatorUsingExchanges; -import com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges; -import com.facebook.presto.cost.CostComparator; -import com.facebook.presto.cost.StatsCalculatorModule; -import com.facebook.presto.event.query.QueryMonitor; -import com.facebook.presto.event.query.QueryMonitorConfig; +import com.facebook.presto.event.SplitMonitor; +import com.facebook.presto.execution.ExecutionFailureInfo; import com.facebook.presto.execution.ExplainAnalyzeContext; import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.execution.MemoryRevokingScheduler; import com.facebook.presto.execution.NodeTaskMap; -import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryManagerConfig; import com.facebook.presto.execution.SqlTaskManager; import com.facebook.presto.execution.StageInfo; @@ -45,16 +38,12 @@ import com.facebook.presto.execution.TaskStatus; import com.facebook.presto.execution.executor.MultilevelSplitQueue; import com.facebook.presto.execution.executor.TaskExecutor; -import com.facebook.presto.execution.resourceGroups.NoOpResourceGroupManager; -import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.execution.scheduler.FlatNetworkTopology; import com.facebook.presto.execution.scheduler.LegacyNetworkTopology; import com.facebook.presto.execution.scheduler.NetworkTopology; import com.facebook.presto.execution.scheduler.NodeScheduler; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; import com.facebook.presto.execution.scheduler.NodeSchedulerExporter; -import com.facebook.presto.failureDetector.FailureDetector; -import com.facebook.presto.failureDetector.NoOpFailureDetector; import com.facebook.presto.index.IndexManager; import com.facebook.presto.memory.LocalMemoryManager; import com.facebook.presto.memory.LocalMemoryManagerExporter; @@ -83,6 +72,7 @@ import com.facebook.presto.operator.ExchangeClientSupplier; import com.facebook.presto.operator.ForExchange; import com.facebook.presto.operator.LookupJoinOperators; +import com.facebook.presto.operator.OperatorStats; import com.facebook.presto.operator.PagesIndex; import com.facebook.presto.operator.index.IndexJoinLookupStats; import com.facebook.presto.server.remotetask.HttpLocationFactory; @@ -125,18 +115,16 @@ import com.facebook.presto.sql.planner.NodePartitioningManager; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; -import com.facebook.presto.transaction.NoOpTransactionManager; -import com.facebook.presto.transaction.TransactionManager; import com.facebook.presto.transaction.TransactionManagerConfig; import com.facebook.presto.type.TypeDeserializer; import com.facebook.presto.type.TypeRegistry; import com.facebook.presto.util.FinalizerService; +import com.facebook.presto.version.EmbedVersion; import com.google.common.collect.ImmutableList; import com.google.inject.Binder; import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Scopes; -import com.google.inject.TypeLiteral; import io.airlift.concurrent.BoundedExecutor; import io.airlift.configuration.AbstractConfigurationAwareModule; import io.airlift.slice.Slice; @@ -157,7 +145,6 @@ import static com.facebook.presto.execution.scheduler.NodeSchedulerConfig.NetworkTopologyType.FLAT; import static com.facebook.presto.execution.scheduler.NodeSchedulerConfig.NetworkTopologyType.LEGACY; import static com.google.common.base.Strings.nullToEmpty; -import static com.google.common.reflect.Reflection.newProxy; import static com.google.inject.multibindings.Multibinder.newSetBinder; import static com.google.inject.multibindings.OptionalBinder.newOptionalBinder; import static io.airlift.concurrent.Threads.daemonThreadsNamed; @@ -195,22 +182,7 @@ protected void setup(Binder binder) install(new CoordinatorModule()); } else { - // Install no-op session supplier on workers, since only coordinators create sessions. - binder.bind(SessionSupplier.class).to(NoOpSessionSupplier.class).in(Scopes.SINGLETON); - - // Install no-op resource group manager on workers, since only coordinators manage resource groups. - binder.bind(ResourceGroupManager.class).to(NoOpResourceGroupManager.class).in(Scopes.SINGLETON); - - // Install no-op transaction manager on workers, since only coordinators manage transactions. - binder.bind(TransactionManager.class).to(NoOpTransactionManager.class).in(Scopes.SINGLETON); - - // Install no-op failure detector on workers, since only coordinators need global node selection. - binder.bind(FailureDetector.class).to(NoOpFailureDetector.class).in(Scopes.SINGLETON); - - // HACK: this binding is needed by SystemConnectorModule, but will only be used on the coordinator - binder.bind(QueryManager.class).toInstance(newProxy(QueryManager.class, (proxy, method, args) -> { - throw new UnsupportedOperationException(); - })); + install(new WorkerModule()); } install(new InternalCommunicationModule()); @@ -237,6 +209,7 @@ protected void setup(Binder binder) // session properties binder.bind(SessionPropertyManager.class).in(Scopes.SINGLETON); binder.bind(SystemSessionProperties.class).in(Scopes.SINGLETON); + binder.bind(SessionPropertyDefaults.class).in(Scopes.SINGLETON); // schema properties binder.bind(SchemaPropertyManager.class).in(Scopes.SINGLETON); @@ -281,6 +254,8 @@ protected void setup(Binder binder) // task execution jaxrsBinder(binder).bind(TaskResource.class); newExporter(binder).export(TaskResource.class).withGeneratedName(); + jaxrsBinder(binder).bind(TaskExecutorResource.class); + newExporter(binder).export(TaskExecutorResource.class).withGeneratedName(); binder.bind(TaskManagementExecutor.class).in(Scopes.SINGLETON); binder.bind(SqlTaskManager.class).in(Scopes.SINGLETON); binder.bind(TaskManager.class).to(Key.get(SqlTaskManager.class)); @@ -297,6 +272,7 @@ protected void setup(Binder binder) configBinder(binder).bindConfig(ReservedSystemMemoryConfig.class); binder.bind(LocalMemoryManager.class).in(Scopes.SINGLETON); binder.bind(LocalMemoryManagerExporter.class).in(Scopes.SINGLETON); + binder.bind(EmbedVersion.class).in(Scopes.SINGLETON); newExporter(binder).export(TaskManager.class).withGeneratedName(); binder.bind(TaskExecutor.class).in(Scopes.SINGLETON); newExporter(binder).export(TaskExecutor.class).withGeneratedName(); @@ -325,10 +301,12 @@ protected void setup(Binder binder) jsonCodecBinder(binder).bindJsonCodec(TaskStatus.class); jsonCodecBinder(binder).bindJsonCodec(StageInfo.class); jsonCodecBinder(binder).bindJsonCodec(TaskInfo.class); + jsonCodecBinder(binder).bindJsonCodec(OperatorStats.class); + jsonCodecBinder(binder).bindJsonCodec(ExecutionFailureInfo.class); jaxrsBinder(binder).bind(PagesResponseWriter.class); // exchange client - binder.bind(new TypeLiteral() {}).to(ExchangeClientFactory.class).in(Scopes.SINGLETON); + binder.bind(ExchangeClientSupplier.class).to(ExchangeClientFactory.class).in(Scopes.SINGLETON); httpClientBinder(binder).bindHttpClient("exchange", ForExchange.class) .withTracing() .withFilter(GenerateTraceTokenRequestFilter.class) @@ -369,14 +347,6 @@ protected void setup(Binder binder) binder.bind(MetadataManager.class).in(Scopes.SINGLETON); binder.bind(Metadata.class).to(MetadataManager.class).in(Scopes.SINGLETON); - // statistics calculator - binder.install(new StatsCalculatorModule()); - - // cost calculator - binder.bind(CostCalculator.class).to(CostCalculatorUsingExchanges.class).in(Scopes.SINGLETON); - binder.bind(CostCalculator.class).annotatedWith(EstimatedExchanges.class).to(CostCalculatorWithEstimatedExchanges.class).in(Scopes.SINGLETON); - binder.bind(CostComparator.class).in(Scopes.SINGLETON); - // type binder.bind(TypeRegistry.class).in(Scopes.SINGLETON); binder.bind(TypeManager.class).to(TypeRegistry.class).in(Scopes.SINGLETON); @@ -410,9 +380,8 @@ protected void setup(Binder binder) jsonBinder(binder).addDeserializerBinding(Expression.class).to(ExpressionDeserializer.class); jsonBinder(binder).addDeserializerBinding(FunctionCall.class).to(FunctionCallDeserializer.class); - // query monitor - configBinder(binder).bindConfig(QueryMonitorConfig.class); - binder.bind(QueryMonitor.class).in(Scopes.SINGLETON); + // split monitor + binder.bind(SplitMonitor.class).in(Scopes.SINGLETON); // Determine the NodeVersion NodeVersion nodeVersion = new NodeVersion(serverConfig.getPrestoVersion()); diff --git a/presto-main/src/main/java/com/facebook/presto/server/SessionPropertyDefaults.java b/presto-main/src/main/java/com/facebook/presto/server/SessionPropertyDefaults.java new file mode 100644 index 0000000000000..60fe6a212194f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/server/SessionPropertyDefaults.java @@ -0,0 +1,111 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.server; + +import com.facebook.presto.Session; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.spi.resourceGroups.SessionPropertyConfigurationManagerContext; +import com.facebook.presto.spi.session.SessionConfigurationContext; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManager; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; +import com.google.common.annotations.VisibleForTesting; +import io.airlift.log.Logger; +import io.airlift.node.NodeInfo; + +import javax.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +import static com.facebook.presto.util.PropertiesUtil.loadProperties; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; + +public class SessionPropertyDefaults +{ + private static final Logger log = Logger.get(SessionPropertyDefaults.class); + private static final File SESSION_PROPERTY_CONFIGURATION = new File("etc/session-property-config.properties"); + private static final String SESSION_PROPERTY_MANAGER_NAME = "session-property-config.configuration-manager"; + + private final SessionPropertyConfigurationManagerContext configurationManagerContext; + private final Map factories = new ConcurrentHashMap<>(); + private final AtomicReference delegate = new AtomicReference<>(); + + @Inject + public SessionPropertyDefaults(NodeInfo nodeInfo) + { + this.configurationManagerContext = new SessionPropertyConfigurationManagerContextInstance(nodeInfo.getEnvironment()); + } + + public void addConfigurationManagerFactory(SessionPropertyConfigurationManagerFactory sessionConfigFactory) + { + if (factories.putIfAbsent(sessionConfigFactory.getName(), sessionConfigFactory) != null) { + throw new IllegalArgumentException(format("Session property configuration manager '%s' is already registered", sessionConfigFactory.getName())); + } + } + + public void loadConfigurationManager() + throws IOException + { + if (!SESSION_PROPERTY_CONFIGURATION.exists()) { + return; + } + + Map propertyMap = new HashMap<>(loadProperties(SESSION_PROPERTY_CONFIGURATION)); + + log.info("-- Loading session property configuration manager --"); + + String configManagerName = propertyMap.remove(SESSION_PROPERTY_MANAGER_NAME); + checkArgument(configManagerName != null, "Session property configuration %s does not contain %s", SESSION_PROPERTY_CONFIGURATION, SESSION_PROPERTY_MANAGER_NAME); + + setConfigurationManager(configManagerName, propertyMap); + + log.info("-- Loaded session property configuration manager %s --", configManagerName); + } + + @VisibleForTesting + public void setConfigurationManager(String name, Map properties) + { + SessionPropertyConfigurationManagerFactory factory = factories.get(name); + checkState(factory != null, "Session property configuration manager %s is not registered"); + + SessionPropertyConfigurationManager manager = factory.create(properties, configurationManagerContext); + checkState(delegate.compareAndSet(null, manager), "sessionPropertyConfigurationManager is already set"); + } + + public Session newSessionWithDefaultProperties(Session session, Optional queryType, ResourceGroupId resourceGroupId) + { + SessionPropertyConfigurationManager configurationManager = delegate.get(); + if (configurationManager == null) { + return session; + } + + SessionConfigurationContext context = new SessionConfigurationContext( + session.getIdentity().getUser(), + session.getSource(), + session.getClientTags(), + queryType, + resourceGroupId); + + Map systemPropertyOverrides = configurationManager.getSystemSessionProperties(context); + Map> catalogPropertyOverrides = configurationManager.getCatalogSessionProperties(context); + return session.withDefaultProperties(systemPropertyOverrides, catalogPropertyOverrides); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/server/SessionSupplier.java b/presto-main/src/main/java/com/facebook/presto/server/SessionSupplier.java index 706c9b9ee4a3d..1aed320765f69 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/SessionSupplier.java +++ b/presto-main/src/main/java/com/facebook/presto/server/SessionSupplier.java @@ -15,18 +15,8 @@ import com.facebook.presto.Session; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; - -import java.io.IOException; -import java.util.Optional; public interface SessionSupplier { - Session createSession(QueryId queryId, SessionContext context, Optional queryType, ResourceGroupId resourceGroupId); - - void addConfigurationManager(SessionPropertyConfigurationManagerFactory sessionConfigFactory); - - void loadConfigurationManager() - throws IOException; + Session createSession(QueryId queryId, SessionContext context); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/TaskExecutorResource.java b/presto-main/src/main/java/com/facebook/presto/server/TaskExecutorResource.java new file mode 100644 index 0000000000000..cfb614b024301 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/server/TaskExecutorResource.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.server; + +import com.facebook.presto.execution.executor.TaskExecutor; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import static java.util.Objects.requireNonNull; + +@Path("/v1/maxActiveSplits") +public class TaskExecutorResource +{ + private final TaskExecutor taskExecutor; + + @Inject + public TaskExecutorResource( + TaskExecutor taskExecutor) + { + this.taskExecutor = requireNonNull(taskExecutor, "taskExecutor is null"); + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getMaxActiveSplit() + { + return taskExecutor.getMaxActiveSplitsInfo(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/server/ThreadResource.java b/presto-main/src/main/java/com/facebook/presto/server/ThreadResource.java index 4d96618c66396..6b7ba052e8a34 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/ThreadResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/ThreadResource.java @@ -17,40 +17,27 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.Ordering; -import com.google.common.io.Resources; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; -import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import java.nio.charset.StandardCharsets; import java.util.Comparator; import java.util.List; import static com.facebook.presto.server.ThreadResource.Info.byName; -import static com.google.common.io.Resources.getResource; @Path("/") public class ThreadResource { - @GET - @Path("/ui/thread") - @Produces(MediaType.TEXT_HTML) - public String getUi() - throws IOException - { - return Resources.toString(getResource(getClass(), "thread.html"), StandardCharsets.UTF_8); - } - @GET @Path("/v1/thread") @Produces(MediaType.APPLICATION_JSON) - public static List getThreadInfo() + public List getThreadInfo() { ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); @@ -94,7 +81,7 @@ public Info( @JsonProperty("id") long id, @JsonProperty("name") String name, @JsonProperty("state") String state, - @JsonProperty("lockOwner") Long lockOwnerId, + @JsonProperty("lockOwnerId") Long lockOwnerId, @JsonProperty("stackTrace") List stackTrace) { this.id = id; @@ -158,7 +145,7 @@ public static class StackLine public StackLine( @JsonProperty("file") String file, @JsonProperty("line") int line, - @JsonProperty("class") String className, + @JsonProperty("className") String className, @JsonProperty("method") String method) { this.file = file; diff --git a/presto-main/src/main/java/com/facebook/presto/server/WebUiResource.java b/presto-main/src/main/java/com/facebook/presto/server/WebUiResource.java index 6e592c41daf4d..66cd0f7c06047 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/WebUiResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/WebUiResource.java @@ -14,20 +14,30 @@ package com.facebook.presto.server; import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; +import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.net.HttpHeaders.X_FORWARDED_PROTO; import static javax.ws.rs.core.Response.Status.MOVED_PERMANENTLY; -import static javax.ws.rs.core.UriBuilder.fromPath; @Path("/") public class WebUiResource { @GET - public Response redirectIndexHtml() + public Response redirectIndexHtml( + @HeaderParam(X_FORWARDED_PROTO) String proto, + @Context UriInfo uriInfo) { + if (isNullOrEmpty(proto)) { + proto = uriInfo.getRequestUri().getScheme(); + } + return Response.status(MOVED_PERMANENTLY) - .location(fromPath("/ui/").build()) + .location(uriInfo.getRequestUriBuilder().scheme(proto).path("/ui/").build()) .build(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/WorkerModule.java b/presto-main/src/main/java/com/facebook/presto/server/WorkerModule.java new file mode 100644 index 0000000000000..a59a9fff4b88f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/server/WorkerModule.java @@ -0,0 +1,62 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.server; + +import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.execution.resourceGroups.NoOpResourceGroupManager; +import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; +import com.facebook.presto.failureDetector.FailureDetector; +import com.facebook.presto.failureDetector.NoOpFailureDetector; +import com.facebook.presto.transaction.NoOpTransactionManager; +import com.facebook.presto.transaction.TransactionManager; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.Scopes; + +import javax.inject.Singleton; + +import static com.google.common.reflect.Reflection.newProxy; + +public class WorkerModule + implements Module +{ + @Override + public void configure(Binder binder) + { + // Install no-op session supplier on workers, since only coordinators create sessions. + binder.bind(SessionSupplier.class).to(NoOpSessionSupplier.class).in(Scopes.SINGLETON); + + // Install no-op resource group manager on workers, since only coordinators manage resource groups. + binder.bind(ResourceGroupManager.class).to(NoOpResourceGroupManager.class).in(Scopes.SINGLETON); + + // Install no-op transaction manager on workers, since only coordinators manage transactions. + binder.bind(TransactionManager.class).to(NoOpTransactionManager.class).in(Scopes.SINGLETON); + + // Install no-op failure detector on workers, since only coordinators need global node selection. + binder.bind(FailureDetector.class).to(NoOpFailureDetector.class).in(Scopes.SINGLETON); + + // HACK: this binding is needed by SystemConnectorModule, but will only be used on the coordinator + binder.bind(QueryManager.class).toInstance(newProxy(QueryManager.class, (proxy, method, args) -> { + throw new UnsupportedOperationException(); + })); + } + + @Provides + @Singleton + public static ResourceGroupManager getResourceGroupManager(@SuppressWarnings("rawtypes") ResourceGroupManager manager) + { + return manager; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java index 1f09c2b688c95..07af5a3e2a1f4 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java @@ -19,7 +19,7 @@ import com.google.common.collect.ImmutableSet; import io.airlift.log.Logger; -import java.util.Optional; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentMap; class PurgeQueriesRunnable @@ -50,15 +50,16 @@ public void run() if (!query.isSubmissionFinished()) { continue; } - Optional state = queryManager.getQueryState(queryId); - - // free up resources if the query completed - if (!state.isPresent() || state.get() == QueryState.FAILED) { - query.dispose(); + try { + // free up resources if the query completed + if (queryManager.getQueryState(queryId) == QueryState.FAILED) { + query.dispose(); + } } - - // forget about this query if the query manager is no longer tracking it - if (!state.isPresent()) { + catch (NoSuchElementException e) { + // make sure all resource are released + query.dispose(); + // forget about this query if the query manager is no longer tracking it queries.remove(queryId); } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java index d814e633b66e7..e9b498d60c943 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java @@ -65,6 +65,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; import java.util.OptionalLong; import java.util.Set; @@ -73,6 +74,7 @@ import java.util.concurrent.atomic.AtomicLong; import static com.facebook.presto.SystemSessionProperties.isExchangeCompressionEnabled; +import static com.facebook.presto.execution.QueryState.FAILED; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.base.MoreObjects.firstNonNull; @@ -81,7 +83,6 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.concurrent.MoreFutures.addSuccessCallback; import static io.airlift.concurrent.MoreFutures.addTimeout; -import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.lang.String.format; import static java.util.Objects.requireNonNull; @@ -89,7 +90,6 @@ class Query { private static final Logger log = Logger.get(Query.class); - private static final long DESIRED_RESULT_BYTES = new DataSize(1, MEGABYTE).toBytes(); private final QueryManager queryManager; private final QueryId queryId; @@ -172,7 +172,7 @@ public static Query create( result.queryManager.addStateChangeListener(result.getQueryId(), state -> { if (state.isDone()) { - QueryInfo queryInfo = queryManager.getQueryInfo(result.getQueryId()); + QueryInfo queryInfo = queryManager.getFullQueryInfo(result.getQueryId()); result.closeExchangeClientIfNecessary(queryInfo); } }); @@ -283,7 +283,7 @@ public synchronized boolean isClearTransactionId() return clearTransactionId; } - public synchronized ListenableFuture waitForResults(OptionalLong token, UriInfo uriInfo, String scheme, Duration wait) + public synchronized ListenableFuture waitForResults(OptionalLong token, UriInfo uriInfo, String scheme, Duration wait, DataSize targetResultSize) { // before waiting, check if this request has already been processed and cached if (token.isPresent()) { @@ -301,7 +301,7 @@ public synchronized ListenableFuture waitForResults(OptionalLong t timeoutExecutor); // when state changes, fetch the next result - return Futures.transform(futureStateChange, ignored -> getNextResult(token, uriInfo, scheme), resultsProcessorExecutor); + return Futures.transform(futureStateChange, ignored -> getNextResult(token, uriInfo, scheme, targetResultSize), resultsProcessorExecutor); } private synchronized ListenableFuture getFutureStateChange() @@ -321,8 +321,12 @@ private synchronized ListenableFuture getFutureStateChange() // otherwise, wait for the query to finish queryManager.recordHeartbeat(queryId); - return queryManager.getQueryState(queryId).map(this::queryDoneFuture) - .orElse(immediateFuture(null)); + try { + return queryDoneFuture(queryManager.getQueryState(queryId)); + } + catch (NoSuchElementException e) { + return immediateFuture(null); + } } private synchronized Optional getCachedResult(long token, UriInfo uriInfo) @@ -332,7 +336,6 @@ private synchronized Optional getCachedResult(long token, UriInfo if (requestedPath.equals(lastResultPath)) { if (submissionFuture.isDone()) { // tell query manager we are still interested in the query - queryManager.getQueryInfo(queryId); queryManager.recordHeartbeat(queryId); } return Optional.of(lastResult); @@ -351,7 +354,7 @@ private synchronized Optional getCachedResult(long token, UriInfo return Optional.empty(); } - public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriInfo, String scheme) + public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriInfo, String scheme, DataSize targetResultSize) { // check if the result for the token have already been created if (token.isPresent()) { @@ -382,6 +385,7 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn .setScheduled(false) .build(), null, + ImmutableList.of(), null, null); @@ -390,7 +394,7 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn } if (session == null) { - session = queryManager.getQueryInfo(queryId).getSession().toSession(sessionPropertyManager); + session = queryManager.getFullQueryInfo(queryId).getSession().toSession(sessionPropertyManager); serde = new PagesSerdeFactory(blockEncodingSerde, isExchangeCompressionEnabled(session)).createPagesSerde(); } @@ -404,14 +408,15 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn ImmutableList.Builder pages = ImmutableList.builder(); long bytes = 0; long rows = 0; - while (bytes < DESIRED_RESULT_BYTES) { + long targetResultBytes = targetResultSize.toBytes(); + while (bytes < targetResultBytes) { SerializedPage serializedPage = exchangeClient.pollPage(); if (serializedPage == null) { break; } Page page = serde.deserialize(serializedPage); - bytes += page.getSizeInBytes(); + bytes += page.getLogicalSizeInBytes(); rows += page.getPositionCount(); pages.add(new RowIterable(session.toConnectorSession(), types, page)); } @@ -426,7 +431,7 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn // get the query info before returning // force update if query manager is closed - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); queryManager.recordHeartbeat(queryId); // TODO: figure out a better way to do this @@ -450,9 +455,13 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn data = ImmutableSet.of(ImmutableList.of(true)); } - // only return a next if the query is not done or there is more data to send (due to buffering) + // only return a next if + // (1) the query is not done AND the query state is not FAILED + // OR + // (2)there is more data to send (due to buffering) URI nextResultsUri = null; - if (!queryInfo.isFinalQueryInfo() || !exchangeClient.isClosed()) { + if (!queryInfo.isFinalQueryInfo() && !queryInfo.getState().equals(QueryState.FAILED) + || !exchangeClient.isClosed()) { nextResultsUri = createNextResultsUri(scheme, uriInfo); } @@ -483,6 +492,7 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn data, toStatementStats(queryInfo), toQueryError(queryInfo), + queryInfo.getWarnings(), queryInfo.getUpdateType(), updateCount); @@ -507,7 +517,7 @@ private synchronized void closeExchangeClientIfNecessary(QueryInfo queryInfo) // Close the exchange client if the query has failed, or if the query // is done and it does not have an output stage. The latter happens // for data definition executions, as those do not have output. - if ((queryInfo.getState() == QueryState.FAILED) || + if ((queryInfo.getState() == FAILED) || (queryInfo.getState().isDone() && !queryInfo.getOutputStage().isPresent())) { exchangeClient.close(); } @@ -570,7 +580,6 @@ private static StatementStats toStatementStats(QueryInfo queryInfo) .setQueuedSplits(queryStats.getQueuedDrivers()) .setRunningSplits(queryStats.getRunningDrivers() + queryStats.getBlockedDrivers()) .setCompletedSplits(queryStats.getCompletedDrivers()) - .setUserTimeMillis(queryStats.getTotalUserTime().toMillis()) .setCpuTimeMillis(queryStats.getTotalCpuTime().toMillis()) .setWallTimeMillis(queryStats.getTotalScheduledTime().toMillis()) .setQueuedTimeMillis(queryStats.getQueuedTime().toMillis()) @@ -611,7 +620,6 @@ private static StageStats toStageStats(StageInfo stageInfo) .setQueuedSplits(stageStats.getQueuedDrivers()) .setRunningSplits(stageStats.getRunningDrivers() + stageStats.getBlockedDrivers()) .setCompletedSplits(stageStats.getCompletedDrivers()) - .setUserTimeMillis(stageStats.getTotalUserTime().toMillis()) .setCpuTimeMillis(stageStats.getTotalCpuTime().toMillis()) .setWallTimeMillis(stageStats.getTotalScheduledTime().toMillis()) .setProcessedRows(stageStats.getRawInputPositions()) @@ -666,12 +674,16 @@ private static URI findCancelableLeafStage(StageInfo stage) private static QueryError toQueryError(QueryInfo queryInfo) { - FailureInfo failure = queryInfo.getFailureInfo(); - if (failure == null) { - QueryState state = queryInfo.getState(); - if ((!state.isDone()) || (state == QueryState.FINISHED)) { - return null; - } + QueryState state = queryInfo.getState(); + if (state != FAILED) { + return null; + } + + FailureInfo failure; + if (queryInfo.getFailureInfo() != null) { + failure = queryInfo.getFailureInfo().toFailureInfo(); + } + else { log.warn("Query %s in state %s has no failure info", queryInfo.getQueryId(), state); failure = toFailure(new RuntimeException(format("Query is %s (reason unknown)", state))).toFailureInfo(); } @@ -720,7 +732,8 @@ private synchronized void submitQuery() } querySubmissionFuture = queryManager.createQuery(queryId, sessionContext, this.query); - Futures.addCallback(querySubmissionFuture, new FutureCallback() { + Futures.addCallback(querySubmissionFuture, new FutureCallback() + { @Override public void onSuccess(Object result) { diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java index da49095deb1db..c1e2197349434 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java @@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.concurrent.BoundedExecutor; +import io.airlift.units.DataSize; import io.airlift.units.Duration; import javax.annotation.PreDestroy; @@ -74,6 +75,7 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.concurrent.Threads.threadsNamed; import static io.airlift.http.server.AsyncResponseHandler.bindAsyncResponse; +import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -85,6 +87,9 @@ public class StatementResource private static final Duration MAX_WAIT_TIME = new Duration(1, SECONDS); private static final Ordering> WAIT_ORDERING = Ordering.natural().nullsLast(); + private static final DataSize DEFAULT_TARGET_RESULT_SIZE = new DataSize(1, MEGABYTE); + private static final DataSize MAX_TARGET_RESULT_SIZE = new DataSize(128, MEGABYTE); + private final QueryManager queryManager; private final SessionPropertyManager sessionPropertyManager; private final ExchangeClientSupplier exchangeClientSupplier; @@ -153,7 +158,7 @@ public Response createQuery( blockEncodingSerde); queries.put(query.getQueryId(), query); - QueryResults queryResults = query.getNextResult(OptionalLong.empty(), uriInfo, proto); + QueryResults queryResults = query.getNextResult(OptionalLong.empty(), uriInfo, proto, DEFAULT_TARGET_RESULT_SIZE); return toResponse(query, queryResults); } @@ -164,6 +169,7 @@ public void getQueryResults( @PathParam("queryId") QueryId queryId, @PathParam("token") long token, @QueryParam("maxWait") Duration maxWait, + @QueryParam("targetResultSize") DataSize targetResultSize, @HeaderParam(X_FORWARDED_PROTO) String proto, @Context UriInfo uriInfo, @Suspended AsyncResponse asyncResponse) @@ -177,13 +183,26 @@ public void getQueryResults( proto = uriInfo.getRequestUri().getScheme(); } - asyncQueryResults(query, OptionalLong.of(token), maxWait, uriInfo, proto, asyncResponse); + asyncQueryResults(query, OptionalLong.of(token), maxWait, targetResultSize, uriInfo, proto, asyncResponse); } - private void asyncQueryResults(Query query, OptionalLong token, Duration maxWait, UriInfo uriInfo, String scheme, AsyncResponse asyncResponse) + private void asyncQueryResults( + Query query, + OptionalLong token, + Duration maxWait, + DataSize targetResultSize, + UriInfo uriInfo, + String scheme, + AsyncResponse asyncResponse) { Duration wait = WAIT_ORDERING.min(MAX_WAIT_TIME, maxWait); - ListenableFuture queryResultsFuture = query.waitForResults(token, uriInfo, scheme, wait); + if (targetResultSize == null) { + targetResultSize = DEFAULT_TARGET_RESULT_SIZE; + } + else { + targetResultSize = Ordering.natural().min(targetResultSize, MAX_TARGET_RESULT_SIZE); + } + ListenableFuture queryResultsFuture = query.waitForResults(token, uriInfo, scheme, wait, targetResultSize); ListenableFuture response = Futures.transform(queryResultsFuture, queryResults -> toResponse(query, queryResults), directExecutor()); diff --git a/presto-main/src/main/java/com/facebook/presto/server/remotetask/ContinuousTaskStatusFetcher.java b/presto-main/src/main/java/com/facebook/presto/server/remotetask/ContinuousTaskStatusFetcher.java index cd1a8c33a586e..2fa6c8cd2d11a 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/remotetask/ContinuousTaskStatusFetcher.java +++ b/presto-main/src/main/java/com/facebook/presto/server/remotetask/ContinuousTaskStatusFetcher.java @@ -244,6 +244,11 @@ public synchronized boolean isRunning() return running; } + /** + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ public void addStateChangeListener(StateMachine.StateChangeListener stateChangeListener) { taskStatus.addStateChangeListener(stateChangeListener); diff --git a/presto-main/src/main/java/com/facebook/presto/server/remotetask/HttpRemoteTask.java b/presto-main/src/main/java/com/facebook/presto/server/remotetask/HttpRemoteTask.java index 10ab6dea7ba9c..0b92c4860cead 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/remotetask/HttpRemoteTask.java +++ b/presto-main/src/main/java/com/facebook/presto/server/remotetask/HttpRemoteTask.java @@ -89,9 +89,9 @@ import static com.google.common.util.concurrent.Futures.immediateFuture; import static io.airlift.http.client.FullJsonResponseHandler.createFullJsonResponseHandler; import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; -import static io.airlift.http.client.JsonBodyGenerator.jsonBodyGenerator; import static io.airlift.http.client.Request.Builder.prepareDelete; import static io.airlift.http.client.Request.Builder.preparePost; +import static io.airlift.http.client.StaticBodyGenerator.createStaticBodyGenerator; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -407,6 +407,12 @@ public void addStateChangeListener(StateChangeListener stateChangeLi } } + @Override + public void addFinalTaskInfoListener(StateChangeListener stateChangeListener) + { + taskInfoFetcher.addFinalTaskInfoListener(stateChangeListener); + } + @Override public synchronized ListenableFuture whenSplitQueueHasSpace(int threshold) { @@ -495,22 +501,23 @@ private synchronized void sendUpdate() List sources = getSources(); - Optional fragment = Optional.empty(); - if (sendPlan.get()) { - fragment = Optional.of(planFragment); - } + Optional fragment = sendPlan.get() ? Optional.of(planFragment) : Optional.empty(); TaskUpdateRequest updateRequest = new TaskUpdateRequest( session.toSessionRepresentation(), fragment, sources, outputBuffers.get(), totalPartitions); + byte[] taskUpdateRequestJson = taskUpdateRequestCodec.toJsonBytes(updateRequest); + if (fragment.isPresent()) { + stats.updateWithPlanBytes(taskUpdateRequestJson.length); + } HttpUriBuilder uriBuilder = getHttpUriBuilder(taskStatus); Request request = preparePost() .setUri(uriBuilder.build()) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString()) - .setBodyGenerator(jsonBodyGenerator(taskUpdateRequestCodec, updateRequest)) + .setBodyGenerator(createStaticBodyGenerator(taskUpdateRequestJson)) .build(); updateErrorTracker.startRequest(); @@ -725,12 +732,12 @@ private HttpUriBuilder getHttpUriBuilder(TaskStatus taskStatus) private static Backoff createCleanupBackoff() { return new Backoff(10, new Duration(10, TimeUnit.MINUTES), Ticker.systemTicker(), ImmutableList.builder() - .add(new Duration(0, MILLISECONDS)) - .add(new Duration(100, MILLISECONDS)) - .add(new Duration(500, MILLISECONDS)) - .add(new Duration(1, SECONDS)) - .add(new Duration(10, SECONDS)) - .build()); + .add(new Duration(0, MILLISECONDS)) + .add(new Duration(100, MILLISECONDS)) + .add(new Duration(500, MILLISECONDS)) + .add(new Duration(1, SECONDS)) + .add(new Duration(10, SECONDS)) + .build()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/server/remotetask/RemoteTaskStats.java b/presto-main/src/main/java/com/facebook/presto/server/remotetask/RemoteTaskStats.java index 9734c66c43ee2..4ca0cc78650b7 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/remotetask/RemoteTaskStats.java +++ b/presto-main/src/main/java/com/facebook/presto/server/remotetask/RemoteTaskStats.java @@ -14,7 +14,9 @@ package com.facebook.presto.server.remotetask; import com.google.common.util.concurrent.AtomicDouble; +import io.airlift.stats.DistributionStat; import org.weakref.jmx.Managed; +import org.weakref.jmx.Nested; import javax.annotation.concurrent.ThreadSafe; @@ -24,6 +26,7 @@ public class RemoteTaskStats private final IncrementalAverage infoRoundTripMillis = new IncrementalAverage(); private final IncrementalAverage statusRoundTripMillis = new IncrementalAverage(); private final IncrementalAverage responseSizeBytes = new IncrementalAverage(); + private final DistributionStat updateWithPlanBytes = new DistributionStat(); private long requestSuccess; private long requestFailure; @@ -58,6 +61,11 @@ public void updateFailure() requestFailure++; } + public void updateWithPlanBytes(long bytes) + { + updateWithPlanBytes.add(bytes); + } + @Managed public double getResponseSizeBytes() { @@ -94,6 +102,13 @@ public long getRequestFailure() return requestFailure; } + @Managed + @Nested + public DistributionStat getUpdateWithPlanBytes() + { + return updateWithPlanBytes; + } + @ThreadSafe private static class IncrementalAverage { diff --git a/presto-main/src/main/java/com/facebook/presto/server/remotetask/TaskInfoFetcher.java b/presto-main/src/main/java/com/facebook/presto/server/remotetask/TaskInfoFetcher.java index a13b7f81cfa10..78fe9fde3e0cd 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/remotetask/TaskInfoFetcher.java +++ b/presto-main/src/main/java/com/facebook/presto/server/remotetask/TaskInfoFetcher.java @@ -14,6 +14,7 @@ package com.facebook.presto.server.remotetask; import com.facebook.presto.execution.StateMachine; +import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.TaskId; import com.facebook.presto.execution.TaskInfo; import com.facebook.presto.execution.TaskStatus; @@ -30,9 +31,11 @@ import javax.annotation.concurrent.GuardedBy; import java.net.URI; +import java.util.Optional; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; @@ -51,6 +54,7 @@ public class TaskInfoFetcher private final TaskId taskId; private final Consumer onFail; private final StateMachine taskInfo; + private final StateMachine> finalTaskInfo; private final JsonCodec taskInfoCodec; private final long updateIntervalMillis; @@ -96,6 +100,7 @@ public TaskInfoFetcher( this.taskId = initialTask.getTaskStatus().getTaskId(); this.onFail = requireNonNull(onFail, "onFail is null"); this.taskInfo = new StateMachine<>("task " + taskId, executor, initialTask); + this.finalTaskInfo = new StateMachine<>("task-" + taskId, executor, Optional.empty()); this.taskInfoCodec = requireNonNull(taskInfoCodec, "taskInfoCodec is null"); this.updateIntervalMillis = requireNonNull(updateInterval, "updateInterval is null").toMillis(); @@ -136,6 +141,24 @@ private synchronized void stop() } } + /** + * Add a listener for the final task info. This notification is guaranteed to be fired only once. + * Listener is always notified asynchronously using a dedicated notification thread pool so, care should + * be taken to avoid leaking {@code this} when adding a listener in a constructor. Additionally, it is + * possible notifications are observed out of order due to the asynchronous execution. + */ + public void addFinalTaskInfoListener(StateChangeListener stateChangeListener) + { + AtomicBoolean done = new AtomicBoolean(); + StateChangeListener> fireOnceStateChangeListener = finalTaskInfo -> { + if (finalTaskInfo.isPresent() && done.compareAndSet(false, true)) { + stateChangeListener.stateChanged(finalTaskInfo.get()); + } + }; + finalTaskInfo.addStateChangeListener(fireOnceStateChangeListener); + fireOnceStateChangeListener.stateChanged(finalTaskInfo.get()); + } + private synchronized void scheduleUpdate() { scheduledFuture = updateScheduledExecutor.scheduleWithFixedDelay(() -> { @@ -204,6 +227,7 @@ synchronized void updateTaskInfo(TaskInfo newValue) }); if (updated && newValue.getTaskStatus().getState().isDone()) { + finalTaskInfo.compareAndSet(Optional.empty(), Optional.of(newValue)); stop(); } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java b/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java index e3c67d9839a19..2d57d6fb8cc67 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java +++ b/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java @@ -17,10 +17,12 @@ import com.facebook.presto.connector.ConnectorManager; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.eventlistener.EventListenerManager; +import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.execution.SqlQueryManager; +import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.TaskManager; import com.facebook.presto.execution.resourceGroups.InternalResourceGroupManager; -import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.memory.ClusterMemoryManager; import com.facebook.presto.memory.LocalMemoryManager; import com.facebook.presto.metadata.AllNodes; @@ -36,12 +38,16 @@ import com.facebook.presto.server.security.ServerSecurityModule; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.Plugin; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.split.PageSourceManager; import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.planner.NodePartitioningManager; +import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.testing.ProcedureTester; import com.facebook.presto.testing.TestingAccessControlManager; import com.facebook.presto.testing.TestingEventListenerManager; +import com.facebook.presto.testing.TestingWarningCollectorModule; import com.facebook.presto.transaction.TransactionManager; import com.google.common.base.Joiner; import com.google.common.base.Splitter; @@ -113,13 +119,14 @@ public class TestingPrestoServer private final ProcedureTester procedureTester; private final Optional resourceGroupManager; private final SplitManager splitManager; + private final PageSourceManager pageSourceManager; private final NodePartitioningManager nodePartitioningManager; private final ClusterMemoryManager clusterMemoryManager; private final LocalMemoryManager localMemoryManager; private final InternalNodeManager nodeManager; private final ServiceSelectorManager serviceSelectorManager; private final Announcer announcer; - private final QueryManager queryManager; + private final SqlQueryManager queryManager; private final TaskManager taskManager; private final GracefulShutdownHandler gracefulShutdownHandler; private final ShutdownAction shutdownAction; @@ -205,6 +212,7 @@ public TestingPrestoServer(boolean coordinator, .add(new TraceTokenModule()) .add(new ServerSecurityModule()) .add(new ServerMainModule(parserOptions)) + .add(new TestingWarningCollectorModule()) .add(binder -> { binder.bind(TestingAccessControlManager.class).in(Scopes.SINGLETON); binder.bind(TestingEventListenerManager.class).in(Scopes.SINGLETON); @@ -246,7 +254,12 @@ public TestingPrestoServer(boolean coordinator, lifeCycleManager = injector.getInstance(LifeCycleManager.class); - queryManager = injector.getInstance(QueryManager.class); + if (coordinator) { + queryManager = (SqlQueryManager) injector.getInstance(QueryManager.class); + } + else { + queryManager = null; + } pluginManager = injector.getInstance(PluginManager.class); @@ -259,8 +272,9 @@ public TestingPrestoServer(boolean coordinator, accessControl = injector.getInstance(TestingAccessControlManager.class); procedureTester = injector.getInstance(ProcedureTester.class); splitManager = injector.getInstance(SplitManager.class); + pageSourceManager = injector.getInstance(PageSourceManager.class); if (coordinator) { - resourceGroupManager = Optional.of((InternalResourceGroupManager) injector.getInstance(ResourceGroupManager.class)); + resourceGroupManager = Optional.of(injector.getInstance(InternalResourceGroupManager.class)); nodePartitioningManager = injector.getInstance(NodePartitioningManager.class); clusterMemoryManager = injector.getInstance(ClusterMemoryManager.class); statsCalculator = injector.getInstance(StatsCalculator.class); @@ -314,6 +328,16 @@ public QueryManager getQueryManager() return queryManager; } + public Plan getQueryPlan(QueryId queryId) + { + return queryManager.getQueryPlan(queryId); + } + + public void addFinalQueryInfoListener(QueryId queryId, StateChangeListener stateChangeListener) + { + queryManager.addFinalQueryInfoListener(queryId, stateChangeListener); + } + public ConnectorId createCatalog(String catalogName, String connectorName) { return createCatalog(catalogName, connectorName, ImmutableMap.of()); @@ -322,7 +346,7 @@ public ConnectorId createCatalog(String catalogName, String connectorName) public ConnectorId createCatalog(String catalogName, String connectorName, Map properties) { ConnectorId connectorId = connectorManager.createConnection(catalogName, connectorName, properties); - updateConnectorIdAnnouncement(announcer, connectorId); + updateConnectorIdAnnouncement(announcer, connectorId, nodeManager); return connectorId; } @@ -388,6 +412,11 @@ public SplitManager getSplitManager() return splitManager; } + public PageSourceManager getPageSourceManager() + { + return pageSourceManager; + } + public Optional getResourceGroupManager() { return resourceGroupManager; @@ -446,7 +475,7 @@ public T getInstance(Key key) return injector.getInstance(key); } - private static void updateConnectorIdAnnouncement(Announcer announcer, ConnectorId connectorId) + private static void updateConnectorIdAnnouncement(Announcer announcer, ConnectorId connectorId, InternalNodeManager nodeManager) { // // This code was copied from PrestoServer, and is a hack that should be removed when the connectorId property is removed @@ -466,6 +495,8 @@ private static void updateConnectorIdAnnouncement(Announcer announcer, Connector announcer.removeServiceAnnouncement(announcement.getId()); announcer.addServiceAnnouncement(serviceAnnouncement(announcement.getType()).addProperties(properties).build()); announcer.forceAnnounce(); + + nodeManager.refreshNodes(); } private static ServiceAnnouncement getPrestoAnnouncement(Set announcements) diff --git a/presto-main/src/main/java/com/facebook/presto/spiller/FileSingleStreamSpiller.java b/presto-main/src/main/java/com/facebook/presto/spiller/FileSingleStreamSpiller.java index 8a2c26438c328..11cfb27cb6e11 100644 --- a/presto-main/src/main/java/com/facebook/presto/spiller/FileSingleStreamSpiller.java +++ b/presto-main/src/main/java/com/facebook/presto/spiller/FileSingleStreamSpiller.java @@ -20,8 +20,8 @@ import com.facebook.presto.operator.SpillContext; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.util.PrestoIterators; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; import com.google.common.util.concurrent.Futures; @@ -33,6 +33,7 @@ import javax.annotation.concurrent.NotThreadSafe; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; @@ -156,7 +157,7 @@ private Iterator readPages() try { InputStream input = closer.register(targetFile.newInputStream()); Iterator pages = PagesSerdeUtil.readPages(serde, new InputStreamSliceInput(input, BUFFER_SIZE)); - return PrestoIterators.closeWhenExhausted(pages, input); + return closeWhenExhausted(pages, input); } catch (IOException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, "Failed to read spilled pages", e); @@ -171,11 +172,8 @@ public void close() try { closer.close(); } - catch (Exception e) { - throw new PrestoException( - GENERIC_INTERNAL_ERROR, - "Failed to close spiller", - e); + catch (IOException e) { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "Failed to close spiller", e); } } @@ -183,4 +181,28 @@ private void checkNoSpillInProgress() { checkState(spillInProgress.isDone(), "spill in progress"); } + + private static Iterator closeWhenExhausted(Iterator iterator, Closeable resource) + { + requireNonNull(iterator, "iterator is null"); + requireNonNull(resource, "resource is null"); + + return new AbstractIterator() + { + @Override + protected T computeNext() + { + if (iterator.hasNext()) { + return iterator.next(); + } + try { + resource.close(); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + return endOfData(); + } + }; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java index 4ecd1df9ccdce..20c68cccd668d 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analysis.java @@ -60,7 +60,6 @@ import java.util.Optional; import java.util.Set; -import static com.facebook.presto.util.MoreLists.listOfListsCopy; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; @@ -89,7 +88,9 @@ public class Analysis private final Map, List> aggregates = new LinkedHashMap<>(); private final Map, List> orderByAggregates = new LinkedHashMap<>(); - private final Map, List>> groupByExpressions = new LinkedHashMap<>(); + private final Map, List> groupByExpressions = new LinkedHashMap<>(); + private final Map, GroupingSetAnalysis> groupingSets = new LinkedHashMap<>(); + private final Map, Expression> where = new LinkedHashMap<>(); private final Map, Expression> having = new LinkedHashMap<>(); private final Map, List> orderByExpressions = new LinkedHashMap<>(); @@ -264,9 +265,19 @@ public Map, LambdaArgumentDeclaration> getLambdaArgumentRefe return unmodifiableMap(lambdaArgumentReferences); } - public void setGroupingSets(QuerySpecification node, List> expressions) + public void setGroupingSets(QuerySpecification node, GroupingSetAnalysis groupingSets) + { + this.groupingSets.put(NodeRef.of(node), groupingSets); + } + + public void setGroupByExpressions(QuerySpecification node, List expressions) + { + groupByExpressions.put(NodeRef.of(node), expressions); + } + + public boolean isAggregation(QuerySpecification node) { - groupByExpressions.put(NodeRef.of(node), listOfListsCopy(expressions)); + return groupByExpressions.containsKey(NodeRef.of(node)); } public boolean isTypeOnlyCoercion(Expression expression) @@ -274,7 +285,12 @@ public boolean isTypeOnlyCoercion(Expression expression) return typeOnlyCoercions.contains(NodeRef.of(expression)); } - public List> getGroupingSets(QuerySpecification node) + public GroupingSetAnalysis getGroupingSets(QuerySpecification node) + { + return groupingSets.get(NodeRef.of(node)); + } + + public List getGroupByExpressions(QuerySpecification node) { return groupByExpressions.get(NodeRef.of(node)); } @@ -698,6 +714,46 @@ public List getOtherRightFields() } } + public static class GroupingSetAnalysis + { + private final List> cubes; + private final List> rollups; + private final List>> ordinarySets; + private final List complexExpressions; + + public GroupingSetAnalysis( + List> cubes, + List> rollups, + List>> ordinarySets, + List complexExpressions) + { + this.cubes = ImmutableList.copyOf(cubes); + this.rollups = ImmutableList.copyOf(rollups); + this.ordinarySets = ImmutableList.copyOf(ordinarySets); + this.complexExpressions = ImmutableList.copyOf(complexExpressions); + } + + public List> getCubes() + { + return cubes; + } + + public List> getRollups() + { + return rollups; + } + + public List>> getOrdinarySets() + { + return ordinarySets; + } + + public List getComplexExpressions() + { + return complexExpressions; + } + } + public static final class AccessControlInfo { private final AccessControl accessControl; diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java index 84caceb4a919e..01f2c312369cc 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Analyzer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.analyzer; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; @@ -43,13 +44,15 @@ public class Analyzer private final Session session; private final Optional queryExplainer; private final List parameters; + private final WarningCollector warningCollector; public Analyzer(Session session, Metadata metadata, SqlParser sqlParser, AccessControl accessControl, Optional queryExplainer, - List parameters) + List parameters, + WarningCollector warningCollector) { this.session = requireNonNull(session, "session is null"); this.metadata = requireNonNull(metadata, "metadata is null"); @@ -57,6 +60,7 @@ public Analyzer(Session session, this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.queryExplainer = requireNonNull(queryExplainer, "query explainer is null"); this.parameters = parameters; + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } public Analysis analyze(Statement statement) @@ -66,9 +70,9 @@ public Analysis analyze(Statement statement) public Analysis analyze(Statement statement, boolean isDescribe) { - Statement rewrittenStatement = StatementRewrite.rewrite(session, metadata, sqlParser, queryExplainer, statement, parameters, accessControl); + Statement rewrittenStatement = StatementRewrite.rewrite(session, metadata, sqlParser, queryExplainer, statement, parameters, accessControl, warningCollector); Analysis analysis = new Analysis(rewrittenStatement, parameters, isDescribe); - StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session); + StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, warningCollector); analyzer.analyze(rewrittenStatement, Optional.empty()); // check column access permissions for each table diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java index 92a32c59b42d4..4e868d0f6fff0 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.OperatorNotFoundException; @@ -191,6 +192,7 @@ public class ExpressionAnalyzer private final Session session; private final List parameters; + private final WarningCollector warningCollector; public ExpressionAnalyzer( FunctionRegistry functionRegistry, @@ -199,6 +201,7 @@ public ExpressionAnalyzer( Session session, TypeProvider symbolTypes, List parameters, + WarningCollector warningCollector, boolean isDescribe) { this.functionRegistry = requireNonNull(functionRegistry, "functionRegistry is null"); @@ -209,6 +212,7 @@ public ExpressionAnalyzer( this.parameters = requireNonNull(parameters, "parameters is null"); this.isDescribe = isDescribe; this.legacyRowFieldOrdinalAccess = isLegacyRowFieldOrdinalAccessEnabled(session); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } public Map, Signature> getResolvedFunctions() @@ -267,13 +271,13 @@ public Map, LambdaArgumentDeclaration> getLambdaArgumentRefe public Type analyze(Expression expression, Scope scope) { - Visitor visitor = new Visitor(scope); + Visitor visitor = new Visitor(scope, warningCollector); return visitor.process(expression, new StackableAstVisitor.StackableAstVisitorContext<>(Context.notInLambda(scope))); } private Type analyze(Expression expression, Scope baseScope, Context context) { - Visitor visitor = new Visitor(baseScope); + Visitor visitor = new Visitor(baseScope, warningCollector); return visitor.process(expression, new StackableAstVisitor.StackableAstVisitorContext<>(context)); } @@ -307,10 +311,12 @@ private class Visitor { // Used to resolve FieldReferences (e.g. during local execution planning) private final Scope baseScope; + private final WarningCollector warningCollector; - public Visitor(Scope baseScope) + public Visitor(Scope baseScope, WarningCollector warningCollector) { this.baseScope = requireNonNull(baseScope, "baseScope is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -404,8 +410,8 @@ private Type handleResolvedField(Expression node, FieldId fieldId, Field field, } } - if (field.getOriginTable().isPresent() && field.getName().isPresent()) { - tableColumnReferences.put(field.getOriginTable().get(), field.getName().get()); + if (field.getOriginTable().isPresent() && field.getOriginColumnName().isPresent()) { + tableColumnReferences.put(field.getOriginTable().get(), field.getOriginColumnName().get()); } FieldId previous = columnReferences.put(NodeRef.of(node), fieldId); @@ -843,6 +849,7 @@ protected Type visitFunctionCall(FunctionCall node, StackableAstVisitorContext, Type> getExpressionTypes( SqlParser sqlParser, TypeProvider types, Expression expression, - List parameters) + List parameters, + WarningCollector warningCollector) { - return getExpressionTypes(session, metadata, sqlParser, types, expression, parameters, false); + return getExpressionTypes(session, metadata, sqlParser, types, expression, parameters, warningCollector, false); } public static Map, Type> getExpressionTypes( @@ -1445,9 +1453,10 @@ public static Map, Type> getExpressionTypes( TypeProvider types, Expression expression, List parameters, + WarningCollector warningCollector, boolean isDescribe) { - return getExpressionTypes(session, metadata, sqlParser, types, ImmutableList.of(expression), parameters, isDescribe); + return getExpressionTypes(session, metadata, sqlParser, types, ImmutableList.of(expression), parameters, warningCollector, isDescribe); } public static Map, Type> getExpressionTypes( @@ -1457,9 +1466,10 @@ public static Map, Type> getExpressionTypes( TypeProvider types, Iterable expressions, List parameters, + WarningCollector warningCollector, boolean isDescribe) { - return analyzeExpressionsWithSymbols(session, metadata, sqlParser, types, expressions, parameters, isDescribe).getExpressionTypes(); + return analyzeExpressionsWithSymbols(session, metadata, sqlParser, types, expressions, parameters, warningCollector, isDescribe).getExpressionTypes(); } public static Map, Type> getExpressionTypesFromInput( @@ -1468,9 +1478,10 @@ public static Map, Type> getExpressionTypesFromInput( SqlParser sqlParser, Map types, Expression expression, - List parameters) + List parameters, + WarningCollector warningCollector) { - return getExpressionTypesFromInput(session, metadata, sqlParser, types, ImmutableList.of(expression), parameters); + return getExpressionTypesFromInput(session, metadata, sqlParser, types, ImmutableList.of(expression), parameters, warningCollector); } public static Map, Type> getExpressionTypesFromInput( @@ -1479,9 +1490,10 @@ public static Map, Type> getExpressionTypesFromInput( SqlParser sqlParser, Map types, Iterable expressions, - List parameters) + List parameters, + WarningCollector warningCollector) { - return analyzeExpressionsWithInputs(session, metadata, sqlParser, types, expressions, parameters).getExpressionTypes(); + return analyzeExpressionsWithInputs(session, metadata, sqlParser, types, expressions, parameters, warningCollector).getExpressionTypes(); } public static ExpressionAnalysis analyzeExpressionsWithSymbols( @@ -1491,9 +1503,10 @@ public static ExpressionAnalysis analyzeExpressionsWithSymbols( TypeProvider types, Iterable expressions, List parameters, + WarningCollector warningCollector, boolean isDescribe) { - return analyzeExpressions(session, metadata, sqlParser, new RelationType(), types, expressions, parameters, isDescribe); + return analyzeExpressions(session, metadata, sqlParser, new RelationType(), types, expressions, parameters, warningCollector, isDescribe); } private static ExpressionAnalysis analyzeExpressionsWithInputs( @@ -1502,7 +1515,8 @@ private static ExpressionAnalysis analyzeExpressionsWithInputs( SqlParser sqlParser, Map types, Iterable expressions, - List parameters) + List parameters, + WarningCollector warningCollector) { Field[] fields = new Field[types.size()]; for (Entry entry : types.entrySet()) { @@ -1510,7 +1524,7 @@ private static ExpressionAnalysis analyzeExpressionsWithInputs( } RelationType tupleDescriptor = new RelationType(fields); - return analyzeExpressions(session, metadata, sqlParser, tupleDescriptor, TypeProvider.empty(), expressions, parameters); + return analyzeExpressions(session, metadata, sqlParser, tupleDescriptor, TypeProvider.empty(), expressions, parameters, warningCollector); } public static ExpressionAnalysis analyzeExpressions( @@ -1520,9 +1534,10 @@ public static ExpressionAnalysis analyzeExpressions( RelationType tupleDescriptor, TypeProvider types, Iterable expressions, - List parameters) + List parameters, + WarningCollector warningCollector) { - return analyzeExpressions(session, metadata, sqlParser, tupleDescriptor, types, expressions, parameters, false); + return analyzeExpressions(session, metadata, sqlParser, tupleDescriptor, types, expressions, parameters, warningCollector, false); } private static ExpressionAnalysis analyzeExpressions( @@ -1533,12 +1548,13 @@ private static ExpressionAnalysis analyzeExpressions( TypeProvider types, Iterable expressions, List parameters, + WarningCollector warningCollector, boolean isDescribe) { // expressions at this point can not have sub queries so deny all access checks // in the future, we will need a full access controller here to verify access to functions Analysis analysis = new Analysis(null, parameters, isDescribe); - ExpressionAnalyzer analyzer = create(analysis, session, metadata, sqlParser, new DenyAllAccessControl(), types); + ExpressionAnalyzer analyzer = create(analysis, session, metadata, sqlParser, new DenyAllAccessControl(), types, warningCollector); for (Expression expression : expressions) { analyzer.analyze(expression, Scope.builder().withRelationType(RelationId.anonymous(), tupleDescriptor).build()); } @@ -1563,9 +1579,10 @@ public static ExpressionAnalysis analyzeExpression( SqlParser sqlParser, Scope scope, Analysis analysis, - Expression expression) + Expression expression, + WarningCollector warningCollector) { - ExpressionAnalyzer analyzer = create(analysis, session, metadata, sqlParser, accessControl, TypeProvider.empty()); + ExpressionAnalyzer analyzer = create(analysis, session, metadata, sqlParser, accessControl, TypeProvider.empty(), warningCollector); analyzer.analyze(expression, scope); Map, Type> expressionTypes = analyzer.getExpressionTypes(); @@ -1593,35 +1610,27 @@ public static ExpressionAnalysis analyzeExpression( analyzer.getWindowFunctions()); } - public static ExpressionAnalyzer create( - Analysis analysis, - Session session, - Metadata metadata, - SqlParser sqlParser, - AccessControl accessControl) - { - return create(analysis, session, metadata, sqlParser, accessControl, TypeProvider.empty()); - } - public static ExpressionAnalyzer create( Analysis analysis, Session session, Metadata metadata, SqlParser sqlParser, AccessControl accessControl, - TypeProvider types) + TypeProvider types, + WarningCollector warningCollector) { return new ExpressionAnalyzer( metadata.getFunctionRegistry(), metadata.getTypeManager(), - node -> new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session), + node -> new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, warningCollector), session, types, analysis.getParameters(), + warningCollector, analysis.isDescribe()); } - public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Session session, List parameters) + public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Session session, List parameters, WarningCollector warningCollector) { return createWithoutSubqueries( metadata.getFunctionRegistry(), @@ -1630,10 +1639,11 @@ public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Sessi parameters, EXPRESSION_NOT_CONSTANT, "Constant expression cannot contain a subquery", + warningCollector, false); } - public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Session session, List parameters, boolean isDescribe) + public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Session session, List parameters, WarningCollector warningCollector, boolean isDescribe) { return createWithoutSubqueries( metadata.getFunctionRegistry(), @@ -1642,6 +1652,7 @@ public static ExpressionAnalyzer createConstantAnalyzer(Metadata metadata, Sessi parameters, EXPRESSION_NOT_CONSTANT, "Constant expression cannot contain a subquery", + warningCollector, isDescribe); } @@ -1652,6 +1663,7 @@ public static ExpressionAnalyzer createWithoutSubqueries( List parameters, SemanticErrorCode errorCode, String message, + WarningCollector warningCollector, boolean isDescribe) { return createWithoutSubqueries( @@ -1661,6 +1673,7 @@ public static ExpressionAnalyzer createWithoutSubqueries( TypeProvider.empty(), parameters, node -> new SemanticException(errorCode, node, message), + warningCollector, isDescribe); } @@ -1671,6 +1684,7 @@ public static ExpressionAnalyzer createWithoutSubqueries( TypeProvider symbolTypes, List parameters, Function statementAnalyzerRejection, + WarningCollector warningCollector, boolean isDescribe) { return new ExpressionAnalyzer( @@ -1682,6 +1696,7 @@ public static ExpressionAnalyzer createWithoutSubqueries( session, symbolTypes, parameters, + warningCollector, isDescribe); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java index a680dca5bd1e6..1fa3a52875000 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/FeaturesConfig.java @@ -15,6 +15,7 @@ import com.facebook.presto.operator.aggregation.arrayagg.ArrayAggGroupImplementation; import com.facebook.presto.operator.aggregation.histogram.HistogramGroupImplementation; +import com.facebook.presto.operator.aggregation.multimapagg.MultimapAggGroupImplementation; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; @@ -40,7 +41,6 @@ import static com.facebook.presto.sql.analyzer.RegexLibrary.JONI; import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.units.DataSize.Unit.KILOBYTE; -import static io.airlift.units.DataSize.succinctBytes; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MINUTES; @@ -64,8 +64,10 @@ public class FeaturesConfig private double networkCostWeight = 15; private boolean distributedIndexJoinsEnabled; private JoinDistributionType joinDistributionType = PARTITIONED; + private DataSize joinMaxBroadcastTableSize; private boolean colocatedJoinsEnabled; private boolean groupedExecutionForAggregationEnabled; + private boolean dynamicScheduleForGroupedExecution; private int concurrentLifespansPerTask; private boolean spatialJoinsEnabled = true; private boolean fastInequalityJoins = true; @@ -84,7 +86,6 @@ public class FeaturesConfig private boolean groupByUsesEqualTo; private boolean legacyTimestamp = true; private boolean legacyMapSubscript; - private boolean legacyRoundNBigint; private boolean legacyRowFieldOrdinalAccess; private boolean legacyCharToVarcharCoercion; private boolean optimizeMixedDistinctAggregations; @@ -99,20 +100,23 @@ public class FeaturesConfig private RegexLibrary regexLibrary = JONI; private HistogramGroupImplementation histogramGroupImplementation = HistogramGroupImplementation.NEW; private ArrayAggGroupImplementation arrayAggGroupImplementation = ArrayAggGroupImplementation.NEW; + private MultimapAggGroupImplementation multimapAggGroupImplementation = MultimapAggGroupImplementation.NEW; private boolean spillEnabled; private DataSize aggregationOperatorUnspillMemoryLimit = new DataSize(4, DataSize.Unit.MEGABYTE); private List spillerSpillPaths = ImmutableList.of(); private int spillerThreads = 4; private double spillMaxUsedSpaceThreshold = 0.9; private boolean iterativeOptimizerEnabled = true; - private boolean enableNewStatsCalculator = true; + private boolean enableStatsCalculator = true; + private boolean ignoreStatsCalculatorFailures = true; + private boolean defaultFilterFactorEnabled; private boolean pushAggregationThroughJoin = true; private double memoryRevokingTarget = 0.5; private double memoryRevokingThreshold = 0.9; private boolean parseDecimalLiteralsAsDouble; private boolean useMarkDistinct = true; private boolean preferPartialAggregation = true; - private DataSize preAllocateMemoryThreshold = succinctBytes(0); + private boolean optimizeTopNRowNumber = true; private Duration iterativeOptimizerTimeout = new Duration(3, MINUTES); // by default let optimizer wait a long time in case it retrieves some data from ConnectorMetadata @@ -193,18 +197,6 @@ public FeaturesConfig setDistributedIndexJoinsEnabled(boolean distributedIndexJo return this; } - @Config("deprecated.legacy-round-n-bigint") - public FeaturesConfig setLegacyRoundNBigint(boolean legacyRoundNBigint) - { - this.legacyRoundNBigint = legacyRoundNBigint; - return this; - } - - public boolean isLegacyRoundNBigint() - { - return legacyRoundNBigint; - } - @Config("deprecated.legacy-row-field-ordinal-access") public FeaturesConfig setLegacyRowFieldOrdinalAccess(boolean value) { @@ -301,6 +293,18 @@ public FeaturesConfig setJoinDistributionType(JoinDistributionType joinDistribut return this; } + public DataSize getJoinMaxBroadcastTableSize() + { + return joinMaxBroadcastTableSize; + } + + @Config("join-max-broadcast-table-size") + public FeaturesConfig setJoinMaxBroadcastTableSize(DataSize joinMaxBroadcastTableSize) + { + this.joinMaxBroadcastTableSize = joinMaxBroadcastTableSize; + return this; + } + public boolean isGroupedExecutionForAggregationEnabled() { return groupedExecutionForAggregationEnabled; @@ -314,6 +318,19 @@ public FeaturesConfig setGroupedExecutionForAggregationEnabled(boolean groupedEx return this; } + public boolean isDynamicScheduleForGroupedExecutionEnabled() + { + return dynamicScheduleForGroupedExecution; + } + + @Config("dynamic-schedule-for-grouped-execution") + @ConfigDescription("Experimental: Use dynamic schedule for grouped execution when possible") + public FeaturesConfig setDynamicScheduleForGroupedExecutionEnabled(boolean dynamicScheduleForGroupedExecution) + { + this.dynamicScheduleForGroupedExecution = dynamicScheduleForGroupedExecution; + return this; + } + public int getConcurrentLifespansPerTask() { return concurrentLifespansPerTask; @@ -469,6 +486,18 @@ public FeaturesConfig setPreferPartialAggregation(boolean value) return this; } + public boolean isOptimizeTopNRowNumber() + { + return optimizeTopNRowNumber; + } + + @Config("optimizer.optimize-top-n-row-number") + public FeaturesConfig setOptimizeTopNRowNumber(boolean optimizeTopNRowNumber) + { + this.optimizeTopNRowNumber = optimizeTopNRowNumber; + return this; + } + public boolean isOptimizeHashGeneration() { return optimizeHashGeneration; @@ -579,18 +608,43 @@ public FeaturesConfig setIterativeOptimizerTimeout(Duration timeout) return this; } - public boolean isEnableNewStatsCalculator() + public boolean isEnableStatsCalculator() { - return enableNewStatsCalculator; + return enableStatsCalculator; } - @Config("experimental.enable-new-stats-calculator") - public FeaturesConfig setEnableNewStatsCalculator(boolean enableNewStatsCalculator) + @Config("experimental.enable-stats-calculator") + public FeaturesConfig setEnableStatsCalculator(boolean enableStatsCalculator) { - this.enableNewStatsCalculator = enableNewStatsCalculator; + this.enableStatsCalculator = enableStatsCalculator; return this; } + public boolean isIgnoreStatsCalculatorFailures() + { + return ignoreStatsCalculatorFailures; + } + + @Config("optimizer.ignore-stats-calculator-failures") + @ConfigDescription("Ignore statistics calculator failures") + public FeaturesConfig setIgnoreStatsCalculatorFailures(boolean ignoreStatsCalculatorFailures) + { + this.ignoreStatsCalculatorFailures = ignoreStatsCalculatorFailures; + return this; + } + + @Config("optimizer.default-filter-factor-enabled") + public FeaturesConfig setDefaultFilterFactorEnabled(boolean defaultFilterFactorEnabled) + { + this.defaultFilterFactorEnabled = defaultFilterFactorEnabled; + return this; + } + + public boolean isDefaultFilterFactorEnabled() + { + return defaultFilterFactorEnabled; + } + public DataSize getAggregationOperatorUnspillMemoryLimit() { return aggregationOperatorUnspillMemoryLimit; @@ -612,7 +666,7 @@ public List getSpillerSpillPaths() public FeaturesConfig setSpillerSpillPaths(String spillPaths) { List spillPathsSplit = ImmutableList.copyOf(Splitter.on(",").trimResults().omitEmptyStrings().split(spillPaths)); - this.spillerSpillPaths = spillPathsSplit.stream().map(path -> Paths.get(path)).collect(toImmutableList()); + this.spillerSpillPaths = spillPathsSplit.stream().map(Paths::get).collect(toImmutableList()); return this; } @@ -811,16 +865,15 @@ public FeaturesConfig setArrayAggGroupImplementation(ArrayAggGroupImplementation return this; } - @NotNull - public DataSize getPreAllocateMemoryThreshold() + public MultimapAggGroupImplementation getMultimapAggGroupImplementation() { - return preAllocateMemoryThreshold; + return multimapAggGroupImplementation; } - @Config("experimental.preallocate-memory-threshold") - public FeaturesConfig setPreAllocateMemoryThreshold(DataSize preAllocateMemoryThreshold) + @Config("multimapagg.implementation") + public FeaturesConfig setMultimapAggGroupImplementation(MultimapAggGroupImplementation groupByMode) { - this.preAllocateMemoryThreshold = preAllocateMemoryThreshold; + this.multimapAggGroupImplementation = groupByMode; return this; } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Field.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Field.java index e7f49914a1448..179857c682d6f 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Field.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/Field.java @@ -24,6 +24,7 @@ public class Field { private final Optional originTable; + private final Optional originColumnName; private final Optional relationAlias; private final Optional name; private final Type type; @@ -35,7 +36,7 @@ public static Field newUnqualified(String name, Type type) requireNonNull(name, "name is null"); requireNonNull(type, "type is null"); - return new Field(Optional.empty(), Optional.of(name), type, false, Optional.empty(), false); + return new Field(Optional.empty(), Optional.of(name), type, false, Optional.empty(), Optional.empty(), false); } public static Field newUnqualified(Optional name, Type type) @@ -43,19 +44,19 @@ public static Field newUnqualified(Optional name, Type type) requireNonNull(name, "name is null"); requireNonNull(type, "type is null"); - return new Field(Optional.empty(), name, type, false, Optional.empty(), false); + return new Field(Optional.empty(), name, type, false, Optional.empty(), Optional.empty(), false); } - public static Field newUnqualified(Optional name, Type type, Optional originTable, boolean aliased) + public static Field newUnqualified(Optional name, Type type, Optional originTable, Optional originColumn, boolean aliased) { requireNonNull(name, "name is null"); requireNonNull(type, "type is null"); requireNonNull(originTable, "originTable is null"); - return new Field(Optional.empty(), name, type, false, originTable, aliased); + return new Field(Optional.empty(), name, type, false, originTable, originColumn, aliased); } - public static Field newQualified(QualifiedName relationAlias, Optional name, Type type, boolean hidden, Optional originTable, boolean aliased) + public static Field newQualified(QualifiedName relationAlias, Optional name, Type type, boolean hidden, Optional originTable, Optional originColumn, boolean aliased) { requireNonNull(relationAlias, "relationAlias is null"); requireNonNull(name, "name is null"); @@ -63,15 +64,16 @@ public static Field newQualified(QualifiedName relationAlias, Optional n requireNonNull(originTable, "originTable is null"); requireNonNull(aliased, "aliased is null"); - return new Field(Optional.of(relationAlias), name, type, hidden, originTable, aliased); + return new Field(Optional.of(relationAlias), name, type, hidden, originTable, originColumn, aliased); } - public Field(Optional relationAlias, Optional name, Type type, boolean hidden, Optional originTable, boolean aliased) + public Field(Optional relationAlias, Optional name, Type type, boolean hidden, Optional originTable, Optional originColumnName, boolean aliased) { requireNonNull(relationAlias, "relationAlias is null"); requireNonNull(name, "name is null"); requireNonNull(type, "type is null"); requireNonNull(originTable, "originTable is null"); + requireNonNull(originColumnName, "originColumnName is null"); requireNonNull(aliased, "aliased is null"); this.relationAlias = relationAlias; @@ -79,6 +81,7 @@ public Field(Optional relationAlias, Optional name, Type this.type = type; this.hidden = hidden; this.originTable = originTable; + this.originColumnName = originColumnName; this.aliased = aliased; } @@ -87,6 +90,11 @@ public Optional getOriginTable() return originTable; } + public Optional getOriginColumnName() + { + return originColumnName; + } + public Optional getRelationAlias() { return relationAlias; diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/QueryExplainer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/QueryExplainer.java index ff3987942dd31..d078308b08307 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/QueryExplainer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/QueryExplainer.java @@ -17,12 +17,12 @@ import com.facebook.presto.cost.CostCalculator; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.execution.DataDefinitionTask; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.LogicalPlanner; -import com.facebook.presto.sql.planner.NodePartitioningManager; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.PlanFragmenter; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; @@ -52,7 +52,6 @@ public class QueryExplainer private final List planOptimizers; private final PlanFragmenter planFragmenter; private final Metadata metadata; - private final NodePartitioningManager nodePartitioningManager; private final AccessControl accessControl; private final SqlParser sqlParser; private final StatsCalculator statsCalculator; @@ -64,7 +63,6 @@ public QueryExplainer( PlanOptimizers planOptimizers, PlanFragmenter planFragmenter, Metadata metadata, - NodePartitioningManager nodePartitioningManager, AccessControl accessControl, SqlParser sqlParser, StatsCalculator statsCalculator, @@ -75,7 +73,6 @@ public QueryExplainer( planOptimizers.get(), planFragmenter, metadata, - nodePartitioningManager, accessControl, sqlParser, statsCalculator, @@ -87,7 +84,6 @@ public QueryExplainer( List planOptimizers, PlanFragmenter planFragmenter, Metadata metadata, - NodePartitioningManager nodePartitioningManager, AccessControl accessControl, SqlParser sqlParser, StatsCalculator statsCalculator, @@ -97,7 +93,6 @@ public QueryExplainer( this.planOptimizers = requireNonNull(planOptimizers, "planOptimizers is null"); this.planFragmenter = requireNonNull(planFragmenter, "planFragmenter is null"); this.metadata = requireNonNull(metadata, "metadata is null"); - this.nodePartitioningManager = requireNonNull(nodePartitioningManager, "nodePartitioningManager is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); @@ -105,13 +100,13 @@ public QueryExplainer( this.dataDefinitionTask = ImmutableMap.copyOf(requireNonNull(dataDefinitionTask, "dataDefinitionTask is null")); } - public Analysis analyze(Session session, Statement statement, List parameters) + public Analysis analyze(Session session, Statement statement, List parameters, WarningCollector warningCollector) { - Analyzer analyzer = new Analyzer(session, metadata, sqlParser, accessControl, Optional.of(this), parameters); + Analyzer analyzer = new Analyzer(session, metadata, sqlParser, accessControl, Optional.of(this), parameters, warningCollector); return analyzer.analyze(statement); } - public String getPlan(Session session, Statement statement, Type planType, List parameters) + public String getPlan(Session session, Statement statement, Type planType, List parameters, WarningCollector warningCollector) { DataDefinitionTask task = dataDefinitionTask.get(statement.getClass()); if (task != null) { @@ -120,13 +115,13 @@ public String getPlan(Session session, Statement statement, Type planType, List< switch (planType) { case LOGICAL: - Plan plan = getLogicalPlan(session, statement, parameters); - return PlanPrinter.textLogicalPlan(plan.getRoot(), plan.getTypes(), metadata.getFunctionRegistry(), statsCalculator, costCalculator, session, 0, false); + Plan plan = getLogicalPlan(session, statement, parameters, warningCollector); + return PlanPrinter.textLogicalPlan(plan.getRoot(), plan.getTypes(), metadata.getFunctionRegistry(), plan.getStatsAndCosts(), session, 0, false); case DISTRIBUTED: - SubPlan subPlan = getDistributedPlan(session, statement, parameters); - return PlanPrinter.textDistributedPlan(subPlan, metadata.getFunctionRegistry(), statsCalculator, costCalculator, session, false); + SubPlan subPlan = getDistributedPlan(session, statement, parameters, warningCollector); + return PlanPrinter.textDistributedPlan(subPlan, metadata.getFunctionRegistry(), session, false); case IO: - return IOPlanPrinter.textIOPlan(getLogicalPlan(session, statement, parameters).getRoot(), metadata, session); + return IOPlanPrinter.textIOPlan(getLogicalPlan(session, statement, parameters, warningCollector).getRoot(), metadata, session); } throw new IllegalArgumentException("Unhandled plan type: " + planType); } @@ -136,7 +131,7 @@ private static String explainTask(Statement statement, Dat return task.explain((T) statement, parameters); } - public String getGraphvizPlan(Session session, Statement statement, Type planType, List parameters) + public String getGraphvizPlan(Session session, Statement statement, Type planType, List parameters, WarningCollector warningCollector) { DataDefinitionTask task = dataDefinitionTask.get(statement.getClass()); if (task != null) { @@ -146,16 +141,16 @@ public String getGraphvizPlan(Session session, Statement statement, Type planTyp switch (planType) { case LOGICAL: - Plan plan = getLogicalPlan(session, statement, parameters); + Plan plan = getLogicalPlan(session, statement, parameters, warningCollector); return PlanPrinter.graphvizLogicalPlan(plan.getRoot(), plan.getTypes()); case DISTRIBUTED: - SubPlan subPlan = getDistributedPlan(session, statement, parameters); + SubPlan subPlan = getDistributedPlan(session, statement, parameters, warningCollector); return PlanPrinter.graphvizDistributedPlan(subPlan); } throw new IllegalArgumentException("Unhandled plan type: " + planType); } - public String getJsonPlan(Session session, Statement statement, Type planType, List parameters) + public String getJsonPlan(Session session, Statement statement, Type planType, List parameters, WarningCollector warningCollector) { DataDefinitionTask task = dataDefinitionTask.get(statement.getClass()); if (task != null) { @@ -165,28 +160,28 @@ public String getJsonPlan(Session session, Statement statement, Type planType, L switch (planType) { case IO: - Plan plan = getLogicalPlan(session, statement, parameters); + Plan plan = getLogicalPlan(session, statement, parameters, warningCollector); return textIOPlan(plan.getRoot(), metadata, session); default: throw new PrestoException(NOT_SUPPORTED, format("Unsupported explain plan type %s for JSON format", planType)); } } - public Plan getLogicalPlan(Session session, Statement statement, List parameters) + public Plan getLogicalPlan(Session session, Statement statement, List parameters, WarningCollector warningCollector) { // analyze statement - Analysis analysis = analyze(session, statement, parameters); + Analysis analysis = analyze(session, statement, parameters, warningCollector); PlanNodeIdAllocator idAllocator = new PlanNodeIdAllocator(); // plan statement - LogicalPlanner logicalPlanner = new LogicalPlanner(session, planOptimizers, idAllocator, metadata, sqlParser); + LogicalPlanner logicalPlanner = new LogicalPlanner(session, planOptimizers, idAllocator, metadata, sqlParser, statsCalculator, costCalculator, warningCollector); return logicalPlanner.plan(analysis); } - private SubPlan getDistributedPlan(Session session, Statement statement, List parameters) + private SubPlan getDistributedPlan(Session session, Statement statement, List parameters, WarningCollector warningCollector) { - Plan plan = getLogicalPlan(session, statement, parameters); - return planFragmenter.createSubPlans(session, metadata, nodePartitioningManager, plan, false); + Plan plan = getLogicalPlan(session, statement, parameters, warningCollector); + return planFragmenter.createSubPlans(session, plan, false); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/RelationType.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/RelationType.java index c6bfab2b6815a..e21d5c8b10863 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/RelationType.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/RelationType.java @@ -180,6 +180,7 @@ public RelationType withAlias(String relationAlias, List columnAliases) field.getType(), field.isHidden(), field.getOriginTable(), + field.getOriginColumnName(), field.isAliased())); } else if (!field.isHidden()) { @@ -191,6 +192,7 @@ else if (!field.isHidden()) { field.getType(), false, field.getOriginTable(), + field.getOriginColumnName(), field.isAliased())); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java index 03185d0c967ae..6de62fad2d21b 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionKind; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.OperatorNotFoundException; @@ -204,6 +205,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.getLast; import static com.google.common.collect.Iterables.transform; import static java.lang.Math.toIntExact; @@ -218,19 +220,22 @@ class StatementAnalyzer private final Session session; private final SqlParser sqlParser; private final AccessControl accessControl; + private final WarningCollector warningCollector; public StatementAnalyzer( Analysis analysis, Metadata metadata, SqlParser sqlParser, AccessControl accessControl, - Session session) + Session session, + WarningCollector warningCollector) { this.analysis = requireNonNull(analysis, "analysis is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.session = requireNonNull(session, "session is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } public Scope analyze(Node node, Scope outerQueryScope) @@ -240,13 +245,7 @@ public Scope analyze(Node node, Scope outerQueryScope) public Scope analyze(Node node, Optional outerQueryScope) { - return new Visitor(outerQueryScope).process(node, Optional.empty()); - } - - private void analyzeWhere(Node node, Scope outerQueryScope, Expression predicate) - { - Visitor visitor = new Visitor(Optional.of(outerQueryScope)); - visitor.analyzeWhere(node, outerQueryScope, predicate); + return new Visitor(outerQueryScope, warningCollector).process(node, Optional.empty()); } /** @@ -258,10 +257,12 @@ private class Visitor extends DefaultTraversalVisitor> { private final Optional outerQueryScope; + private final WarningCollector warningCollector; - private Visitor(Optional outerQueryScope) + private Visitor(Optional outerQueryScope, WarningCollector warningCollector) { this.outerQueryScope = requireNonNull(outerQueryScope, "outerQueryScope is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } public Scope process(Node node, Optional scope) @@ -388,7 +389,8 @@ protected Scope visitDelete(Delete node, Optional scope) metadata, sqlParser, new AllowAllAccessControl(), - session); + session, + WarningCollector.NOOP); Scope tableScope = analyzer.analyze(table, scope); node.getWhere().ifPresent(where -> analyzeWhere(node, tableScope, where)); @@ -456,7 +458,7 @@ protected Scope visitCreateView(CreateView node, Optional scope) QualifiedObjectName viewName = createQualifiedObjectName(session, node, node.getName()); // analyze the query that creates the view - StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session); + StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, WarningCollector.NOOP); Scope queryScope = analyzer.analyze(node.getQuery(), scope); @@ -515,7 +517,7 @@ protected Scope visitCreateTable(CreateTable node, Optional scope) protected Scope visitProperty(Property node, Optional scope) { // Property value expressions must be constant - createConstantAnalyzer(metadata, session, analysis.getParameters(), analysis.isDescribe()) + createConstantAnalyzer(metadata, session, analysis.getParameters(), WarningCollector.NOOP, analysis.isDescribe()) .analyze(node.getValue(), createScope(scope)); return createAndAssignScope(node, scope); } @@ -727,7 +729,7 @@ else if (expressionType instanceof MapType) { @Override protected Scope visitLateral(Lateral node, Optional scope) { - StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session); + StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, WarningCollector.NOOP); Scope queryScope = analyzer.analyze(node.getQuery(), scope); return createAndAssignScope(node, scope, queryScope.getRelationType()); } @@ -762,6 +764,7 @@ protected Scope visitTable(Table table, Optional scope) inputField.getType(), false, inputField.getOriginTable(), + inputField.getOriginColumnName(), inputField.isAliased())); field++; @@ -777,6 +780,7 @@ protected Scope visitTable(Table table, Optional scope) field.getType(), field.isHidden(), field.getOriginTable(), + field.getOriginColumnName(), field.isAliased())) .collect(toImmutableList()); } @@ -825,6 +829,7 @@ protected Scope visitTable(Table table, Optional scope) column.getType(), false, Optional.of(name), + Optional.of(column.getName()), false)) .collect(toImmutableList()); @@ -855,6 +860,7 @@ protected Scope visitTable(Table table, Optional scope) column.getType(), column.isHidden(), Optional.of(name), + Optional.of(column.getName()), false); fields.add(field); ColumnHandle columnHandle = columnHandles.get(column.getName()); @@ -907,6 +913,7 @@ protected Scope visitSampledRelation(SampledRelation relation, Optional s TypeProvider.empty(), relation.getSamplePercentage(), analysis.getParameters(), + WarningCollector.NOOP, analysis.isDescribe()); ExpressionInterpreter samplePercentageEval = expressionOptimizer(relation.getSamplePercentage(), metadata, session, expressionTypes); @@ -935,7 +942,7 @@ protected Scope visitSampledRelation(SampledRelation relation, Optional s @Override protected Scope visitTableSubquery(TableSubquery node, Optional scope) { - StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session); + StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, WarningCollector.NOOP); Scope queryScope = analyzer.analyze(node.getQuery(), scope); return createAndAssignScope(node, scope, queryScope.getRelationType()); } @@ -951,7 +958,7 @@ protected Scope visitQuerySpecification(QuerySpecification node, Optional node.getWhere().ifPresent(where -> analyzeWhere(node, sourceScope, where)); List outputExpressions = analyzeSelect(node, sourceScope); - List> groupByExpressions = analyzeGroupBy(node, sourceScope, outputExpressions); + List groupByExpressions = analyzeGroupBy(node, sourceScope, outputExpressions); analyzeHaving(node, sourceScope); Scope outputScope = computeAndAssignOutputScope(node, scope, sourceScope); @@ -973,7 +980,7 @@ protected Scope visitQuerySpecification(QuerySpecification node, Optional List aggregations = analyzeAggregations(node, sourceScope, orderByScope, groupByExpressions, sourceExpressions, orderByExpressions); analyzeWindowFunctions(node, outputExpressions, orderByExpressions); - if (!groupByExpressions.isEmpty() && node.getOrderBy().isPresent()) { + if (analysis.isAggregation(node) && node.getOrderBy().isPresent()) { // Create a different scope for ORDER BY expressions when aggregation is present. // This is because planner requires scope in order to resolve names against fields. // Original ORDER BY scope "sees" FROM query fields. However, during planning @@ -1043,6 +1050,7 @@ protected Scope visitSetOperation(SetOperation node, Optional scope) outputFieldTypes[i], oldField.isHidden(), oldField.getOriginTable(), + oldField.getOriginColumnName(), oldField.isAliased()); } @@ -1477,106 +1485,103 @@ else if (element instanceof GroupingSets) { } } - private List> analyzeGroupBy(QuerySpecification node, Scope scope, List outputExpressions) + private List analyzeGroupBy(QuerySpecification node, Scope scope, List outputExpressions) { - List> computedGroupingSets = ImmutableList.of(); // empty list = no aggregations - if (node.getGroupBy().isPresent()) { + ImmutableList.Builder> cubes = ImmutableList.builder(); + ImmutableList.Builder> rollups = ImmutableList.builder(); + ImmutableList.Builder>> sets = ImmutableList.builder(); + ImmutableList.Builder complexExpressions = ImmutableList.builder(); + ImmutableList.Builder groupingExpressions = ImmutableList.builder(); + checkGroupingSetsCount(node.getGroupBy().get()); + for (GroupingElement groupingElement : node.getGroupBy().get().getGroupingElements()) { + if (groupingElement instanceof SimpleGroupBy) { + for (Expression column : groupingElement.getExpressions()) { + // simple GROUP BY expressions allow ordinals or arbitrary expressions + if (column instanceof LongLiteral) { + long ordinal = ((LongLiteral) column).getValue(); + if (ordinal < 1 || ordinal > outputExpressions.size()) { + throw new SemanticException(INVALID_ORDINAL, column, "GROUP BY position %s is not in select list", ordinal); + } + + column = outputExpressions.get(toIntExact(ordinal - 1)); + } + else { + analyzeExpression(column, scope); + } - node.getGroupBy().get().getGroupingElements().stream() - .filter(element -> !(element instanceof SimpleGroupBy)) // Only SimpleGroupBy allows complex expressions (a deviation from the SQL specification) - .flatMap(element -> element.getExpressions().stream()) - .forEach(column -> { + FieldId field = analysis.getColumnReferenceFields().get(NodeRef.of(column)); + if (field != null) { + sets.add(ImmutableList.of(ImmutableSet.of(field))); + } + else { + Analyzer.verifyNoAggregateWindowOrGroupingFunctions(metadata.getFunctionRegistry(), column, "GROUP BY clause"); + analysis.recordSubqueries(node, analyzeExpression(column, scope)); + complexExpressions.add(column); + } + + groupingExpressions.add(column); + } + } + else { + for (Expression column : groupingElement.getExpressions()) { analyzeExpression(column, scope); if (!analysis.getColumnReferences().contains(NodeRef.of(column))) { throw new SemanticException(SemanticErrorCode.MUST_BE_COLUMN_REFERENCE, column, "GROUP BY expression must be a column reference: %s", column); } - }); - List>> enumeratedGroupingSets = node.getGroupBy().get().getGroupingElements().stream() - .map(GroupingElement::enumerateGroupingSets) - .collect(toImmutableList()); - - // compute cross product of enumerated grouping sets, if there are any - computedGroupingSets = computeGroupingSetsCrossProduct(enumeratedGroupingSets, node.getGroupBy().get().isDistinct()); - checkState(!computedGroupingSets.isEmpty(), "computed grouping sets cannot be empty"); - } - else if (hasAggregates(node)) { - // if there are aggregates, but no group by, create a grand total grouping set (global aggregation) - computedGroupingSets = ImmutableList.of(ImmutableSet.of()); - } - - List> analyzedGroupingSets = computedGroupingSets.stream() - .map(groupingSet -> analyzeGroupingColumns(groupingSet, node, scope, outputExpressions)) - .collect(toImmutableList()); - - analysis.setGroupingSets(node, analyzedGroupingSets); - return analyzedGroupingSets; - } + groupingExpressions.add(column); + } - private List> computeGroupingSetsCrossProduct(List>> enumeratedGroupingSets, boolean isDistinct) - { - checkState(!enumeratedGroupingSets.isEmpty(), "enumeratedGroupingSets cannot be empty"); + if (groupingElement instanceof Cube) { + Set cube = groupingElement.getExpressions().stream() + .map(NodeRef::of) + .map(analysis.getColumnReferenceFields()::get) + .collect(toImmutableSet()); - List> groupingSetsCrossProduct = new ArrayList<>(); - enumeratedGroupingSets.get(0) - .stream() - .map(ImmutableSet::copyOf) - .forEach(groupingSetsCrossProduct::add); + cubes.add(cube); + } + else if (groupingElement instanceof Rollup) { + List rollup = groupingElement.getExpressions().stream() + .map(NodeRef::of) + .map(analysis.getColumnReferenceFields()::get) + .collect(toImmutableList()); - for (int i = 1; i < enumeratedGroupingSets.size(); i++) { - List> groupingSets = enumeratedGroupingSets.get(i); - List> oldGroupingSetsCrossProduct = ImmutableList.copyOf(groupingSetsCrossProduct); - groupingSetsCrossProduct.clear(); - for (Set existingSet : oldGroupingSetsCrossProduct) { - for (Set groupingSet : groupingSets) { - Set concatenatedSet = ImmutableSet.builder() - .addAll(existingSet) - .addAll(groupingSet) - .build(); - groupingSetsCrossProduct.add(concatenatedSet); + rollups.add(rollup); + } + else if (groupingElement instanceof GroupingSets) { + List> groupingSets = ((GroupingSets) groupingElement).getSets().stream() + .map(set -> set.stream() + .map(NodeRef::of) + .map(analysis.getColumnReferenceFields()::get) + .collect(toImmutableSet())) + .collect(toImmutableList()); + + sets.add(groupingSets); + } } } - } - - if (isDistinct) { - return ImmutableList.copyOf(ImmutableSet.copyOf(groupingSetsCrossProduct)); - } - - return groupingSetsCrossProduct; - } - - private List analyzeGroupingColumns(Set groupingColumns, QuerySpecification node, Scope scope, List outputExpressions) - { - ImmutableList.Builder groupingColumnsBuilder = ImmutableList.builder(); - for (Expression groupingColumn : groupingColumns) { - // first, see if this is an ordinal - Expression groupByExpression; - if (groupingColumn instanceof LongLiteral) { - long ordinal = ((LongLiteral) groupingColumn).getValue(); - if (ordinal < 1 || ordinal > outputExpressions.size()) { - throw new SemanticException(INVALID_ORDINAL, groupingColumn, "GROUP BY position %s is not in select list", ordinal); + List expressions = groupingExpressions.build(); + for (Expression expression : expressions) { + Type type = analysis.getType(expression); + if (!type.isComparable()) { + throw new SemanticException(TYPE_MISMATCH, node, "%s is not comparable, and therefore cannot be used in GROUP BY", type); } - - groupByExpression = outputExpressions.get(toIntExact(ordinal - 1)); - } - else { - ExpressionAnalysis expressionAnalysis = analyzeExpression(groupingColumn, scope); - analysis.recordSubqueries(node, expressionAnalysis); - groupByExpression = groupingColumn; } - Analyzer.verifyNoAggregateWindowOrGroupingFunctions(metadata.getFunctionRegistry(), groupByExpression, "GROUP BY clause"); - Type type = analysis.getType(groupByExpression); - if (!type.isComparable()) { - throw new SemanticException(TYPE_MISMATCH, node, "%s is not comparable, and therefore cannot be used in GROUP BY", type); - } + analysis.setGroupByExpressions(node, expressions); + analysis.setGroupingSets(node, new Analysis.GroupingSetAnalysis(cubes.build(), rollups.build(), sets.build(), complexExpressions.build())); - groupingColumnsBuilder.add(groupByExpression); + return expressions; } - return groupingColumnsBuilder.build(); + + if (hasAggregates(node)) { + analysis.setGroupByExpressions(node, ImmutableList.of()); + } + + return ImmutableList.of(); } private Scope computeAndAssignOutputScope(QuerySpecification node, Optional scope, Scope sourceScope) @@ -1589,7 +1594,7 @@ private Scope computeAndAssignOutputScope(QuerySpecification node, Optional starPrefix = ((AllColumns) item).getPrefix(); for (Field field : sourceScope.getRelationType().resolveFieldsWithPrefix(starPrefix)) { - outputFields.add(Field.newUnqualified(field.getName(), field.getType(), field.getOriginTable(), false)); + outputFields.add(Field.newUnqualified(field.getName(), field.getType(), field.getOriginTable(), field.getOriginColumnName(), false)); } } else if (item instanceof SingleColumn) { @@ -1599,6 +1604,7 @@ else if (item instanceof SingleColumn) { Optional field = column.getAlias(); Optional originTable = Optional.empty(); + Optional originColumn = Optional.empty(); QualifiedName name = null; if (expression instanceof Identifier) { @@ -1612,6 +1618,7 @@ else if (expression instanceof DereferenceExpression) { List matchingFields = sourceScope.getRelationType().resolveFields(name); if (!matchingFields.isEmpty()) { originTable = matchingFields.get(0).getOriginTable(); + originColumn = matchingFields.get(0).getOriginColumnName(); } } @@ -1621,7 +1628,7 @@ else if (expression instanceof DereferenceExpression) { } } - outputFields.add(Field.newUnqualified(field.map(Identifier::getValue), analysis.getType(expression), originTable, column.getAlias().isPresent())); // TODO don't use analysis as a side-channel. Use outputExpressions to look up the type + outputFields.add(Field.newUnqualified(field.map(Identifier::getValue), analysis.getType(expression), originTable, originColumn, column.getAlias().isPresent())); // TODO don't use analysis as a side-channel. Use outputExpressions to look up the type } else { throw new IllegalArgumentException("Unsupported SelectItem type: " + item.getClass().getName()); @@ -1642,16 +1649,14 @@ private Scope computeAndAssignOrderByScope(OrderBy node, Scope sourceScope, Scop return orderByScope; } - private Scope computeAndAssignOrderByScopeWithAggregation(OrderBy node, Scope sourceScope, Scope outputScope, List aggregations, List> groupByExpressions, List groupingOperations) + private Scope computeAndAssignOrderByScopeWithAggregation(OrderBy node, Scope sourceScope, Scope outputScope, List aggregations, List groupByExpressions, List groupingOperations) { // This scope is only used for planning. When aggregation is present then // only output fields, groups and aggregation expressions should be visible from ORDER BY expression - ImmutableList.Builder orderByAggregationExpressionsBuilder = ImmutableList.builder(); - groupByExpressions.stream() - .flatMap(List::stream) - .forEach(orderByAggregationExpressionsBuilder::add); - orderByAggregationExpressionsBuilder.addAll(aggregations); - orderByAggregationExpressionsBuilder.addAll(groupingOperations); + ImmutableList.Builder orderByAggregationExpressionsBuilder = ImmutableList.builder() + .addAll(groupByExpressions) + .addAll(aggregations) + .addAll(groupingOperations); // Don't add aggregate complex expressions that contains references to output column because the names would clash in TranslationMap during planning. List orderByExpressionsReferencingOutputScope = AstUtils.preOrder(node) @@ -1791,7 +1796,7 @@ private List analyzeAggregations( QuerySpecification node, Scope sourceScope, Optional orderByScope, - List> groupingSets, + List groupByExpressions, List outputExpressions, List orderByExpressions) { @@ -1800,15 +1805,13 @@ private List analyzeAggregations( List aggregates = extractAggregateFunctions(Iterables.concat(outputExpressions, orderByExpressions), metadata.getFunctionRegistry()); analysis.setAggregates(node, aggregates); - // is this an aggregation query? - if (!groupingSets.isEmpty()) { + if (analysis.isAggregation(node)) { // ensure SELECT, ORDER BY and HAVING are constant with respect to group // e.g, these are all valid expressions: // SELECT f(a) GROUP BY a // SELECT f(a + 1) GROUP BY a + 1 // SELECT a + sum(b) GROUP BY a - ImmutableList distinctGroupingColumns = groupingSets.stream() - .flatMap(Collection::stream) + List distinctGroupingColumns = groupByExpressions.stream() .distinct() .collect(toImmutableList()); @@ -1873,7 +1876,7 @@ private RelationType analyzeView(Query query, QualifiedObjectName name, Optional .setStartTime(session.getStartTime()) .build(); - StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, viewAccessControl, viewSession); + StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, viewAccessControl, viewSession, WarningCollector.NOOP); Scope queryScope = analyzer.analyze(query, Scope.create()); return queryScope.getRelationType().withAlias(name.getObjectName(), null); } @@ -1921,7 +1924,8 @@ private ExpressionAnalysis analyzeExpression(Expression expression, Scope scope) sqlParser, scope, analysis, - expression); + expression, + WarningCollector.NOOP); } private List descriptorToFields(Scope scope) @@ -2039,7 +2043,8 @@ private List analyzeOrderBy(Node node, List sortItems, Sco sqlParser, orderByScope, analysis, - expression); + expression, + WarningCollector.NOOP); analysis.recordSubqueries(node, expressionAnalysis); Type type = analysis.getType(expression); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionCompiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionCompiler.java index f6e4296bb7a79..0edad777a48dd 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionCompiler.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionCompiler.java @@ -20,6 +20,7 @@ import com.facebook.presto.operator.project.PageProjection; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.relational.RowExpression; +import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -34,6 +35,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; import java.util.function.Supplier; import static com.facebook.presto.spi.StandardErrorCode.COMPILER_ERROR; @@ -89,6 +91,15 @@ public Supplier compileCursorProcessor(Optional } public Supplier compilePageProcessor(Optional filter, List projections, Optional classNameSuffix) + { + return compilePageProcessor(filter, projections, classNameSuffix, OptionalInt.empty()); + } + + private Supplier compilePageProcessor( + Optional filter, + List projections, + Optional classNameSuffix, + OptionalInt initialBatchSize) { Optional> filterFunctionSupplier = filter.map(expression -> pageFunctionCompiler.compileFilter(expression, classNameSuffix)); List> pageProjectionSuppliers = projections.stream() @@ -100,7 +111,7 @@ public Supplier compilePageProcessor(Optional filt List pageProjections = pageProjectionSuppliers.stream() .map(Supplier::get) .collect(toImmutableList()); - return new PageProcessor(filterFunction, pageProjections); + return new PageProcessor(filterFunction, pageProjections, initialBatchSize); }; } @@ -109,6 +120,12 @@ public Supplier compilePageProcessor(Optional filt return compilePageProcessor(filter, projections, Optional.empty()); } + @VisibleForTesting + public Supplier compilePageProcessor(Optional filter, List projections, int initialBatchSize) + { + return compilePageProcessor(filter, projections, Optional.empty(), OptionalInt.of(initialBatchSize)); + } + private Class compile(Optional filter, List projections, BodyCompiler bodyCompiler, Class superType) { // create filter and project page iterator class diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionProfiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionProfiler.java new file mode 100644 index 0000000000000..3a6cb7a7358a3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/ExpressionProfiler.java @@ -0,0 +1,76 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.gen; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Ticker; +import io.airlift.units.Duration; + +import static com.facebook.presto.execution.executor.PrioritizedSplitRunner.SPLIT_RUN_QUANTA; +import static com.google.common.base.Ticker.systemTicker; +import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +public class ExpressionProfiler +{ + private static final Duration EXPENSIVE_EXPRESSION_THRESHOLD = SPLIT_RUN_QUANTA; + private static final int NOT_INITALIZED = -1; + + private final Ticker ticker; + private final double expensiveExpressionThresholdNanos; + private double totalExecutionTimeNanos; + private int samples; + private long previousTimestamp = NOT_INITALIZED; + private boolean isExpressionExpensive = true; + + public ExpressionProfiler() + { + this(systemTicker(), EXPENSIVE_EXPRESSION_THRESHOLD); + } + + @VisibleForTesting + public ExpressionProfiler(Ticker ticker, Duration expensiveExpressionThreshold) + { + requireNonNull(ticker, "ticker is null"); + requireNonNull(expensiveExpressionThreshold, "expensiveExpressionThreshold is null"); + this.expensiveExpressionThresholdNanos = expensiveExpressionThreshold.getValue(NANOSECONDS); + this.ticker = ticker; + } + + public void start() + { + previousTimestamp = ticker.read(); + } + + public void stop(int batchSize) + { + verify(previousTimestamp != NOT_INITALIZED, "start() is not called"); + verify(batchSize > 0, "batchSize must be positive"); + + long now = ticker.read(); + long delta = now - previousTimestamp; + totalExecutionTimeNanos += delta; + samples += batchSize; + if ((totalExecutionTimeNanos / samples) < expensiveExpressionThresholdNanos) { + isExpressionExpensive = false; + } + previousTimestamp = NOT_INITALIZED; + } + + public boolean isExpressionExpensive() + { + return isExpressionExpensive; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/InCodeGenerator.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/InCodeGenerator.java index c586c52a21757..4765f8fbd4b5b 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/InCodeGenerator.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/InCodeGenerator.java @@ -16,6 +16,7 @@ import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; +import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.type.BigintType; import com.facebook.presto.spi.type.DateType; import com.facebook.presto.spi.type.IntegerType; @@ -32,7 +33,7 @@ import io.airlift.bytecode.Scope; import io.airlift.bytecode.Variable; import io.airlift.bytecode.control.IfStatement; -import io.airlift.bytecode.control.LookupSwitch; +import io.airlift.bytecode.control.SwitchStatement.SwitchBuilder; import io.airlift.bytecode.instruction.LabelNode; import java.lang.invoke.MethodHandle; @@ -41,18 +42,17 @@ import java.util.Map; import java.util.Set; -import static com.facebook.presto.metadata.Signature.internalOperator; -import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.spi.function.OperatorType.HASH_CODE; -import static com.facebook.presto.spi.type.BigintType.BIGINT; -import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.function.OperatorType.INDETERMINATE; import static com.facebook.presto.sql.gen.BytecodeUtils.ifWasNullPopAndGoto; import static com.facebook.presto.sql.gen.BytecodeUtils.invoke; import static com.facebook.presto.sql.gen.BytecodeUtils.loadConstant; import static com.facebook.presto.util.FastutilSetHelper.toFastutilHashSet; -import static io.airlift.bytecode.control.LookupSwitch.lookupSwitchBuilder; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Throwables.throwIfUnchecked; import static io.airlift.bytecode.expression.BytecodeExpressions.constantFalse; import static io.airlift.bytecode.expression.BytecodeExpressions.constantTrue; +import static io.airlift.bytecode.expression.BytecodeExpressions.invokeStatic; import static io.airlift.bytecode.instruction.JumpInstruction.jump; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; @@ -109,17 +109,20 @@ static SwitchGenerationCase checkSwitchGenerationCase(Type type, List arguments) { - BytecodeNode value = generatorContext.generate(arguments.get(0)); - List values = arguments.subList(1, arguments.size()); + // empty IN statements are not allowed by the standard, and not possible here + // the implementation assumes this condition is always met + checkArgument(values.size() > 0, "values must not be empty"); Type type = arguments.get(0).getType(); Class javaType = type.getJavaType(); SwitchGenerationCase switchGenerationCase = checkSwitchGenerationCase(type, values); - Signature hashCodeSignature = internalOperator(HASH_CODE, BIGINT, ImmutableList.of(type)); + Signature hashCodeSignature = generatorContext.getRegistry().resolveOperator(HASH_CODE, ImmutableList.of(type)); MethodHandle hashCodeFunction = generatorContext.getRegistry().getScalarFunctionImplementation(hashCodeSignature).getMethodHandle(); + Signature isIndeterminateSignature = generatorContext.getRegistry().resolveOperator(INDETERMINATE, ImmutableList.of(type)); + ScalarFunctionImplementation isIndeterminateFunction = generatorContext.getRegistry().getScalarFunctionImplementation(isIndeterminateSignature); ImmutableListMultimap.Builder hashBucketsBuilder = ImmutableListMultimap.builder(); ImmutableList.Builder defaultBucket = ImmutableList.builder(); @@ -128,7 +131,7 @@ public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorCon for (RowExpression testValue : values) { BytecodeNode testBytecode = generatorContext.generate(testValue); - if (testValue instanceof ConstantExpression && ((ConstantExpression) testValue).getValue() != null) { + if (isDeterminateConstant(testValue, isIndeterminateFunction.getMethodHandle())) { ConstantExpression constant = (ConstantExpression) testValue; Object object = constant.getValue(); switch (switchGenerationCase) { @@ -163,51 +166,56 @@ public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorCon LabelNode defaultLabel = new LabelNode("default"); Scope scope = generatorContext.getScope(); + Variable value = scope.createTempVariable(javaType); BytecodeNode switchBlock; - BytecodeBlock switchCaseBlocks = new BytecodeBlock(); - LookupSwitch.LookupSwitchBuilder switchBuilder = lookupSwitchBuilder(); + Variable expression = scope.createTempVariable(int.class); + SwitchBuilder switchBuilder = new SwitchBuilder().expression(expression); + switch (switchGenerationCase) { case DIRECT_SWITCH: // A white-list is used to select types eligible for DIRECT_SWITCH. // For these types, it's safe to not use presto HASH_CODE and EQUAL operator. for (Object constantValue : constantValues) { - switchBuilder.addCase(toIntExact((Long) constantValue), match); + switchBuilder.addCase(toIntExact((Long) constantValue), jump(match)); } - switchBuilder.defaultCase(defaultLabel); + switchBuilder.defaultCase(jump(defaultLabel)); switchBlock = new BytecodeBlock() .comment("lookupSwitch())") - .dup(javaType) .append(new IfStatement() - .condition(new BytecodeBlock() - .dup(javaType) - .invokeStatic(InCodeGenerator.class, "isInteger", boolean.class, long.class)) + .condition(invokeStatic(InCodeGenerator.class, "isInteger", boolean.class, value)) .ifFalse(new BytecodeBlock() - .pop(javaType) .gotoLabel(defaultLabel))) - .longToInt() + .append(expression.set(value.cast(int.class))) .append(switchBuilder.build()); break; case HASH_SWITCH: for (Map.Entry> bucket : hashBuckets.asMap().entrySet()) { - LabelNode label = new LabelNode("inHash" + bucket.getKey()); - switchBuilder.addCase(bucket.getKey(), label); Collection testValues = bucket.getValue(); - - BytecodeBlock caseBlock = buildInCase(generatorContext, scope, type, label, match, defaultLabel, testValues, false); - switchCaseBlocks.append(caseBlock.setDescription("case " + bucket.getKey())); + BytecodeBlock caseBlock = buildInCase( + generatorContext, + scope, + type, + match, + defaultLabel, + value, + testValues, + false, + isIndeterminateSignature, + isIndeterminateFunction); + switchBuilder.addCase(bucket.getKey(), caseBlock); } - switchBuilder.defaultCase(defaultLabel); + switchBuilder.defaultCase(jump(defaultLabel)); Binding hashCodeBinding = generatorContext .getCallSiteBinder() .bind(hashCodeFunction); switchBlock = new BytecodeBlock() .comment("lookupSwitch(hashCode())") - .dup(javaType) + .getVariable(value) .append(invoke(hashCodeBinding, hashCodeSignature)) .invokeStatic(Long.class, "hashCode", int.class, long.class) - .append(switchBuilder.build()) - .append(switchCaseBlocks); + .putVariable(expression) + .append(switchBuilder.build()); break; case SET_CONTAINS: Set constantValuesSet = toFastutilHashSet(constantValues, type, registry); @@ -218,7 +226,7 @@ public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorCon .append(new IfStatement() .condition(new BytecodeBlock() .comment("value") - .dup(javaType) + .getVariable(value) .comment("set") .append(loadConstant(constant)) // TODO: use invokeVirtual on the set instead. This requires swapping the two elements in the stack @@ -229,19 +237,31 @@ public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorCon throw new IllegalArgumentException("Not supported switch generation case: " + switchGenerationCase); } - BytecodeBlock defaultCaseBlock = buildInCase(generatorContext, scope, type, defaultLabel, match, noMatch, defaultBucket.build(), true).setDescription("default"); + BytecodeBlock defaultCaseBlock = buildInCase( + generatorContext, + scope, + type, + match, + noMatch, + value, + defaultBucket.build(), + true, + isIndeterminateSignature, + isIndeterminateFunction) + .setDescription("default"); BytecodeBlock block = new BytecodeBlock() .comment("IN") - .append(value) + .append(generatorContext.generate(arguments.get(0))) .append(ifWasNullPopAndGoto(scope, end, boolean.class, javaType)) + .putVariable(value) .append(switchBlock) + .visitLabel(defaultLabel) .append(defaultCaseBlock); BytecodeBlock matchBlock = new BytecodeBlock() .setDescription("match") .visitLabel(match) - .pop(javaType) .append(generatorContext.wasNull().set(constantFalse())) .push(true) .gotoLabel(end); @@ -250,7 +270,6 @@ public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorCon BytecodeBlock noMatchBlock = new BytecodeBlock() .setDescription("noMatch") .visitLabel(noMatch) - .pop(javaType) .push(false) .gotoLabel(end); block.append(noMatchBlock); @@ -265,22 +284,24 @@ public static boolean isInteger(long value) return value == (int) value; } - private static BytecodeBlock buildInCase(BytecodeGeneratorContext generatorContext, + private static BytecodeBlock buildInCase( + BytecodeGeneratorContext generatorContext, Scope scope, Type type, - LabelNode caseLabel, LabelNode matchLabel, LabelNode noMatchLabel, + Variable value, Collection testValues, - boolean checkForNulls) + boolean checkForNulls, + Signature isIndeterminateSignature, + ScalarFunctionImplementation isIndeterminateFunction) { Variable caseWasNull = null; // caseWasNull is set to true the first time a null in `testValues` is encountered if (checkForNulls) { caseWasNull = scope.createTempVariable(boolean.class); } - BytecodeBlock caseBlock = new BytecodeBlock() - .visitLabel(caseLabel); + BytecodeBlock caseBlock = new BytecodeBlock(); if (checkForNulls) { caseBlock.putVariable(caseWasNull, false); @@ -292,40 +313,50 @@ private static BytecodeBlock buildInCase(BytecodeGeneratorContext generatorConte Variable wasNull = generatorContext.wasNull(); if (checkForNulls) { - elseBlock.append(wasNull.set(caseWasNull)); + // Consider following expression: "ARRAY[null] IN (ARRAY[1], ARRAY[2], ARRAY[3]) => NULL" + // All lookup values will go to the SET_CONTAINS, since neither of them is indeterminate. + // As ARRAY[null] is not among them, the code will fall through to the defaultCaseBlock. + // Since there is no values in the defaultCaseBlock, the defaultCaseBlock will return FALSE. + // That is incorrect. Doing an explicit check for indeterminate is required to correctly return NULL. + if (testValues.isEmpty()) { + elseBlock.append(new BytecodeBlock() + .append(generatorContext.generateCall(isIndeterminateSignature.getName(), isIndeterminateFunction, ImmutableList.of(value))) + .putVariable(wasNull)); + } + else { + elseBlock.append(wasNull.set(caseWasNull)); + } } elseBlock.gotoLabel(noMatchLabel); - ScalarFunctionImplementation operator = generatorContext.getRegistry().getScalarFunctionImplementation(internalOperator(EQUAL, BOOLEAN, ImmutableList.of(type, type))); - - Binding equalsFunction = generatorContext - .getCallSiteBinder() - .bind(operator.getMethodHandle()); + Signature equalsSignature = generatorContext.getRegistry().resolveOperator(OperatorType.EQUAL, ImmutableList.of(type, type)); + ScalarFunctionImplementation equalsFunction = generatorContext.getRegistry().getScalarFunctionImplementation(equalsSignature); BytecodeNode elseNode = elseBlock; for (BytecodeNode testNode : testValues) { LabelNode testLabel = new LabelNode("test"); IfStatement test = new IfStatement(); + BytecodeNode equalsCall = generatorContext.generateCall( + equalsSignature.getName(), + equalsFunction, + ImmutableList.of(value, testNode)); + test.condition() .visitLabel(testLabel) - .dup(type.getJavaType()) - .append(testNode); + .append(equalsCall); if (checkForNulls) { - IfStatement wasNullCheck = new IfStatement("if wasNull, set caseWasNull to true, clear wasNull, pop 2 values of type, and goto next test value"); + IfStatement wasNullCheck = new IfStatement("if wasNull, set caseWasNull to true, clear wasNull, pop boolean, and goto next test value"); wasNullCheck.condition(wasNull); wasNullCheck.ifTrue(new BytecodeBlock() .append(caseWasNull.set(constantTrue())) .append(wasNull.set(constantFalse())) - .pop(type.getJavaType()) - .pop(type.getJavaType()) + .pop(boolean.class) .gotoLabel(elseLabel)); test.condition().append(wasNullCheck); } - test.condition() - .append(invoke(equalsFunction, EQUAL.name())); test.ifTrue().gotoLabel(matchLabel); test.ifFalse(elseNode); @@ -336,4 +367,24 @@ private static BytecodeBlock buildInCase(BytecodeGeneratorContext generatorConte caseBlock.append(elseNode); return caseBlock; } + + private static boolean isDeterminateConstant(RowExpression expression, MethodHandle isIndeterminateFunction) + { + if (!(expression instanceof ConstantExpression)) { + return false; + } + ConstantExpression constantExpression = (ConstantExpression) expression; + Object value = constantExpression.getValue(); + boolean isNull = value == null; + if (isNull) { + return false; + } + try { + return !(boolean) isIndeterminateFunction.invoke(value, false); + } + catch (Throwable t) { + throwIfUnchecked(t); + throw new RuntimeException(t); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinCompiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinCompiler.java index 0eaeb1a30d279..b2e6bf030fcef 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinCompiler.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinCompiler.java @@ -693,7 +693,7 @@ private void generatePositionNotDistinctFromRowWithPageMethod( // The plan is to allow scalar function to optionally provide an additional implementation using Block+position calling convention. // At that point, we'll be able to fully deprecate Type.equalTo (and friends) and remove this hack. if (type.getJavaType().equals(Slice.class)) { - switch(type.getTypeSignature().getBase()) { + switch (type.getTypeSignature().getBase()) { case StandardTypes.CHAR: case StandardTypes.IPADDRESS: case StandardTypes.JSON: diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinFilterFunctionCompiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinFilterFunctionCompiler.java index 44f4153fbd424..bfad5be6f6141 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinFilterFunctionCompiler.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinFilterFunctionCompiler.java @@ -247,7 +247,7 @@ private static void generateToString(ClassDefinition classDefinition, CallSiteBi public interface JoinFilterFunctionFactory { - JoinFilterFunction create(ConnectorSession session, LongArrayList addresses, List> channels); + JoinFilterFunction create(ConnectorSession session, LongArrayList addresses, List pages); } private static RowExpressionVisitor fieldReferenceCompiler( @@ -344,11 +344,11 @@ public IsolatedJoinFilterFunctionFactory(Class> channels) + public JoinFilterFunction create(ConnectorSession session, LongArrayList addresses, List pages) { try { InternalJoinFilterFunction internalJoinFilterFunction = internalJoinFilterFunctionConstructor.newInstance(session); - return isolatedJoinFilterFunctionConstructor.newInstance(internalJoinFilterFunction, addresses, channels); + return isolatedJoinFilterFunctionConstructor.newInstance(internalJoinFilterFunction, addresses, pages); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/LambdaBytecodeGenerator.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/LambdaBytecodeGenerator.java index a13532ca7db95..e40e8d21541d3 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/LambdaBytecodeGenerator.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/LambdaBytecodeGenerator.java @@ -14,6 +14,8 @@ package com.facebook.presto.sql.gen; import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.operator.aggregation.AccumulatorCompiler; +import com.facebook.presto.operator.aggregation.LambdaProvider; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.sql.relational.CallExpression; import com.facebook.presto.sql.relational.ConstantExpression; @@ -25,10 +27,13 @@ import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Primitives; +import io.airlift.bytecode.Access; import io.airlift.bytecode.BytecodeBlock; import io.airlift.bytecode.BytecodeNode; import io.airlift.bytecode.ClassDefinition; +import io.airlift.bytecode.FieldDefinition; import io.airlift.bytecode.MethodDefinition; import io.airlift.bytecode.Parameter; import io.airlift.bytecode.ParameterizedType; @@ -43,14 +48,19 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; import static com.facebook.presto.spi.StandardErrorCode.COMPILER_ERROR; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.sql.gen.BytecodeUtils.boxPrimitiveIfNecessary; import static com.facebook.presto.sql.gen.BytecodeUtils.unboxPrimitiveIfNecessary; import static com.facebook.presto.sql.gen.LambdaCapture.LAMBDA_CAPTURE_METHOD; +import static com.facebook.presto.sql.gen.LambdaExpressionExtractor.extractLambdaExpressions; +import static com.facebook.presto.util.CompilerUtils.defineClass; +import static com.facebook.presto.util.CompilerUtils.makeClassName; import static com.facebook.presto.util.Failures.checkCondition; import static com.google.common.collect.ImmutableList.toImmutableList; +import static io.airlift.bytecode.Access.PRIVATE; import static io.airlift.bytecode.Access.PUBLIC; import static io.airlift.bytecode.Access.a; import static io.airlift.bytecode.Parameter.arg; @@ -67,6 +77,33 @@ private LambdaBytecodeGenerator() { } + public static Map generateMethodsForLambda( + ClassDefinition containerClassDefinition, + CallSiteBinder callSiteBinder, + CachedInstanceBinder cachedInstanceBinder, + RowExpression expression, + FunctionRegistry functionRegistry) + { + Set lambdaExpressions = ImmutableSet.copyOf(extractLambdaExpressions(expression)); + ImmutableMap.Builder compiledLambdaMap = ImmutableMap.builder(); + + int counter = 0; + for (LambdaDefinitionExpression lambdaExpression : lambdaExpressions) { + CompiledLambda compiledLambda = LambdaBytecodeGenerator.preGenerateLambdaExpression( + lambdaExpression, + "lambda_" + counter, + containerClassDefinition, + compiledLambdaMap.build(), + callSiteBinder, + cachedInstanceBinder, + functionRegistry); + compiledLambdaMap.put(lambdaExpression, compiledLambda); + counter++; + } + + return compiledLambdaMap.build(); + } + /** * @return a MethodHandle field that represents the lambda expression */ @@ -192,6 +229,77 @@ public static BytecodeNode generateLambda( return block; } + public static Class compileLambdaProvider(LambdaDefinitionExpression lambdaExpression, FunctionRegistry functionRegistry, Class lambdaInterface) + { + ClassDefinition lambdaProviderClassDefinition = new ClassDefinition( + a(PUBLIC, Access.FINAL), + makeClassName("LambdaProvider"), + type(Object.class), + type(LambdaProvider.class)); + + FieldDefinition sessionField = lambdaProviderClassDefinition.declareField(a(PRIVATE), "session", ConnectorSession.class); + + CallSiteBinder callSiteBinder = new CallSiteBinder(); + CachedInstanceBinder cachedInstanceBinder = new CachedInstanceBinder(lambdaProviderClassDefinition, callSiteBinder); + + Map compiledLambdaMap = generateMethodsForLambda( + lambdaProviderClassDefinition, + callSiteBinder, + cachedInstanceBinder, + lambdaExpression, + functionRegistry); + + MethodDefinition method = lambdaProviderClassDefinition.declareMethod( + a(PUBLIC), + "getLambda", + type(Object.class), + ImmutableList.of()); + + Scope scope = method.getScope(); + BytecodeBlock body = method.getBody(); + scope.declareVariable("wasNull", body, constantFalse()); + scope.declareVariable("session", body, method.getThis().getField(sessionField)); + + RowExpressionCompiler rowExpressionCompiler = new RowExpressionCompiler( + callSiteBinder, + cachedInstanceBinder, + variableReferenceCompiler(ImmutableMap.of()), + functionRegistry, + compiledLambdaMap); + + BytecodeGeneratorContext generatorContext = new BytecodeGeneratorContext( + rowExpressionCompiler, + scope, + callSiteBinder, + cachedInstanceBinder, + functionRegistry); + + body.append( + generateLambda( + generatorContext, + ImmutableList.of(), + compiledLambdaMap.get(lambdaExpression), + lambdaInterface)) + .retObject(); + + // constructor + Parameter sessionParameter = arg("session", ConnectorSession.class); + + MethodDefinition constructorDefinition = lambdaProviderClassDefinition.declareConstructor(a(PUBLIC), sessionParameter); + BytecodeBlock constructorBody = constructorDefinition.getBody(); + Variable constructorThisVariable = constructorDefinition.getThis(); + + constructorBody.comment("super();") + .append(constructorThisVariable) + .invokeConstructor(Object.class) + .append(constructorThisVariable.setField(sessionField, sessionParameter)); + + cachedInstanceBinder.generateInitializations(constructorThisVariable, constructorBody); + constructorBody.ret(); + + return defineClass(lambdaProviderClassDefinition, LambdaProvider.class, callSiteBinder.getBindings(), AccumulatorCompiler.class.getClassLoader()); + } + private static Method getSingleApplyMethod(Class lambdaFunctionInterface) { checkCondition(lambdaFunctionInterface.isAnnotationPresent(FunctionalInterface.class), COMPILER_ERROR, "Lambda function interface is required to be annotated with FunctionalInterface"); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/PageFunctionCompiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/PageFunctionCompiler.java index e69be283c15ea..4d0bf1ef6e030 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/gen/PageFunctionCompiler.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/PageFunctionCompiler.java @@ -14,7 +14,6 @@ package com.facebook.presto.sql.gen; import com.facebook.presto.metadata.Metadata; -import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.Work; import com.facebook.presto.operator.project.ConstantPageProjection; import com.facebook.presto.operator.project.GeneratedPageProjection; @@ -202,7 +201,7 @@ private Supplier compileProjectionInternal(RowExpression project result.getRewrittenExpression(), determinismEvaluator.isDeterministic(result.getRewrittenExpression()), result.getInputChannels(), - constructorMethodHandle(pageProjectionWorkClass, BlockBuilder.class, ConnectorSession.class, DriverYieldSignal.class, Page.class, SelectedPositions.class)); + constructorMethodHandle(pageProjectionWorkClass, BlockBuilder.class, ConnectorSession.class, Page.class, SelectedPositions.class)); } private static ParameterizedType generateProjectionWorkClassName(Optional classNameSuffix) @@ -220,7 +219,6 @@ private ClassDefinition definePageProjectWorkClass(RowExpression projection, Cal FieldDefinition blockBuilderField = classDefinition.declareField(a(PRIVATE), "blockBuilder", BlockBuilder.class); FieldDefinition sessionField = classDefinition.declareField(a(PRIVATE), "session", ConnectorSession.class); - FieldDefinition yieldSignalField = classDefinition.declareField(a(PRIVATE), "yieldSignal", DriverYieldSignal.class); FieldDefinition pageField = classDefinition.declareField(a(PRIVATE), "page", Page.class); FieldDefinition selectedPositionsField = classDefinition.declareField(a(PRIVATE), "selectedPositions", SelectedPositions.class); FieldDefinition nextIndexOrPositionField = classDefinition.declareField(a(PRIVATE), "nextIndexOrPosition", int.class); @@ -229,7 +227,7 @@ private ClassDefinition definePageProjectWorkClass(RowExpression projection, Cal CachedInstanceBinder cachedInstanceBinder = new CachedInstanceBinder(classDefinition, callSiteBinder); // process - generateProcessMethod(classDefinition, blockBuilderField, sessionField, yieldSignalField, pageField, selectedPositionsField, nextIndexOrPositionField, resultField); + generateProcessMethod(classDefinition, blockBuilderField, sessionField, pageField, selectedPositionsField, nextIndexOrPositionField, resultField); // getResult MethodDefinition method = classDefinition.declareMethod(a(PUBLIC), "getResult", type(Object.class), ImmutableList.of()); @@ -242,11 +240,10 @@ private ClassDefinition definePageProjectWorkClass(RowExpression projection, Cal // constructor Parameter blockBuilder = arg("blockBuilder", BlockBuilder.class); Parameter session = arg("session", ConnectorSession.class); - Parameter yieldSignal = arg("yieldSignal", DriverYieldSignal.class); Parameter page = arg("page", Page.class); Parameter selectedPositions = arg("selectedPositions", SelectedPositions.class); - MethodDefinition constructorDefinition = classDefinition.declareConstructor(a(PUBLIC), blockBuilder, session, yieldSignal, page, selectedPositions); + MethodDefinition constructorDefinition = classDefinition.declareConstructor(a(PUBLIC), blockBuilder, session, page, selectedPositions); BytecodeBlock body = constructorDefinition.getBody(); Variable thisVariable = constructorDefinition.getThis(); @@ -256,7 +253,6 @@ private ClassDefinition definePageProjectWorkClass(RowExpression projection, Cal .invokeConstructor(Object.class) .append(thisVariable.setField(blockBuilderField, blockBuilder)) .append(thisVariable.setField(sessionField, session)) - .append(thisVariable.setField(yieldSignalField, yieldSignal)) .append(thisVariable.setField(pageField, page)) .append(thisVariable.setField(selectedPositionsField, selectedPositions)) .append(thisVariable.setField(nextIndexOrPositionField, selectedPositions.invoke("getOffset", int.class))) @@ -272,7 +268,6 @@ private static MethodDefinition generateProcessMethod( ClassDefinition classDefinition, FieldDefinition blockBuilder, FieldDefinition session, - FieldDefinition yieldSignal, FieldDefinition page, FieldDefinition selectedPositions, FieldDefinition nextIndexOrPosition, @@ -300,16 +295,14 @@ private static MethodDefinition generateProcessMethod( .condition(lessThan(index, to)) .update(index.increment()) .body(new BytecodeBlock() - .append(thisVariable.invoke("evaluate", void.class, thisVariable.getField(session), thisVariable.getField(page), positions.getElement(index))) - .append(generateCheckYieldBlock(thisVariable, index, yieldSignal, nextIndexOrPosition))))); + .append(thisVariable.invoke("evaluate", void.class, thisVariable.getField(session), thisVariable.getField(page), positions.getElement(index)))))); ifStatement.ifFalse(new ForLoop("range based loop") .initialize(index.set(from)) .condition(lessThan(index, to)) .update(index.increment()) .body(new BytecodeBlock() - .append(thisVariable.invoke("evaluate", void.class, thisVariable.getField(session), thisVariable.getField(page), index)) - .append(generateCheckYieldBlock(thisVariable, index, yieldSignal, nextIndexOrPosition)))); + .append(thisVariable.invoke("evaluate", void.class, thisVariable.getField(session), thisVariable.getField(page), index)))); body.comment("result = this.blockBuilder.build(); return true;") .append(thisVariable.setField(result, thisVariable.getField(blockBuilder).invoke("build", Block.class))) @@ -319,20 +312,6 @@ private static MethodDefinition generateProcessMethod( return method; } - private static BytecodeBlock generateCheckYieldBlock(Variable thisVariable, Variable index, FieldDefinition yieldSignal, FieldDefinition nextIndexOrPosition) - { - return new BytecodeBlock() - .comment("if (yieldSignal.isSet())") - .append(new IfStatement() - .condition(thisVariable.getField(yieldSignal).invoke("isSet", boolean.class)) - .ifTrue(new BytecodeBlock() - .comment("nextIndexOrPosition = index + 1;") - .append(thisVariable.setField(nextIndexOrPosition, add(index, constantInt(1)))) - .comment("return false;") - .push(false) - .retBoolean())); - } - private MethodDefinition generateEvaluateMethod( ClassDefinition classDefinition, CallSiteBinder callSiteBinder, diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/DesugarAtTimeZoneRewriter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/DesugarAtTimeZoneRewriter.java index e110b3d47f7ca..760d7c259ee88 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/DesugarAtTimeZoneRewriter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/DesugarAtTimeZoneRewriter.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.parser.SqlParser; @@ -56,7 +57,7 @@ public static Expression rewrite(Expression expression, Session session, Metadat if (expression instanceof SymbolReference) { return expression; } - Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList()); // parameters already replaced + Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList(), WarningCollector.NOOP); return rewrite(expression, expressionTypes); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/DistributedExecutionPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/DistributedExecutionPlanner.java index 005ebdf406675..5f663dcae4135 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/DistributedExecutionPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/DistributedExecutionPlanner.java @@ -42,6 +42,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -174,6 +175,17 @@ public Map visitSemiJoin(SemiJoinNode node, Void contex .build(); } + @Override + public Map visitSpatialJoin(SpatialJoinNode node, Void context) + { + Map leftSplits = node.getLeft().accept(this, context); + Map rightSplits = node.getRight().accept(this, context); + return ImmutableMap.builder() + .putAll(leftSplits) + .putAll(rightSplits) + .build(); + } + @Override public Map visitIndexJoin(IndexJoinNode node, Void context) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/DomainTranslator.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/DomainTranslator.java index dc01ff59ee7ba..1ec6adf4924a8 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/DomainTranslator.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/DomainTranslator.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.block.Block; @@ -488,7 +489,7 @@ private boolean isImplicitCoercion(Cast cast) private Map, Type> analyzeExpression(Expression expression) { - return ExpressionAnalyzer.getExpressionTypes(session, metadata, new SqlParser(), types, expression, emptyList() /* parameters already replaced */); + return ExpressionAnalyzer.getExpressionTypes(session, metadata, new SqlParser(), types, expression, emptyList(), WarningCollector.NOOP); } private static ExtractionResult createComparisonExtractionResult(ComparisonExpression.Operator comparisonOperator, Symbol column, Type type, @Nullable Object value, boolean complement) @@ -758,7 +759,7 @@ protected ExtractionResult visitNullLiteral(NullLiteral node, Boolean complement private static Type typeOf(Expression expression, Session session, Metadata metadata, TypeProvider types) { - Map, Type> expressionTypes = ExpressionAnalyzer.getExpressionTypes(session, metadata, new SqlParser(), types, expression, emptyList() /* parameters already replaced */); + Map, Type> expressionTypes = ExpressionAnalyzer.getExpressionTypes(session, metadata, new SqlParser(), types, expression, emptyList(), WarningCollector.NOOP); return expressionTypes.get(NodeRef.of(expression)); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/EffectivePredicateExtractor.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/EffectivePredicateExtractor.java index ca6cd3df2ccf6..5a2d2191c0513 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/EffectivePredicateExtractor.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/EffectivePredicateExtractor.java @@ -26,6 +26,7 @@ import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TopNNode; import com.facebook.presto.sql.planner.plan.UnionNode; @@ -230,11 +231,12 @@ public Expression visitJoin(JoinNode node, Void context) switch (node.getType()) { case INNER: - return combineConjuncts(ImmutableList.builder() - .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) - .add(pullExpressionThroughSymbols(rightPredicate, node.getOutputSymbols())) - .addAll(pullExpressionsThroughSymbols(joinConjuncts, node.getOutputSymbols())) - .build()); + return pullExpressionThroughSymbols(combineConjuncts(ImmutableList.builder() + .add(leftPredicate) + .add(rightPredicate) + .add(combineConjuncts(joinConjuncts)) + .add(node.getFilter().orElse(TRUE_LITERAL)) + .build()), node.getOutputSymbols()); case LEFT: return combineConjuncts(ImmutableList.builder() .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) @@ -275,6 +277,28 @@ public Expression visitSemiJoin(SemiJoinNode node, Void context) return node.getSource().accept(this, context); } + @Override + public Expression visitSpatialJoin(SpatialJoinNode node, Void context) + { + Expression leftPredicate = node.getLeft().accept(this, context); + Expression rightPredicate = node.getRight().accept(this, context); + + switch (node.getType()) { + case INNER: + return combineConjuncts(ImmutableList.builder() + .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) + .add(pullExpressionThroughSymbols(rightPredicate, node.getOutputSymbols())) + .build()); + case LEFT: + return combineConjuncts(ImmutableList.builder() + .add(pullExpressionThroughSymbols(leftPredicate, node.getOutputSymbols())) + .addAll(pullNullableConjunctsThroughOuterJoin(extractConjuncts(rightPredicate), node.getOutputSymbols(), node.getRight().getOutputSymbols()::contains)) + .build()); + default: + throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); + } + } + private Expression deriveCommonPredicates(PlanNode node, Function>> mapping) { // Find the predicates that can be pulled up from each source diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionExtractor.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionExtractor.java index 863709476b089..022510164b2fd 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionExtractor.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionExtractor.java @@ -21,7 +21,6 @@ import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.ProjectNode; -import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.tree.Expression; import com.google.common.collect.ImmutableList; @@ -108,15 +107,6 @@ public Void visitProject(ProjectNode node, ImmutableList.Builder con return super.visitProject(node, context); } - @Override - public Void visitTableScan(TableScanNode node, ImmutableList.Builder context) - { - if (node.getOriginalConstraint() != null) { - context.add(node.getOriginalConstraint()); - } - return super.visitTableScan(node, context); - } - @Override public Void visitJoin(JoinNode node, ImmutableList.Builder context) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionInterpreter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionInterpreter.java index c037a0eb16b69..d9a5c34e18a69 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionInterpreter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/ExpressionInterpreter.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.client.FailureInfo; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; @@ -167,7 +168,7 @@ public static ExpressionInterpreter expressionOptimizer(Expression expression, M public static Object evaluateConstantExpression(Expression expression, Type expectedType, Metadata metadata, Session session, List parameters) { - ExpressionAnalyzer analyzer = createConstantAnalyzer(metadata, session, parameters); + ExpressionAnalyzer analyzer = createConstantAnalyzer(metadata, session, parameters, WarningCollector.NOOP); analyzer.analyze(expression, Scope.create()); Type actualType = analyzer.getExpressionTypes().get(NodeRef.of(expression)); @@ -201,7 +202,7 @@ private static Object evaluateConstantExpression( Expression rewrite = Coercer.addCoercions(expression, coercions, typeOnlyCoercions); // redo the analysis since above expression rewriter might create new expressions which do not have entries in the type map - ExpressionAnalyzer analyzer = createConstantAnalyzer(metadata, session, parameters); + ExpressionAnalyzer analyzer = createConstantAnalyzer(metadata, session, parameters, WarningCollector.NOOP); analyzer.analyze(rewrite, Scope.create()); // remove syntax sugar @@ -213,7 +214,7 @@ private static Object evaluateConstantExpression( // The optimization above may have rewritten the expression tree which breaks all the identity maps, so redo the analysis // to re-analyze coercions that might be necessary - analyzer = createConstantAnalyzer(metadata, session, parameters); + analyzer = createConstantAnalyzer(metadata, session, parameters, WarningCollector.NOOP); analyzer.analyze(canonicalized, Scope.create()); // evaluate the expression @@ -530,7 +531,7 @@ else if (whenOperand != null && isEqual(operand, operandType, whenOperand, type( private boolean isEqual(Object operand1, Type type1, Object operand2, Type type2) { - return (Boolean) invokeOperator(OperatorType.EQUAL, ImmutableList.of(type1, type2), ImmutableList.of(operand1, operand2)); + return Boolean.TRUE.equals(invokeOperator(OperatorType.EQUAL, ImmutableList.of(type1, type2), ImmutableList.of(operand1, operand2))); } private Type type(Expression expression) @@ -618,9 +619,15 @@ protected Object visitInPredicate(InPredicate node, Object context) if (inValue == null) { hasNullValue = true; } - else if (!found && (Boolean) invokeOperator(OperatorType.EQUAL, types(node.getValue(), expression), ImmutableList.of(value, inValue))) { - // in does not short-circuit so we must evaluate all value in the list - found = true; + else { + Boolean result = (Boolean) invokeOperator(OperatorType.EQUAL, types(node.getValue(), expression), ImmutableList.of(value, inValue)); + if (result == null) { + hasNullValue = true; + } + else if (!found && result) { + // in does not short-circuit so we must evaluate all value in the list + found = true; + } } } if (found) { @@ -797,12 +804,12 @@ protected Object visitNullIfExpression(NullIfExpression node, Object context) Signature secondCast = metadata.getFunctionRegistry().getCoercion(secondType, commonType); // cast(first as ) == cast(second as ) - boolean equal = (Boolean) invokeOperator( + boolean equal = Boolean.TRUE.equals(invokeOperator( OperatorType.EQUAL, ImmutableList.of(commonType, commonType), ImmutableList.of( functionInvoker.invoke(firstCast, session, ImmutableList.of(first)), - functionInvoker.invoke(secondCast, session, ImmutableList.of(second)))); + functionInvoker.invoke(secondCast, session, ImmutableList.of(second))))); if (equal) { return null; diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/GroupingOperationRewriter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/GroupingOperationRewriter.java index 0853e2cb503bf..8ad4290fc2ac2 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/GroupingOperationRewriter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/GroupingOperationRewriter.java @@ -27,18 +27,18 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import static com.facebook.presto.sql.tree.ArithmeticBinaryExpression.Operator.ADD; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; -import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; public final class GroupingOperationRewriter { private GroupingOperationRewriter() {} - public static Expression rewriteGroupingOperation(GroupingOperation expression, List> groupingSets, Map, FieldId> columnReferenceFields, Optional groupIdSymbol) + public static Expression rewriteGroupingOperation(GroupingOperation expression, List> groupingSets, Map, FieldId> columnReferenceFields, Optional groupIdSymbol) { requireNonNull(groupIdSymbol, "groupIdSymbol is null"); @@ -62,18 +62,8 @@ public static Expression rewriteGroupingOperation(GroupingOperation expression, .map(fieldId -> translateFieldToInteger(fieldId, relationId)) .collect(toImmutableList()); - List> groupingSetDescriptors = groupingSets.stream() - .map(groupingSet -> groupingSet.stream() - .map(NodeRef::of) - .filter(columnReferenceFields::containsKey) - .map(columnReferenceFields::get) - .map(fieldId -> translateFieldToInteger(fieldId, relationId)) - .collect(toImmutableList())) - .collect(toImmutableList()); - - List groupingResults = groupingSetDescriptors.stream() - .map(groupingSetDescriptors::indexOf) - .map(groupId -> String.valueOf(calculateGrouping(groupId, columns, groupingSetDescriptors))) + List groupingResults = groupingSets.stream() + .map(groupingSet -> String.valueOf(calculateGrouping(groupingSet, columns))) .map(LongLiteral::new) .collect(toImmutableList()); @@ -106,28 +96,22 @@ private static int translateFieldToInteger(FieldId fieldId, RelationId requiredO * grouping and 1 otherwise. For an example, see the SQL documentation for the * function. * - * @param groupId An ordinal indicating which grouping is currently being processed. - * Each grouping is assigned a unique monotonically increasing integer. - * @param columns The column arguments with which the function was - * invoked converted to ordinals with respect to the base table column - * ordering. - * @param groupingSetDescriptors A collection of ordinal lists where the index of - * the list is the groupId and the list itself contains the ordinals of the - * columns present in the grouping. For example: [[0, 2], [2], [0, 1, 2]] - * means the the 0th list contains the set of columns that are present in - * the 0th grouping. + * @param columns The column arguments with which the function was invoked + * converted to ordinals with respect to the base table column ordering. + * @param groupingSet A collection containing the ordinals of the + * columns present in the grouping. * @return A bit set converted to decimal indicating which columns are present in - * the grouping. If a column is NOT present in the grouping its corresponding - * bit is set to 1 and to 0 if the column is present in the grouping. + * the grouping. If a column is NOT present in the grouping its corresponding + * bit is set to 1 and to 0 if the column is present in the grouping. */ - static long calculateGrouping(long groupId, List columns, List> groupingSetDescriptors) + static long calculateGrouping(Set groupingSet, List columns) { long grouping = (1L << columns.size()) - 1; - List groupingSet = groupingSetDescriptors.get(toIntExact(groupId)); - for (Integer groupingColumn : groupingSet) { - int index = columns.indexOf(groupingColumn); - if (index != -1) { + for (int index = 0; index < columns.size(); index++) { + int column = columns.get(index); + + if (groupingSet.contains(column)) { // Leftmost argument to grouping() (i.e. when index = 0) corresponds to // the most significant bit in the result. That is why we shift 1L starting // from the columns.size() - 1 bit index. diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/LiteralEncoder.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/LiteralEncoder.java index 41b0d5f47276d..c172d6abdcef7 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/LiteralEncoder.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/LiteralEncoder.java @@ -54,6 +54,8 @@ import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.spi.type.SmallintType.SMALLINT; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.type.UnknownType.UNKNOWN; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.Float.intBitsToFloat; @@ -99,6 +101,14 @@ public Expression toExpression(Object object, Type type) return new Cast(new NullLiteral(), type.getTypeSignature().toString(), false, true); } + if (type.equals(TINYINT)) { + return new GenericLiteral("TINYINT", object.toString()); + } + + if (type.equals(SMALLINT)) { + return new GenericLiteral("SMALLINT", object.toString()); + } + if (type.equals(INTEGER)) { return new LongLiteral(object.toString()); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/LocalExecutionPlanner.java index edecd9eae2cb7..beae47d6ff092 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/LocalExecutionPlanner.java @@ -37,8 +37,9 @@ import com.facebook.presto.operator.HashAggregationOperator.HashAggregationOperatorFactory; import com.facebook.presto.operator.HashBuilderOperator.HashBuilderOperatorFactory; import com.facebook.presto.operator.HashSemiJoinOperator.HashSemiJoinOperatorFactory; -import com.facebook.presto.operator.JoinBridgeDataManager; +import com.facebook.presto.operator.JoinBridgeManager; import com.facebook.presto.operator.JoinOperatorFactory; +import com.facebook.presto.operator.JoinOperatorFactory.OuterOperatorFactoryResult; import com.facebook.presto.operator.LimitOperator.LimitOperatorFactory; import com.facebook.presto.operator.LocalPlannerAware; import com.facebook.presto.operator.LookupJoinOperators; @@ -47,7 +48,7 @@ import com.facebook.presto.operator.MarkDistinctOperator.MarkDistinctOperatorFactory; import com.facebook.presto.operator.MergeOperator.MergeOperatorFactory; import com.facebook.presto.operator.MetadataDeleteOperator.MetadataDeleteOperatorFactory; -import com.facebook.presto.operator.NestedLoopJoinPagesBridge; +import com.facebook.presto.operator.NestedLoopJoinBridge; import com.facebook.presto.operator.NestedLoopJoinPagesSupplier; import com.facebook.presto.operator.OperatorFactory; import com.facebook.presto.operator.OrderByOperator.OrderByOperatorFactory; @@ -77,6 +78,8 @@ import com.facebook.presto.operator.WindowFunctionDefinition; import com.facebook.presto.operator.WindowOperator.WindowOperatorFactory; import com.facebook.presto.operator.aggregation.AccumulatorFactory; +import com.facebook.presto.operator.aggregation.InternalAggregationFunction; +import com.facebook.presto.operator.aggregation.LambdaProvider; import com.facebook.presto.operator.exchange.LocalExchange.LocalExchangeFactory; import com.facebook.presto.operator.exchange.LocalExchangeSinkOperator.LocalExchangeSinkOperatorFactory; import com.facebook.presto.operator.exchange.LocalExchangeSourceOperator.LocalExchangeSourceOperatorFactory; @@ -94,6 +97,7 @@ import com.facebook.presto.operator.window.WindowFunctionSupplier; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorIndex; +import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.PrestoException; @@ -145,6 +149,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.StatisticAggregationsDescriptor; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; @@ -157,16 +162,20 @@ import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.planner.plan.WindowNode; import com.facebook.presto.sql.planner.plan.WindowNode.Frame; +import com.facebook.presto.sql.relational.LambdaDefinitionExpression; import com.facebook.presto.sql.relational.RowExpression; import com.facebook.presto.sql.relational.SqlToRowExpressionTranslator; import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FieldReference; import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.LambdaArgumentDeclaration; +import com.facebook.presto.sql.tree.LambdaExpression; import com.facebook.presto.sql.tree.NodeRef; import com.facebook.presto.sql.tree.OrderBy; import com.facebook.presto.sql.tree.SortItem; import com.facebook.presto.sql.tree.SymbolReference; +import com.facebook.presto.type.FunctionType; import com.google.common.base.VerifyException; import com.google.common.collect.ContiguousSet; import com.google.common.collect.HashMultimap; @@ -210,6 +219,7 @@ import static com.facebook.presto.SystemSessionProperties.getTaskWriterCount; import static com.facebook.presto.SystemSessionProperties.isExchangeCompressionEnabled; import static com.facebook.presto.SystemSessionProperties.isSpillEnabled; +import static com.facebook.presto.execution.warnings.WarningCollector.NOOP; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.operator.DistinctLimitOperator.DistinctLimitOperatorFactory; import static com.facebook.presto.operator.NestedLoopBuildOperator.NestedLoopBuildOperatorFactory; @@ -220,6 +230,7 @@ import static com.facebook.presto.operator.TableFinishOperator.TableFinisher; import static com.facebook.presto.operator.TableWriterOperator.FRAGMENT_CHANNEL; import static com.facebook.presto.operator.TableWriterOperator.ROW_COUNT_CHANNEL; +import static com.facebook.presto.operator.TableWriterOperator.STATS_START_CHANNEL; import static com.facebook.presto.operator.TableWriterOperator.TableWriterOperatorFactory; import static com.facebook.presto.operator.UnnestOperator.UnnestOperatorFactory; import static com.facebook.presto.operator.WindowFunctionDefinition.window; @@ -228,6 +239,7 @@ import static com.facebook.presto.spi.type.TypeUtils.writeNativeValue; import static com.facebook.presto.sql.analyzer.ExpressionAnalyzer.getExpressionTypes; import static com.facebook.presto.sql.analyzer.ExpressionAnalyzer.getExpressionTypesFromInput; +import static com.facebook.presto.sql.gen.LambdaBytecodeGenerator.compileLambdaProvider; import static com.facebook.presto.sql.planner.ExpressionNodeInliner.replaceExpression; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.COORDINATOR_DISTRIBUTION; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.FIXED_ARBITRARY_DISTRIBUTION; @@ -238,6 +250,7 @@ import static com.facebook.presto.sql.planner.plan.AggregationNode.Step.PARTIAL; import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.LOCAL; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.FULL; +import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.RIGHT; import static com.facebook.presto.sql.planner.plan.TableWriterNode.CreateHandle; import static com.facebook.presto.sql.planner.plan.TableWriterNode.InsertHandle; @@ -245,9 +258,11 @@ import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.LESS_THAN; import static com.facebook.presto.sql.tree.ComparisonExpression.Operator.LESS_THAN_OR_EQUAL; +import static com.facebook.presto.util.Reflection.constructorMethodHandle; import static com.facebook.presto.util.SpatialJoinUtils.ST_CONTAINS; import static com.facebook.presto.util.SpatialJoinUtils.ST_DISTANCE; import static com.facebook.presto.util.SpatialJoinUtils.ST_INTERSECTS; +import static com.facebook.presto.util.SpatialJoinUtils.ST_WITHIN; import static com.facebook.presto.util.SpatialJoinUtils.extractSupportedSpatialComparisons; import static com.facebook.presto.util.SpatialJoinUtils.extractSupportedSpatialFunctions; import static com.google.common.base.Functions.forMap; @@ -485,18 +500,18 @@ private static void addLookupOuterDrivers(LocalExecutionPlanContext context) } JoinOperatorFactory lookupJoin = (JoinOperatorFactory) operatorFactory; - Optional outerOperatorFactory = lookupJoin.createOuterOperatorFactory(); - if (outerOperatorFactory.isPresent()) { + Optional outerOperatorFactoryResult = lookupJoin.createOuterOperatorFactory(); + if (outerOperatorFactoryResult.isPresent()) { // Add a new driver to output the unmatched rows in an outer join. // We duplicate all of the factories above the JoinOperator (the ones reading from the joins), // and replace the JoinOperator with the OuterOperator (the one that produces unmatched rows). ImmutableList.Builder newOperators = ImmutableList.builder(); - newOperators.add(outerOperatorFactory.get()); + newOperators.add(outerOperatorFactoryResult.get().getOuterOperatorFactory()); operatorFactories.subList(i + 1, operatorFactories.size()).stream() .map(OperatorFactory::duplicate) .forEach(newOperators::add); - context.addDriverFactory(false, factory.isOutputDriver(), newOperators.build(), OptionalInt.of(1), factory.getPipelineExecutionStrategy()); + context.addDriverFactory(false, factory.isOutputDriver(), newOperators.build(), OptionalInt.of(1), outerOperatorFactoryResult.get().getBuildExecutionStrategy()); } } } @@ -738,14 +753,11 @@ public PhysicalOperation visitExplainAnalyze(ExplainAnalyzeNode node, LocalExecu ExplainAnalyzeContext analyzeContext = explainAnalyzeContext .orElseThrow(() -> new IllegalStateException("ExplainAnalyze can only run on coordinator")); PhysicalOperation source = node.getSource().accept(this, context); - OperatorFactory operatorFactory = new ExplainAnalyzeOperatorFactory( context.getNextOperatorId(), node.getId(), analyzeContext.getQueryPerformanceFetcher(), metadata.getFunctionRegistry(), - analyzeContext.getStatsCalculator(), - analyzeContext.getCostCalculator(), node.isVerbose()); return new PhysicalOperation(operatorFactory, makeLayout(node), context, source); } @@ -1199,7 +1211,8 @@ private PhysicalOperation visitScanFilterAndProject( sqlParser, sourceTypes, concat(rewrittenFilter.map(ImmutableList::of).orElse(ImmutableList.of()), rewrittenProjections), - emptyList()); + emptyList(), + NOOP); Optional translatedFilter = rewrittenFilter.map(filter -> toRowExpression(filter, expressionTypes)); List translatedProjections = rewrittenProjections.stream() @@ -1293,6 +1306,7 @@ public PhysicalOperation visitValues(ValuesNode node, LocalExecutionPlanContext TypeProvider.empty(), ImmutableList.copyOf(row), emptyList(), + NOOP, false); for (int i = 0; i < row.size(); i++) { // evaluate the literal value @@ -1528,7 +1542,8 @@ public PhysicalOperation visitIndexJoin(IndexJoinNode node, LocalExecutionPlanCo verify(probeSource.getPipelineExecutionStrategy() == UNGROUPED_EXECUTION); verify(indexSource.getPipelineExecutionStrategy() == UNGROUPED_EXECUTION); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookup( + JoinBridgeManager lookupSourceFactoryManager = new JoinBridgeManager<>( + false, UNGROUPED_EXECUTION, UNGROUPED_EXECUTION, lifespan -> indexLookupSourceFactory, @@ -1563,10 +1578,6 @@ public PhysicalOperation visitIndexJoin(IndexJoinNode node, LocalExecutionPlanCo @Override public PhysicalOperation visitJoin(JoinNode node, LocalExecutionPlanContext context) { - if (node.isSpatialJoin()) { - return createSpatialJoin(node, context); - } - if (node.isCrossJoin()) { return createNestedLoopJoin(node, context); } @@ -1586,10 +1597,10 @@ public PhysicalOperation visitJoin(JoinNode node, LocalExecutionPlanContext cont } } - private PhysicalOperation createSpatialJoin(JoinNode node, LocalExecutionPlanContext context) + @Override + public PhysicalOperation visitSpatialJoin(SpatialJoinNode node, LocalExecutionPlanContext context) { - verify(node.getFilter().isPresent() && node.getCriteria().isEmpty()); - Expression filterExpression = node.getFilter().get(); + Expression filterExpression = node.getFilter(); List spatialFunctions = extractSupportedSpatialFunctions(filterExpression); for (FunctionCall spatialFunction : spatialFunctions) { Optional operation = tryCreateSpatialJoin(context, node, removeExpressionFromFilter(filterExpression, spatialFunction), spatialFunction, Optional.empty(), Optional.empty()); @@ -1618,7 +1629,7 @@ private PhysicalOperation createSpatialJoin(JoinNode node, LocalExecutionPlanCon private Optional tryCreateSpatialJoin( LocalExecutionPlanContext context, - JoinNode node, + SpatialJoinNode node, Optional filterExpression, FunctionCall spatialFunction, Optional radius, @@ -1683,6 +1694,13 @@ private SpatialPredicate spatialTest(FunctionCall functionCall, boolean probeFir else { return (buildGeometry, probeGeometry, radius) -> buildGeometry.contains(probeGeometry); } + case ST_WITHIN: + if (probeFirst) { + return (buildGeometry, probeGeometry, radius) -> probeGeometry.within(buildGeometry); + } + else { + return (buildGeometry, probeGeometry, radius) -> buildGeometry.within(probeGeometry); + } case ST_INTERSECTS: return (buildGeometry, probeGeometry, radius) -> buildGeometry.intersects(probeGeometry); case ST_DISTANCE: @@ -1715,8 +1733,10 @@ private PhysicalOperation createNestedLoopJoin(JoinNode node, LocalExecutionPlan checkState( buildSource.getPipelineExecutionStrategy() == UNGROUPED_EXECUTION, "Build source of a nested loop join is expected to be GROUPED_EXECUTION."); + checkArgument(node.getType() == INNER, "NestedLoopJoin is only used for inner join"); - JoinBridgeDataManager nestedLoopJoinPagesSupplierManager = JoinBridgeDataManager.nestedLoop( + JoinBridgeManager nestedLoopJoinBridgeManager = new JoinBridgeManager<>( + false, probeSource.getPipelineExecutionStrategy(), buildSource.getPipelineExecutionStrategy(), lifespan -> new NestedLoopJoinPagesSupplier(), @@ -1724,7 +1744,7 @@ private PhysicalOperation createNestedLoopJoin(JoinNode node, LocalExecutionPlan NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory( buildContext.getNextOperatorId(), node.getId(), - nestedLoopJoinPagesSupplierManager); + nestedLoopJoinBridgeManager); checkArgument(buildContext.getDriverInstanceCount().orElse(1) == 1, "Expected local execution to not be parallel"); context.addDriverFactory( @@ -1747,12 +1767,12 @@ private PhysicalOperation createNestedLoopJoin(JoinNode node, LocalExecutionPlan outputMappings.put(entry.getKey(), offset + entry.getValue()); } - OperatorFactory operatorFactory = new NestedLoopJoinOperatorFactory(context.getNextOperatorId(), node.getId(), nestedLoopJoinPagesSupplierManager); + OperatorFactory operatorFactory = new NestedLoopJoinOperatorFactory(context.getNextOperatorId(), node.getId(), nestedLoopJoinBridgeManager); return new PhysicalOperation(operatorFactory, outputMappings.build(), context, probeSource); } private PhysicalOperation createSpatialLookupJoin( - JoinNode node, + SpatialJoinNode node, PlanNode probeNode, Symbol probeSymbol, PlanNode buildNode, @@ -1787,7 +1807,7 @@ private PhysicalOperation createSpatialLookupJoin( return new PhysicalOperation(operator, outputMappings.build(), context, probeSource); } - private OperatorFactory createSpatialLookupJoin(JoinNode node, + private OperatorFactory createSpatialLookupJoin(SpatialJoinNode node, PlanNode probeNode, PhysicalOperation probeSource, Symbol probeSymbol, @@ -1799,7 +1819,10 @@ private OperatorFactory createSpatialLookupJoin(JoinNode node, .filter(symbol -> probeNode.getOutputSymbols().contains(symbol)) .collect(toImmutableList()); List probeOutputChannels = ImmutableList.copyOf(getChannelsForSymbols(probeOutputSymbols, probeSource.getLayout())); - Integer probeChannel = channelGetter(probeSource).apply(probeSymbol); + Function probeChannelGetter = channelGetter(probeSource); + int probeChannel = probeChannelGetter.apply(probeSymbol); + + Optional partitionChannel = node.getLeftPartitionSymbol().map(probeChannelGetter::apply); return new SpatialJoinOperatorFactory( context.getNextOperatorId(), @@ -1808,11 +1831,12 @@ private OperatorFactory createSpatialLookupJoin(JoinNode node, probeTypes, probeOutputChannels, probeChannel, + partitionChannel, pagesSpatialIndexFactory); } private PagesSpatialIndexFactory createPagesSpatialIndexFactory( - JoinNode node, + SpatialJoinNode node, PlanNode buildNode, Symbol buildSymbol, Optional radiusSymbol, @@ -1840,6 +1864,8 @@ private PagesSpatialIndexFactory createPagesSpatialIndexFactory( context.getTypes(), context.getSession())); + Optional partitionChannel = node.getRightPartitionSymbol().map(buildChannelGetter::apply); + SpatialIndexBuilderOperatorFactory builderOperatorFactory = new SpatialIndexBuilderOperatorFactory( buildContext.getNextOperatorId(), node.getId(), @@ -1847,7 +1873,9 @@ private PagesSpatialIndexFactory createPagesSpatialIndexFactory( buildOutputChannels, buildChannel, radiusChannel, + partitionChannel, spatialRelationshipTest, + node.getKdbTree(), filterFunctionFactory, 10_000, pagesIndexFactory); @@ -1878,7 +1906,7 @@ private PhysicalOperation createLookupJoin(JoinNode node, PhysicalOperation probeSource = probeNode.accept(this, context); // Plan build - JoinBridgeDataManager lookupSourceFactory = + JoinBridgeManager lookupSourceFactory = createLookupSourceFactory(node, buildNode, buildSymbols, buildHashSymbol, probeSource, context); OperatorFactory operator = createLookupJoin(node, probeSource, probeSymbols, probeHashSymbol, lookupSourceFactory, context); @@ -1893,7 +1921,7 @@ private PhysicalOperation createLookupJoin(JoinNode node, return new PhysicalOperation(operator, outputMappings.build(), context, probeSource); } - private JoinBridgeDataManager createLookupSourceFactory( + private JoinBridgeManager createLookupSourceFactory( JoinNode node, PlanNode buildNode, List buildSymbols, @@ -1954,7 +1982,8 @@ private JoinBridgeDataManager createLookupSourceFactory( ImmutableList buildOutputTypes = buildOutputChannels.stream() .map(buildSource.getTypes()::get) .collect(toImmutableList()); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookup( + JoinBridgeManager lookupSourceFactoryManager = new JoinBridgeManager<>( + buildOuter, probeSource.getPipelineExecutionStrategy(), buildSource.getPipelineExecutionStrategy(), lifespan -> new PartitionedLookupSourceFactory( @@ -1965,7 +1994,7 @@ private JoinBridgeDataManager createLookupSourceFactory( .collect(toImmutableList()), buildContext.getDriverInstanceCount().orElse(1), buildSource.getLayout(), - node.getType() == RIGHT || node.getType() == FULL), + buildOuter), buildOutputTypes); HashBuilderOperatorFactory hashBuilderOperatorFactory = new HashBuilderOperatorFactory( buildContext.getNextOperatorId(), @@ -2014,7 +2043,8 @@ private JoinFilterFunctionFactory compileJoinFilterFunction( sqlParser, sourceTypes, rewrittenFilter, - emptyList() /* parameters have already been replaced */); + emptyList(), /* parameters have already been replaced */ + NOOP); RowExpression translatedFilter = toRowExpression(rewrittenFilter, expressionTypes); return joinFilterFunctionCompiler.compileJoinFilterFunction(translatedFilter, buildLayout.size()); @@ -2036,7 +2066,7 @@ private OperatorFactory createLookupJoin( PhysicalOperation probeSource, List probeSymbols, Optional probeHashSymbol, - JoinBridgeDataManager lookupSourceFactory, + JoinBridgeManager lookupSourceFactoryManager, LocalExecutionPlanContext context) { List probeTypes = probeSource.getTypes(); @@ -2051,13 +2081,13 @@ private OperatorFactory createLookupJoin( switch (node.getType()) { case INNER: - return lookupJoinOperators.innerJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactory, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); + return lookupJoinOperators.innerJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactoryManager, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); case LEFT: - return lookupJoinOperators.probeOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactory, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); + return lookupJoinOperators.probeOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactoryManager, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); case RIGHT: - return lookupJoinOperators.lookupOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactory, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); + return lookupJoinOperators.lookupOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactoryManager, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); case FULL: - return lookupJoinOperators.fullOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactory, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); + return lookupJoinOperators.fullOuterJoin(context.getNextOperatorId(), node.getId(), lookupSourceFactoryManager, probeTypes, probeJoinChannels, probeHashChannel, Optional.of(probeOutputChannels), totalOperatorsCount, partitioningSpillerFactory); default: throw new UnsupportedOperationException("Unsupported join type: " + node.getType()); } @@ -2153,10 +2183,11 @@ public PhysicalOperation visitTableWriter(TableWriterNode node, LocalExecutionPl node.getId(), aggregation.getAggregations(), PARTIAL, - 2, + STATS_START_CHANNEL, outputMapping, source, - context); + context, + true); } return createHashAggregationOperatorFactory( node.getId(), @@ -2172,8 +2203,16 @@ public PhysicalOperation visitTableWriter(TableWriterNode node, LocalExecutionPl false, new DataSize(0, BYTE), context, - 2, - outputMapping); + STATS_START_CHANNEL, + outputMapping, + 200, + // This aggregation must behave as INTERMEDIATE. + // Using INTERMEDIATE aggregation directly + // is not possible, as it doesn't accept raw input data. + // Disabling partial pre-aggregation memory limit effectively + // turns PARTIAL aggregation into INTERMEDIATE. + Optional.empty(), + true); }).orElse(new DevNullOperatorFactory(context.getNextOperatorId(), node.getId())); List inputChannels = node.getColumns().stream() @@ -2210,7 +2249,8 @@ public PhysicalOperation visitTableFinish(TableFinishNode node, LocalExecutionPl 0, outputMapping, source, - context); + context, + true); } return createHashAggregationOperatorFactory( node.getId(), @@ -2227,7 +2267,11 @@ public PhysicalOperation visitTableFinish(TableFinishNode node, LocalExecutionPl new DataSize(0, BYTE), context, 0, - outputMapping); + outputMapping, + 200, + // final aggregation ignores partial pre-aggregation memory limit + Optional.empty(), + true); }).orElse(new DevNullOperatorFactory(context.getNextOperatorId(), node.getId())); Map aggregationOutput = outputMapping.build(); @@ -2240,7 +2284,8 @@ public PhysicalOperation visitTableFinish(TableFinishNode node, LocalExecutionPl node.getId(), createTableFinisher(session, node, metadata), statisticsAggregation, - descriptor); + descriptor, + session); Map layout = ImmutableMap.of(node.getOutputSymbols().get(0), 0); return new PhysicalOperation(operatorFactory, layout, context, source); @@ -2457,10 +2502,82 @@ private AccumulatorFactory buildAccumulatorFactory( PhysicalOperation source, Aggregation aggregation) { - List arguments = new ArrayList<>(); + InternalAggregationFunction internalAggregationFunction = metadata + .getFunctionRegistry() + .getAggregateFunctionImplementation(aggregation.getSignature()); + + List valueChannels = new ArrayList<>(); for (Expression argument : aggregation.getCall().getArguments()) { - Symbol argumentSymbol = Symbol.from(argument); - arguments.add(source.getLayout().get(argumentSymbol)); + if (!(argument instanceof LambdaExpression)) { + Symbol argumentSymbol = Symbol.from(argument); + valueChannels.add(source.getLayout().get(argumentSymbol)); + } + } + + List lambdaProviders = new ArrayList<>(); + List lambdaExpressions = aggregation.getCall().getArguments().stream() + .filter(LambdaExpression.class::isInstance) + .map(LambdaExpression.class::cast) + .collect(toImmutableList()); + if (!lambdaExpressions.isEmpty()) { + List functionTypes = aggregation.getSignature().getArgumentTypes().stream() + .filter(typeSignature -> typeSignature.getBase().equals(FunctionType.NAME)) + .map(typeSignature -> (FunctionType) (metadata.getTypeManager().getType(typeSignature))) + .collect(toImmutableList()); + List lambdaInterfaces = internalAggregationFunction.getLambdaInterfaces(); + verify(lambdaExpressions.size() == functionTypes.size()); + verify(lambdaExpressions.size() == lambdaInterfaces.size()); + + for (int i = 0; i < lambdaExpressions.size(); i++) { + LambdaExpression lambdaExpression = lambdaExpressions.get(i); + FunctionType functionType = functionTypes.get(i); + + // To compile lambda, LambdaDefinitionExpression needs to be generated from LambdaExpression, + // which requires the types of all sub-expressions. + // + // In project and filter expression compilation, ExpressionAnalyzer.getExpressionTypesFromInput + // is used to generate the types of all sub-expressions. (see visitScanFilterAndProject and visitFilter) + // + // This does not work here since the function call representation in final aggregation node + // is currently a hack: it takes intermediate type as input, and may not be a valid + // function call in Presto. + // + // TODO: Once the final aggregation function call representation is fixed, + // the same mechanism in project and filter expression should be used here. + verify(lambdaExpression.getArguments().size() == functionType.getArgumentTypes().size()); + Map, Type> lambdaArgumentExpressionTypes = new HashMap<>(); + Map lambdaArgumentSymbolTypes = new HashMap<>(); + for (int j = 0; j < lambdaExpression.getArguments().size(); j++) { + LambdaArgumentDeclaration argument = lambdaExpression.getArguments().get(j); + Type type = functionType.getArgumentTypes().get(j); + lambdaArgumentExpressionTypes.put(NodeRef.of(argument), type); + lambdaArgumentSymbolTypes.put(new Symbol(argument.getName().getValue()), type); + } + Map, Type> expressionTypes = ImmutableMap., Type>builder() + // the lambda expression itself + .put(NodeRef.of(lambdaExpression), functionType) + // expressions from lambda arguments + .putAll(lambdaArgumentExpressionTypes) + // expressions from lambda body + .putAll(getExpressionTypes( + session, + metadata, + sqlParser, + TypeProvider.copyOf(lambdaArgumentSymbolTypes), + lambdaExpression.getBody(), + emptyList(), + NOOP)) + .build(); + + LambdaDefinitionExpression lambda = (LambdaDefinitionExpression) toRowExpression(lambdaExpression, expressionTypes); + Class lambdaProviderClass = compileLambdaProvider(lambda, metadata.getFunctionRegistry(), lambdaInterfaces.get(i)); + try { + lambdaProviders.add((LambdaProvider) constructorMethodHandle(lambdaProviderClass, ConnectorSession.class).invoke(session.toConnectorSession())); + } + catch (Throwable t) { + throw new RuntimeException(t); + } + } } Optional maskChannel = aggregation.getMask().map(value -> source.getLayout().get(value)); @@ -2479,10 +2596,17 @@ private AccumulatorFactory buildAccumulatorFactory( .collect(toImmutableList()); } - return metadata - .getFunctionRegistry() - .getAggregateFunctionImplementation(aggregation.getSignature()) - .bind(arguments, maskChannel, source.getTypes(), getChannelsForSymbols(sortKeys, source.getLayout()), sortOrders, pagesIndexFactory, aggregation.getCall().isDistinct(), joinCompiler, session); + return internalAggregationFunction.bind( + valueChannels, + maskChannel, + source.getTypes(), + getChannelsForSymbols(sortKeys, source.getLayout()), + sortOrders, + pagesIndexFactory, + aggregation.getCall().isDistinct(), + joinCompiler, + lambdaProviders, + session); } private PhysicalOperation planGlobalAggregation(AggregationNode node, PhysicalOperation source, LocalExecutionPlanContext context) @@ -2495,7 +2619,8 @@ private PhysicalOperation planGlobalAggregation(AggregationNode node, PhysicalOp 0, outputMappings, source, - context); + context, + node.getStep().isOutputPartial()); return new PhysicalOperation(operatorFactory, outputMappings.build(), context, source); } @@ -2506,7 +2631,8 @@ private AggregationOperatorFactory createAggregationOperatorFactory( int startOutputChannel, ImmutableMap.Builder outputMappings, PhysicalOperation source, - LocalExecutionPlanContext context) + LocalExecutionPlanContext context, + boolean useSystemMemory) { int outputChannel = startOutputChannel; ImmutableList.Builder accumulatorFactories = ImmutableList.builder(); @@ -2517,7 +2643,7 @@ private AggregationOperatorFactory createAggregationOperatorFactory( outputMappings.put(symbol, outputChannel); // one aggregation per channel outputChannel++; } - return new AggregationOperatorFactory(context.getNextOperatorId(), planNodeId, step, accumulatorFactories.build()); + return new AggregationOperatorFactory(context.getNextOperatorId(), planNodeId, step, accumulatorFactories.build(), useSystemMemory); } private PhysicalOperation planGroupByAggregation( @@ -2543,7 +2669,10 @@ private PhysicalOperation planGroupByAggregation( unspillMemoryLimit, context, 0, - mappings); + mappings, + 10_000, + Optional.of(maxPartialAggregationMemorySize), + node.getStep().isOutputPartial()); return new PhysicalOperation(operatorFactory, mappings.build(), context, source); } @@ -2562,7 +2691,10 @@ private OperatorFactory createHashAggregationOperatorFactory( DataSize unspillMemoryLimit, LocalExecutionPlanContext context, int startOutputChannel, - ImmutableMap.Builder outputMappings) + ImmutableMap.Builder outputMappings, + int expectedGroups, + Optional maxPartialAggregationMemorySize, + boolean useSystemMemory) { List aggregationOutputSymbols = new ArrayList<>(); List accumulatorFactories = new ArrayList<>(); @@ -2625,12 +2757,13 @@ private OperatorFactory createHashAggregationOperatorFactory( accumulatorFactories, hashChannel, groupIdChannel, - 10_000, + expectedGroups, maxPartialAggregationMemorySize, spillEnabled, unspillMemoryLimit, spillerFactory, - joinCompiler); + joinCompiler, + useSystemMemory); } } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java index 31ae111be62cf..39fb55ce18f22 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/LogicalPlanner.java @@ -15,6 +15,14 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.CachingCostProvider; +import com.facebook.presto.cost.CachingStatsProvider; +import com.facebook.presto.cost.CostCalculator; +import com.facebook.presto.cost.CostProvider; +import com.facebook.presto.cost.StatsAndCosts; +import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.NewTableLayout; import com.facebook.presto.metadata.QualifiedObjectName; @@ -101,14 +109,20 @@ public enum Stage private final Metadata metadata; private final SqlParser sqlParser; private final StatisticsAggregationPlanner statisticsAggregationPlanner; + private final StatsCalculator statsCalculator; + private final CostCalculator costCalculator; + private final WarningCollector warningCollector; public LogicalPlanner(Session session, List planOptimizers, PlanNodeIdAllocator idAllocator, Metadata metadata, - SqlParser sqlParser) + SqlParser sqlParser, + StatsCalculator statsCalculator, + CostCalculator costCalculator, + WarningCollector warningCollector) { - this(session, planOptimizers, DISTRIBUTED_PLAN_SANITY_CHECKER, idAllocator, metadata, sqlParser); + this(session, planOptimizers, DISTRIBUTED_PLAN_SANITY_CHECKER, idAllocator, metadata, sqlParser, statsCalculator, costCalculator, warningCollector); } public LogicalPlanner(Session session, @@ -116,22 +130,21 @@ public LogicalPlanner(Session session, PlanSanityChecker planSanityChecker, PlanNodeIdAllocator idAllocator, Metadata metadata, - SqlParser sqlParser) + SqlParser sqlParser, + StatsCalculator statsCalculator, + CostCalculator costCalculator, + WarningCollector warningCollector) { - requireNonNull(session, "session is null"); - requireNonNull(planOptimizers, "planOptimizers is null"); - requireNonNull(planSanityChecker, "planSanityChecker is null"); - requireNonNull(idAllocator, "idAllocator is null"); - requireNonNull(metadata, "metadata is null"); - requireNonNull(sqlParser, "sqlParser is null"); - - this.session = session; - this.planOptimizers = planOptimizers; - this.planSanityChecker = planSanityChecker; - this.idAllocator = idAllocator; - this.metadata = metadata; - this.sqlParser = sqlParser; + this.session = requireNonNull(session, "session is null"); + this.planOptimizers = requireNonNull(planOptimizers, "planOptimizers is null"); + this.planSanityChecker = requireNonNull(planSanityChecker, "planSanityChecker is null"); + this.idAllocator = requireNonNull(idAllocator, "idAllocator is null"); + this.metadata = requireNonNull(metadata, "metadata is null"); + this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.statisticsAggregationPlanner = new StatisticsAggregationPlanner(symbolAllocator, metadata); + this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); + this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } public Plan plan(Analysis analysis) @@ -143,21 +156,24 @@ public Plan plan(Analysis analysis, Stage stage) { PlanNode root = planStatement(analysis, analysis.getStatement()); - planSanityChecker.validateIntermediatePlan(root, session, metadata, sqlParser, symbolAllocator.getTypes()); + planSanityChecker.validateIntermediatePlan(root, session, metadata, sqlParser, symbolAllocator.getTypes(), warningCollector); if (stage.ordinal() >= Stage.OPTIMIZED.ordinal()) { for (PlanOptimizer optimizer : planOptimizers) { - root = optimizer.optimize(root, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator); + root = optimizer.optimize(root, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator, warningCollector); requireNonNull(root, format("%s returned a null plan", optimizer.getClass().getName())); } } if (stage.ordinal() >= Stage.OPTIMIZED_AND_VALIDATED.ordinal()) { // make sure we produce a valid plan after optimizations run. This is mainly to catch programming errors - planSanityChecker.validateFinalPlan(root, session, metadata, sqlParser, symbolAllocator.getTypes()); + planSanityChecker.validateFinalPlan(root, session, metadata, sqlParser, symbolAllocator.getTypes(), warningCollector); } - return new Plan(root, symbolAllocator.getTypes()); + TypeProvider types = symbolAllocator.getTypes(); + StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, types); + CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.empty(), session, types); + return new Plan(root, types, StatsAndCosts.create(root, statsProvider, costProvider)); } public PlanNode planStatement(Analysis analysis, Statement statement) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitionMap.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitionMap.java index 50232eab7f5f6..d3216fb762156 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitionMap.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitionMap.java @@ -13,38 +13,47 @@ */ package com.facebook.presto.sql.planner; +import com.facebook.presto.execution.scheduler.BucketNodeMap; +import com.facebook.presto.execution.scheduler.FixedBucketNodeMap; import com.facebook.presto.metadata.Split; import com.facebook.presto.spi.Node; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; -import java.util.Map; +import java.util.List; import java.util.function.ToIntFunction; import java.util.stream.IntStream; import static java.util.Objects.requireNonNull; +// When the probe side of join is bucketed but builder side is not, +// bucket to partition mapping has to be populated to builder side remote fragment. +// NodePartitionMap is required in this case and cannot be replaced by BucketNodeMap. +// +// Join +// / \ +// Scan Remote +// public class NodePartitionMap { - private final Map partitionToNode; + private final List partitionToNode; private final int[] bucketToPartition; private final ToIntFunction splitToBucket; - public NodePartitionMap(Map partitionToNode, ToIntFunction splitToBucket) + public NodePartitionMap(List partitionToNode, ToIntFunction splitToBucket) { - this.partitionToNode = ImmutableMap.copyOf(requireNonNull(partitionToNode, "partitionToNode is null")); - + this.partitionToNode = ImmutableList.copyOf(requireNonNull(partitionToNode, "partitionToNode is null")); this.bucketToPartition = IntStream.range(0, partitionToNode.size()).toArray(); this.splitToBucket = requireNonNull(splitToBucket, "splitToBucket is null"); } - public NodePartitionMap(Map partitionToNode, int[] bucketToPartition, ToIntFunction splitToBucket) + public NodePartitionMap(List partitionToNode, int[] bucketToPartition, ToIntFunction splitToBucket) { this.bucketToPartition = requireNonNull(bucketToPartition, "bucketToPartition is null"); - this.partitionToNode = ImmutableMap.copyOf(requireNonNull(partitionToNode, "partitionToNode is null")); + this.partitionToNode = ImmutableList.copyOf(requireNonNull(partitionToNode, "partitionToNode is null")); this.splitToBucket = requireNonNull(splitToBucket, "splitToBucket is null"); } - public Map getPartitionToNode() + public List getPartitionToNode() { return partitionToNode; } @@ -60,4 +69,13 @@ public Node getNode(Split split) int partition = bucketToPartition[bucket]; return requireNonNull(partitionToNode.get(partition)); } + + public BucketNodeMap asBucketNodeMap() + { + ImmutableList.Builder bucketToNode = ImmutableList.builder(); + for (int partition : bucketToPartition) { + bucketToNode.add(partitionToNode.get(partition)); + } + return new FixedBucketNodeMap(splitToBucket, bucketToNode.build()); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitioningManager.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitioningManager.java index 5041ae2ede1f5..066b4a4db05c2 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitioningManager.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/NodePartitioningManager.java @@ -15,12 +15,17 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.scheduler.BucketNodeMap; +import com.facebook.presto.execution.scheduler.FixedBucketNodeMap; import com.facebook.presto.execution.scheduler.NodeScheduler; +import com.facebook.presto.execution.scheduler.group.DynamicBucketNodeMap; +import com.facebook.presto.metadata.Split; import com.facebook.presto.operator.BucketPartitionFunction; import com.facebook.presto.operator.PartitionFunction; import com.facebook.presto.spi.BucketFunction; import com.facebook.presto.spi.ConnectorSplit; import com.facebook.presto.spi.Node; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; import com.facebook.presto.spi.type.Type; @@ -30,15 +35,19 @@ import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.ToIntFunction; +import java.util.stream.IntStream; +import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; public class NodePartitioningManager @@ -119,49 +128,117 @@ public NodePartitionMap getNodePartitioningMap(Session session, PartitioningHand return ((SystemPartitioningHandle) partitioningHandle.getConnectorHandle()).getNodePartitionMap(session, nodeScheduler); } - ConnectorNodePartitioningProvider partitioningProvider = partitioningProviders.get(partitioningHandle.getConnectorId().get()); - checkArgument(partitioningProvider != null, "No partitioning provider for connector %s", partitioningHandle.getConnectorId().get()); - - Map bucketToNode = partitioningProvider.getBucketToNode( - partitioningHandle.getTransactionHandle().orElse(null), - session.toConnectorSession(), - partitioningHandle.getConnectorHandle()); - checkArgument(bucketToNode != null, "No partition map %s", partitioningHandle); - checkArgument(!bucketToNode.isEmpty(), "Partition map %s is empty", partitioningHandle); - - int bucketCount = bucketToNode.keySet().stream() - .mapToInt(Integer::intValue) - .max() - .getAsInt() + 1; + ConnectorId connectorId = partitioningHandle.getConnectorId() + .orElseThrow(() -> new IllegalArgumentException("No connector ID for partitioning handle: " + partitioningHandle)); + ConnectorNodePartitioningProvider partitioningProvider = partitioningProviders.get(connectorId); + checkArgument(partitioningProvider != null, "No partitioning provider for connector %s", connectorId); + ConnectorBucketNodeMap connectorBucketNodeMap = getConnectorBucketNodeMap(session, partitioningHandle); // safety check for crazy partitioning - checkArgument(bucketCount < 1_000_000, "Too many buckets in partitioning: %s", bucketCount); + checkArgument(connectorBucketNodeMap.getBucketCount() < 1_000_000, "Too many buckets in partitioning: %s", connectorBucketNodeMap.getBucketCount()); + + List bucketToNode; + if (connectorBucketNodeMap.hasFixedMapping()) { + bucketToNode = connectorBucketNodeMap.getFixedMapping(); + } + else { + bucketToNode = createArbitraryBucketToNode( + nodeScheduler.createNodeSelector(connectorId).allNodes(), + connectorBucketNodeMap.getBucketCount()); + } - int[] bucketToPartition = new int[bucketCount]; + int[] bucketToPartition = new int[connectorBucketNodeMap.getBucketCount()]; BiMap nodeToPartition = HashBiMap.create(); int nextPartitionId = 0; - for (Entry entry : bucketToNode.entrySet()) { - Integer partitionId = nodeToPartition.get(entry.getValue()); + for (int bucket = 0; bucket < bucketToNode.size(); bucket++) { + Node node = bucketToNode.get(bucket); + Integer partitionId = nodeToPartition.get(node); if (partitionId == null) { partitionId = nextPartitionId++; - nodeToPartition.put(entry.getValue(), partitionId); + nodeToPartition.put(node, partitionId); } - bucketToPartition[entry.getKey()] = partitionId; + bucketToPartition[bucket] = partitionId; } + List partitionToNode = IntStream.range(0, nodeToPartition.size()) + .mapToObj(partitionId -> nodeToPartition.inverse().get(partitionId)) + .collect(toImmutableList()); + + return new NodePartitionMap(partitionToNode, bucketToPartition, getSplitToBucket(session, partitioningHandle)); + } + + public BucketNodeMap getBucketNodeMap(Session session, PartitioningHandle partitioningHandle, boolean preferDynamic) + { + ConnectorBucketNodeMap connectorBucketNodeMap = getConnectorBucketNodeMap(session, partitioningHandle); + + if (connectorBucketNodeMap.hasFixedMapping()) { + return new FixedBucketNodeMap(getSplitToBucket(session, partitioningHandle), connectorBucketNodeMap.getFixedMapping()); + } + + if (preferDynamic) { + return new DynamicBucketNodeMap(getSplitToBucket(session, partitioningHandle), connectorBucketNodeMap.getBucketCount()); + } + + return new FixedBucketNodeMap( + getSplitToBucket(session, partitioningHandle), + createArbitraryBucketToNode( + new ArrayList<>(nodeScheduler.createNodeSelector(partitioningHandle.getConnectorId().get()).allNodes()), + connectorBucketNodeMap.getBucketCount())); + } + + private ConnectorBucketNodeMap getConnectorBucketNodeMap(Session session, PartitioningHandle partitioningHandle) + { + checkArgument(!(partitioningHandle.getConnectorHandle() instanceof SystemPartitioningHandle)); + + ConnectorNodePartitioningProvider partitioningProvider = partitioningProviders.get(partitioningHandle.getConnectorId().get()); + checkArgument(partitioningProvider != null, "No partitioning provider for connector %s", partitioningHandle.getConnectorId().get()); + + ConnectorBucketNodeMap connectorBucketNodeMap = partitioningProvider.getBucketNodeMap( + partitioningHandle.getTransactionHandle().orElse(null), + session.toConnectorSession(), + partitioningHandle.getConnectorHandle()); + + checkArgument(connectorBucketNodeMap != null, "No partition map %s", partitioningHandle); + return connectorBucketNodeMap; + } + + private ToIntFunction getSplitToBucket(Session session, PartitioningHandle partitioningHandle) + { + ConnectorNodePartitioningProvider partitioningProvider = partitioningProviders.get(partitioningHandle.getConnectorId().get()); + checkArgument(partitioningProvider != null, "No partitioning provider for connector %s", partitioningHandle.getConnectorId().get()); + ToIntFunction splitBucketFunction = partitioningProvider.getSplitBucketFunction( partitioningHandle.getTransactionHandle().orElse(null), session.toConnectorSession(), partitioningHandle.getConnectorHandle()); checkArgument(splitBucketFunction != null, "No partitioning %s", partitioningHandle); - return new NodePartitionMap(nodeToPartition.inverse(), bucketToPartition, split -> { - // Assign EmptySplit to bucket 0. - // More details about EmptySplit can be found in SourcePartitionedScheduler. + return split -> { + int bucket; if (split.getConnectorSplit() instanceof EmptySplit) { - return 0; + bucket = split.getLifespan().isTaskWide() ? 0 : split.getLifespan().getId(); } - return splitBucketFunction.applyAsInt(split.getConnectorSplit()); - }); + else { + bucket = splitBucketFunction.applyAsInt(split.getConnectorSplit()); + } + if (!split.getLifespan().isTaskWide()) { + checkArgument(split.getLifespan().getId() == bucket); + } + return bucket; + }; + } + + private static List createArbitraryBucketToNode(List nodes, int bucketCount) + { + return cyclingShuffledStream(nodes) + .limit(bucketCount) + .collect(toImmutableList()); + } + + private static Stream cyclingShuffledStream(Collection collection) + { + List list = new ArrayList<>(collection); + Collections.shuffle(list); + return Stream.generate(() -> list).flatMap(List::stream); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/Partitioning.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/Partitioning.java index 2c244f5b4bb91..d7b6854725ebf 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/Partitioning.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/Partitioning.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.sql.planner; +import com.facebook.presto.Session; +import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.predicate.NullableValue; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -82,12 +84,27 @@ public Set getColumns() .collect(toImmutableSet()); } - public boolean isPartitionedWith(Partitioning right, + public boolean isCompatibleWith( + Partitioning right, + Metadata metadata, + Session session) + { + if (!handle.equals(right.handle) && !metadata.getCommonPartitioning(session, handle, right.handle).isPresent()) { + return false; + } + + return arguments.equals(right.arguments); + } + + public boolean isCompatibleWith( + Partitioning right, Function> leftToRightMappings, Function> leftConstantMapping, - Function> rightConstantMapping) + Function> rightConstantMapping, + Metadata metadata, + Session session) { - if (!handle.equals(right.handle)) { + if (!handle.equals(right.handle) && !metadata.getCommonPartitioning(session, handle, right.handle).isPresent()) { return false; } @@ -191,6 +208,11 @@ public Optional translate(Function> trans return Optional.of(new Partitioning(handle, newArguments.build())); } + public Partitioning withAlternativePartitiongingHandle(PartitioningHandle partitiongingHandle) + { + return new Partitioning(partitiongingHandle, this.arguments); + } + @Override public int hashCode() { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/Plan.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/Plan.java index bb21c591de879..dcb3b2d316df3 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/Plan.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/Plan.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql.planner; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.sql.planner.plan.PlanNode; import static java.util.Objects.requireNonNull; @@ -21,14 +22,13 @@ public class Plan { private final PlanNode root; private final TypeProvider types; + private final StatsAndCosts statsAndCosts; - public Plan(PlanNode root, TypeProvider types) + public Plan(PlanNode root, TypeProvider types, StatsAndCosts statsAndCosts) { - requireNonNull(root, "root is null"); - requireNonNull(types, "types is null"); - - this.root = root; - this.types = types; + this.root = requireNonNull(root, "root is null"); + this.types = requireNonNull(types, "types is null"); + this.statsAndCosts = requireNonNull(statsAndCosts, "statsAndCosts is null"); } public PlanNode getRoot() @@ -40,4 +40,9 @@ public TypeProvider getTypes() { return types; } + + public StatsAndCosts getStatsAndCosts() + { + return statsAndCosts; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragment.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragment.java index b2677e1281473..bcdef51538482 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragment.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragment.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql.planner; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.plan.PlanFragmentId; @@ -51,6 +52,7 @@ public class PlanFragment private final List remoteSourceNodes; private final PartitioningScheme partitioningScheme; private final StageExecutionStrategy stageExecutionStrategy; + private final StatsAndCosts statsAndCosts; @JsonCreator public PlanFragment( @@ -60,7 +62,8 @@ public PlanFragment( @JsonProperty("partitioning") PartitioningHandle partitioning, @JsonProperty("partitionedSources") List partitionedSources, @JsonProperty("partitioningScheme") PartitioningScheme partitioningScheme, - @JsonProperty("stageExecutionStrategy") StageExecutionStrategy stageExecutionStrategy) + @JsonProperty("stageExecutionStrategy") StageExecutionStrategy stageExecutionStrategy, + @JsonProperty("statsAndCosts") StatsAndCosts statsAndCosts) { this.id = requireNonNull(id, "id is null"); this.root = requireNonNull(root, "root is null"); @@ -69,6 +72,7 @@ public PlanFragment( this.partitionedSources = ImmutableList.copyOf(requireNonNull(partitionedSources, "partitionedSources is null")); this.partitionedSourcesSet = ImmutableSet.copyOf(partitionedSources); this.stageExecutionStrategy = requireNonNull(stageExecutionStrategy, "stageExecutionStrategy is null"); + this.statsAndCosts = requireNonNull(statsAndCosts, "statsAndCosts is null"); checkArgument(partitionedSourcesSet.size() == partitionedSources.size(), "partitionedSources contains duplicates"); checkArgument(ImmutableSet.copyOf(root.getOutputSymbols()).containsAll(partitioningScheme.getOutputLayout()), @@ -134,6 +138,12 @@ public StageExecutionStrategy getStageExecutionStrategy() return stageExecutionStrategy; } + @JsonProperty + public StatsAndCosts getStatsAndCosts() + { + return statsAndCosts; + } + public List getTypes() { return types; @@ -185,12 +195,12 @@ private static void findRemoteSourceNodes(PlanNode node, Builder bucketToPartition) { - return new PlanFragment(id, root, symbols, partitioning, partitionedSources, partitioningScheme.withBucketToPartition(bucketToPartition), stageExecutionStrategy); + return new PlanFragment(id, root, symbols, partitioning, partitionedSources, partitioningScheme.withBucketToPartition(bucketToPartition), stageExecutionStrategy, statsAndCosts); } public PlanFragment withGroupedExecution(List capableTableScanNodes) { - return new PlanFragment(id, root, symbols, partitioning, partitionedSources, partitioningScheme, StageExecutionStrategy.groupedExecution(capableTableScanNodes)); + return new PlanFragment(id, root, symbols, partitioning, partitionedSources, partitioningScheme, StageExecutionStrategy.groupedExecution(capableTableScanNodes), statsAndCosts); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragmenter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragmenter.java index 1a86195f824e4..135701d29e03a 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragmenter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanFragmenter.java @@ -15,10 +15,12 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.execution.QueryManagerConfig; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableLayout; import com.facebook.presto.metadata.TableLayout.TablePartitioning; +import com.facebook.presto.metadata.TableLayoutHandle; import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; @@ -37,6 +39,7 @@ import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; +import com.facebook.presto.sql.planner.plan.TableWriterNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -71,18 +74,19 @@ */ public class PlanFragmenter { + private final Metadata metadata; + private final NodePartitioningManager nodePartitioningManager; + @Inject - public PlanFragmenter(QueryManagerConfig queryManagerConfig) + public PlanFragmenter(Metadata metadata, NodePartitioningManager nodePartitioningManager, QueryManagerConfig queryManagerConfig) { - // TODO: Remove query_max_stage_count session property and use queryManagerConfig.getMaxStageCount() here - this(); + this.metadata = requireNonNull(metadata, "metadata is null"); + this.nodePartitioningManager = requireNonNull(nodePartitioningManager, "nodePartitioningManager is null"); } - private PlanFragmenter() {} - - public SubPlan createSubPlans(Session session, Metadata metadata, NodePartitioningManager nodePartitioningManager, Plan plan, boolean forceSingleNode) + public SubPlan createSubPlans(Session session, Plan plan, boolean forceSingleNode) { - Fragmenter fragmenter = new Fragmenter(session, metadata, plan.getTypes()); + Fragmenter fragmenter = new Fragmenter(session, metadata, plan.getTypes(), plan.getStatsAndCosts()); FragmentProperties properties = new FragmentProperties(new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), plan.getRoot().getOutputSymbols())); if (forceSingleNode || isForceSingleNodeOutput(session)) { @@ -91,9 +95,12 @@ public SubPlan createSubPlans(Session session, Metadata metadata, NodePartitioni PlanNode root = SimplePlanRewriter.rewriteWith(fragmenter, plan.getRoot(), properties); SubPlan subPlan = fragmenter.buildRootFragment(root, properties); - subPlan = analyzeGroupedExecution(session, metadata, nodePartitioningManager, subPlan); + subPlan = analyzeGroupedExecution(session, subPlan); + subPlan = reassignPartitioningHandleIfNecessary(session, subPlan); checkState(!isForceSingleNodeOutput(session) || subPlan.getFragment().getPartitioning().isSingleNode(), "Root of PlanFragment is not single node"); + + // TODO: Remove query_max_stage_count session property and use queryManagerConfig.getMaxStageCount() here sanityCheckFragmentedPlan(subPlan, getQueryMaxStageCount(session)); return subPlan; @@ -112,7 +119,7 @@ private void sanityCheckFragmentedPlan(SubPlan subPlan, int maxStageCount) } } - private static SubPlan analyzeGroupedExecution(Session session, Metadata metadata, NodePartitioningManager nodePartitioningManager, SubPlan subPlan) + private SubPlan analyzeGroupedExecution(Session session, SubPlan subPlan) { PlanFragment fragment = subPlan.getFragment(); GroupedExecutionProperties properties = fragment.getRoot().accept(new GroupedExecutionTagger(session, metadata, nodePartitioningManager), null); @@ -121,11 +128,54 @@ private static SubPlan analyzeGroupedExecution(Session session, Metadata metadat } ImmutableList.Builder result = ImmutableList.builder(); for (SubPlan child : subPlan.getChildren()) { - result.add(analyzeGroupedExecution(session, metadata, nodePartitioningManager, child)); + result.add(analyzeGroupedExecution(session, child)); } return new SubPlan(fragment, result.build()); } + private SubPlan reassignPartitioningHandleIfNecessary(Session session, SubPlan subPlan) + { + return reassignPartitioningHandleIfNecessaryHelper(session, subPlan, subPlan.getFragment().getPartitioning()); + } + + private SubPlan reassignPartitioningHandleIfNecessaryHelper(Session session, SubPlan subPlan, PartitioningHandle newOutputPartitioningHandle) + { + PlanFragment fragment = subPlan.getFragment(); + + PlanNode newRoot = fragment.getRoot(); + // If the fragment's partitioning is SINGLE or COORDINATOR_ONLY, leave the sources as is (this is for single-node execution) + if (!fragment.getPartitioning().isSingleNode()) { + PartitioningHandleReassigner partitioningHandleReassigner = new PartitioningHandleReassigner(fragment.getPartitioning(), metadata, session); + newRoot = SimplePlanRewriter.rewriteWith(partitioningHandleReassigner, newRoot); + } + PartitioningScheme outputPartitioningScheme = fragment.getPartitioningScheme(); + Partitioning newOutputPartitioning = outputPartitioningScheme.getPartitioning(); + if (outputPartitioningScheme.getPartitioning().getHandle().getConnectorId().isPresent()) { + // Do not replace the handle if the source's output handle is a system one, e.g. broadcast. + newOutputPartitioning = newOutputPartitioning.withAlternativePartitiongingHandle(newOutputPartitioningHandle); + } + PlanFragment newFragment = new PlanFragment( + fragment.getId(), + newRoot, + fragment.getSymbols(), + fragment.getPartitioning(), + fragment.getPartitionedSources(), + new PartitioningScheme( + newOutputPartitioning, + outputPartitioningScheme.getOutputLayout(), + outputPartitioningScheme.getHashColumn(), + outputPartitioningScheme.isReplicateNullsAndAny(), + outputPartitioningScheme.getBucketToPartition()), + fragment.getStageExecutionStrategy(), + fragment.getStatsAndCosts()); + + ImmutableList.Builder childrenBuilder = ImmutableList.builder(); + for (SubPlan child : subPlan.getChildren()) { + childrenBuilder.add(reassignPartitioningHandleIfNecessaryHelper(session, child, fragment.getPartitioning())); + } + return new SubPlan(newFragment, childrenBuilder.build()); + } + private static class Fragmenter extends SimplePlanRewriter { @@ -134,13 +184,15 @@ private static class Fragmenter private final Session session; private final Metadata metadata; private final TypeProvider types; + private final StatsAndCosts statsAndCosts; private int nextFragmentId = ROOT_FRAGMENT_ID + 1; - public Fragmenter(Session session, Metadata metadata, TypeProvider types) + public Fragmenter(Session session, Metadata metadata, TypeProvider types, StatsAndCosts statsAndCosts) { this.session = requireNonNull(session, "session is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.types = requireNonNull(types, "types is null"); + this.statsAndCosts = requireNonNull(statsAndCosts, "statsAndCosts is null"); } public SubPlan buildRootFragment(PlanNode root, FragmentProperties properties) @@ -168,7 +220,8 @@ private SubPlan buildFragment(PlanNode root, FragmentProperties properties, Plan properties.getPartitioningHandle(), schedulingOrder, properties.getPartitioningScheme(), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + statsAndCosts.getForSubplan(root)); return new SubPlan(fragment, properties.getChildren()); } @@ -213,7 +266,16 @@ public PlanNode visitTableScan(TableScanNode node, RewriteContext context) + { + if (node.getPartitioningScheme().isPresent()) { + context.get().setDistribution(node.getPartitioningScheme().get().getPartitioning().getHandle(), metadata, session); + } return context.defaultRewrite(node, context.get()); } @@ -237,7 +299,7 @@ public PlanNode visitExchange(ExchangeNode exchange, RewriteContext builder = ImmutableList.builder(); @@ -254,7 +316,7 @@ else if (exchange.getType() == ExchangeNode.Type.REPARTITION) { .map(PlanFragment::getId) .collect(toImmutableList()); - return new RemoteSourceNode(exchange.getId(), childrenIds, exchange.getOutputSymbols(), exchange.getOrderingScheme()); + return new RemoteSourceNode(exchange.getId(), childrenIds, exchange.getOutputSymbols(), exchange.getOrderingScheme(), exchange.getType()); } private SubPlan buildSubPlan(PlanNode node, FragmentProperties properties, RewriteContext context) @@ -301,34 +363,43 @@ public FragmentProperties setSingleNodeDistribution() return this; } - public FragmentProperties setDistribution(PartitioningHandle distribution) + public FragmentProperties setDistribution(PartitioningHandle distribution, Metadata metadata, Session session) { - if (partitioningHandle.isPresent()) { - chooseDistribution(distribution); + if (!partitioningHandle.isPresent()) { + partitioningHandle = Optional.of(distribution); return this; } - partitioningHandle = Optional.of(distribution); - return this; - } + PartitioningHandle currentPartitioning = this.partitioningHandle.get(); - private void chooseDistribution(PartitioningHandle distribution) - { - checkState(partitioningHandle.isPresent(), "No partitioning to choose from"); + if (isCompatibleSystemPartitioning(distribution)) { + return this; + } + + if (currentPartitioning.equals(SOURCE_DISTRIBUTION)) { + this.partitioningHandle = Optional.of(distribution); + return this; + } - if (partitioningHandle.get().equals(distribution) || - partitioningHandle.get().isSingleNode() || - isCompatibleSystemPartitioning(distribution)) { - return; + // If already system SINGLE or COORDINATOR_ONLY, leave it as is (this is for single-node execution) + if (currentPartitioning.isSingleNode()) { + return this; } - if (partitioningHandle.get().equals(SOURCE_DISTRIBUTION)) { - partitioningHandle = Optional.of(distribution); - return; + + if (currentPartitioning.equals(distribution)) { + return this; + } + + Optional commonPartitioning = metadata.getCommonPartitioning(session, currentPartitioning, distribution); + if (commonPartitioning.isPresent()) { + partitioningHandle = commonPartitioning; + return this; } + throw new IllegalStateException(format( "Cannot set distribution to %s. Already set to %s", distribution, - partitioningHandle)); + this.partitioningHandle)); } private boolean isCompatibleSystemPartitioning(PartitioningHandle distribution) @@ -361,28 +432,38 @@ public FragmentProperties setCoordinatorOnlyDistribution() return this; } - public FragmentProperties addSourceDistribution(PlanNodeId source, PartitioningHandle distribution) + public FragmentProperties addSourceDistribution(PlanNodeId source, PartitioningHandle distribution, Metadata metadata, Session session) { requireNonNull(source, "source is null"); requireNonNull(distribution, "distribution is null"); partitionedSources.add(source); - if (partitioningHandle.isPresent()) { - PartitioningHandle currentPartitioning = partitioningHandle.get(); - if (!currentPartitioning.equals(distribution)) { - // If already system SINGLE or COORDINATOR_ONLY, leave it as is (this is for single-node execution) - checkState( - currentPartitioning.equals(SINGLE_DISTRIBUTION) || currentPartitioning.equals(COORDINATOR_DISTRIBUTION), - "Cannot overwrite distribution with %s (currently set to %s)", - distribution, - currentPartitioning); - return this; - } + if (!partitioningHandle.isPresent()) { + partitioningHandle = Optional.of(distribution); + return this; } - partitioningHandle = Optional.of(distribution); - return this; + PartitioningHandle currentPartitioning = partitioningHandle.get(); + + // If already system SINGLE or COORDINATOR_ONLY, leave it as is (this is for single-node execution) + if (currentPartitioning.equals(SINGLE_DISTRIBUTION) || currentPartitioning.equals(COORDINATOR_DISTRIBUTION)) { + return this; + } + + if (currentPartitioning.equals(distribution)) { + return this; + } + + Optional commonPartitioning = metadata.getCommonPartitioning(session, currentPartitioning, distribution); + if (commonPartitioning.isPresent()) { + partitioningHandle = commonPartitioning; + return this; + } + + throw new IllegalStateException(String.format( + "Cannot overwrite distribution with %s (currently set to %s)", + distribution, currentPartitioning)); } public FragmentProperties addChildren(List children) @@ -421,7 +502,7 @@ public GroupedExecutionTagger(Session session, Metadata metadata, NodePartitioni this.session = requireNonNull(session, "session is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.nodePartitioningManager = requireNonNull(nodePartitioningManager, "nodePartitioningManager is null"); - this.groupedExecutionForAggregation = SystemSessionProperties.isGroupedExecutionForJoinEnabled(session); + this.groupedExecutionForAggregation = SystemSessionProperties.isGroupedExecutionForAggregationEnabled(session); } @Override @@ -444,6 +525,29 @@ public GroupedExecutionProperties visitJoin(JoinNode node, Void context) return GroupedExecutionProperties.notCapable(); } + if ((node.getType() == JoinNode.Type.RIGHT || node.getType() == JoinNode.Type.FULL) && !right.currentNodeCapable) { + // For a plan like this, if the fragment participates in grouped execution, + // the LookupOuterOperator corresponding to the RJoin will not work execute properly. + // + // * The operator has to execute as not-grouped because it can only look at the "used" flags in + // join build after all probe has finished. + // * The operator has to execute as grouped the subsequent LJoin expects that incoming + // operators are grouped. Otherwise, the LJoin won't be able to throw out the build side + // for each group as soon as the group completes. + // + // LJoin + // / \ + // RJoin Scan + // / \ + // Scan Remote + // + // TODO: + // The RJoin can still execute as grouped if there is no subsequent operator that depends + // on the RJoin being executed in a grouped manner. However, this is not currently implemented. + // Support for this scenario is already implemented in the execution side. + return GroupedExecutionProperties.notCapable(); + } + switch (node.getDistributionType().get()) { case REPLICATED: // Broadcast join maintains partitioning for the left side. @@ -464,7 +568,7 @@ public GroupedExecutionProperties visitJoin(JoinNode node, Void context) // It's not particularly helpful to do grouped execution on the right side // because the benefit is likely cancelled out due to required buffering for hash build. // In theory, it could still be helpful (e.g. when the underlying aggregation's intermediate group state maybe larger than aggregation output). - // However, this is not currently implemented. JoinBridgeDataManager need to support such a lifecycle. + // However, this is not currently implemented. JoinBridgeManager need to support such a lifecycle. // !right.currentNodeCapable: // The build/right side needs to buffer fully for this JOIN, but the probe/left side will still stream through. // As a result, there is no reason to change currentNodeCapable or subTreeUseful to false. @@ -582,4 +686,43 @@ public List getCapableTableScanNodes() return capableTableScanNodes; } } + + private static final class PartitioningHandleReassigner + extends SimplePlanRewriter + { + private final PartitioningHandle fragmentPartitioningHandle; + private final Metadata metadata; + private final Session session; + + public PartitioningHandleReassigner(PartitioningHandle fragmentPartitioningHandle, Metadata metadata, Session session) + { + this.fragmentPartitioningHandle = fragmentPartitioningHandle; + this.metadata = metadata; + this.session = session; + } + + @Override + public PlanNode visitTableScan(TableScanNode node, RewriteContext context) + { + PartitioningHandle partitioning = node.getLayout() + .map(layout -> metadata.getLayout(session, layout)) + .flatMap(TableLayout::getTablePartitioning) + .map(TablePartitioning::getPartitioningHandle) + .orElse(SOURCE_DISTRIBUTION); + if (partitioning.equals(fragmentPartitioningHandle)) { + // do nothing if the current scan node's partitioning matches the fragment's + return node; + } + + TableLayoutHandle newTableLayoutHandle = metadata.getAlternativeLayoutHandle(session, node.getLayout().get(), fragmentPartitioningHandle); + return new TableScanNode( + node.getId(), + node.getTable(), + node.getOutputSymbols(), + node.getAssignments(), + Optional.of(newTableLayoutHandle), + node.getCurrentConstraint(), + node.getEnforcedConstraint()); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java index aca93237dd1fb..7c4381da27506 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/PlanOptimizers.java @@ -17,7 +17,10 @@ import com.facebook.presto.cost.CostCalculator.EstimatedExchanges; import com.facebook.presto.cost.CostComparator; import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.cost.TaskCountEstimator; import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.iterative.IterativeOptimizer; @@ -34,6 +37,7 @@ import com.facebook.presto.sql.planner.iterative.rule.EliminateCrossJoins; import com.facebook.presto.sql.planner.iterative.rule.EvaluateZeroLimit; import com.facebook.presto.sql.planner.iterative.rule.EvaluateZeroSample; +import com.facebook.presto.sql.planner.iterative.rule.ExtractSpatialJoins; import com.facebook.presto.sql.planner.iterative.rule.GatherAndMergeWindows; import com.facebook.presto.sql.planner.iterative.rule.ImplementBernoulliSampleAsFilter; import com.facebook.presto.sql.planner.iterative.rule.ImplementFilteredAggregations; @@ -82,6 +86,7 @@ import com.facebook.presto.sql.planner.iterative.rule.RemoveUnreferencedScalarApplyNodes; import com.facebook.presto.sql.planner.iterative.rule.RemoveUnreferencedScalarLateralNodes; import com.facebook.presto.sql.planner.iterative.rule.ReorderJoins; +import com.facebook.presto.sql.planner.iterative.rule.RewriteSpatialPartitioningAggregation; import com.facebook.presto.sql.planner.iterative.rule.SimplifyCountOverConstant; import com.facebook.presto.sql.planner.iterative.rule.SimplifyExpressions; import com.facebook.presto.sql.planner.iterative.rule.SingleDistinctAggregationToGroupBy; @@ -91,7 +96,6 @@ import com.facebook.presto.sql.planner.iterative.rule.TransformCorrelatedScalarSubquery; import com.facebook.presto.sql.planner.iterative.rule.TransformCorrelatedSingleRowSubqueryToProject; import com.facebook.presto.sql.planner.iterative.rule.TransformExistsApplyToLateralNode; -import com.facebook.presto.sql.planner.iterative.rule.TransformSpatialPredicates; import com.facebook.presto.sql.planner.iterative.rule.TransformUncorrelatedInPredicateSubqueryToSemiJoin; import com.facebook.presto.sql.planner.iterative.rule.TransformUncorrelatedLateralToJoin; import com.facebook.presto.sql.planner.optimizations.AddExchanges; @@ -138,20 +142,26 @@ public PlanOptimizers( SqlParser sqlParser, FeaturesConfig featuresConfig, MBeanExporter exporter, + SplitManager splitManager, + PageSourceManager pageSourceManager, StatsCalculator statsCalculator, CostCalculator costCalculator, @EstimatedExchanges CostCalculator estimatedExchangesCostCalculator, - CostComparator costComparator) + CostComparator costComparator, + TaskCountEstimator taskCountEstimator) { this(metadata, sqlParser, featuresConfig, false, exporter, + splitManager, + pageSourceManager, statsCalculator, costCalculator, estimatedExchangesCostCalculator, - costComparator); + costComparator, + taskCountEstimator); } @PostConstruct @@ -174,10 +184,13 @@ public PlanOptimizers( FeaturesConfig featuresConfig, boolean forceSingleNode, MBeanExporter exporter, + SplitManager splitManager, + PageSourceManager pageSourceManager, StatsCalculator statsCalculator, CostCalculator costCalculator, CostCalculator estimatedExchangesCostCalculator, - CostComparator costComparator) + CostComparator costComparator, + TaskCountEstimator taskCountEstimator) { this.exporter = exporter; ImmutableList.Builder builder = ImmutableList.builder(); @@ -272,7 +285,8 @@ public PlanOptimizers( new ImplementBernoulliSampleAsFilter(), new MergeLimitWithDistinct(), new PruneCountAggregationOverScalar(), - new PruneOrderByInAggregation(metadata.getFunctionRegistry()))) + new PruneOrderByInAggregation(metadata.getFunctionRegistry()), + new RewriteSpatialPartitioningAggregation(metadata))) .build()), simplifyOptimizer, new UnaliasSymbolReferences(), @@ -321,8 +335,6 @@ public PlanOptimizers( ruleStats, statsCalculator, estimatedExchangesCostCalculator, - ImmutableList.of( - new com.facebook.presto.sql.planner.optimizations.TransformCorrelatedSingleRowSubqueryToProject()), ImmutableSet.of( new InlineProjections(), new RemoveRedundantIdentityProjections(), @@ -333,7 +345,7 @@ public PlanOptimizers( ruleStats, statsCalculator, estimatedExchangesCostCalculator, - new PickTableLayout(metadata).rules()), + new PickTableLayout(metadata, sqlParser).rules()), new PruneUnreferencedOutputs(), new IterativeOptimizer( ruleStats, @@ -353,6 +365,7 @@ public PlanOptimizers( statsCalculator, estimatedExchangesCostCalculator, ImmutableSet.of(new SimplifyCountOverConstant())), + new LimitPushDown(), // Run LimitPushDown before WindowFilterPushDown new WindowFilterPushDown(metadata), // This must run after PredicatePushDown and LimitPushDown so that it squashes any successive filter nodes and limits new IterativeOptimizer( ruleStats, @@ -382,7 +395,7 @@ public PlanOptimizers( ruleStats, statsCalculator, estimatedExchangesCostCalculator, - new PickTableLayout(metadata).rules()), + new PickTableLayout(metadata, sqlParser).rules()), projectionPushDown, new PruneUnreferencedOutputs(), new IterativeOptimizer( @@ -409,13 +422,22 @@ public PlanOptimizers( ImmutableSet.of( new CreatePartialTopN(), new PushTopNThroughUnion()))); + builder.add(new IterativeOptimizer( + ruleStats, + statsCalculator, + costCalculator, + ImmutableSet.>builder() + .add(new RemoveRedundantIdentityProjections()) + .addAll(new ExtractSpatialJoins(metadata, splitManager, pageSourceManager).rules()) + .add(new InlineProjections()) + .build())); if (!forceSingleNode) { builder.add((new IterativeOptimizer( ruleStats, statsCalculator, estimatedExchangesCostCalculator, - ImmutableSet.of(new DetermineJoinDistributionType())))); // Must run before AddExchanges + ImmutableSet.of(new DetermineJoinDistributionType(costComparator, taskCountEstimator))))); // Must run before AddExchanges builder.add(new DetermineSemiJoinDistributionType()); // Must run before AddExchanges builder.add( new IterativeOptimizer( @@ -441,15 +463,13 @@ public PlanOptimizers( builder.add(inlineProjections); builder.add(new UnaliasSymbolReferences()); // Run unalias after merging projections to simplify projections more efficiently builder.add(new PruneUnreferencedOutputs()); - // TODO Make PredicatePushDown aware of spatial joins, move TransformSpatialPredicateToJoin - // before AddExchanges and update AddExchanges to set REPLICATED distribution for the build side. + builder.add(new IterativeOptimizer( ruleStats, statsCalculator, costCalculator, ImmutableSet.>builder() .add(new RemoveRedundantIdentityProjections()) - .addAll(new TransformSpatialPredicates(metadata).rules()) .add(new PushRemoteExchangeThroughAssignUniqueId()) .add(new InlineProjections()) .build())); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java index 046453ac6bf44..9af4e526775d5 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/QueryPlanner.java @@ -18,10 +18,10 @@ import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.block.SortOrder; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.analyzer.Analysis; import com.facebook.presto.sql.analyzer.Field; +import com.facebook.presto.sql.analyzer.FieldId; import com.facebook.presto.sql.analyzer.RelationId; import com.facebook.presto.sql.analyzer.RelationType; import com.facebook.presto.sql.analyzer.Scope; @@ -48,6 +48,7 @@ import com.facebook.presto.sql.tree.FunctionCall; import com.facebook.presto.sql.tree.GroupingOperation; import com.facebook.presto.sql.tree.LambdaArgumentDeclaration; +import com.facebook.presto.sql.tree.LambdaExpression; import com.facebook.presto.sql.tree.Node; import com.facebook.presto.sql.tree.NodeRef; import com.facebook.presto.sql.tree.OrderBy; @@ -63,9 +64,9 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -73,6 +74,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.stream.IntStream; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; @@ -155,7 +157,7 @@ public RelationPlan plan(QuerySpecification node) builder = handleSubqueries(builder, node, outputs); if (node.getOrderBy().isPresent()) { - if (analysis.getGroupingSets(node).isEmpty()) { + if (!analysis.isAggregation(node)) { // ORDER BY requires both output and source fields to be visible if there are no aggregations builder = project(builder, outputs, fromRelationPlan); outputs = toSymbolReferences(computeOutputs(builder, outputs)); @@ -216,7 +218,7 @@ public DeleteNode plan(Delete node) fields.add(rowIdField); // create table scan - PlanNode tableScan = new TableScanNode(idAllocator.getNextId(), handle, outputSymbols.build(), columns.build(), Optional.empty(), TupleDomain.all(), null); + PlanNode tableScan = new TableScanNode(idAllocator.getNextId(), handle, outputSymbols.build(), columns.build()); Scope scope = Scope.builder().withRelationType(RelationId.anonymous(), new RelationType(fields.build())).build(); RelationPlan relationPlan = new RelationPlan(tableScan, scope, outputSymbols.build()); @@ -419,20 +421,18 @@ private PlanBuilder explicitCoercionSymbols(PlanBuilder subPlan, Iterable> groupingSets = analysis.getGroupingSets(node); - if (groupingSets.isEmpty()) { + if (!analysis.isAggregation(node)) { return subPlan; } // 1. Pre-project all scalar inputs (arguments and non-trivial group by expressions) - Set distinctGroupingColumns = groupingSets.stream() - .flatMap(Collection::stream) - .collect(toImmutableSet()); + Set groupByExpressions = ImmutableSet.copyOf(analysis.getGroupByExpressions(node)); ImmutableList.Builder arguments = ImmutableList.builder(); analysis.getAggregates(node).stream() .map(FunctionCall::getArguments) .flatMap(List::stream) + .filter(exp -> !(exp instanceof LambdaExpression)) // lambda expression is generated at execution time .forEach(arguments::add); analysis.getAggregates(node).stream() @@ -451,7 +451,7 @@ private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) .map(Optional::get) .forEach(arguments::add); - Iterable inputs = Iterables.concat(distinctGroupingColumns, arguments.build()); + Iterable inputs = Iterables.concat(groupByExpressions, arguments.build()); subPlan = handleSubqueries(subPlan, node, inputs); if (!Iterables.isEmpty(inputs)) { // avoid an empty projection if the only aggregation is COUNT (which has no arguments) @@ -473,34 +473,60 @@ private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) // 2.b. Rewrite grouping columns TranslationMap groupingTranslations = new TranslationMap(subPlan.getRelationPlan(), analysis, lambdaDeclarationToSymbolMap); - Map groupingSetMappings = new HashMap<>(); - List> groupingSymbols = new ArrayList<>(); - - for (List groupingSet : groupingSets) { - ImmutableList.Builder symbols = ImmutableList.builder(); - for (Expression expression : groupingSet) { - Symbol input = subPlan.translate(expression); - - Symbol output; - if (!groupingTranslations.containsSymbol(expression)) { - output = symbolAllocator.newSymbol(expression, analysis.getTypeWithCoercions(expression), "gid"); - groupingTranslations.put(expression, output); - } - else { - output = groupingTranslations.get(expression); - } + Map groupingSetMappings = new LinkedHashMap<>(); + + for (Expression expression : groupByExpressions) { + Symbol input = subPlan.translate(expression); + Symbol output = symbolAllocator.newSymbol(expression, analysis.getTypeWithCoercions(expression), "gid"); + groupingTranslations.put(expression, output); + groupingSetMappings.put(output, input); + } + + // This tracks the grouping sets before complex expressions are considered (see comments below) + // It's also used to compute the descriptors needed to implement grouping() + List> columnOnlyGroupingSets = ImmutableList.of(ImmutableSet.of()); + List> groupingSets = ImmutableList.of(ImmutableList.of()); + + if (node.getGroupBy().isPresent()) { + // For the purpose of "distinct", we need to canonicalize column references that may have varying + // syntactic forms (e.g., "t.a" vs "a"). Thus we need to enumerate grouping sets based on the underlying + // fieldId associated with each column reference expression. + + // The catch is that simple group-by expressions can be arbitrary expressions (this is a departure from the SQL specification). + // But, they don't affect the number of grouping sets or the behavior of "distinct" . We can compute all the candidate + // grouping sets in terms of fieldId, dedup as appropriate and then cross-join them with the complex expressions. + Analysis.GroupingSetAnalysis groupingSetAnalysis = analysis.getGroupingSets(node); + columnOnlyGroupingSets = enumerateGroupingSets(groupingSetAnalysis); + + if (node.getGroupBy().get().isDistinct()) { + columnOnlyGroupingSets = columnOnlyGroupingSets.stream() + .distinct() + .collect(toImmutableList()); + } - groupingSetMappings.put(output, input); - symbols.add(output); + // add in the complex expressions an turn materialize the grouping sets in terms of plan columns + ImmutableList.Builder> groupingSetBuilder = ImmutableList.builder(); + for (Set groupingSet : columnOnlyGroupingSets) { + ImmutableList.Builder columns = ImmutableList.builder(); + groupingSetAnalysis.getComplexExpressions().stream() + .map(groupingTranslations::get) + .forEach(columns::add); + + groupingSet.stream() + .map(field -> groupingTranslations.get(new FieldReference(field.getFieldIndex()))) + .forEach(columns::add); + + groupingSetBuilder.add(columns.build()); } - groupingSymbols.add(symbols.build()); + + groupingSets = groupingSetBuilder.build(); } // 2.c. Generate GroupIdNode (multiple grouping sets) or ProjectNode (single grouping set) Optional groupIdSymbol = Optional.empty(); if (groupingSets.size() > 1) { groupIdSymbol = Optional.of(symbolAllocator.newSymbol("groupId", BIGINT)); - GroupIdNode groupId = new GroupIdNode(idAllocator.getNextId(), subPlan.getRoot(), groupingSymbols, groupingSetMappings, aggregationArguments, groupIdSymbol.get()); + GroupIdNode groupId = new GroupIdNode(idAllocator.getNextId(), subPlan.getRoot(), groupingSets, groupingSetMappings, aggregationArguments, groupIdSymbol.get()); subPlan = new PlanBuilder(groupingTranslations, groupId, analysis.getParameters()); } else { @@ -542,7 +568,7 @@ private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) } ImmutableList.Builder groupingKeys = ImmutableList.builder(); - groupingSymbols.stream() + groupingSets.stream() .flatMap(List::stream) .distinct() .forEach(groupingKeys::add); @@ -568,17 +594,64 @@ private PlanBuilder aggregate(PlanBuilder subPlan, QuerySpecification node) // TODO: this is a hack, we should change type coercions to coerce the inputs to functions/operators instead of coercing the output if (needPostProjectionCoercion) { ImmutableList.Builder alreadyCoerced = ImmutableList.builder(); - alreadyCoerced.addAll(distinctGroupingColumns); + alreadyCoerced.addAll(groupByExpressions); groupIdSymbol.map(Symbol::toSymbolReference).ifPresent(alreadyCoerced::add); subPlan = explicitCoercionFields(subPlan, alreadyCoerced.build(), analysis.getAggregates(node)); } // 4. Project and re-write all grouping functions - return handleGroupingOperations(subPlan, node, groupIdSymbol); + return handleGroupingOperations(subPlan, node, groupIdSymbol, columnOnlyGroupingSets); + } + + private List> enumerateGroupingSets(Analysis.GroupingSetAnalysis groupingSetAnalysis) + { + List>> partialSets = new ArrayList<>(); + + for (Set cube : groupingSetAnalysis.getCubes()) { + partialSets.add(ImmutableList.copyOf(Sets.powerSet(cube))); + } + + for (List rollup : groupingSetAnalysis.getRollups()) { + List> sets = IntStream.rangeClosed(0, rollup.size()) + .mapToObj(i -> ImmutableSet.copyOf(rollup.subList(0, i))) + .collect(toImmutableList()); + + partialSets.add(sets); + } + + partialSets.addAll(groupingSetAnalysis.getOrdinarySets()); + + if (partialSets.isEmpty()) { + return ImmutableList.of(ImmutableSet.of()); + } + + // compute the cross product of the partial sets + List> allSets = new ArrayList<>(); + partialSets.get(0) + .stream() + .map(ImmutableSet::copyOf) + .forEach(allSets::add); + + for (int i = 1; i < partialSets.size(); i++) { + List> groupingSets = partialSets.get(i); + List> oldGroupingSetsCrossProduct = ImmutableList.copyOf(allSets); + allSets.clear(); + for (Set existingSet : oldGroupingSetsCrossProduct) { + for (Set groupingSet : groupingSets) { + Set concatenatedSet = ImmutableSet.builder() + .addAll(existingSet) + .addAll(groupingSet) + .build(); + allSets.add(concatenatedSet); + } + } + } + + return allSets; } - private PlanBuilder handleGroupingOperations(PlanBuilder subPlan, QuerySpecification node, Optional groupIdSymbol) + private PlanBuilder handleGroupingOperations(PlanBuilder subPlan, QuerySpecification node, Optional groupIdSymbol, List> groupingSets) { if (analysis.getGroupingOperations(node).isEmpty()) { return subPlan; @@ -589,8 +662,14 @@ private PlanBuilder handleGroupingOperations(PlanBuilder subPlan, QuerySpecifica Assignments.Builder projections = Assignments.builder(); projections.putIdentities(subPlan.getRoot().getOutputSymbols()); + List> descriptor = groupingSets.stream() + .map(set -> set.stream() + .map(FieldId::getFieldIndex) + .collect(toImmutableSet())) + .collect(toImmutableList()); + for (GroupingOperation groupingOperation : analysis.getGroupingOperations(node)) { - Expression rewritten = GroupingOperationRewriter.rewriteGroupingOperation(groupingOperation, analysis.getGroupingSets(node), analysis.getColumnReferenceFields(), groupIdSymbol); + Expression rewritten = GroupingOperationRewriter.rewriteGroupingOperation(groupingOperation, descriptor, analysis.getColumnReferenceFields(), groupIdSymbol); Type coercion = analysis.getCoercion(groupingOperation); Symbol symbol = symbolAllocator.newSymbol(rewritten, analysis.getTypeWithCoercions(groupingOperation)); if (coercion != null) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java index f89fafaab8d5a..ebd9ed3f90897 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/RelationPlanner.java @@ -18,7 +18,6 @@ import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.spi.ColumnHandle; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.RowType; @@ -159,7 +158,7 @@ protected RelationPlan visitTable(Table node, Void context) } List outputSymbols = outputSymbolsBuilder.build(); - PlanNode root = new TableScanNode(idAllocator.getNextId(), handle, outputSymbols, columns.build(), Optional.empty(), TupleDomain.all(), null); + PlanNode root = new TableScanNode(idAllocator.getNextId(), handle, outputSymbols, columns.build()); return new RelationPlan(root, scope, outputSymbols); } @@ -168,7 +167,28 @@ protected RelationPlan visitAliasedRelation(AliasedRelation node, Void context) { RelationPlan subPlan = process(node.getRelation(), context); - return new RelationPlan(subPlan.getRoot(), analysis.getScope(node), subPlan.getFieldMappings()); + PlanNode root = subPlan.getRoot(); + List mappings = subPlan.getFieldMappings(); + + if (node.getColumnNames() != null) { + ImmutableList.Builder newMappings = ImmutableList.builder(); + Assignments.Builder assignments = Assignments.builder(); + + // project only the visible columns from the underlying relation + for (int i = 0; i < subPlan.getDescriptor().getAllFieldCount(); i++) { + Field field = subPlan.getDescriptor().getFieldByIndex(i); + if (!field.isHidden()) { + Symbol aliasedColumn = symbolAllocator.newSymbol(field); + assignments.put(aliasedColumn, subPlan.getFieldMappings().get(i).toSymbolReference()); + newMappings.add(aliasedColumn); + } + } + + root = new ProjectNode(idAllocator.getNextId(), subPlan.getRoot(), assignments.build()); + mappings = newMappings.build(); + } + + return new RelationPlan(root, analysis.getScope(node), mappings); } @Override @@ -724,6 +744,7 @@ private RelationPlan addCoercions(RelationPlan plan, Type[] targetColumnTypes) targetColumnTypes[i], oldField.isHidden(), oldField.getOriginTable(), + oldField.getOriginColumnName(), oldField.isAliased()); } ProjectNode projectNode = new ProjectNode(idAllocator.getNextId(), plan.getRoot(), assignments.build()); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/SchedulingOrderVisitor.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/SchedulingOrderVisitor.java index bccd56a8188a0..ed6c18001eadc 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/SchedulingOrderVisitor.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/SchedulingOrderVisitor.java @@ -20,6 +20,7 @@ import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.PlanVisitor; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.google.common.collect.ImmutableList; @@ -65,6 +66,14 @@ public Void visitSemiJoin(SemiJoinNode node, Consumer schedulingOrde return null; } + @Override + public Void visitSpatialJoin(SpatialJoinNode node, Consumer schedulingOrder) + { + node.getRight().accept(this, schedulingOrder); + node.getLeft().accept(this, schedulingOrder); + return null; + } + @Override public Void visitIndexJoin(IndexJoinNode node, Consumer schedulingOrder) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/SystemPartitioningHandle.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/SystemPartitioningHandle.java index d66aa7ddf8265..32cf4101aea27 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/SystemPartitioningHandle.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/SystemPartitioningHandle.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import java.util.List; import java.util.Objects; @@ -154,12 +153,7 @@ else if (partitioning == SystemPartitioning.FIXED) { checkCondition(!nodes.isEmpty(), NO_NODES_AVAILABLE, "No worker nodes available"); - ImmutableMap.Builder partitionToNode = ImmutableMap.builder(); - for (int i = 0; i < nodes.size(); i++) { - Node node = nodes.get(i); - partitionToNode.put(i, node); - } - return new NodePartitionMap(partitionToNode.build(), split -> { + return new NodePartitionMap(nodes, split -> { throw new UnsupportedOperationException("System distribution does not support source splits"); }); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/IterativeOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/IterativeOptimizer.java index 23ddc8d605cf8..9971d731dfbce 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/IterativeOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/IterativeOptimizer.java @@ -21,6 +21,7 @@ import com.facebook.presto.cost.CostProvider; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.matching.Match; import com.facebook.presto.matching.Matcher; import com.facebook.presto.spi.PrestoException; @@ -74,12 +75,12 @@ public IterativeOptimizer(RuleStatsRecorder stats, StatsCalculator statsCalculat } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { // only disable new rules if we have legacy rules to fall back to if (!SystemSessionProperties.isNewOptimizerEnabled(session) && !legacyRules.isEmpty()) { for (PlanOptimizer optimizer : legacyRules) { - plan = optimizer.optimize(plan, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator); + plan = optimizer.optimize(plan, session, symbolAllocator.getTypes(), symbolAllocator, idAllocator, warningCollector); } return plan; @@ -90,7 +91,7 @@ public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, Sym Matcher matcher = new PlanNodeMatcher(lookup); Duration timeout = SystemSessionProperties.getOptimizerTimeout(session); - Context context = new Context(memo, lookup, idAllocator, symbolAllocator, System.nanoTime(), timeout.toMillis(), session); + Context context = new Context(memo, lookup, idAllocator, symbolAllocator, System.nanoTime(), timeout.toMillis(), session, warningCollector); exploreGroup(memo.getRootGroup(), context, matcher); return memo.extract(); @@ -193,7 +194,7 @@ private boolean exploreChildren(int group, Context context, Matcher matcher) private Rule.Context ruleContext(Context context) { StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, Optional.of(context.memo), context.lookup, context.session, context.symbolAllocator.getTypes()); - CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.of(context.memo), context.lookup, context.session, context.symbolAllocator.getTypes()); + CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.of(context.memo), context.session, context.symbolAllocator.getTypes()); return new Rule.Context() { @@ -238,6 +239,12 @@ public void checkTimeoutNotExhausted() { context.checkTimeoutNotExhausted(); } + + @Override + public WarningCollector getWarningCollector() + { + return context.warningCollector; + } }; } @@ -250,6 +257,7 @@ private static class Context private final long startTimeInNanos; private final long timeoutInMilliseconds; private final Session session; + private final WarningCollector warningCollector; public Context( Memo memo, @@ -258,7 +266,8 @@ public Context( SymbolAllocator symbolAllocator, long startTimeInNanos, long timeoutInMilliseconds, - Session session) + Session session, + WarningCollector warningCollector) { checkArgument(timeoutInMilliseconds >= 0, "Timeout has to be a non-negative number [milliseconds]"); @@ -269,6 +278,7 @@ public Context( this.startTimeInNanos = startTimeInNanos; this.timeoutInMilliseconds = timeoutInMilliseconds; this.session = session; + this.warningCollector = warningCollector; } public void checkTimeoutNotExhausted() diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Lookup.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Lookup.java index e410308f08aca..d8acbd0214e8a 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Lookup.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Lookup.java @@ -43,7 +43,7 @@ default PlanNode resolve(PlanNode node) /** * Resolves nodes by materializing GroupReference nodes * representing symbolic references to other nodes. - *

+ * * @throws IllegalArgumentException if the node is not a GroupReference */ Stream resolveGroup(PlanNode node); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Rule.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Rule.java index 8ecec50ea62e0..a82b7afbd0ead 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Rule.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/Rule.java @@ -16,6 +16,7 @@ import com.facebook.presto.Session; import com.facebook.presto.cost.CostProvider; import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.matching.Captures; import com.facebook.presto.matching.Pattern; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; @@ -55,6 +56,8 @@ interface Context CostProvider getCostProvider(); void checkTimeoutNotExhausted(); + + WarningCollector getWarningCollector(); } final class Result diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarAtTimeZone.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarAtTimeZone.java index ed9af3fe50a5f..8aba4c6ce945a 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarAtTimeZone.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarAtTimeZone.java @@ -38,7 +38,6 @@ public Set> rules() projectExpressionRewrite(), aggregationExpressionRewrite(), filterExpressionRewrite(), - tableScanExpressionRewrite(), joinExpressionRewrite(), valuesExpressionRewrite()); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarLambdaExpression.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarLambdaExpression.java index 1a14a2774af6b..f8a6f89bde5d4 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarLambdaExpression.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DesugarLambdaExpression.java @@ -34,7 +34,6 @@ public Set> rules() projectExpressionRewrite(), aggregationExpressionRewrite(), filterExpressionRewrite(), - tableScanExpressionRewrite(), joinExpressionRewrite(), valuesExpressionRewrite()); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DetermineJoinDistributionType.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DetermineJoinDistributionType.java index 99934b24edfec..a1c616a487615 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DetermineJoinDistributionType.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/DetermineJoinDistributionType.java @@ -14,13 +14,30 @@ package com.facebook.presto.sql.planner.iterative.rule; +import com.facebook.presto.cost.CostComparator; +import com.facebook.presto.cost.PlanNodeCostEstimate; +import com.facebook.presto.cost.PlanNodeStatsEstimate; +import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.cost.TaskCountEstimator; import com.facebook.presto.matching.Captures; import com.facebook.presto.matching.Pattern; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; +import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.sql.planner.plan.JoinNode; -import com.facebook.presto.sql.planner.plan.JoinNode.DistributionType; +import com.facebook.presto.sql.planner.plan.PlanNode; +import com.google.common.collect.Ordering; +import io.airlift.units.DataSize; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import static com.facebook.presto.SystemSessionProperties.getJoinDistributionType; +import static com.facebook.presto.SystemSessionProperties.getJoinMaxBroadcastTableSize; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateJoinExchangeCost; +import static com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges.calculateJoinInputCost; +import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.AUTOMATIC; import static com.facebook.presto.sql.planner.optimizations.QueryCardinalityUtil.isAtMostScalar; import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.PARTITIONED; import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED; @@ -29,12 +46,22 @@ import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.RIGHT; import static com.facebook.presto.sql.planner.plan.Patterns.join; +import static java.util.Objects.requireNonNull; public class DetermineJoinDistributionType implements Rule { private static final Pattern PATTERN = join().matching(joinNode -> !joinNode.getDistributionType().isPresent()); + private final CostComparator costComparator; + private final TaskCountEstimator taskCountEstimator; + + public DetermineJoinDistributionType(CostComparator costComparator, TaskCountEstimator taskCountEstimator) + { + this.costComparator = requireNonNull(costComparator, "costComparator is null"); + this.taskCountEstimator = requireNonNull(taskCountEstimator, "exchangeCostCalculator is null"); + } + @Override public Pattern getPattern() { @@ -42,33 +69,153 @@ public Pattern getPattern() } @Override - public Result apply(JoinNode node, Captures captures, Context context) + public Result apply(JoinNode joinNode, Captures captures, Context context) { - DistributionType distributionType = determineDistributionType(node, context); - return Result.ofPlanNode(node.withDistributionType(distributionType)); + JoinDistributionType joinDistributionType = getJoinDistributionType(context.getSession()); + if (joinDistributionType == AUTOMATIC) { + return Result.ofPlanNode(getCostBasedJoin(joinNode, context)); + } + return Result.ofPlanNode(getSyntacticOrderJoin(joinNode, context, joinDistributionType)); + } + + public static boolean canReplicate(JoinNode joinNode, Context context) + { + JoinDistributionType joinDistributionType = getJoinDistributionType(context.getSession()); + if (!joinDistributionType.canReplicate()) { + return false; + } + + Optional joinMaxBroadcastTableSize = getJoinMaxBroadcastTableSize(context.getSession()); + if (!joinMaxBroadcastTableSize.isPresent()) { + return true; + } + + PlanNode buildSide = joinNode.getRight(); + PlanNodeStatsEstimate buildSideStatsEstimate = context.getStatsProvider().getStats(buildSide); + double buildSideSizeInBytes = buildSideStatsEstimate.getOutputSizeInBytes(buildSide.getOutputSymbols(), context.getSymbolAllocator().getTypes()); + return buildSideSizeInBytes <= joinMaxBroadcastTableSize.get().toBytes(); } - private static DistributionType determineDistributionType(JoinNode node, Context context) + private PlanNode getCostBasedJoin(JoinNode joinNode, Context context) { - JoinNode.Type type = node.getType(); - if (type == RIGHT || type == FULL) { - // With REPLICATED, the unmatched rows from right-side would be duplicated. - return PARTITIONED; + List possibleJoinNodes = new ArrayList<>(); + + addJoinsWithDifferentDistributions(joinNode, possibleJoinNodes, context); + addJoinsWithDifferentDistributions(joinNode.flipChildren(), possibleJoinNodes, context); + + if (possibleJoinNodes.stream().anyMatch(result -> result.getCost().hasUnknownComponents()) || possibleJoinNodes.isEmpty()) { + return getSyntacticOrderJoin(joinNode, context, AUTOMATIC); } - if (node.getCriteria().isEmpty() && (type == INNER || type == LEFT)) { + // Using Ordering to facilitate rule determinism + Ordering planNodeOrderings = costComparator.forSession(context.getSession()).onResultOf(PlanNodeWithCost::getCost); + return planNodeOrderings.min(possibleJoinNodes).getPlanNode(); + } + + private void addJoinsWithDifferentDistributions(JoinNode joinNode, List possibleJoinNodes, Context context) + { + if (!mustPartition(joinNode) && canReplicate(joinNode, context)) { + possibleJoinNodes.add(getJoinNodeWithCost(context, joinNode.withDistributionType(REPLICATED))); + } + if (!mustReplicate(joinNode, context)) { + possibleJoinNodes.add(getJoinNodeWithCost(context, joinNode.withDistributionType(PARTITIONED))); + } + } + + private PlanNode getSyntacticOrderJoin(JoinNode joinNode, Context context, JoinDistributionType joinDistributionType) + { + if (mustPartition(joinNode)) { + return joinNode.withDistributionType(PARTITIONED); + } + if (mustReplicate(joinNode, context)) { + return joinNode.withDistributionType(REPLICATED); + } + if (joinDistributionType.canPartition()) { + return joinNode.withDistributionType(PARTITIONED); + } + return joinNode.withDistributionType(REPLICATED); + } + + private static boolean mustPartition(JoinNode joinNode) + { + JoinNode.Type type = joinNode.getType(); + // With REPLICATED, the unmatched rows from right-side would be duplicated. + return type == RIGHT || type == FULL; + } + + private static boolean mustReplicate(JoinNode joinNode, Context context) + { + JoinNode.Type type = joinNode.getType(); + if (joinNode.getCriteria().isEmpty() && (type == INNER || type == LEFT)) { // There is nothing to partition on - return REPLICATED; + return true; } + return isAtMostScalar(joinNode.getRight(), context.getLookup()); + } - if (isAtMostScalar(node.getRight(), context.getLookup())) { - return REPLICATED; + private PlanNodeWithCost getJoinNodeWithCost(Context context, JoinNode possibleJoinNode) + { + TypeProvider types = context.getSymbolAllocator().getTypes(); + StatsProvider stats = context.getStatsProvider(); + boolean replicated = possibleJoinNode.getDistributionType().get().equals(REPLICATED); + /* + * HACK! + * + * Currently cost model always has to compute the total cost of an operation. + * For JOIN the total cost consist of 4 parts: + * - Cost of exchanges that have to be introduced to execute a JOIN + * - Cost of building a hash table + * - Cost of probing a hash table + * - Cost of building an output for matched rows + * + * When output size for a JOIN cannot be estimated the cost model returns + * UNKNOWN cost for the join. + * + * However assuming the cost of JOIN output is always the same, we can still make + * cost based decisions based on the input cost for different types of JOINs. + * + * Although the side flipping can be made purely based on stats (smaller side + * always goes to the right), determining JOIN type is not that simple. As when + * choosing REPLICATED over REPARTITIONED join the cost of exchanging and building + * the hash table scales with the number of nodes where the build side is replicated. + */ + int estimatedSourceDistributedTaskCount = taskCountEstimator.estimateSourceDistributedTaskCount(); + PlanNodeCostEstimate exchangesCost = calculateJoinExchangeCost( + possibleJoinNode.getLeft(), + possibleJoinNode.getRight(), + stats, + types, + replicated, + estimatedSourceDistributedTaskCount); + PlanNodeCostEstimate inputCost = calculateJoinInputCost( + possibleJoinNode.getLeft(), + possibleJoinNode.getRight(), + stats, + types, + replicated, + estimatedSourceDistributedTaskCount); + return new PlanNodeWithCost(exchangesCost.add(inputCost), possibleJoinNode); + } + + private static class PlanNodeWithCost + { + private final PlanNode planNode; + private final PlanNodeCostEstimate cost; + + public PlanNodeWithCost(PlanNodeCostEstimate cost, PlanNode planNode) + { + this.cost = requireNonNull(cost, "cost is null"); + this.planNode = requireNonNull(planNode, "planNode is null"); } - if (getJoinDistributionType(context.getSession()).canPartition()) { - return PARTITIONED; + public PlanNode getPlanNode() + { + return planNode; } - return REPLICATED; + public PlanNodeCostEstimate getCost() + { + return cost; + } } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExpressionRewriteRuleSet.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExpressionRewriteRuleSet.java index 77a20c30833fc..4fbead2e3140a 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExpressionRewriteRuleSet.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExpressionRewriteRuleSet.java @@ -24,7 +24,6 @@ import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.ProjectNode; -import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; @@ -42,7 +41,6 @@ import static com.facebook.presto.sql.planner.plan.Patterns.filter; import static com.facebook.presto.sql.planner.plan.Patterns.join; import static com.facebook.presto.sql.planner.plan.Patterns.project; -import static com.facebook.presto.sql.planner.plan.Patterns.tableScan; import static com.facebook.presto.sql.planner.plan.Patterns.values; import static java.util.Objects.requireNonNull; @@ -66,7 +64,6 @@ public Set> rules() projectExpressionRewrite(), aggregationExpressionRewrite(), filterExpressionRewrite(), - tableScanExpressionRewrite(), joinExpressionRewrite(), valuesExpressionRewrite(), applyExpressionRewrite()); @@ -87,11 +84,6 @@ public Rule filterExpressionRewrite() return new FilterExpressionRewrite(rewriter); } - public Rule tableScanExpressionRewrite() - { - return new TableScanExpressionRewrite(rewriter); - } - public Rule joinExpressionRewrite() { return new JoinExpressionRewrite(rewriter); @@ -207,43 +199,6 @@ public Result apply(FilterNode filterNode, Captures captures, Context context) } } - private static final class TableScanExpressionRewrite - implements Rule - { - private final ExpressionRewriter rewriter; - - TableScanExpressionRewrite(ExpressionRewriter rewriter) - { - this.rewriter = rewriter; - } - - @Override - public Pattern getPattern() - { - return tableScan(); - } - - @Override - public Result apply(TableScanNode tableScanNode, Captures captures, Context context) - { - if (tableScanNode.getOriginalConstraint() == null) { - return Result.empty(); - } - Expression rewrittenOriginalContraint = rewriter.rewrite(tableScanNode.getOriginalConstraint(), context); - if (!tableScanNode.getOriginalConstraint().equals(rewrittenOriginalContraint)) { - return Result.ofPlanNode(new TableScanNode( - tableScanNode.getId(), - tableScanNode.getTable(), - tableScanNode.getOutputSymbols(), - tableScanNode.getAssignments(), - tableScanNode.getLayout(), - tableScanNode.getCurrentConstraint(), - rewrittenOriginalContraint)); - } - return Result.empty(); - } - } - private static final class JoinExpressionRewrite implements Rule { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformSpatialPredicates.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExtractSpatialJoins.java similarity index 54% rename from presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformSpatialPredicates.java rename to presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExtractSpatialJoins.java index 2675d1121b2f1..3f158c9544ad7 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformSpatialPredicates.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ExtractSpatialJoins.java @@ -14,11 +14,28 @@ package com.facebook.presto.sql.planner.iterative.rule; import com.facebook.presto.Session; +import com.facebook.presto.execution.Lifespan; +import com.facebook.presto.geospatial.KdbTree; +import com.facebook.presto.geospatial.KdbTreeUtils; import com.facebook.presto.matching.Capture; import com.facebook.presto.matching.Captures; import com.facebook.presto.matching.Pattern; import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.metadata.QualifiedObjectName; +import com.facebook.presto.metadata.Split; +import com.facebook.presto.metadata.TableHandle; +import com.facebook.presto.metadata.TableLayoutResult; +import com.facebook.presto.spi.ColumnHandle; +import com.facebook.presto.spi.ConnectorPageSource; +import com.facebook.presto.spi.Constraint; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; +import com.facebook.presto.split.SplitSource; +import com.facebook.presto.split.SplitSource.SplitBatch; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.sql.planner.iterative.Rule.Context; @@ -29,25 +46,43 @@ import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; +import com.facebook.presto.sql.planner.plan.UnnestNode; +import com.facebook.presto.sql.tree.Cast; import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.QualifiedName; +import com.facebook.presto.sql.tree.StringLiteral; import com.facebook.presto.sql.tree.SymbolReference; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import java.io.IOException; +import java.io.UncheckedIOException; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; +import static com.facebook.presto.SystemSessionProperties.getSpatialPartitioningTableName; import static com.facebook.presto.SystemSessionProperties.isSpatialJoinEnabled; import static com.facebook.presto.matching.Capture.newCapture; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_SPATIAL_PARTITIONING; +import static com.facebook.presto.spi.connector.ConnectorSplitManager.SplitSchedulingStrategy.UNGROUPED_SCHEDULING; +import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.ExpressionNodeInliner.replaceExpression; import static com.facebook.presto.sql.planner.SymbolsExtractor.extractUnique; +import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; import static com.facebook.presto.sql.planner.plan.Patterns.filter; import static com.facebook.presto.sql.planner.plan.Patterns.join; @@ -57,6 +92,9 @@ import static com.facebook.presto.util.SpatialJoinUtils.extractSupportedSpatialComparisons; import static com.facebook.presto.util.SpatialJoinUtils.extractSupportedSpatialFunctions; import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static io.airlift.concurrent.MoreFutures.getFutureValue; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; /** @@ -65,10 +103,10 @@ *

* For example: *

    - *
  • SELECT ... FROM a, b WHERE ST_Contains(b.geometry, a.geometry)
  • - *
  • SELECT ... FROM a, b WHERE ST_Intersects(b.geometry, a.geometry)
  • - *
  • SELECT ... FROM a, b WHERE ST_Distance(b.geometry, a.geometry) <= 300
  • - *
  • SELECT ... FROM a, b WHERE 15.5 > ST_Distance(b.geometry, a.geometry)
  • + *
  • SELECT ... FROM a, b WHERE ST_Contains(b.geometry, a.geometry)
  • + *
  • SELECT ... FROM a, b WHERE ST_Intersects(b.geometry, a.geometry)
  • + *
  • SELECT ... FROM a, b WHERE ST_Distance(b.geometry, a.geometry) <= 300
  • + *
  • SELECT ... FROM a, b WHERE 15.5 > ST_Distance(b.geometry, a.geometry)
  • *
*

* Joins expressed via ST_Contains and ST_Intersects functions must match all of @@ -91,40 +129,47 @@ * arguments and radius into projections on top of join child nodes. *

* Examples: - *

+ *

  * Point-in-polygon inner join
  *      ST_Contains(ST_GeometryFromText(a.wkt), ST_Point(b.longitude, b.latitude))
  * becomes a spatial join
  *      ST_Contains(st_geometryfromtext, st_point)
  * with st_geometryfromtext -> 'ST_GeometryFromText(a.wkt)' and
  * st_point -> 'ST_Point(b.longitude, b.latitude)' projections on top of child nodes.
- * 

+ * * Distance query * ST_Distance(ST_Point(a.lon, a.lat), ST_Point(b.lon, b.lat)) <= 10 / (111.321 * cos(radians(b.lat))) * becomes a spatial join * ST_Distance(st_point_a, st_point_b) <= radius * with st_point_a -> 'ST_Point(a.lon, a.lat)', st_point_b -> 'ST_Point(b.lon, b.lat)' * and radius -> '10 / (111.321 * cos(radians(b.lat)))' projections on top of child nodes. + *

*/ -public class TransformSpatialPredicates +public class ExtractSpatialJoins { private static final TypeSignature GEOMETRY_TYPE_SIGNATURE = parseTypeSignature("Geometry"); + private static final String KDB_TREE_TYPENAME = "KdbTree"; private final Metadata metadata; + private final SplitManager splitManager; + private final PageSourceManager pageSourceManager; - public TransformSpatialPredicates(Metadata metadata) + public ExtractSpatialJoins(Metadata metadata, SplitManager splitManager, PageSourceManager pageSourceManager) { this.metadata = requireNonNull(metadata, "metadata is null"); + this.splitManager = requireNonNull(splitManager, "splitManager is null"); + this.pageSourceManager = requireNonNull(pageSourceManager, "pageSourceManager is null"); } public Set> rules() { return ImmutableSet.of( - new TransformSpatialPredicateToJoin(metadata), - new TransformSpatialPredicateToLeftJoin(metadata)); + new ExtractSpatialInnerJoin(metadata, splitManager, pageSourceManager), + new ExtractSpatialLeftJoin(metadata, splitManager, pageSourceManager)); } - public static final class TransformSpatialPredicateToJoin + @VisibleForTesting + public static final class ExtractSpatialInnerJoin implements Rule { private static final Capture JOIN = newCapture(); @@ -132,10 +177,14 @@ public static final class TransformSpatialPredicateToJoin .with(source().matching(join().capturedAs(JOIN).matching(JoinNode::isCrossJoin))); private final Metadata metadata; + private final SplitManager splitManager; + private final PageSourceManager pageSourceManager; - public TransformSpatialPredicateToJoin(Metadata metadata) + public ExtractSpatialInnerJoin(Metadata metadata, SplitManager splitManager, PageSourceManager pageSourceManager) { - this.metadata = metadata; + this.metadata = requireNonNull(metadata, "metadata is null"); + this.splitManager = requireNonNull(splitManager, "splitManager is null"); + this.pageSourceManager = requireNonNull(pageSourceManager, "pageSourceManager is null"); } @Override @@ -159,7 +208,7 @@ public Result apply(FilterNode node, Captures captures, Context context) List spatialFunctions = extractSupportedSpatialFunctions(filter); for (FunctionCall spatialFunction : spatialFunctions) { - Result result = tryCreateSpatialJoin(context, joinNode, filter, node.getId(), node.getOutputSymbols(), spatialFunction, metadata); + Result result = tryCreateSpatialJoin(context, joinNode, filter, node.getId(), node.getOutputSymbols(), spatialFunction, Optional.empty(), metadata, splitManager, pageSourceManager); if (!result.isEmpty()) { return result; } @@ -167,7 +216,7 @@ public Result apply(FilterNode node, Captures captures, Context context) List spatialComparisons = extractSupportedSpatialComparisons(filter); for (ComparisonExpression spatialComparison : spatialComparisons) { - Result result = tryCreateSpatialJoin(context, joinNode, filter, node.getId(), node.getOutputSymbols(), spatialComparison, metadata); + Result result = tryCreateSpatialJoin(context, joinNode, filter, node.getId(), node.getOutputSymbols(), spatialComparison, metadata, splitManager, pageSourceManager); if (!result.isEmpty()) { return result; } @@ -177,16 +226,21 @@ public Result apply(FilterNode node, Captures captures, Context context) } } - public static final class TransformSpatialPredicateToLeftJoin + @VisibleForTesting + public static final class ExtractSpatialLeftJoin implements Rule { - private static final Pattern PATTERN = join().matching(node -> node.getCriteria().isEmpty() && node.getFilter().isPresent() && node.getType() == LEFT && !node.isSpatialJoin()); + private static final Pattern PATTERN = join().matching(node -> node.getCriteria().isEmpty() && node.getFilter().isPresent() && node.getType() == LEFT); private final Metadata metadata; + private final SplitManager splitManager; + private final PageSourceManager pageSourceManager; - public TransformSpatialPredicateToLeftJoin(Metadata metadata) + public ExtractSpatialLeftJoin(Metadata metadata, SplitManager splitManager, PageSourceManager pageSourceManager) { - this.metadata = metadata; + this.metadata = requireNonNull(metadata, "metadata is null"); + this.splitManager = requireNonNull(splitManager, "splitManager is null"); + this.pageSourceManager = requireNonNull(pageSourceManager, "pageSourceManager is null"); } @Override @@ -208,7 +262,7 @@ public Result apply(JoinNode joinNode, Captures captures, Context context) List spatialFunctions = extractSupportedSpatialFunctions(filter); for (FunctionCall spatialFunction : spatialFunctions) { - Result result = tryCreateSpatialJoin(context, joinNode, filter, joinNode.getId(), joinNode.getOutputSymbols(), spatialFunction, metadata); + Result result = tryCreateSpatialJoin(context, joinNode, filter, joinNode.getId(), joinNode.getOutputSymbols(), spatialFunction, Optional.empty(), metadata, splitManager, pageSourceManager); if (!result.isEmpty()) { return result; } @@ -216,7 +270,7 @@ public Result apply(JoinNode joinNode, Captures captures, Context context) List spatialComparisons = extractSupportedSpatialComparisons(filter); for (ComparisonExpression spatialComparison : spatialComparisons) { - Result result = tryCreateSpatialJoin(context, joinNode, filter, joinNode.getId(), joinNode.getOutputSymbols(), spatialComparison, metadata); + Result result = tryCreateSpatialJoin(context, joinNode, filter, joinNode.getId(), joinNode.getOutputSymbols(), spatialComparison, metadata, splitManager, pageSourceManager); if (!result.isEmpty()) { return result; } @@ -226,7 +280,16 @@ public Result apply(JoinNode joinNode, Captures captures, Context context) } } - private static Result tryCreateSpatialJoin(Context context, JoinNode joinNode, Expression filter, PlanNodeId nodeId, List outputSymbols, ComparisonExpression spatialComparison, Metadata metadata) + private static Result tryCreateSpatialJoin( + Context context, + JoinNode joinNode, + Expression filter, + PlanNodeId nodeId, + List outputSymbols, + ComparisonExpression spatialComparison, + Metadata metadata, + SplitManager splitManager, + PageSourceManager pageSourceManager) { PlanNode leftNode = joinNode.getLeft(); PlanNode rightNode = joinNode.getRight(); @@ -277,11 +340,25 @@ private static Result tryCreateSpatialJoin(Context context, JoinNode joinNode, E joinNode.getRightHashSymbol(), joinNode.getDistributionType()); - return tryCreateSpatialJoin(context, newJoinNode, newFilter, nodeId, outputSymbols, (FunctionCall) newComparison.getLeft(), metadata); + return tryCreateSpatialJoin(context, newJoinNode, newFilter, nodeId, outputSymbols, (FunctionCall) newComparison.getLeft(), Optional.of(newComparison.getRight()), metadata, splitManager, pageSourceManager); } - private static Result tryCreateSpatialJoin(Context context, JoinNode joinNode, Expression filter, PlanNodeId nodeId, List outputSymbols, FunctionCall spatialFunction, Metadata metadata) + private static Result tryCreateSpatialJoin( + Context context, + JoinNode joinNode, + Expression filter, + PlanNodeId nodeId, + List outputSymbols, + FunctionCall spatialFunction, + Optional radius, + Metadata metadata, + SplitManager splitManager, + PageSourceManager pageSourceManager) { + // TODO Add support for distributed left spatial joins + Optional spatialPartitioningTableName = joinNode.getType() == INNER ? getSpatialPartitioningTableName(context.getSession()) : Optional.empty(); + Optional kdbTree = spatialPartitioningTableName.map(tableName -> loadKdbTree(tableName, context.getSession(), metadata, splitManager, pageSourceManager)); + List arguments = spatialFunction.getArguments(); verify(arguments.size() == 2); @@ -304,6 +381,7 @@ private static Result tryCreateSpatialJoin(Context context, JoinNode joinNode, E PlanNode newLeftNode; PlanNode newRightNode; + // Check if the order of arguments of the spatial function matches the order of join sides int alignment = checkAlignment(joinNode, firstSymbols, secondSymbols); if (alignment > 0) { newLeftNode = newFirstSymbol.map(symbol -> addProjection(context, leftNode, symbol, firstArgument)).orElse(leftNode); @@ -320,20 +398,116 @@ else if (alignment < 0) { Expression newFirstArgument = toExpression(newFirstSymbol, firstArgument); Expression newSecondArgument = toExpression(newSecondSymbol, secondArgument); + Optional leftPartitionSymbol = Optional.empty(); + Optional rightPartitionSymbol = Optional.empty(); + if (kdbTree.isPresent()) { + leftPartitionSymbol = Optional.of(context.getSymbolAllocator().newSymbol("pid", INTEGER)); + rightPartitionSymbol = Optional.of(context.getSymbolAllocator().newSymbol("pid", INTEGER)); + + if (alignment > 0) { + newLeftNode = addPartitioningNodes(context, newLeftNode, leftPartitionSymbol.get(), kdbTree.get(), newFirstArgument, Optional.empty()); + newRightNode = addPartitioningNodes(context, newRightNode, rightPartitionSymbol.get(), kdbTree.get(), newSecondArgument, radius); + } + else { + newLeftNode = addPartitioningNodes(context, newLeftNode, leftPartitionSymbol.get(), kdbTree.get(), newSecondArgument, Optional.empty()); + newRightNode = addPartitioningNodes(context, newRightNode, rightPartitionSymbol.get(), kdbTree.get(), newFirstArgument, radius); + } + } + Expression newSpatialFunction = new FunctionCall(spatialFunction.getName(), ImmutableList.of(newFirstArgument, newSecondArgument)); Expression newFilter = replaceExpression(filter, ImmutableMap.of(spatialFunction, newSpatialFunction)); - return Result.ofPlanNode(new JoinNode( + return Result.ofPlanNode(new SpatialJoinNode( nodeId, - joinNode.getType(), + SpatialJoinNode.Type.fromJoinNodeType(joinNode.getType()), newLeftNode, newRightNode, - joinNode.getCriteria(), outputSymbols, - Optional.of(newFilter), - joinNode.getLeftHashSymbol(), - joinNode.getRightHashSymbol(), - joinNode.getDistributionType())); + newFilter, + leftPartitionSymbol, + rightPartitionSymbol, + kdbTree.map(KdbTreeUtils::toJson))); + } + + private static KdbTree loadKdbTree(String tableName, Session session, Metadata metadata, SplitManager splitManager, PageSourceManager pageSourceManager) + { + QualifiedObjectName name = toQualifiedObjectName(tableName, session.getCatalog().get(), session.getSchema().get()); + TableHandle tableHandle = metadata.getTableHandle(session, name) + .orElseThrow(() -> new PrestoException(INVALID_SPATIAL_PARTITIONING, format("Table not found: %s", name))); + Map columnHandles = metadata.getColumnHandles(session, tableHandle); + List visibleColumnHandles = columnHandles.values().stream() + .filter(handle -> !metadata.getColumnMetadata(session, tableHandle, handle).isHidden()) + .collect(toImmutableList()); + checkSpatialPartitioningTable(visibleColumnHandles.size() == 1, "Expected single column for table %s, but found %s columns", name, columnHandles.size()); + + ColumnHandle kdbTreeColumn = Iterables.getOnlyElement(visibleColumnHandles); + + List layouts = metadata.getLayouts(session, tableHandle, Constraint.alwaysTrue(), Optional.of(ImmutableSet.of(kdbTreeColumn))); + checkSpatialPartitioningTable(!layouts.isEmpty(), "Table is empty: %s", name); + + Optional kdbTree = Optional.empty(); + try (SplitSource splitSource = splitManager.getSplits(session, layouts.get(0).getLayout().getHandle(), UNGROUPED_SCHEDULING)) { + while (!Thread.currentThread().isInterrupted()) { + SplitBatch splitBatch = getFutureValue(splitSource.getNextBatch(NOT_PARTITIONED, Lifespan.taskWide(), 1000)); + List splits = splitBatch.getSplits(); + + for (Split split : splits) { + try (ConnectorPageSource pageSource = pageSourceManager.createPageSource(session, split, ImmutableList.of(kdbTreeColumn))) { + do { + getFutureValue(pageSource.isBlocked()); + Page page = pageSource.getNextPage(); + if (page != null && page.getPositionCount() > 0) { + checkSpatialPartitioningTable(!kdbTree.isPresent(), "Expected exactly one row for table %s, but found more", name); + checkSpatialPartitioningTable(page.getPositionCount() == 1, "Expected exactly one row for table %s, but found %s rows", name, page.getPositionCount()); + String kdbTreeJson = VARCHAR.getSlice(page.getBlock(0), 0).toStringUtf8(); + try { + kdbTree = Optional.of(KdbTreeUtils.fromJson(kdbTreeJson)); + } + catch (IllegalArgumentException e) { + checkSpatialPartitioningTable(false, "Invalid JSON string for KDB tree: %s", e.getMessage()); + } + } + } + while (!pageSource.isFinished()); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + if (splitBatch.isLastBatch()) { + break; + } + } + } + + checkSpatialPartitioningTable(kdbTree.isPresent(), "Expected exactly one row for table %s, but got none", name); + return kdbTree.get(); + } + + private static void checkSpatialPartitioningTable(boolean condition, String message, Object... arguments) + { + if (!condition) { + throw new PrestoException(INVALID_SPATIAL_PARTITIONING, format(message, arguments)); + } + } + + private static QualifiedObjectName toQualifiedObjectName(String name, String catalog, String schema) + { + ImmutableList ids = ImmutableList.copyOf(Splitter.on('.').split(name)); + if (ids.size() == 3) { + return new QualifiedObjectName(ids.get(0), ids.get(1), ids.get(2)); + } + + if (ids.size() == 2) { + return new QualifiedObjectName(catalog, ids.get(0), ids.get(1)); + } + + if (ids.size() == 1) { + return new QualifiedObjectName(catalog, schema, ids.get(0)); + } + + throw new PrestoException(INVALID_SPATIAL_PARTITIONING, format("Invalid name: %s", name)); } private static int checkAlignment(JoinNode joinNode, Set maybeLeftSymbols, Set maybeRightSymbols) @@ -392,6 +566,30 @@ private static PlanNode addProjection(Context context, PlanNode node, Symbol sym return new ProjectNode(context.getIdAllocator().getNextId(), node, projections.build()); } + private static PlanNode addPartitioningNodes(Context context, PlanNode node, Symbol partitionSymbol, KdbTree kdbTree, Expression geometry, Optional radius) + { + Assignments.Builder projections = Assignments.builder(); + for (Symbol outputSymbol : node.getOutputSymbols()) { + projections.putIdentity(outputSymbol); + } + + ImmutableList.Builder partitioningArguments = ImmutableList.builder() + .add(new Cast(new StringLiteral(KdbTreeUtils.toJson(kdbTree)), KDB_TREE_TYPENAME)) + .add(geometry); + radius.map(partitioningArguments::add); + + FunctionCall partitioningFunction = new FunctionCall(QualifiedName.of("spatial_partitions"), partitioningArguments.build()); + Symbol partitionsSymbol = context.getSymbolAllocator().newSymbol(partitioningFunction, new ArrayType(INTEGER)); + projections.put(partitionsSymbol, partitioningFunction); + + return new UnnestNode( + context.getIdAllocator().getNextId(), + new ProjectNode(context.getIdAllocator().getNextId(), node, projections.build()), + node.getOutputSymbols(), + ImmutableMap.of(partitionsSymbol, ImmutableList.of(partitionSymbol)), + Optional.empty()); + } + private static boolean containsNone(Collection values, Collection testValues) { return values.stream().noneMatch(ImmutableSet.copyOf(testValues)::contains); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/GatherAndMergeWindows.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/GatherAndMergeWindows.java index 73c634a0c5c69..b4aa55db2c23e 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/GatherAndMergeWindows.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/GatherAndMergeWindows.java @@ -58,12 +58,12 @@ public static Set> rules() // TODO convert to a pattern that allows for a sequence of ProjectNode, instead // of a canned number, once the pattern system supports it. return IntStream.range(0, 5) - .boxed() - .flatMap(numProjects -> - Stream.of( - new MergeAdjacentWindowsOverProjects(numProjects), - new SwapAdjacentWindowsBySpecifications(numProjects))) - .collect(toImmutableSet()); + .boxed() + .flatMap(numProjects -> + Stream.of( + new MergeAdjacentWindowsOverProjects(numProjects), + new SwapAdjacentWindowsBySpecifications(numProjects))) + .collect(toImmutableSet()); } private abstract static class ManipulateAdjacentWindowsOverProjects @@ -115,6 +115,7 @@ public Result apply(WindowNode parent, Captures captures, Context context) * Looks for the pattern (ProjectNode*)WindowNode, and rewrites it to WindowNode(ProjectNode*), * returning an empty option if it can't rewrite the projects, for example because they rely on * the output of the WindowNode. + * * @param projects the nodes above the target, bottom first. */ protected static Optional pullWindowNodeAboveProjects( diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ImplementFilteredAggregations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ImplementFilteredAggregations.java index 5623ecceae054..3cc5a6f1030dd 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ImplementFilteredAggregations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ImplementFilteredAggregations.java @@ -20,6 +20,7 @@ import com.facebook.presto.sql.planner.plan.AggregationNode; import com.facebook.presto.sql.planner.plan.AggregationNode.Aggregation; import com.facebook.presto.sql.planner.plan.Assignments; +import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; @@ -30,7 +31,9 @@ import java.util.Optional; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.sql.ExpressionUtils.combineDisjunctsWithDefault; import static com.facebook.presto.sql.planner.plan.Patterns.aggregation; +import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL; import static com.google.common.base.Verify.verify; /** @@ -46,6 +49,7 @@ * - Aggregation * F1(...) mask ($0) * F2(...) mask ($1) + * - Filter(mask ($0) OR mask ($1)) * - Project * <identity projections for existing fields> * $0 = C1(...) @@ -78,6 +82,8 @@ public Result apply(AggregationNode aggregation, Captures captures, Context cont { Assignments.Builder newAssignments = Assignments.builder(); ImmutableMap.Builder aggregations = ImmutableMap.builder(); + ImmutableList.Builder maskSymbols = ImmutableList.builder(); + boolean aggregateWithoutFilterPresent = false; for (Map.Entry entry : aggregation.getAggregations().entrySet()) { Symbol output = entry.getKey(); @@ -92,23 +98,37 @@ public Result apply(AggregationNode aggregation, Captures captures, Context cont verify(!mask.isPresent(), "Expected aggregation without mask symbols, see Rule pattern"); newAssignments.put(symbol, filter); mask = Optional.of(symbol); + + maskSymbols.add(symbol.toSymbolReference()); + } + else { + aggregateWithoutFilterPresent = true; } + aggregations.put(output, new Aggregation( new FunctionCall(call.getName(), call.getWindow(), Optional.empty(), call.getOrderBy(), call.isDistinct(), call.getArguments()), entry.getValue().getSignature(), mask)); } + Expression predicate = TRUE_LITERAL; + if (!aggregation.hasNonEmptyGroupingSet() && !aggregateWithoutFilterPresent) { + predicate = combineDisjunctsWithDefault(maskSymbols.build(), TRUE_LITERAL); + } + // identity projection for all existing inputs newAssignments.putIdentities(aggregation.getSource().getOutputSymbols()); return Result.ofPlanNode( new AggregationNode( context.getIdAllocator().getNextId(), - new ProjectNode( + new FilterNode( context.getIdAllocator().getNextId(), - aggregation.getSource(), - newAssignments.build()), + new ProjectNode( + context.getIdAllocator().getNextId(), + aggregation.getSource(), + newAssignments.build()), + predicate), aggregations.build(), aggregation.getGroupingSets(), ImmutableList.of(), diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PickTableLayout.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PickTableLayout.java index 419e3cf4b21d7..c696d5f7bcef4 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PickTableLayout.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PickTableLayout.java @@ -14,23 +14,35 @@ package com.facebook.presto.sql.planner.iterative.rule; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.matching.Capture; import com.facebook.presto.matching.Captures; import com.facebook.presto.matching.Pattern; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableLayoutResult; +import com.facebook.presto.operator.scalar.TryFunction; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.Constraint; +import com.facebook.presto.spi.predicate.NullableValue; import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.DomainTranslator; +import com.facebook.presto.sql.planner.ExpressionInterpreter; import com.facebook.presto.sql.planner.LiteralEncoder; +import com.facebook.presto.sql.planner.LookupSymbolResolver; +import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.SymbolsExtractor; +import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.sql.tree.NodeRef; +import com.facebook.presto.sql.tree.NullLiteral; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @@ -43,16 +55,23 @@ import static com.facebook.presto.SystemSessionProperties.isNewOptimizerEnabled; import static com.facebook.presto.matching.Capture.newCapture; +import static com.facebook.presto.metadata.TableLayoutResult.computeEnforced; import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; import static com.facebook.presto.sql.ExpressionUtils.filterDeterministicConjuncts; import static com.facebook.presto.sql.ExpressionUtils.filterNonDeterministicConjuncts; +import static com.facebook.presto.sql.analyzer.ExpressionAnalyzer.getExpressionTypes; import static com.facebook.presto.sql.planner.iterative.rule.PreconditionRules.checkRulesAreFiredBeforeAddExchangesRule; import static com.facebook.presto.sql.planner.plan.Patterns.filter; import static com.facebook.presto.sql.planner.plan.Patterns.source; import static com.facebook.presto.sql.planner.plan.Patterns.tableScan; import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.collect.Sets.intersection; +import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toList; /** * These rules should not be run after AddExchanges so as not to overwrite the TableLayout @@ -61,11 +80,13 @@ public class PickTableLayout { private final Metadata metadata; + private final SqlParser parser; private final DomainTranslator domainTranslator; - public PickTableLayout(Metadata metadata) + public PickTableLayout(Metadata metadata, SqlParser parser) { this.metadata = requireNonNull(metadata, "metadata is null"); + this.parser = requireNonNull(parser, "parser is null"); this.domainTranslator = new DomainTranslator(new LiteralEncoder(metadata.getBlockEncodingSerde())); } @@ -79,23 +100,25 @@ public Set> rules() public PickTableLayoutForPredicate pickTableLayoutForPredicate() { - return new PickTableLayoutForPredicate(metadata, domainTranslator); + return new PickTableLayoutForPredicate(metadata, parser, domainTranslator); } public PickTableLayoutWithoutPredicate pickTableLayoutWithoutPredicate() { - return new PickTableLayoutWithoutPredicate(metadata, domainTranslator); + return new PickTableLayoutWithoutPredicate(metadata, parser, domainTranslator); } private static final class PickTableLayoutForPredicate implements Rule { private final Metadata metadata; + private final SqlParser parser; private final DomainTranslator domainTranslator; - private PickTableLayoutForPredicate(Metadata metadata, DomainTranslator domainTranslator) + private PickTableLayoutForPredicate(Metadata metadata, SqlParser parser, DomainTranslator domainTranslator) { this.metadata = requireNonNull(metadata, "metadata is null"); + this.parser = requireNonNull(parser, "parser is null"); this.domainTranslator = requireNonNull(domainTranslator, "domainTranslator is null"); } @@ -121,7 +144,7 @@ public Result apply(FilterNode filterNode, Captures captures, Context context) { TableScanNode tableScan = captures.get(TABLE_SCAN); - PlanNode rewritten = planTableScan(tableScan, filterNode.getPredicate(), context, metadata, domainTranslator); + PlanNode rewritten = planTableScan(tableScan, filterNode.getPredicate(), context.getSession(), context.getSymbolAllocator().getTypes(), context.getIdAllocator(), metadata, parser, domainTranslator); if (arePlansSame(filterNode, tableScan, rewritten)) { return Result.empty(); @@ -146,7 +169,13 @@ private boolean arePlansSame(FilterNode filter, TableScanNode tableScan, PlanNod } TableScanNode rewrittenTableScan = (TableScanNode) rewrittenFilter.getSource(); - return Objects.equals(tableScan.getCurrentConstraint(), rewrittenTableScan.getCurrentConstraint()); + + if (!tableScan.getLayout().isPresent() && rewrittenTableScan.getLayout().isPresent()) { + return false; + } + + return Objects.equals(tableScan.getCurrentConstraint(), rewrittenTableScan.getCurrentConstraint()) + && Objects.equals(tableScan.getEnforcedConstraint(), rewrittenTableScan.getEnforcedConstraint()); } } @@ -154,11 +183,13 @@ private static final class PickTableLayoutWithoutPredicate implements Rule { private final Metadata metadata; + private final SqlParser parser; private final DomainTranslator domainTranslator; - private PickTableLayoutWithoutPredicate(Metadata metadata, DomainTranslator domainTranslator) + private PickTableLayoutWithoutPredicate(Metadata metadata, SqlParser parser, DomainTranslator domainTranslator) { this.metadata = requireNonNull(metadata, "metadata is null"); + this.parser = requireNonNull(parser, "parser is null"); this.domainTranslator = requireNonNull(domainTranslator, "domainTranslator is null"); } @@ -183,56 +214,171 @@ public Result apply(TableScanNode tableScanNode, Captures captures, Context cont return Result.empty(); } - return Result.ofPlanNode(planTableScan(tableScanNode, TRUE_LITERAL, context, metadata, domainTranslator)); + return Result.ofPlanNode(planTableScan(tableScanNode, TRUE_LITERAL, context.getSession(), context.getSymbolAllocator().getTypes(), context.getIdAllocator(), metadata, parser, domainTranslator)); } } - private static PlanNode planTableScan(TableScanNode node, Expression predicate, Rule.Context context, Metadata metadata, DomainTranslator domainTranslator) + private static PlanNode planTableScan( + TableScanNode node, + Expression predicate, + Session session, + TypeProvider types, + PlanNodeIdAllocator idAllocator, + Metadata metadata, + SqlParser parser, + DomainTranslator domainTranslator) + { + return listTableLayouts( + node, + predicate, + false, + session, + types, + idAllocator, + metadata, + parser, + domainTranslator) + .get(0); + } + + public static List listTableLayouts( + TableScanNode node, + Expression predicate, + boolean pruneWithPredicateExpression, + Session session, + TypeProvider types, + PlanNodeIdAllocator idAllocator, + Metadata metadata, + SqlParser parser, + DomainTranslator domainTranslator) { + // don't include non-deterministic predicates Expression deterministicPredicate = filterDeterministicConjuncts(predicate); + DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( metadata, - context.getSession(), + session, deterministicPredicate, - context.getSymbolAllocator().getTypes()); + types); - TupleDomain simplifiedConstraint = decomposedPredicate.getTupleDomain() + TupleDomain newDomain = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) - .intersect(node.getCurrentConstraint()); + .intersect(node.getEnforcedConstraint()); + + Map assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); + + Constraint constraint; + if (pruneWithPredicateExpression) { + LayoutConstraintEvaluator evaluator = new LayoutConstraintEvaluator( + metadata, + parser, + session, + types, + node.getAssignments(), + combineConjuncts( + deterministicPredicate, + // Simplify the tuple domain to avoid creating an expression with too many nodes, + // which would be expensive to evaluate in the call to isCandidate below. + domainTranslator.toPredicate(newDomain.simplify().transform(assignments::get)))); + constraint = new Constraint<>(newDomain, evaluator::isCandidate); + } + else { + // Currently, invoking the expression interpreter is very expensive. + // TODO invoke the interpreter unconditionally when the interpreter becomes cheap enough. + constraint = new Constraint<>(newDomain); + } + // Layouts will be returned in order of the connector's preference List layouts = metadata.getLayouts( - context.getSession(), + session, node.getTable(), - new Constraint<>(simplifiedConstraint), - Optional.of(ImmutableSet.copyOf(node.getAssignments().values()))); + constraint, + Optional.of(node.getOutputSymbols().stream() + .map(node.getAssignments()::get) + .collect(toImmutableSet()))); + if (layouts.isEmpty()) { - return new ValuesNode(context.getIdAllocator().getNextId(), node.getOutputSymbols(), ImmutableList.of()); + return ImmutableList.of(new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of())); } + + // Filter out layouts that cannot supply all the required columns layouts = layouts.stream() .filter(layout -> layout.hasAllOutputs(node)) + .collect(toList()); + checkState(!layouts.isEmpty(), "No usable layouts for %s", node); + + if (layouts.stream().anyMatch(layout -> layout.getLayout().getPredicate().isNone())) { + return ImmutableList.of(new ValuesNode(idAllocator.getNextId(), node.getOutputSymbols(), ImmutableList.of())); + } + + return layouts.stream() + .map(layout -> { + TableScanNode tableScan = new TableScanNode( + node.getId(), + node.getTable(), + node.getOutputSymbols(), + node.getAssignments(), + Optional.of(layout.getLayout().getHandle()), + layout.getLayout().getPredicate(), + computeEnforced(newDomain, layout.getUnenforcedConstraint())); + + // The order of the arguments to combineConjuncts matters: + // * Unenforced constraints go first because they can only be simple column references, + // which are not prone to logic errors such as out-of-bound access, div-by-zero, etc. + // * Conjuncts in non-deterministic expressions and non-TupleDomain-expressible expressions should + // retain their original (maybe intermixed) order from the input predicate. However, this is not implemented yet. + // * Short of implementing the previous bullet point, the current order of non-deterministic expressions + // and non-TupleDomain-expressible expressions should be retained. Changing the order can lead + // to failures of previously successful queries. + Expression resultingPredicate = combineConjuncts( + domainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get)), + filterNonDeterministicConjuncts(predicate), + decomposedPredicate.getRemainingExpression()); + + if (!TRUE_LITERAL.equals(resultingPredicate)) { + return new FilterNode(idAllocator.getNextId(), tableScan, resultingPredicate); + } + + return tableScan; + }) .collect(toImmutableList()); + } - TableLayoutResult layout = layouts.get(0); + private static class LayoutConstraintEvaluator + { + private final Map assignments; + private final ExpressionInterpreter evaluator; + private final Set arguments; - TableScanNode result = new TableScanNode( - node.getId(), - node.getTable(), - node.getOutputSymbols(), - node.getAssignments(), - Optional.of(layout.getLayout().getHandle()), - simplifiedConstraint.intersect(layout.getLayout().getPredicate()), - Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); + public LayoutConstraintEvaluator(Metadata metadata, SqlParser parser, Session session, TypeProvider types, Map assignments, Expression expression) + { + this.assignments = assignments; - Map assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); - Expression resultingPredicate = combineConjuncts( - decomposedPredicate.getRemainingExpression(), - filterNonDeterministicConjuncts(predicate), - domainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get))); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, parser, types, expression, emptyList(), WarningCollector.NOOP); - if (!TRUE_LITERAL.equals(resultingPredicate)) { - return new FilterNode(context.getIdAllocator().getNextId(), result, resultingPredicate); + evaluator = ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes); + arguments = SymbolsExtractor.extractUnique(expression).stream() + .map(assignments::get) + .collect(toImmutableSet()); } - return result; + private boolean isCandidate(Map bindings) + { + if (intersection(bindings.keySet(), arguments).isEmpty()) { + return true; + } + LookupSymbolResolver inputs = new LookupSymbolResolver(assignments, bindings); + + // Skip pruning if evaluation fails in a recoverable way. Failing here can cause + // spurious query failures for partitions that would otherwise be filtered out. + Object optimized = TryFunction.evaluate(() -> evaluator.optimize(inputs), true); + + // If any conjuncts evaluate to FALSE or null, then the whole predicate will never be true and so the partition should be pruned + if (Boolean.FALSE.equals(optimized) || optimized == null || optimized instanceof NullLiteral) { + return false; + } + + return true; + } } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneCrossJoinColumns.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneCrossJoinColumns.java index 669c4956522d8..c7004ddb33f66 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneCrossJoinColumns.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneCrossJoinColumns.java @@ -17,11 +17,12 @@ import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.PlanNode; +import com.google.common.collect.ImmutableList; import java.util.Optional; import java.util.Set; -import static com.facebook.presto.sql.planner.iterative.rule.Util.restrictChildOutputs; +import static com.facebook.presto.sql.planner.iterative.rule.Util.restrictOutputs; import static com.facebook.presto.sql.planner.plan.Patterns.join; /** @@ -38,6 +39,27 @@ public PruneCrossJoinColumns() @Override protected Optional pushDownProjectOff(PlanNodeIdAllocator idAllocator, JoinNode joinNode, Set referencedOutputs) { - return restrictChildOutputs(idAllocator, joinNode, referencedOutputs, referencedOutputs); + Optional newLeft = restrictOutputs(idAllocator, joinNode.getLeft(), referencedOutputs); + Optional newRight = restrictOutputs(idAllocator, joinNode.getRight(), referencedOutputs); + + if (!newLeft.isPresent() && !newRight.isPresent()) { + return Optional.empty(); + } + + ImmutableList.Builder outputSymbolBuilder = ImmutableList.builder(); + outputSymbolBuilder.addAll(newLeft.orElse(joinNode.getLeft()).getOutputSymbols()); + outputSymbolBuilder.addAll(newRight.orElse(joinNode.getRight()).getOutputSymbols()); + + return Optional.of(new JoinNode( + idAllocator.getNextId(), + joinNode.getType(), + newLeft.orElse(joinNode.getLeft()), + newRight.orElse(joinNode.getRight()), + joinNode.getCriteria(), + outputSymbolBuilder.build(), + joinNode.getFilter(), + joinNode.getLeftHashSymbol(), + joinNode.getRightHashSymbol(), + joinNode.getDistributionType())); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneTableScanColumns.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneTableScanColumns.java index ac0994c2ae808..f89784bd7fe76 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneTableScanColumns.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PruneTableScanColumns.java @@ -44,6 +44,6 @@ protected Optional pushDownProjectOff(PlanNodeIdAllocator idAllocator, filterKeys(tableScanNode.getAssignments(), referencedOutputs::contains), tableScanNode.getLayout(), tableScanNode.getCurrentConstraint(), - tableScanNode.getOriginalConstraint())); + tableScanNode.getEnforcedConstraint())); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PushPartialAggregationThroughExchange.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PushPartialAggregationThroughExchange.java index 9b60a96486d62..102f43f332843 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PushPartialAggregationThroughExchange.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/PushPartialAggregationThroughExchange.java @@ -29,7 +29,9 @@ import com.facebook.presto.sql.planner.plan.ExchangeNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.LambdaExpression; import com.facebook.presto.sql.tree.QualifiedName; import com.google.common.collect.ImmutableList; @@ -52,6 +54,7 @@ import static com.facebook.presto.sql.planner.plan.Patterns.source; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; +import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; public class PushPartialAggregationThroughExchange @@ -209,7 +212,15 @@ private PlanNode split(AggregationNode node, Context context) // rewrite final aggregation in terms of intermediate function finalAggregation.put(entry.getKey(), new AggregationNode.Aggregation( - new FunctionCall(QualifiedName.of(signature.getName()), ImmutableList.of(intermediateSymbol.toSymbolReference())), + new FunctionCall( + QualifiedName.of(signature.getName()), + ImmutableList.builder() + .add(intermediateSymbol.toSymbolReference()) + .addAll(originalAggregation.getCall().getArguments().stream() + .filter(LambdaExpression.class::isInstance) + .collect(toImmutableList())) + .build()), + signature, Optional.empty())); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ReorderJoins.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ReorderJoins.java index 0e91c8e9ad01d..417c998319b7c 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ReorderJoins.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/ReorderJoins.java @@ -29,6 +29,7 @@ import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.JoinNode; +import com.facebook.presto.sql.planner.plan.JoinNode.DistributionType; import com.facebook.presto.sql.planner.plan.JoinNode.EquiJoinClause; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.tree.ComparisonExpression; @@ -55,8 +56,6 @@ import static com.facebook.presto.SystemSessionProperties.getJoinDistributionType; import static com.facebook.presto.SystemSessionProperties.getJoinReorderingStrategy; import static com.facebook.presto.SystemSessionProperties.getMaxReorderedJoins; -import static com.facebook.presto.cost.PlanNodeCostEstimate.INFINITE_COST; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; import static com.facebook.presto.sql.ExpressionUtils.and; import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; import static com.facebook.presto.sql.ExpressionUtils.extractConjuncts; @@ -64,6 +63,7 @@ import static com.facebook.presto.sql.planner.DeterminismEvaluator.isDeterministic; import static com.facebook.presto.sql.planner.EqualityInference.createEqualityInference; import static com.facebook.presto.sql.planner.EqualityInference.nonInferrableConjuncts; +import static com.facebook.presto.sql.planner.iterative.rule.DetermineJoinDistributionType.canReplicate; import static com.facebook.presto.sql.planner.iterative.rule.ReorderJoins.JoinEnumerationResult.INFINITE_COST_RESULT; import static com.facebook.presto.sql.planner.iterative.rule.ReorderJoins.JoinEnumerationResult.UNKNOWN_COST_RESULT; import static com.facebook.presto.sql.planner.iterative.rule.ReorderJoins.MultiJoinNode.toMultiJoinNode; @@ -77,6 +77,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.in; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.getOnlyElement; @@ -369,23 +370,46 @@ private JoinEnumerationResult setJoinNodeProperties(JoinNode joinNode) if (isAtMostScalar(joinNode.getLeft(), lookup)) { return createJoinEnumerationResult(joinNode.flipChildren().withDistributionType(REPLICATED)); } - - List possibleJoinNodes = new ArrayList<>(); - JoinDistributionType joinDistributionType = getJoinDistributionType(session); - if (joinDistributionType.canPartition() && !joinNode.isCrossJoin()) { - possibleJoinNodes.add(createJoinEnumerationResult(joinNode.withDistributionType(PARTITIONED))); - possibleJoinNodes.add(createJoinEnumerationResult(joinNode.flipChildren().withDistributionType(PARTITIONED))); - } - if (joinDistributionType.canReplicate()) { - possibleJoinNodes.add(createJoinEnumerationResult(joinNode.withDistributionType(REPLICATED))); - possibleJoinNodes.add(createJoinEnumerationResult(joinNode.flipChildren().withDistributionType(REPLICATED))); - } + List possibleJoinNodes = getPossibleJoinNodes(joinNode, getJoinDistributionType(session)); + verify(!possibleJoinNodes.isEmpty(), "possibleJoinNodes is empty"); if (possibleJoinNodes.stream().anyMatch(UNKNOWN_COST_RESULT::equals)) { return UNKNOWN_COST_RESULT; } return resultComparator.min(possibleJoinNodes); } + private List getPossibleJoinNodes(JoinNode joinNode, JoinDistributionType distributionType) + { + checkArgument(joinNode.getType() == INNER, "unexpected join node type: %s", joinNode.getType()); + + if (joinNode.isCrossJoin()) { + return getPossibleJoinNodes(joinNode, REPLICATED); + } + + switch (distributionType) { + case PARTITIONED: + return getPossibleJoinNodes(joinNode, PARTITIONED); + case BROADCAST: + return getPossibleJoinNodes(joinNode, REPLICATED); + case AUTOMATIC: + ImmutableList.Builder result = ImmutableList.builder(); + result.addAll(getPossibleJoinNodes(joinNode, PARTITIONED)); + if (canReplicate(joinNode, context)) { + result.addAll(getPossibleJoinNodes(joinNode, REPLICATED)); + } + return result.build(); + default: + throw new IllegalArgumentException("unexpected join distribution type: " + distributionType); + } + } + + private List getPossibleJoinNodes(JoinNode joinNode, DistributionType distributionType) + { + return ImmutableList.of( + createJoinEnumerationResult(joinNode.withDistributionType(distributionType)), + createJoinEnumerationResult(joinNode.flipChildren().withDistributionType(distributionType))); + } + private JoinEnumerationResult createJoinEnumerationResult(PlanNode planNode) { return JoinEnumerationResult.createJoinEnumerationResult(Optional.of(planNode), costProvider.getCumulativeCost(planNode)); @@ -544,8 +568,8 @@ public MultiJoinNode build() @VisibleForTesting static class JoinEnumerationResult { - public static final JoinEnumerationResult UNKNOWN_COST_RESULT = new JoinEnumerationResult(Optional.empty(), UNKNOWN_COST); - public static final JoinEnumerationResult INFINITE_COST_RESULT = new JoinEnumerationResult(Optional.empty(), INFINITE_COST); + public static final JoinEnumerationResult UNKNOWN_COST_RESULT = new JoinEnumerationResult(Optional.empty(), PlanNodeCostEstimate.unknown()); + public static final JoinEnumerationResult INFINITE_COST_RESULT = new JoinEnumerationResult(Optional.empty(), PlanNodeCostEstimate.infinite()); private final Optional planNode; private final PlanNodeCostEstimate cost; @@ -554,8 +578,8 @@ private JoinEnumerationResult(Optional planNode, PlanNodeCostEstimate { this.planNode = requireNonNull(planNode, "planNode is null"); this.cost = requireNonNull(cost, "cost is null"); - checkArgument((cost.hasUnknownComponents() || cost.equals(INFINITE_COST)) && !planNode.isPresent() - || (!cost.hasUnknownComponents() || !cost.equals(INFINITE_COST)) && planNode.isPresent(), + checkArgument((cost.hasUnknownComponents() || cost.equals(PlanNodeCostEstimate.infinite())) && !planNode.isPresent() + || (!cost.hasUnknownComponents() || !cost.equals(PlanNodeCostEstimate.infinite())) && planNode.isPresent(), "planNode should be present if and only if cost is known"); } @@ -574,7 +598,7 @@ static JoinEnumerationResult createJoinEnumerationResult(Optional plan if (cost.hasUnknownComponents()) { return UNKNOWN_COST_RESULT; } - if (cost.equals(INFINITE_COST)) { + if (cost.equals(PlanNodeCostEstimate.infinite())) { return INFINITE_COST_RESULT; } return new JoinEnumerationResult(planNode, cost); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/RewriteSpatialPartitioningAggregation.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/RewriteSpatialPartitioningAggregation.java new file mode 100644 index 0000000000000..83c81c0e7e8c5 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/RewriteSpatialPartitioningAggregation.java @@ -0,0 +1,137 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.planner.iterative.rule; + +import com.facebook.presto.matching.Captures; +import com.facebook.presto.matching.Pattern; +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.iterative.Rule; +import com.facebook.presto.sql.planner.plan.AggregationNode; +import com.facebook.presto.sql.planner.plan.AggregationNode.Aggregation; +import com.facebook.presto.sql.planner.plan.Assignments; +import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.LongLiteral; +import com.facebook.presto.sql.tree.QualifiedName; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +import static com.facebook.presto.SystemSessionProperties.getHashPartitionCount; +import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.facebook.presto.sql.planner.plan.Patterns.aggregation; +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.util.Objects.requireNonNull; + +/** + * Re-writes spatial_partitioning(geometry) aggregations into spatial_partitioning(envelope, partition_count) + * on top of ST_Envelope(geometry) projection, e.g. + * + * - Aggregation: spatial_partitioning(geometry) + * - source + * + * becomes + * + * - Aggregation: spatial_partitioning(envelope, partition_count) + * - Project: envelope := ST_Envelope(geometry) + * - source + * + * , where partition_count is the value of session property hash_partition_count + */ +public class RewriteSpatialPartitioningAggregation + implements Rule +{ + private static final TypeSignature GEOMETRY_TYPE_SIGNATURE = parseTypeSignature("Geometry"); + private static final String NAME = "spatial_partitioning"; + private static final Signature INTERNAL_SIGNATURE = new Signature(NAME, AGGREGATE, VARCHAR.getTypeSignature(), GEOMETRY_TYPE_SIGNATURE, INTEGER.getTypeSignature()); + private static final Pattern PATTERN = aggregation() + .matching(RewriteSpatialPartitioningAggregation::hasSpatialPartitioningAggregation); + + private final Metadata metadata; + + public RewriteSpatialPartitioningAggregation(Metadata metadata) + { + this.metadata = requireNonNull(metadata, "metadata is null"); + } + + private static boolean hasSpatialPartitioningAggregation(AggregationNode aggregation) + { + return aggregation.getAggregations().values().stream() + .map(Aggregation::getCall) + .anyMatch(call -> call.getName().toString().equals(NAME) && call.getArguments().size() == 1); + } + + @Override + public Pattern getPattern() + { + return PATTERN; + } + + @Override + public Result apply(AggregationNode node, Captures captures, Context context) + { + ImmutableMap.Builder aggregations = ImmutableMap.builder(); + Symbol partitionCountSymbol = context.getSymbolAllocator().newSymbol("partition_count", INTEGER); + ImmutableMap.Builder envelopeAssignments = ImmutableMap.builder(); + for (Map.Entry entry : node.getAggregations().entrySet()) { + Aggregation aggregation = entry.getValue(); + FunctionCall call = aggregation.getCall(); + QualifiedName name = call.getName(); + if (name.toString().equals(NAME) && call.getArguments().size() == 1) { + Expression geometry = getOnlyElement(call.getArguments()); + Symbol envelopeSymbol = context.getSymbolAllocator().newSymbol("envelope", metadata.getType(GEOMETRY_TYPE_SIGNATURE)); + if (geometry instanceof FunctionCall && ((FunctionCall) geometry).getName().toString().equalsIgnoreCase("ST_Envelope")) { + envelopeAssignments.put(envelopeSymbol, geometry); + } + else { + envelopeAssignments.put(envelopeSymbol, new FunctionCall(QualifiedName.of("ST_Envelope"), ImmutableList.of(geometry))); + } + aggregations.put(entry.getKey(), + new Aggregation( + new FunctionCall(name, ImmutableList.of(envelopeSymbol.toSymbolReference(), partitionCountSymbol.toSymbolReference())), + INTERNAL_SIGNATURE, + aggregation.getMask())); + } + else { + aggregations.put(entry); + } + } + + return Result.ofPlanNode( + new AggregationNode( + node.getId(), + new ProjectNode( + context.getIdAllocator().getNextId(), + node.getSource(), + Assignments.builder() + .putIdentities(node.getSource().getOutputSymbols()) + .put(partitionCountSymbol, new LongLiteral(Integer.toString(getHashPartitionCount(context.getSession())))) + .putAll(envelopeAssignments.build()) + .build()), + aggregations.build(), + node.getGroupingSets(), + node.getPreGroupedSymbols(), + node.getStep(), + node.getHashSymbol(), + node.getGroupIdSymbol())); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/SimplifyExpressions.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/SimplifyExpressions.java index 9a2f695e6e24d..a70b3a35dd4b3 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/SimplifyExpressions.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/SimplifyExpressions.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.iterative.rule; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.parser.SqlParser; @@ -50,7 +51,7 @@ static Expression rewrite(Expression expression, Session session, SymbolAllocato } expression = pushDownNegations(expression); expression = extractCommonPredicates(expression); - Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList() /* parameters already replaced */); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList(), WarningCollector.NOOP); ExpressionInterpreter interpreter = ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes); return literalEncoder.toExpression(interpreter.optimize(NoOpSymbolResolver.INSTANCE), expressionTypes.get(NodeRef.of(expression))); } @@ -66,7 +67,6 @@ public Set> rules() return ImmutableSet.of( projectExpressionRewrite(), filterExpressionRewrite(), - tableScanExpressionRewrite(), joinExpressionRewrite(), valuesExpressionRewrite()); // ApplyNode and AggregationNode are not supported, because ExpressionInterpreter doesn't support them } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformCorrelatedScalarSubquery.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformCorrelatedScalarSubquery.java index 9a7e953400bc3..a2363fb0c2fe6 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformCorrelatedScalarSubquery.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/iterative/rule/TransformCorrelatedScalarSubquery.java @@ -73,8 +73,7 @@ * - non scalar subquery * *

- * - * This must be run after {@link TransformCorrelatedScalarAggregationToJoin} + * This must be run after {@link TransformCorrelatedScalarAggregationToJoin} */ public class TransformCorrelatedScalarSubquery implements Rule diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ActualProperties.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ActualProperties.java index e2fca3a008445..762d8c68689bc 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ActualProperties.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ActualProperties.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.sql.planner.optimizations; +import com.facebook.presto.Session; +import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.ConstantProperty; import com.facebook.presto.spi.LocalProperty; import com.facebook.presto.spi.predicate.NullableValue; @@ -39,6 +41,7 @@ import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; import static com.facebook.presto.util.MoreLists.filteredCopy; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.transform; import static java.util.Objects.requireNonNull; @@ -116,18 +119,20 @@ public boolean isNodePartitionedOn(Collection columns, boolean nullsAndA return global.isNodePartitionedOn(columns, constants.keySet(), nullsAndAnyReplicated); } - public boolean isNodePartitionedOn(Partitioning partitioning, boolean nullsAndAnyReplicated) + public boolean isCompatibleTablePartitioningWith(Partitioning partitioning, boolean nullsAndAnyReplicated, Metadata metadata, Session session) { - return global.isNodePartitionedOn(partitioning, nullsAndAnyReplicated); + return global.isCompatibleTablePartitioningWith(partitioning, nullsAndAnyReplicated, metadata, session); } - public boolean isNodePartitionedWith(ActualProperties other, Function> symbolMappings) + public boolean isCompatibleTablePartitioningWith(ActualProperties other, Function> symbolMappings, Metadata metadata, Session session) { - return global.isNodePartitionedWith( + return global.isCompatibleTablePartitioningWith( other.global, symbolMappings, symbol -> Optional.ofNullable(constants.get(symbol)), - symbol -> Optional.ofNullable(other.constants.get(symbol))); + symbol -> Optional.ofNullable(other.constants.get(symbol)), + metadata, + session); } /** @@ -301,6 +306,11 @@ public static final class Global private Global(Optional nodePartitioning, Optional streamPartitioning, boolean nullsAndAnyReplicated) { + checkArgument(!nodePartitioning.isPresent() + || !streamPartitioning.isPresent() + || nodePartitioning.get().getColumns().containsAll(streamPartitioning.get().getColumns()) + || streamPartitioning.get().getColumns().containsAll(nodePartitioning.get().getColumns()), + "Global stream partitioning columns should match node partitioning columns"); this.nodePartitioning = requireNonNull(nodePartitioning, "nodePartitioning is null"); this.streamPartitioning = requireNonNull(streamPartitioning, "streamPartitioning is null"); this.nullsAndAnyReplicated = nullsAndAnyReplicated; @@ -387,24 +397,28 @@ private boolean isNodePartitionedOn(Collection columns, Set cons return nodePartitioning.isPresent() && nodePartitioning.get().isPartitionedOn(columns, constants) && this.nullsAndAnyReplicated == nullsAndAnyReplicated; } - private boolean isNodePartitionedOn(Partitioning partitioning, boolean nullsAndAnyReplicated) + private boolean isCompatibleTablePartitioningWith(Partitioning partitioning, boolean nullsAndAnyReplicated, Metadata metadata, Session session) { - return nodePartitioning.isPresent() && nodePartitioning.get().equals(partitioning) && this.nullsAndAnyReplicated == nullsAndAnyReplicated; + return nodePartitioning.isPresent() && nodePartitioning.get().isCompatibleWith(partitioning, metadata, session) && this.nullsAndAnyReplicated == nullsAndAnyReplicated; } - private boolean isNodePartitionedWith( + private boolean isCompatibleTablePartitioningWith( Global other, Function> symbolMappings, Function> leftConstantMapping, - Function> rightConstantMapping) + Function> rightConstantMapping, + Metadata metadata, + Session session) { return nodePartitioning.isPresent() && other.nodePartitioning.isPresent() && - nodePartitioning.get().isPartitionedWith( + nodePartitioning.get().isCompatibleWith( other.nodePartitioning.get(), symbolMappings, leftConstantMapping, - rightConstantMapping) && + rightConstantMapping, + metadata, + session) && nullsAndAnyReplicated == other.nullsAndAnyReplicated; } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddExchanges.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddExchanges.java index 00aba258794f1..e02c6a6b54813 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddExchanges.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddExchanges.java @@ -15,28 +15,21 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; -import com.facebook.presto.metadata.TableLayoutResult; -import com.facebook.presto.spi.ColumnHandle; -import com.facebook.presto.spi.Constraint; import com.facebook.presto.spi.GroupingProperty; import com.facebook.presto.spi.LocalProperty; import com.facebook.presto.spi.SortingProperty; -import com.facebook.presto.spi.predicate.NullableValue; -import com.facebook.presto.spi.predicate.TupleDomain; -import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.DomainTranslator; -import com.facebook.presto.sql.planner.ExpressionInterpreter; import com.facebook.presto.sql.planner.LiteralEncoder; -import com.facebook.presto.sql.planner.LookupSymbolResolver; import com.facebook.presto.sql.planner.Partitioning; import com.facebook.presto.sql.planner.PartitioningScheme; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.SymbolAllocator; -import com.facebook.presto.sql.planner.SymbolsExtractor; import com.facebook.presto.sql.planner.TypeProvider; +import com.facebook.presto.sql.planner.iterative.rule.PickTableLayout; import com.facebook.presto.sql.planner.plan.AggregationNode; import com.facebook.presto.sql.planner.plan.ApplyNode; import com.facebook.presto.sql.planner.plan.Assignments; @@ -60,6 +53,7 @@ import com.facebook.presto.sql.planner.plan.RowNumberNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -69,17 +63,13 @@ import com.facebook.presto.sql.planner.plan.UnnestNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.planner.plan.WindowNode; -import com.facebook.presto.sql.tree.BooleanLiteral; import com.facebook.presto.sql.tree.Expression; -import com.facebook.presto.sql.tree.NodeRef; -import com.facebook.presto.sql.tree.NullLiteral; import com.facebook.presto.sql.tree.SymbolReference; import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ComparisonChain; -import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; @@ -100,10 +90,6 @@ import static com.facebook.presto.SystemSessionProperties.isColocatedJoinEnabled; import static com.facebook.presto.SystemSessionProperties.isDistributedSortEnabled; import static com.facebook.presto.SystemSessionProperties.isForceSingleNodeOutput; -import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts; -import static com.facebook.presto.sql.ExpressionUtils.filterDeterministicConjuncts; -import static com.facebook.presto.sql.ExpressionUtils.filterNonDeterministicConjuncts; -import static com.facebook.presto.sql.analyzer.ExpressionAnalyzer.getExpressionTypes; import static com.facebook.presto.sql.planner.FragmentTableScanCounter.countSources; import static com.facebook.presto.sql.planner.FragmentTableScanCounter.hasMultipleSources; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.FIXED_ARBITRARY_DISTRIBUTION; @@ -121,15 +107,13 @@ import static com.facebook.presto.sql.planner.plan.ExchangeNode.partitionedExchange; import static com.facebook.presto.sql.planner.plan.ExchangeNode.replicatedExchange; import static com.facebook.presto.sql.planner.plan.ExchangeNode.roundRobinExchange; +import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.getOnlyElement; -import static com.google.common.collect.Sets.intersection; import static java.lang.String.format; -import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; public class AddExchanges @@ -147,7 +131,7 @@ public AddExchanges(Metadata metadata, SqlParser parser) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { PlanWithProperties result = plan.accept(new Rewriter(idAllocator, symbolAllocator, session), PreferredProperties.any()); return result.getNode(); @@ -520,7 +504,7 @@ public PlanWithProperties visitFilter(FilterNode node, PreferredProperties prefe @Override public PlanWithProperties visitTableScan(TableScanNode node, PreferredProperties preferredProperties) { - return planTableScan(node, BooleanLiteral.TRUE_LITERAL, preferredProperties); + return planTableScan(node, TRUE_LITERAL, preferredProperties); } @Override @@ -538,7 +522,7 @@ else if (redistributeWrites) { } } - if (partitioningScheme.isPresent() && !source.getProperties().isNodePartitionedOn(partitioningScheme.get().getPartitioning(), false)) { + if (partitioningScheme.isPresent() && !source.getProperties().isCompatibleTablePartitioningWith(partitioningScheme.get().getPartitioning(), false, metadata, session)) { source = withDerivedProperties( partitionedExchange( idAllocator.getNextId(), @@ -552,93 +536,11 @@ else if (redistributeWrites) { private PlanWithProperties planTableScan(TableScanNode node, Expression predicate, PreferredProperties preferredProperties) { - // don't include non-deterministic predicates - Expression deterministicPredicate = filterDeterministicConjuncts(predicate); - - DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate( - metadata, - session, - deterministicPredicate, - types); - - TupleDomain newDomain = decomposedPredicate.getTupleDomain() - .transform(node.getAssignments()::get) - .intersect(node.getCurrentConstraint()); - - Map assignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); - - // Simplify the tuple domain to avoid creating an expression with too many nodes that's - // expensive to evaluate in the call to shouldPrune below. - Expression constraint = combineConjuncts( - deterministicPredicate, - domainTranslator.toPredicate(newDomain.simplify().transform(assignments::get))); - - LayoutConstraintEvaluator evaluator = new LayoutConstraintEvaluator( - session, - symbolAllocator.getTypes(), - node.getAssignments(), - constraint); - - // Layouts will be returned in order of the connector's preference - List layouts = metadata.getLayouts( - session, node.getTable(), - new Constraint<>(newDomain, evaluator::isCandidate), - Optional.of(node.getOutputSymbols().stream() - .map(node.getAssignments()::get) - .collect(toImmutableSet()))); - - if (layouts.isEmpty()) { - return emptyRelation(node.getOutputSymbols()); - } - - // Filter out layouts that cannot supply all the required columns - layouts = layouts.stream() - .filter(layout -> layout.hasAllOutputs(node)) - .collect(toList()); - checkState(!layouts.isEmpty(), "No usable layouts for %s", node); - - if (layouts.stream().anyMatch(layout -> layout.getLayout().getPredicate().isNone())) { - return emptyRelation(node.getOutputSymbols()); - } - - List possiblePlans = layouts.stream() - .map(layout -> { - TableScanNode tableScan = new TableScanNode( - node.getId(), - node.getTable(), - node.getOutputSymbols(), - node.getAssignments(), - Optional.of(layout.getLayout().getHandle()), - newDomain.intersect(layout.getLayout().getPredicate()), - Optional.ofNullable(node.getOriginalConstraint()).orElse(predicate)); - - PlanWithProperties result = new PlanWithProperties(tableScan, deriveProperties(tableScan, ImmutableList.of())); - - Expression resultingPredicate = combineConjuncts( - domainTranslator.toPredicate(layout.getUnenforcedConstraint().transform(assignments::get)), - filterNonDeterministicConjuncts(predicate), - decomposedPredicate.getRemainingExpression()); - - if (!BooleanLiteral.TRUE_LITERAL.equals(resultingPredicate)) { - return withDerivedProperties( - new FilterNode(idAllocator.getNextId(), result.getNode(), resultingPredicate), - deriveProperties(tableScan, ImmutableList.of())); - } - - return result; - }) - .collect(toList()); - - return pickPlan(possiblePlans, preferredProperties); - } - - private PlanWithProperties emptyRelation(List outputSymbols) - { - return new PlanWithProperties( - new ValuesNode(idAllocator.getNextId(), outputSymbols, ImmutableList.of()), - ActualProperties.builder() - .global(singleStreamPartition()) - .build()); + List possiblePlans = PickTableLayout.listTableLayouts(node, predicate, true, session, types, idAllocator, metadata, parser, domainTranslator); + List possiblePlansWithProperties = possiblePlans.stream() + .map(planNode -> new PlanWithProperties(planNode, derivePropertiesRecursively(planNode))) + .collect(toImmutableList()); + return pickPlan(possiblePlansWithProperties, preferredProperties); } /** @@ -694,7 +596,7 @@ public PlanWithProperties visitTableFinish(TableFinishNode node, PreferredProper return rebaseAndDeriveProperties(node, child); } - if (!child.getProperties().isSingleNode() || !child.getProperties().isCoordinatorOnly()) { + if (!child.getProperties().isCoordinatorOnly()) { child = withDerivedProperties( gatheringExchange(idAllocator.getNextId(), REMOTE, child.getNode()), child.getProperties()); @@ -766,7 +668,7 @@ private PlanWithProperties planPartitionedJoin(JoinNode node, List leftS if (left.getProperties().isNodePartitionedOn(leftSymbols) && !left.getProperties().isSingleNode()) { Partitioning rightPartitioning = left.getProperties().translate(createTranslator(leftToRight)).getNodePartitioning().get(); right = node.getRight().accept(this, PreferredProperties.partitioned(rightPartitioning)); - if (!right.getProperties().isNodePartitionedWith(left.getProperties(), rightToLeft::get)) { + if (!right.getProperties().isCompatibleTablePartitioningWith(left.getProperties(), rightToLeft::get, metadata, session)) { right = withDerivedProperties( partitionedExchange(idAllocator.getNextId(), REMOTE, right.getNode(), new PartitioningScheme(rightPartitioning, right.getNode().getOutputSymbols())), right.getProperties()); @@ -791,7 +693,7 @@ private PlanWithProperties planPartitionedJoin(JoinNode node, List leftS } } - verify(left.getProperties().isNodePartitionedWith(right.getProperties(), leftToRight::get)); + verify(left.getProperties().isCompatibleTablePartitioningWith(right.getProperties(), leftToRight::get, metadata, session)); // if colocated joins are disabled, force redistribute when using a custom partitioning if (!isColocatedJoinEnabled(session) && hasMultipleSources(left.getNode(), right.getNode())) { @@ -842,6 +744,41 @@ private PlanWithProperties buildJoin(JoinNode node, PlanWithProperties newLeft, return new PlanWithProperties(result, deriveProperties(result, ImmutableList.of(newLeft.getProperties(), newRight.getProperties()))); } + @Override + public PlanWithProperties visitSpatialJoin(SpatialJoinNode node, PreferredProperties preferredProperties) + { + SpatialJoinNode.DistributionType distributionType = node.getDistributionType(); + + PlanWithProperties left = node.getLeft().accept(this, PreferredProperties.any()); + PlanWithProperties right = node.getRight().accept(this, PreferredProperties.any()); + + if (distributionType == SpatialJoinNode.DistributionType.REPLICATED) { + if (left.getProperties().isSingleNode()) { + if (!right.getProperties().isSingleNode()) { + right = withDerivedProperties( + gatheringExchange(idAllocator.getNextId(), REMOTE, right.getNode()), + right.getProperties()); + } + } + else { + right = withDerivedProperties( + replicatedExchange(idAllocator.getNextId(), REMOTE, right.getNode()), + right.getProperties()); + } + } + else { + left = withDerivedProperties( + partitionedExchange(idAllocator.getNextId(), REMOTE, left.getNode(), ImmutableList.of(node.getLeftPartitionSymbol().get()), Optional.empty()), + left.getProperties()); + right = withDerivedProperties( + partitionedExchange(idAllocator.getNextId(), REMOTE, right.getNode(), ImmutableList.of(node.getRightPartitionSymbol().get()), Optional.empty()), + right.getProperties()); + } + + PlanNode newJoinNode = node.replaceChildren(ImmutableList.of(left.getNode(), right.getNode())); + return new PlanWithProperties(newJoinNode, deriveProperties(newJoinNode, ImmutableList.of(left.getProperties(), right.getProperties()))); + } + @Override public PlanWithProperties visitUnnest(UnnestNode node, PreferredProperties preferredProperties) { @@ -869,7 +806,7 @@ public PlanWithProperties visitSemiJoin(SemiJoinNode node, PreferredProperties p if (source.getProperties().isNodePartitionedOn(sourceSymbols) && !source.getProperties().isSingleNode()) { Partitioning filteringPartitioning = source.getProperties().translate(createTranslator(sourceToFiltering)).getNodePartitioning().get(); filteringSource = node.getFilteringSource().accept(this, PreferredProperties.partitionedWithNullsAndAnyReplicated(filteringPartitioning)); - if (!source.getProperties().withReplicatedNulls(true).isNodePartitionedWith(filteringSource.getProperties(), sourceToFiltering::get)) { + if (!source.getProperties().withReplicatedNulls(true).isCompatibleTablePartitioningWith(filteringSource.getProperties(), sourceToFiltering::get, metadata, session)) { filteringSource = withDerivedProperties( partitionedExchange(idAllocator.getNextId(), REMOTE, filteringSource.getNode(), new PartitioningScheme( filteringPartitioning, @@ -899,7 +836,7 @@ public PlanWithProperties visitSemiJoin(SemiJoinNode node, PreferredProperties p } } - verify(source.getProperties().withReplicatedNulls(true).isNodePartitionedWith(filteringSource.getProperties(), sourceToFiltering::get)); + verify(source.getProperties().withReplicatedNulls(true).isCompatibleTablePartitioningWith(filteringSource.getProperties(), sourceToFiltering::get, metadata, session)); // if colocated joins are disabled, force redistribute when using a custom partitioning if (!isColocatedJoinEnabled(session) && hasMultipleSources(source.getNode(), filteringSource.getNode())) { @@ -1063,7 +1000,7 @@ public PlanWithProperties visitUnion(UnionNode node, PreferredProperties parentP .build(); PlanWithProperties source = node.getSources().get(sourceIndex).accept(this, childPreferred); - if (!source.getProperties().isNodePartitionedOn(childPartitioning, nullsAndAnyReplicated)) { + if (!source.getProperties().isCompatibleTablePartitioningWith(childPartitioning, nullsAndAnyReplicated, metadata, session)) { source = withDerivedProperties( partitionedExchange( idAllocator.getNextId(), @@ -1264,6 +1201,11 @@ private ActualProperties deriveProperties(PlanNode result, List computeIdentityTranslations(Assignments assignments) @@ -1372,39 +1314,4 @@ public ActualProperties getProperties() return properties; } } - - private class LayoutConstraintEvaluator - { - private final Map assignments; - private final ExpressionInterpreter evaluator; - private final Set arguments; - - public LayoutConstraintEvaluator(Session session, TypeProvider types, Map assignments, Expression expression) - { - this.assignments = assignments; - - Map, Type> expressionTypes = getExpressionTypes(session, metadata, parser, types, expression, emptyList()); - - evaluator = ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes); - arguments = SymbolsExtractor.extractUnique(expression).stream() - .map(assignments::get) - .collect(toImmutableSet()); - } - - private boolean isCandidate(Map bindings) - { - if (intersection(bindings.keySet(), arguments).isEmpty()) { - return true; - } - LookupSymbolResolver inputs = new LookupSymbolResolver(assignments, bindings); - - // If any conjuncts evaluate to FALSE or null, then the whole predicate will never be true and so the partition should be pruned - Object optimized = evaluator.optimize(inputs); - if (Boolean.FALSE.equals(optimized) || optimized == null || optimized instanceof NullLiteral) { - return false; - } - - return true; - } - } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddLocalExchanges.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddLocalExchanges.java index a955fa9bc6a20..74e182db67439 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddLocalExchanges.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/AddLocalExchanges.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.ConstantProperty; import com.facebook.presto.spi.GroupingProperty; @@ -44,6 +45,7 @@ import com.facebook.presto.sql.planner.plan.RowNumberNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; import com.facebook.presto.sql.planner.plan.TopNNode; @@ -101,7 +103,7 @@ public AddLocalExchanges(Metadata metadata, SqlParser parser) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { PlanWithProperties result = plan.accept(new Rewriter(symbolAllocator, idAllocator, session), any()); return result.getNode(); @@ -387,21 +389,21 @@ public PlanWithProperties visitMarkDistinct(MarkDistinctNode node, StreamPreferr /** * Prune redundant distinct symbols to reduce CPU cost of hashing corresponding values and amount of memory * needed to store all the distinct values. - * + *

* Consider the following plan, - * + *

          *  - MarkDistinctNode (unique, c1, c2)
          *      - Join
          *          - AssignUniqueId (unique)
          *              - probe (c1, c2)
          *          - build
-         *
+         * 
* In this case MarkDistinctNode (unique, c1, c2) is equivalent to MarkDistinctNode (unique), * because if two rows match on `unique`, they must match on `c1` and `c2` as well. - * + *

* More generally, any distinct symbol that is functionally dependent on a subset of * other distinct symbols can be dropped. - * + *

* Ideally, this logic would be encapsulated in a separate rule, but currently no rule other * than AddLocalExchanges can reason about local properties. */ @@ -607,6 +609,20 @@ public PlanWithProperties visitSemiJoin(SemiJoinNode node, StreamPreferredProper return rebaseAndDeriveProperties(node, ImmutableList.of(source, filteringSource)); } + @Override + public PlanWithProperties visitSpatialJoin(SpatialJoinNode node, StreamPreferredProperties parentPreferences) + { + PlanWithProperties probe = planAndEnforce( + node.getLeft(), + defaultParallelism(session), + parentPreferences.constrainTo(node.getLeft().getOutputSymbols()) + .withDefaultParallelism(session)); + + PlanWithProperties build = planAndEnforce(node.getRight(), singleStream(), singleStream()); + + return rebaseAndDeriveProperties(node, ImmutableList.of(probe, build)); + } + @Override public PlanWithProperties visitIndexJoin(IndexJoinNode node, StreamPreferredProperties parentPreferences) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/BeginTableWrite.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/BeginTableWrite.java index 59e2d1dba9086..487c1c1da7695 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/BeginTableWrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/BeginTableWrite.java @@ -14,11 +14,13 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableHandle; -import com.facebook.presto.metadata.TableLayoutHandle; import com.facebook.presto.metadata.TableLayoutResult; +import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.Constraint; +import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; import com.facebook.presto.sql.planner.TypeProvider; @@ -42,6 +44,7 @@ import java.util.Optional; import java.util.Set; +import static com.facebook.presto.metadata.TableLayoutResult.computeEnforced; import static com.facebook.presto.sql.planner.optimizations.QueryCardinalityUtil.isAtMostScalar; import static com.facebook.presto.sql.planner.plan.ChildReplacer.replaceChildren; import static com.google.common.base.Preconditions.checkState; @@ -66,7 +69,7 @@ public BeginTableWrite(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { return SimplePlanRewriter.rewriteWith(new Rewriter(session), plan, new Context()); } @@ -173,23 +176,24 @@ private PlanNode rewriteDeleteTableScan(PlanNode node, TableHandle handle) { if (node instanceof TableScanNode) { TableScanNode scan = (TableScanNode) node; + TupleDomain originalEnforcedConstraint = scan.getEnforcedConstraint(); List layouts = metadata.getLayouts( session, handle, - new Constraint<>(scan.getCurrentConstraint()), + new Constraint<>(originalEnforcedConstraint), Optional.of(ImmutableSet.copyOf(scan.getAssignments().values()))); verify(layouts.size() == 1, "Expected exactly one layout for delete"); - TableLayoutHandle layout = Iterables.getOnlyElement(layouts).getLayout().getHandle(); + TableLayoutResult layoutResult = Iterables.getOnlyElement(layouts); return new TableScanNode( scan.getId(), handle, scan.getOutputSymbols(), scan.getAssignments(), - Optional.of(layout), - scan.getCurrentConstraint(), - scan.getOriginalConstraint()); + Optional.of(layoutResult.getLayout().getHandle()), + layoutResult.getLayout().getPredicate(), + computeEnforced(originalEnforcedConstraint, layoutResult.getUnenforcedConstraint())); } if (node instanceof FilterNode) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/CheckSubqueryNodesAreRewritten.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/CheckSubqueryNodesAreRewritten.java index 2bce3fa4a01e3..4b1263a737faf 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/CheckSubqueryNodesAreRewritten.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/CheckSubqueryNodesAreRewritten.java @@ -15,6 +15,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; @@ -35,7 +36,7 @@ public class CheckSubqueryNodesAreRewritten implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { searchFrom(plan).where(ApplyNode.class::isInstance) .findFirst() diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/DetermineSemiJoinDistributionType.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/DetermineSemiJoinDistributionType.java index 4b94dbcdd6d5d..676bcf070b820 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/DetermineSemiJoinDistributionType.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/DetermineSemiJoinDistributionType.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; import com.facebook.presto.sql.planner.TypeProvider; @@ -31,7 +32,7 @@ public class DetermineSemiJoinDistributionType implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ExpressionEquivalence.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ExpressionEquivalence.java index 0878180713abd..0e67c84a4a7f1 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ExpressionEquivalence.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ExpressionEquivalence.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.type.Type; @@ -107,7 +108,8 @@ private RowExpression toRowExpression(Session session, Expression expression, Ma sqlParser, inputTypes, expressionWithInputReferences, - emptyList() /* parameters have already been replaced */); + emptyList(), /* parameters have already been replaced */ + WarningCollector.NOOP); // convert to row expression return translate(expressionWithInputReferences, SCALAR, expressionTypes, metadata.getFunctionRegistry(), metadata.getTypeManager(), session, false); @@ -255,6 +257,18 @@ public int compare(RowExpression left, RowExpression right) Object leftValue = leftConstant.getValue(); Object rightValue = rightConstant.getValue(); + if (leftValue == null) { + if (rightValue == null) { + return 0; + } + else { + return -1; + } + } + else if (rightValue == null) { + return 1; + } + Class javaType = leftConstant.getType().getJavaType(); if (javaType == boolean.class) { return ((Boolean) leftValue).compareTo((Boolean) rightValue); @@ -278,6 +292,33 @@ public int compare(RowExpression left, RowExpression right) return Integer.compare(((InputReferenceExpression) left).getField(), ((InputReferenceExpression) right).getField()); } + if (left instanceof LambdaDefinitionExpression) { + LambdaDefinitionExpression leftLambda = (LambdaDefinitionExpression) left; + LambdaDefinitionExpression rightLambda = (LambdaDefinitionExpression) right; + + return ComparisonChain.start() + .compare( + leftLambda.getArgumentTypes(), + rightLambda.getArgumentTypes(), + new ListComparator<>(Comparator.comparing(Object::toString))) + .compare( + leftLambda.getArguments(), + rightLambda.getArguments(), + new ListComparator<>(Comparator.naturalOrder())) + .compare(leftLambda.getBody(), rightLambda.getBody(), this) + .result(); + } + + if (left instanceof VariableReferenceExpression) { + VariableReferenceExpression leftVariableReference = (VariableReferenceExpression) left; + VariableReferenceExpression rightVariableReference = (VariableReferenceExpression) right; + + return ComparisonChain.start() + .compare(leftVariableReference.getName(), rightVariableReference.getName()) + .compare(leftVariableReference.getType(), rightVariableReference.getType(), Comparator.comparing(Object::toString)) + .result(); + } + throw new IllegalArgumentException("Unsupported RowExpression type " + left.getClass().getSimpleName()); } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/HashGenerationOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/HashGenerationOptimizer.java index 99d163c5a1c5f..a8ccac5013bdd 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/HashGenerationOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/HashGenerationOptimizer.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.type.StandardTypes; @@ -41,6 +42,7 @@ import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.planner.plan.RowNumberNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TopNRowNumberNode; import com.facebook.presto.sql.planner.plan.UnionNode; import com.facebook.presto.sql.planner.plan.UnnestNode; @@ -81,6 +83,7 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableSet.toImmutableSet; @@ -99,7 +102,7 @@ public class HashGenerationOptimizer ImmutableList.of(BIGINT.getTypeSignature(), BIGINT.getTypeSignature())); @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); @@ -200,7 +203,7 @@ public PlanWithProperties visitGroupId(GroupIdNode node, HashComputationSet pare public PlanWithProperties visitDistinctLimit(DistinctLimitNode node, HashComputationSet parentPreference) { // skip hash symbol generation for single bigint - if (!canSkipHashGeneration(node.getDistinctSymbols())) { + if (canSkipHashGeneration(node.getDistinctSymbols())) { return planSimpleNodeWithProperties(node, parentPreference); } @@ -212,16 +215,19 @@ public PlanWithProperties visitDistinctLimit(DistinctLimitNode node, HashComputa parentPreference.withHashComputation(node, hashComputation)); Symbol hashSymbol = child.getRequiredHashSymbol(hashComputation.get()); + // TODO: we need to reason about how pre-computed hashes from child relate to distinct symbols. We should be able to include any precomputed hash + // that's functionally dependent on the distinct field in the set of distinct fields of the new node to be able to propagate it downstream. + // Currently, such precomputed hashes will be dropped by this operation. return new PlanWithProperties( new DistinctLimitNode(node.getId(), child.getNode(), node.getLimit(), node.isPartial(), node.getDistinctSymbols(), Optional.of(hashSymbol)), - child.getHashSymbols()); + ImmutableMap.of(hashComputation.get(), hashSymbol)); } @Override public PlanWithProperties visitMarkDistinct(MarkDistinctNode node, HashComputationSet parentPreference) { // skip hash symbol generation for single bigint - if (!canSkipHashGeneration(node.getDistinctSymbols())) { + if (canSkipHashGeneration(node.getDistinctSymbols())) { return planSimpleNodeWithProperties(node, parentPreference); } @@ -395,6 +401,18 @@ public PlanWithProperties visitSemiJoin(SemiJoinNode node, HashComputationSet pa source.getHashSymbols()); } + @Override + public PlanWithProperties visitSpatialJoin(SpatialJoinNode node, HashComputationSet parentPreference) + { + PlanWithProperties left = planAndEnforce(node.getLeft(), new HashComputationSet(), true, new HashComputationSet()); + PlanWithProperties right = planAndEnforce(node.getRight(), new HashComputationSet(), true, new HashComputationSet()); + verify(left.getHashSymbols().isEmpty(), "probe side of the spatial join should not include hash symbols"); + verify(right.getHashSymbols().isEmpty(), "build side of the spatial join should not include hash symbols"); + return new PlanWithProperties( + replaceChildren(node, ImmutableList.of(left.getNode(), right.getNode())), + ImmutableMap.of()); + } + @Override public PlanWithProperties visitIndexJoin(IndexJoinNode node, HashComputationSet parentPreference) { @@ -679,8 +697,8 @@ private PlanWithProperties planAndEnforce( .addAll(requiredHashes.getHashes()) .addAll(preferredHashes.getHashes()) .build(); - preferenceSatisfied = resultHashes.containsAll(requiredHashes.getHashes()) - && requiredAndPreferredHashes.containsAll(resultHashes); + preferenceSatisfied = resultHashes.containsAll(requiredHashes.getHashes()) && + requiredAndPreferredHashes.containsAll(resultHashes); } else { preferenceSatisfied = result.getHashSymbols().keySet().containsAll(requiredHashes.getHashes()); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ImplementIntersectAndExceptAsUnion.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ImplementIntersectAndExceptAsUnion.java index d5e9d894720fc..fef7cc07ba8f0 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ImplementIntersectAndExceptAsUnion.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/ImplementIntersectAndExceptAsUnion.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; @@ -105,7 +106,7 @@ public class ImplementIntersectAndExceptAsUnion implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/IndexJoinOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/IndexJoinOptimizer.java index e1e5643ed6861..35fd5b37c06ec 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/IndexJoinOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/IndexJoinOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.ResolvedIndex; import com.facebook.presto.spi.ColumnHandle; @@ -76,11 +77,10 @@ public IndexJoinOptimizer(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider type, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); - requireNonNull(types, "types is null"); requireNonNull(symbolAllocator, "symbolAllocator is null"); requireNonNull(idAllocator, "idAllocator is null"); @@ -278,7 +278,7 @@ private PlanNode planTableScan(TableScanNode node, Expression predicate, Context TupleDomain simplifiedConstraint = decomposedPredicate.getTupleDomain() .transform(node.getAssignments()::get) - .intersect(node.getCurrentConstraint()); + .intersect(node.getEnforcedConstraint()); checkState(node.getOutputSymbols().containsAll(context.getLookupSymbols())); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/LimitPushDown.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/LimitPushDown.java index a262030774857..a3fd0fea6c49e 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/LimitPushDown.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/LimitPushDown.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; import com.facebook.presto.sql.planner.TypeProvider; @@ -42,7 +43,7 @@ public class LimitPushDown implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataDeleteOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataDeleteOptimizer.java index df864846fe1cd..a1ae5cd2e0f33 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataDeleteOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataDeleteOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; @@ -57,7 +58,7 @@ public MetadataDeleteOptimizer(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { return SimplePlanRewriter.rewriteWith(new Optimizer(session, metadata, idAllocator), plan, null); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataQueryOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataQueryOptimizer.java index a1e03fc50cf2f..368a275e77303 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataQueryOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/MetadataQueryOptimizer.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableLayout; import com.facebook.presto.metadata.TableLayoutResult; @@ -77,7 +78,7 @@ public MetadataQueryOptimizer(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { if (!SystemSessionProperties.isOptimizeMetadataQueries(session)) { return plan; diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/OptimizeMixedDistinctAggregations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/OptimizeMixedDistinctAggregations.java index e142d2c0fdaf2..04aa9593b34c7 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/OptimizeMixedDistinctAggregations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/OptimizeMixedDistinctAggregations.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.type.BigintType; @@ -32,6 +33,7 @@ import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.tree.Cast; +import com.facebook.presto.sql.tree.CoalesceExpression; import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; @@ -82,7 +84,7 @@ public OptimizeMixedDistinctAggregations(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { if (isOptimizeDistinctAggregationEnabled(session)) { return SimplePlanRewriter.rewriteWith(new Optimizer(idAllocator, symbolAllocator, metadata), plan, Optional.empty()); @@ -150,6 +152,9 @@ public PlanNode visitAggregation(AggregationNode node, RewriteContext aggregations = ImmutableMap.builder(); + // Add coalesce projection node to handle count(), count_if(), approx_distinct() functions return a + // non-null result without any input + ImmutableMap.Builder coalesceSymbolsBuilder = ImmutableMap.builder(); for (Map.Entry entry : node.getAggregations().entrySet()) { FunctionCall functionCall = entry.getValue().getCall(); if (entry.getValue().getMask().isPresent()) { @@ -166,14 +171,25 @@ public PlanNode visitAggregation(AggregationNode node, RewriteContext coalesceSymbols = coalesceSymbolsBuilder.build(); - return new AggregationNode( + AggregationNode aggregationNode = new AggregationNode( idAllocator.getNextId(), source, aggregations.build(), @@ -182,6 +198,23 @@ public PlanNode visitAggregation(AggregationNode node, RewriteContext * TODO: replace it with a support for plan (physical) properties in rules pattern matching */ public static PlanNodeSearcher searchFrom(PlanNode node, Lookup lookup) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PlanOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PlanOptimizer.java index 1b6ab8aa2dbc9..d2407cd8f14c5 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PlanOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PlanOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; import com.facebook.presto.sql.planner.TypeProvider; @@ -25,5 +26,6 @@ PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, - PlanNodeIdAllocator idAllocator); + PlanNodeIdAllocator idAllocator, + WarningCollector warningCollector); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java index 921ea0e606a06..c8f2e4860ccba 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.parser.SqlParser; @@ -43,9 +44,11 @@ import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.UnionNode; import com.facebook.presto.sql.planner.plan.UnnestNode; +import com.facebook.presto.sql.planner.plan.WindowNode; import com.facebook.presto.sql.tree.BooleanLiteral; import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.Expression; @@ -56,7 +59,6 @@ import com.facebook.presto.sql.tree.SymbolReference; import com.facebook.presto.sql.tree.TryExpression; import com.facebook.presto.sql.util.AstUtils; -import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -89,6 +91,7 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.not; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.filter; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; @@ -110,7 +113,7 @@ public PredicatePushDown(Metadata metadata, SqlParser sqlParser) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); @@ -204,6 +207,32 @@ public PlanNode visitExchange(ExchangeNode node, RewriteContext cont return node; } + @Override + public PlanNode visitWindow(WindowNode node, RewriteContext context) + { + List partitionSymbols = node.getPartitionBy(); + + // TODO: This could be broader. We can push down conjucts if they are constant for all rows in a window partition. + // The simplest way to guarantee this is if the conjucts are deterministic functions of the partitioning symbols. + // This can leave out cases where they're both functions of some set of common expressions and the partitioning + // function is injective, but that's a rare case. The majority of window nodes are expected to be partitioned by + // pre-projected symbols. + Predicate isSupported = conjunct -> + DeterminismEvaluator.isDeterministic(conjunct) && + SymbolsExtractor.extractUnique(conjunct).stream() + .allMatch(partitionSymbols::contains); + + Map> conjuncts = extractConjuncts(context.get()).stream().collect(Collectors.partitioningBy(isSupported)); + + PlanNode rewrittenNode = context.defaultRewrite(node, combineConjuncts(conjuncts.get(true))); + + if (!conjuncts.get(false).isEmpty()) { + rewrittenNode = new FilterNode(idAllocator.getNextId(), rewrittenNode, combineConjuncts(conjuncts.get(false))); + } + + return rewrittenNode; + } + @Override public PlanNode visitProject(ProjectNode node, RewriteContext context) { @@ -249,7 +278,7 @@ private boolean isInliningCandidate(Expression expression, ProjectNode node) // TryExpressions should not be pushed down. However they are now being handled as lambda // passed to a FunctionCall now and should not affect predicate push down. So we want to make // sure the conjuncts are not TryExpressions. - Verify.verify(AstUtils.preOrder(expression).noneMatch(TryExpression.class::isInstance)); + verify(AstUtils.preOrder(expression).noneMatch(TryExpression.class::isInstance)); // candidate symbols for inlining are // 1. references to simple constants @@ -334,7 +363,18 @@ public PlanNode visitUnion(UnionNode node, RewriteContext context) @Override public PlanNode visitFilter(FilterNode node, RewriteContext context) { - return context.rewrite(node.getSource(), combineConjuncts(node.getPredicate(), context.get())); + PlanNode rewrittenPlan = context.rewrite(node.getSource(), combineConjuncts(node.getPredicate(), context.get())); + if (!(rewrittenPlan instanceof FilterNode)) { + return rewrittenPlan; + } + + FilterNode rewrittenFilterNode = (FilterNode) rewrittenPlan; + if (!areExpressionsEquivalent(rewrittenFilterNode.getPredicate(), node.getPredicate()) + || node.getSource() != rewrittenFilterNode.getSource()) { + return rewrittenPlan; + } + + return node; } @Override @@ -408,63 +448,68 @@ public PlanNode visitJoin(JoinNode node, RewriteContext context) PlanNode rightSource = context.rewrite(node.getRight(), rightPredicate); PlanNode output = node; - if (leftSource != node.getLeft() || - rightSource != node.getRight() || - !expressionEquivalence.areExpressionsEquivalent(session, newJoinPredicate, joinPredicate, types) || - node.getCriteria().isEmpty()) { - // Create identity projections for all existing symbols - Assignments.Builder leftProjections = Assignments.builder(); - leftProjections.putAll(node.getLeft() - .getOutputSymbols().stream() - .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); - - Assignments.Builder rightProjections = Assignments.builder(); - rightProjections.putAll(node.getRight() - .getOutputSymbols().stream() - .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); - - // Create new projections for the new join clauses - List equiJoinClauses = new ArrayList<>(); - ImmutableList.Builder joinFilterBuilder = ImmutableList.builder(); - for (Expression conjunct : extractConjuncts(newJoinPredicate)) { - if (joinEqualityExpression(node.getLeft().getOutputSymbols()).test(conjunct)) { - ComparisonExpression equality = (ComparisonExpression) conjunct; - boolean alignedComparison = Iterables.all(SymbolsExtractor.extractUnique(equality.getLeft()), in(node.getLeft().getOutputSymbols())); - Expression leftExpression = (alignedComparison) ? equality.getLeft() : equality.getRight(); - Expression rightExpression = (alignedComparison) ? equality.getRight() : equality.getLeft(); - - Symbol leftSymbol = symbolForExpression(leftExpression); - if (!node.getLeft().getOutputSymbols().contains(leftSymbol)) { - leftProjections.put(leftSymbol, leftExpression); - } - - Symbol rightSymbol = symbolForExpression(rightExpression); - if (!node.getRight().getOutputSymbols().contains(rightSymbol)) { - rightProjections.put(rightSymbol, rightExpression); - } - - equiJoinClauses.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol)); + // Create identity projections for all existing symbols + Assignments.Builder leftProjections = Assignments.builder(); + leftProjections.putAll(node.getLeft() + .getOutputSymbols().stream() + .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); + + Assignments.Builder rightProjections = Assignments.builder(); + rightProjections.putAll(node.getRight() + .getOutputSymbols().stream() + .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); + + // Create new projections for the new join clauses + List equiJoinClauses = new ArrayList<>(); + ImmutableList.Builder joinFilterBuilder = ImmutableList.builder(); + for (Expression conjunct : extractConjuncts(newJoinPredicate)) { + if (joinEqualityExpression(node.getLeft().getOutputSymbols()).test(conjunct)) { + ComparisonExpression equality = (ComparisonExpression) conjunct; + + boolean alignedComparison = Iterables.all(SymbolsExtractor.extractUnique(equality.getLeft()), in(node.getLeft().getOutputSymbols())); + Expression leftExpression = (alignedComparison) ? equality.getLeft() : equality.getRight(); + Expression rightExpression = (alignedComparison) ? equality.getRight() : equality.getLeft(); + + Symbol leftSymbol = symbolForExpression(leftExpression); + if (!node.getLeft().getOutputSymbols().contains(leftSymbol)) { + leftProjections.put(leftSymbol, leftExpression); } - else { - joinFilterBuilder.add(conjunct); + + Symbol rightSymbol = symbolForExpression(rightExpression); + if (!node.getRight().getOutputSymbols().contains(rightSymbol)) { + rightProjections.put(rightSymbol, rightExpression); } - } - Optional newJoinFilter = Optional.of(combineConjuncts(joinFilterBuilder.build())); - if (newJoinFilter.get() == TRUE_LITERAL) { - newJoinFilter = Optional.empty(); + equiJoinClauses.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol)); } - - if (node.getType() == INNER && newJoinFilter.isPresent() && equiJoinClauses.isEmpty()) { - // if we do not have any equi conjunct we do not pushdown non-equality condition into - // inner join, so we plan execution as nested-loops-join followed by filter instead - // hash join. - // todo: remove the code when we have support for filter function in nested loop join - postJoinPredicate = combineConjuncts(postJoinPredicate, newJoinFilter.get()); - newJoinFilter = Optional.empty(); + else { + joinFilterBuilder.add(conjunct); } + } + + Optional newJoinFilter = Optional.of(combineConjuncts(joinFilterBuilder.build())); + if (newJoinFilter.get() == TRUE_LITERAL) { + newJoinFilter = Optional.empty(); + } + if (node.getType() == INNER && newJoinFilter.isPresent() && equiJoinClauses.isEmpty()) { + // if we do not have any equi conjunct we do not pushdown non-equality condition into + // inner join, so we plan execution as nested-loops-join followed by filter instead + // hash join. + // todo: remove the code when we have support for filter function in nested loop join + postJoinPredicate = combineConjuncts(postJoinPredicate, newJoinFilter.get()); + newJoinFilter = Optional.empty(); + } + + boolean filtersEquivalent = + newJoinFilter.isPresent() == node.getFilter().isPresent() && + (!newJoinFilter.isPresent() || areExpressionsEquivalent(newJoinFilter.get(), node.getFilter().get())); + + if (leftSource != node.getLeft() || + rightSource != node.getRight() || + !filtersEquivalent || + !ImmutableSet.copyOf(equiJoinClauses).equals(ImmutableSet.copyOf(node.getCriteria()))) { leftSource = new ProjectNode(idAllocator.getNextId(), leftSource, leftProjections.build()); rightSource = new ProjectNode(idAllocator.getNextId(), rightSource, rightProjections.build()); @@ -488,7 +533,101 @@ public PlanNode visitJoin(JoinNode node, RewriteContext context) output = new FilterNode(idAllocator.getNextId(), output, postJoinPredicate); } - output = new ProjectNode(idAllocator.getNextId(), output, Assignments.identity(node.getOutputSymbols())); + if (!node.getOutputSymbols().equals(output.getOutputSymbols())) { + output = new ProjectNode(idAllocator.getNextId(), output, Assignments.identity(node.getOutputSymbols())); + } + + return output; + } + + @Override + public PlanNode visitSpatialJoin(SpatialJoinNode node, RewriteContext context) + { + Expression inheritedPredicate = context.get(); + + // See if we can rewrite left join in terms of a plain inner join + if (node.getType() == SpatialJoinNode.Type.LEFT && canConvertOuterToInner(node.getRight().getOutputSymbols(), inheritedPredicate)) { + node = new SpatialJoinNode(node.getId(), SpatialJoinNode.Type.INNER, node.getLeft(), node.getRight(), node.getOutputSymbols(), node.getFilter(), node.getLeftPartitionSymbol(), node.getRightPartitionSymbol(), node.getKdbTree()); + } + + Expression leftEffectivePredicate = effectivePredicateExtractor.extract(node.getLeft()); + Expression rightEffectivePredicate = effectivePredicateExtractor.extract(node.getRight()); + Expression joinPredicate = node.getFilter(); + + Expression leftPredicate; + Expression rightPredicate; + Expression postJoinPredicate; + Expression newJoinPredicate; + + switch (node.getType()) { + case INNER: + InnerJoinPushDownResult innerJoinPushDownResult = processInnerJoin( + inheritedPredicate, + leftEffectivePredicate, + rightEffectivePredicate, + joinPredicate, + node.getLeft().getOutputSymbols()); + leftPredicate = innerJoinPushDownResult.getLeftPredicate(); + rightPredicate = innerJoinPushDownResult.getRightPredicate(); + postJoinPredicate = innerJoinPushDownResult.getPostJoinPredicate(); + newJoinPredicate = innerJoinPushDownResult.getJoinPredicate(); + break; + case LEFT: + OuterJoinPushDownResult leftOuterJoinPushDownResult = processLimitedOuterJoin( + inheritedPredicate, + leftEffectivePredicate, + rightEffectivePredicate, + joinPredicate, + node.getLeft().getOutputSymbols()); + leftPredicate = leftOuterJoinPushDownResult.getOuterJoinPredicate(); + rightPredicate = leftOuterJoinPushDownResult.getInnerJoinPredicate(); + postJoinPredicate = leftOuterJoinPushDownResult.getPostJoinPredicate(); + newJoinPredicate = leftOuterJoinPushDownResult.getJoinPredicate(); + break; + default: + throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); + } + + newJoinPredicate = simplifyExpression(newJoinPredicate); + verify(!newJoinPredicate.equals(BooleanLiteral.FALSE_LITERAL), "Spatial join predicate is missing"); + + PlanNode leftSource = context.rewrite(node.getLeft(), leftPredicate); + PlanNode rightSource = context.rewrite(node.getRight(), rightPredicate); + + PlanNode output = node; + if (leftSource != node.getLeft() || + rightSource != node.getRight() || + !areExpressionsEquivalent(newJoinPredicate, joinPredicate)) { + // Create identity projections for all existing symbols + Assignments.Builder leftProjections = Assignments.builder(); + leftProjections.putAll(node.getLeft() + .getOutputSymbols().stream() + .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); + + Assignments.Builder rightProjections = Assignments.builder(); + rightProjections.putAll(node.getRight() + .getOutputSymbols().stream() + .collect(Collectors.toMap(key -> key, Symbol::toSymbolReference))); + + leftSource = new ProjectNode(idAllocator.getNextId(), leftSource, leftProjections.build()); + rightSource = new ProjectNode(idAllocator.getNextId(), rightSource, rightProjections.build()); + + output = new SpatialJoinNode( + node.getId(), + node.getType(), + leftSource, + rightSource, + node.getOutputSymbols(), + newJoinPredicate, + node.getLeftPartitionSymbol(), + node.getRightPartitionSymbol(), + node.getKdbTree()); + } + + if (!postJoinPredicate.equals(TRUE_LITERAL)) { + output = new FilterNode(idAllocator.getNextId(), output, postJoinPredicate); + } + return output; } @@ -753,7 +892,7 @@ private static Expression extractJoinPredicate(JoinNode joinNode) private Type extractType(Expression expression) { - Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList() /* parameters have already been replaced */); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, symbolAllocator.getTypes(), expression, emptyList(), /* parameters have already been replaced */WarningCollector.NOOP); return expressionTypes.get(NodeRef.of(expression)); } @@ -814,11 +953,17 @@ private Expression simplifyExpression(Expression expression) sqlParser, symbolAllocator.getTypes(), expression, - emptyList() /* parameters have already been replaced */); + emptyList(), /* parameters have already been replaced */ + WarningCollector.NOOP); ExpressionInterpreter optimizer = ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes); return literalEncoder.toExpression(optimizer.optimize(NoOpSymbolResolver.INSTANCE), expressionTypes.get(NodeRef.of(expression))); } + private boolean areExpressionsEquivalent(Expression leftExpression, Expression rightExpression) + { + return expressionEquivalence.areExpressionsEquivalent(session, leftExpression, rightExpression, types); + } + /** * Evaluates an expression's response to binding the specified input symbols to NULL */ @@ -830,7 +975,8 @@ private Object nullInputEvaluator(final Collection nullSymbols, Expressi sqlParser, symbolAllocator.getTypes(), expression, - emptyList() /* parameters have already been replaced */); + emptyList(), /* parameters have already been replaced */ + WarningCollector.NOOP); return ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes) .optimize(symbol -> nullSymbols.contains(symbol) ? null : symbol.toSymbolReference()); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PropertyDerivations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PropertyDerivations.java index 87b5fde4f75c6..66fc8bf9da925 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PropertyDerivations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PropertyDerivations.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableLayout; import com.facebook.presto.metadata.TableLayout.TablePartitioning; @@ -57,6 +58,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -437,6 +439,32 @@ public ActualProperties visitSemiJoin(SemiJoinNode node, List return inputProperties.get(0); } + @Override + public ActualProperties visitSpatialJoin(SpatialJoinNode node, List inputProperties) + { + ActualProperties probeProperties = inputProperties.get(0); + ActualProperties buildProperties = inputProperties.get(1); + + switch (node.getType()) { + case INNER: + probeProperties = probeProperties.translate(column -> filterIfMissing(node.getOutputSymbols(), column)); + buildProperties = buildProperties.translate(column -> filterIfMissing(node.getOutputSymbols(), column)); + + Map constants = new HashMap<>(); + constants.putAll(probeProperties.getConstants()); + constants.putAll(buildProperties.getConstants()); + + return ActualProperties.builderFrom(probeProperties) + .constants(constants) + .build(); + case LEFT: + return ActualProperties.builderFrom(probeProperties.translate(column -> filterIfMissing(node.getOutputSymbols(), column))) + .build(); + default: + throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); + } + } + @Override public ActualProperties visitIndexJoin(IndexJoinNode node, List inputProperties) { @@ -585,7 +613,7 @@ public ActualProperties visitProject(ProjectNode node, List in for (Map.Entry assignment : node.getAssignments().entrySet()) { Expression expression = assignment.getValue(); - Map, Type> expressionTypes = getExpressionTypes(session, metadata, parser, types, expression, emptyList() /* parameters already replaced */); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, parser, types, expression, emptyList(), WarningCollector.NOOP); Type type = requireNonNull(expressionTypes.get(NodeRef.of(expression))); ExpressionInterpreter optimizer = ExpressionInterpreter.expressionOptimizer(expression, metadata, session, expressionTypes); // TODO: diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PruneUnreferencedOutputs.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PruneUnreferencedOutputs.java index 45386a19e0427..08b8936656f60 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PruneUnreferencedOutputs.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PruneUnreferencedOutputs.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.sql.planner.PartitioningScheme; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; @@ -48,6 +49,7 @@ import com.facebook.presto.sql.planner.plan.SetOperationNode; import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.StatisticAggregations; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; @@ -101,7 +103,7 @@ public class PruneUnreferencedOutputs implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); @@ -214,10 +216,9 @@ public PlanNode visitJoin(JoinNode node, RewriteContext> context) .build(); } else { - Set seenSymbol = new HashSet<>(); outputSymbols = node.getOutputSymbols().stream() .filter(context.get()::contains) - .filter(seenSymbol::add) + .distinct() .collect(toImmutableList()); } @@ -255,6 +256,31 @@ public PlanNode visitSemiJoin(SemiJoinNode node, RewriteContext> con node.getDistributionType()); } + @Override + public PlanNode visitSpatialJoin(SpatialJoinNode node, RewriteContext> context) + { + Set requiredInputs = ImmutableSet.builder() + .addAll(SymbolsExtractor.extractUnique(node.getFilter())) + .addAll(context.get()) + .build(); + + ImmutableSet.Builder leftInputs = ImmutableSet.builder(); + node.getLeftPartitionSymbol().map(leftInputs::add); + + ImmutableSet.Builder rightInputs = ImmutableSet.builder(); + node.getRightPartitionSymbol().map(rightInputs::add); + + PlanNode left = context.rewrite(node.getLeft(), leftInputs.addAll(requiredInputs).build()); + PlanNode right = context.rewrite(node.getRight(), rightInputs.addAll(requiredInputs).build()); + + List outputSymbols = node.getOutputSymbols().stream() + .filter(context.get()::contains) + .distinct() + .collect(toImmutableList()); + + return new SpatialJoinNode(node.getId(), node.getType(), left, right, outputSymbols, node.getFilter(), node.getLeftPartitionSymbol(), node.getRightPartitionSymbol(), node.getKdbTree()); + } + @Override public PlanNode visitIndexJoin(IndexJoinNode node, RewriteContext> context) { @@ -402,7 +428,7 @@ public PlanNode visitTableScan(TableScanNode node, RewriteContext> c newAssignments, node.getLayout(), node.getCurrentConstraint(), - node.getOriginalConstraint()); + node.getEnforcedConstraint()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/QueryCardinalityUtil.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/QueryCardinalityUtil.java index a8138612a840f..6d0b9eb12af0f 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/QueryCardinalityUtil.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/QueryCardinalityUtil.java @@ -134,6 +134,7 @@ public Range visitFilter(FilterNode node, Void context) return Range.atLeast(0L); } + @Override public Range visitValues(ValuesNode node, Void context) { return Range.singleton((long) node.getRows().size()); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SetFlatteningOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SetFlatteningOptimizer.java index cd04bf31f2e9a..75857acd60fa6 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SetFlatteningOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SetFlatteningOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.SymbolAllocator; @@ -38,7 +39,7 @@ public class SetFlatteningOptimizer implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StatsRecordingPlanOptimizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StatsRecordingPlanOptimizer.java index aa685fe7dc975..770e59c799694 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StatsRecordingPlanOptimizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StatsRecordingPlanOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.OptimizerStatsRecorder; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.SymbolAllocator; @@ -40,13 +41,14 @@ public final PlanNode optimize( Session session, TypeProvider types, SymbolAllocator symbolAllocator, - PlanNodeIdAllocator idAllocator) + PlanNodeIdAllocator idAllocator, + WarningCollector warningCollector) { PlanNode result; long duration; try { long start = System.nanoTime(); - result = delegate.optimize(plan, session, types, symbolAllocator, idAllocator); + result = delegate.optimize(plan, session, types, symbolAllocator, idAllocator, warningCollector); duration = System.nanoTime() - start; } catch (RuntimeException e) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StreamPropertyDerivations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StreamPropertyDerivations.java index ef99f9e79d9b0..fbbaa7022d575 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StreamPropertyDerivations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/StreamPropertyDerivations.java @@ -46,6 +46,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -201,6 +202,20 @@ public StreamProperties visitJoin(JoinNode node, List inputPro } } + @Override + public StreamProperties visitSpatialJoin(SpatialJoinNode node, List inputProperties) + { + StreamProperties leftProperties = inputProperties.get(0); + + switch (node.getType()) { + case INNER: + case LEFT: + return leftProperties.translate(column -> PropertyDerivations.filterIfMissing(node.getOutputSymbols(), column)); + default: + throw new IllegalArgumentException("Unsupported spatial join type: " + node.getType()); + } + } + @Override public StreamProperties visitIndexJoin(IndexJoinNode node, List inputProperties) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformCorrelatedSingleRowSubqueryToProject.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformCorrelatedSingleRowSubqueryToProject.java deleted file mode 100644 index f59ef039952c2..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformCorrelatedSingleRowSubqueryToProject.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.presto.sql.planner.optimizations; - -import com.facebook.presto.Session; -import com.facebook.presto.sql.planner.PlanNodeIdAllocator; -import com.facebook.presto.sql.planner.SymbolAllocator; -import com.facebook.presto.sql.planner.TypeProvider; -import com.facebook.presto.sql.planner.plan.Assignments; -import com.facebook.presto.sql.planner.plan.LateralJoinNode; -import com.facebook.presto.sql.planner.plan.PlanNode; -import com.facebook.presto.sql.planner.plan.ProjectNode; -import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; -import com.facebook.presto.sql.planner.plan.ValuesNode; - -import java.util.List; - -import static com.facebook.presto.sql.planner.optimizations.PlanNodeSearcher.searchFrom; -import static com.facebook.presto.sql.planner.plan.SimplePlanRewriter.rewriteWith; -import static java.util.Objects.requireNonNull; - -/** - * This optimizer can rewrite correlated single row subquery to projection in a way described here: - * From: - *

- * - Lateral(with correlation list: [A, C])
- *   - (input) plan which produces symbols: [A, B, C]
- *   - (subquery)
- *     - Project (A + C)
- *       - single row VALUES()
- * 
- * to: - *
- *   - Project(A, B, C, A + C)
- *       - (input) plan which produces symbols: [A, B, C]
- * 
- */ -@Deprecated -public class TransformCorrelatedSingleRowSubqueryToProject - implements PlanOptimizer -{ - @Override - public PlanNode optimize( - PlanNode plan, - Session session, - TypeProvider types, - SymbolAllocator symbolAllocator, - PlanNodeIdAllocator idAllocator) - { - return rewriteWith(new Rewriter(idAllocator), plan, null); - } - - private static class Rewriter - extends SimplePlanRewriter - { - private final PlanNodeIdAllocator idAllocator; - - public Rewriter(PlanNodeIdAllocator idAllocator) - { - this.idAllocator = requireNonNull(idAllocator, "idAllocator is null"); - } - - @Override - public PlanNode visitLateralJoin(LateralJoinNode lateral, RewriteContext context) - { - LateralJoinNode rewrittenLateral = (LateralJoinNode) context.defaultRewrite(lateral, context.get()); - if (rewrittenLateral.getCorrelation().isEmpty()) { - return rewrittenLateral; - } - - List values = searchFrom(lateral.getSubquery()) - .recurseOnlyWhen(ProjectNode.class::isInstance) - .where(ValuesNode.class::isInstance) - .findAll(); - - if (values.size() != 1 || !isSingleRowValuesWithNoColumns(values.get(0))) { - return rewrittenLateral; - } - - List subqueryProjections = searchFrom(rewrittenLateral.getSubquery()) - .where(node -> node instanceof ProjectNode && !node.getOutputSymbols().equals(rewrittenLateral.getCorrelation())) - .findAll(); - - if (subqueryProjections.size() == 0) { - return rewrittenLateral.getInput(); - } - else if (subqueryProjections.size() == 1) { - Assignments assignments = Assignments.builder() - .putIdentities(rewrittenLateral.getInput().getOutputSymbols()) - .putAll(subqueryProjections.get(0).getAssignments()) - .build(); - return projectNode(rewrittenLateral.getInput(), assignments); - } - return rewrittenLateral; - } - - private ProjectNode projectNode(PlanNode source, Assignments assignments) - { - return new ProjectNode(idAllocator.getNextId(), source, assignments); - } - - private static boolean isSingleRowValuesWithNoColumns(ValuesNode values) - { - return values.getRows().size() == 1 && values.getRows().get(0).size() == 0; - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformQuantifiedComparisonApplyToLateralJoin.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformQuantifiedComparisonApplyToLateralJoin.java index a943013090e35..7d0d8f1b0ca7a 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformQuantifiedComparisonApplyToLateralJoin.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/TransformQuantifiedComparisonApplyToLateralJoin.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.type.BigintType; @@ -82,7 +83,7 @@ public TransformQuantifiedComparisonApplyToLateralJoin(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { return rewriteWith(new Rewriter(idAllocator, types, symbolAllocator, metadata), plan, null); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/UnaliasSymbolReferences.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/UnaliasSymbolReferences.java index ddab96e28862e..509fd52cd5d24 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/UnaliasSymbolReferences.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/UnaliasSymbolReferences.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.block.SortOrder; import com.facebook.presto.sql.planner.DeterminismEvaluator; @@ -52,6 +53,7 @@ import com.facebook.presto.sql.planner.plan.SetOperationNode; import com.facebook.presto.sql.planner.plan.SimplePlanRewriter; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -105,7 +107,7 @@ public class UnaliasSymbolReferences implements PlanOptimizer { @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); @@ -223,18 +225,7 @@ private WindowNode.Frame canonicalize(WindowNode.Frame frame) @Override public PlanNode visitTableScan(TableScanNode node, RewriteContext context) { - Expression originalConstraint = null; - if (node.getOriginalConstraint() != null) { - originalConstraint = canonicalize(node.getOriginalConstraint()); - } - return new TableScanNode( - node.getId(), - node.getTable(), - node.getOutputSymbols(), - node.getAssignments(), - node.getLayout(), - node.getCurrentConstraint(), - originalConstraint); + return node; } @Override @@ -328,7 +319,8 @@ public PlanNode visitRemoteSource(RemoteSourceNode node, RewriteContext co node.getId(), node.getSourceFragmentIds(), canonicalizeAndDistinct(node.getOutputSymbols()), - node.getOrderingScheme().map(this::canonicalizeAndDistinct)); + node.getOrderingScheme().map(this::canonicalizeAndDistinct), + node.getExchangeType()); } @Override @@ -512,6 +504,15 @@ public PlanNode visitSemiJoin(SemiJoinNode node, RewriteContext context) node.getDistributionType()); } + @Override + public PlanNode visitSpatialJoin(SpatialJoinNode node, RewriteContext context) + { + PlanNode left = context.rewrite(node.getLeft()); + PlanNode right = context.rewrite(node.getRight()); + + return new SpatialJoinNode(node.getId(), node.getType(), left, right, canonicalizeAndDistinct(node.getOutputSymbols()), canonicalize(node.getFilter()), canonicalize(node.getLeftPartitionSymbol()), canonicalize(node.getRightPartitionSymbol()), node.getKdbTree()); + } + @Override public PlanNode visitIndexSource(IndexSourceNode node, RewriteContext context) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/WindowFilterPushDown.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/WindowFilterPushDown.java index df4f0982ee060..bc2d3a291fed9 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/WindowFilterPushDown.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/WindowFilterPushDown.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.optimizations; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.predicate.Domain; @@ -43,6 +44,7 @@ import java.util.Optional; import java.util.OptionalInt; +import static com.facebook.presto.SystemSessionProperties.isOptimizeTopNRowNumber; import static com.facebook.presto.metadata.FunctionKind.WINDOW; import static com.facebook.presto.spi.predicate.Marker.Bound.BELOW; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -72,7 +74,7 @@ public WindowFilterPushDown(Metadata metadata) } @Override - public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator) + public PlanNode optimize(PlanNode plan, Session session, TypeProvider types, SymbolAllocator symbolAllocator, PlanNodeIdAllocator idAllocator, WarningCollector warningCollector) { requireNonNull(plan, "plan is null"); requireNonNull(session, "session is null"); @@ -135,7 +137,7 @@ public PlanNode visitLimit(LimitNode node, RewriteContext context) } source = rowNumberNode; } - else if (source instanceof WindowNode && canOptimizeWindowFunction((WindowNode) source)) { + else if (source instanceof WindowNode && canOptimizeWindowFunction((WindowNode) source) && isOptimizeTopNRowNumber(session)) { WindowNode windowNode = (WindowNode) source; // verify that unordered row_number window functions are replaced by RowNumberNode verify(windowNode.getOrderingScheme().isPresent()); @@ -164,7 +166,7 @@ public PlanNode visitFilter(FilterNode node, RewriteContext context) return rewriteFilterSource(node, source, rowNumberSymbol, upperBound.getAsInt()); } } - else if (source instanceof WindowNode && canOptimizeWindowFunction((WindowNode) source)) { + else if (source instanceof WindowNode && canOptimizeWindowFunction((WindowNode) source) && isOptimizeTopNRowNumber(session)) { WindowNode windowNode = (WindowNode) source; Symbol rowNumberSymbol = getOnlyElement(windowNode.getWindowFunctions().entrySet()).getKey(); OptionalInt upperBound = extractUpperBound(tupleDomain, rowNumberSymbol); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/JoinNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/JoinNode.java index 2fcb0a543ec42..a26c9d5e8acf5 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/JoinNode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/JoinNode.java @@ -29,15 +29,16 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import static com.facebook.presto.sql.planner.SortExpressionExtractor.extractSortExpression; +import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.PARTITIONED; +import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.FULL; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.RIGHT; -import static com.facebook.presto.util.SpatialJoinUtils.isSpatialJoinFilter; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.lang.String.format; @@ -51,18 +52,11 @@ public class JoinNode private final PlanNode left; private final PlanNode right; private final List criteria; - /** - * List of output symbols produced by join. Output symbols - * must be from either left or right side of join. Symbols - * from left join side must precede symbols from right side - * of join. - */ private final List outputSymbols; private final Optional filter; private final Optional leftHashSymbol; private final Optional rightHashSymbol; private final Optional distributionType; - private Optional spatialJoin = Optional.empty(); @JsonCreator public JoinNode(@JsonProperty("id") PlanNodeId id, @@ -97,15 +91,30 @@ public JoinNode(@JsonProperty("id") PlanNodeId id, this.rightHashSymbol = rightHashSymbol; this.distributionType = distributionType; - List inputSymbols = ImmutableList.builder() + Set inputSymbols = ImmutableSet.builder() .addAll(left.getOutputSymbols()) .addAll(right.getOutputSymbols()) .build(); checkArgument(new HashSet<>(inputSymbols).containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); - checkArgument(!isCrossJoin() || inputSymbols.equals(outputSymbols), "Cross join does not support output symbols pruning or reordering"); + checkArgument(!isCrossJoin() || inputSymbols.size() == outputSymbols.size(), "Cross join does not support output symbols pruning or reordering"); checkArgument(!(criteria.isEmpty() && leftHashSymbol.isPresent()), "Left hash symbol is only valid in an equijoin"); checkArgument(!(criteria.isEmpty() && rightHashSymbol.isPresent()), "Right hash symbol is only valid in an equijoin"); + + if (distributionType.isPresent()) { + // The implementation of full outer join only works if the data is hash partitioned. + checkArgument( + !(distributionType.get() == REPLICATED && (type == RIGHT || type == FULL)), + "%s join do not work with %s distribution type", + type, + distributionType.get()); + // It does not make sense to PARTITION when there is nothing to partition on + checkArgument( + !(distributionType.get() == PARTITIONED && criteria.isEmpty() && type != RIGHT && type != FULL), + "Equi criteria are empty, so %s join should not have %s distribution type", + type, + distributionType.get()); + } } public JoinNode flipChildren() @@ -282,13 +291,7 @@ public R accept(PlanVisitor visitor, C context) public PlanNode replaceChildren(List newChildren) { checkArgument(newChildren.size() == 2, "expected newChildren to contain 2 nodes"); - PlanNode newLeft = newChildren.get(0); - PlanNode newRight = newChildren.get(1); - // Reshuffle join output symbols (for cross joins) since order of symbols in child nodes might have changed - List newOutputSymbols = Stream.concat(newLeft.getOutputSymbols().stream(), newRight.getOutputSymbols().stream()) - .filter(outputSymbols::contains) - .collect(toImmutableList()); - return new JoinNode(getId(), type, newLeft, newRight, criteria, newOutputSymbols, filter, leftHashSymbol, rightHashSymbol, distributionType); + return new JoinNode(getId(), type, newChildren.get(0), newChildren.get(1), criteria, outputSymbols, filter, leftHashSymbol, rightHashSymbol, distributionType); } public JoinNode withDistributionType(DistributionType distributionType) @@ -301,15 +304,6 @@ public boolean isCrossJoin() return criteria.isEmpty() && !filter.isPresent() && type == INNER; } - public boolean isSpatialJoin() - { - if (spatialJoin.isPresent()) { - return spatialJoin.get(); - } - spatialJoin = Optional.of((type == INNER || type == LEFT) && criteria.isEmpty() && filter.isPresent() && isSpatialJoinFilter(left, right, filter.get())); - return spatialJoin.get(); - } - public static class EquiJoinClause { private final Symbol left; diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java index 037c73180d725..ec9a55c39ff93 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/Patterns.java @@ -24,7 +24,6 @@ import static com.facebook.presto.matching.Pattern.typeOf; import static com.facebook.presto.matching.Property.optionalProperty; import static com.facebook.presto.matching.Property.property; -import static java.util.Optional.empty; public class Patterns { @@ -75,6 +74,11 @@ public static Pattern join() return typeOf(JoinNode.class); } + public static Pattern spatialJoin() + { + return typeOf(SpatialJoinNode.class); + } + public static Pattern lateralJoin() { return typeOf(LateralJoinNode.class); @@ -150,11 +154,16 @@ public static Pattern window() return typeOf(WindowNode.class); } + public static Pattern rowNumber() + { + return typeOf(RowNumberNode.class); + } + public static Property source() { return optionalProperty("source", node -> node.getSources().size() == 1 ? Optional.of(node.getSources().get(0)) : - empty()); + Optional.empty()); } public static Property> sources() diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanNode.java index 1333dabfbf452..8748c1dce8b0e 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanNode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanNode.java @@ -45,6 +45,7 @@ @JsonSubTypes.Type(value = RemoteSourceNode.class, name = "remoteSource"), @JsonSubTypes.Type(value = JoinNode.class, name = "join"), @JsonSubTypes.Type(value = SemiJoinNode.class, name = "semijoin"), + @JsonSubTypes.Type(value = SpatialJoinNode.class, name = "spatialjoin"), @JsonSubTypes.Type(value = IndexJoinNode.class, name = "indexjoin"), @JsonSubTypes.Type(value = IndexSourceNode.class, name = "indexsource"), @JsonSubTypes.Type(value = TableWriterNode.class, name = "tablewriter"), diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanVisitor.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanVisitor.java index 5e1a21b611339..89e0eb690b49f 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanVisitor.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/PlanVisitor.java @@ -94,6 +94,11 @@ public R visitSemiJoin(SemiJoinNode node, C context) return visitPlan(node, context); } + public R visitSpatialJoin(SpatialJoinNode node, C context) + { + return visitPlan(node, context); + } + public R visitIndexJoin(IndexJoinNode node, C context) { return visitPlan(node, context); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/RemoteSourceNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/RemoteSourceNode.java index 8fac6027e6e6c..498213d7ea48c 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/RemoteSourceNode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/RemoteSourceNode.java @@ -34,13 +34,15 @@ public class RemoteSourceNode private final List sourceFragmentIds; private final List outputs; private final Optional orderingScheme; + private final ExchangeNode.Type exchangeType; // This is needed to "unfragment" to compute stats correctly. @JsonCreator public RemoteSourceNode( @JsonProperty("id") PlanNodeId id, @JsonProperty("sourceFragmentIds") List sourceFragmentIds, @JsonProperty("outputs") List outputs, - @JsonProperty("orderingScheme") Optional orderingScheme) + @JsonProperty("orderingScheme") Optional orderingScheme, + @JsonProperty("exchangeType") ExchangeNode.Type exchangeType) { super(id); @@ -49,11 +51,12 @@ public RemoteSourceNode( this.sourceFragmentIds = sourceFragmentIds; this.outputs = ImmutableList.copyOf(outputs); this.orderingScheme = requireNonNull(orderingScheme, "orderingScheme is null"); + this.exchangeType = requireNonNull(exchangeType, "exchangeType is null"); } - public RemoteSourceNode(PlanNodeId id, PlanFragmentId sourceFragmentId, List outputs, Optional orderingScheme) + public RemoteSourceNode(PlanNodeId id, PlanFragmentId sourceFragmentId, List outputs, Optional orderingScheme, ExchangeNode.Type exchangeType) { - this(id, ImmutableList.of(sourceFragmentId), outputs, orderingScheme); + this(id, ImmutableList.of(sourceFragmentId), outputs, orderingScheme, exchangeType); } @Override @@ -81,6 +84,12 @@ public Optional getOrderingScheme() return orderingScheme; } + @JsonProperty("exchangeType") + public ExchangeNode.Type getExchangeType() + { + return exchangeType; + } + @Override public R accept(PlanVisitor visitor, C context) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/SpatialJoinNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/SpatialJoinNode.java new file mode 100644 index 0000000000000..e9700338f6eaa --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/SpatialJoinNode.java @@ -0,0 +1,198 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.planner.plan; + +import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.tree.Expression; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +import javax.annotation.concurrent.Immutable; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +@Immutable +public class SpatialJoinNode + extends PlanNode +{ + public enum Type + { + INNER("SpatialInnerJoin"), + LEFT("SpatialLeftJoin"); + + private final String joinLabel; + + Type(String joinLabel) + { + this.joinLabel = joinLabel; + } + + public String getJoinLabel() + { + return joinLabel; + } + + public static Type fromJoinNodeType(JoinNode.Type joinNodeType) + { + switch (joinNodeType) { + case INNER: + return Type.INNER; + case LEFT: + return Type.LEFT; + default: + throw new IllegalArgumentException("Unsupported spatial join type: " + joinNodeType); + } + } + } + + private final Type type; + private final PlanNode left; + private final PlanNode right; + private final List outputSymbols; + private final Expression filter; + private final Optional leftPartitionSymbol; + private final Optional rightPartitionSymbol; + private final Optional kdbTree; + private final DistributionType distributionType; + + public enum DistributionType + { + PARTITIONED, + REPLICATED + } + + @JsonCreator + public SpatialJoinNode( + @JsonProperty("id") PlanNodeId id, + @JsonProperty("type") Type type, + @JsonProperty("left") PlanNode left, + @JsonProperty("right") PlanNode right, + @JsonProperty("outputSymbols") List outputSymbols, + @JsonProperty("filter") Expression filter, + @JsonProperty("leftPartitionSymbol") Optional leftPartitionSymbol, + @JsonProperty("rightPartitionSymbol") Optional rightPartitionSymbol, + @JsonProperty("kdbTree") Optional kdbTree) + { + super(id); + + this.type = requireNonNull(type, "type is null"); + this.left = requireNonNull(left, "left is null"); + this.right = requireNonNull(right, "right is null"); + this.outputSymbols = ImmutableList.copyOf(requireNonNull(outputSymbols, "outputSymbols is null")); + this.filter = requireNonNull(filter, "filter is null"); + this.leftPartitionSymbol = requireNonNull(leftPartitionSymbol, "leftPartitionSymbol is null"); + this.rightPartitionSymbol = requireNonNull(rightPartitionSymbol, "rightPartitionSymbol is null"); + this.kdbTree = requireNonNull(kdbTree, "kdbTree is null"); + + Set inputSymbols = ImmutableSet.builder() + .addAll(left.getOutputSymbols()) + .addAll(right.getOutputSymbols()) + .build(); + + checkArgument(inputSymbols.containsAll(outputSymbols), "Left and right join inputs do not contain all output symbols"); + if (kdbTree.isPresent()) { + checkArgument(leftPartitionSymbol.isPresent(), "Left partition symbol is missing"); + checkArgument(rightPartitionSymbol.isPresent(), "Right partition symbol is missing"); + checkArgument(left.getOutputSymbols().contains(leftPartitionSymbol.get()), "Left join input does not contain left partition symbol"); + checkArgument(right.getOutputSymbols().contains(rightPartitionSymbol.get()), "Right join input does not contain right partition symbol"); + this.distributionType = DistributionType.PARTITIONED; + } + else { + checkArgument(!leftPartitionSymbol.isPresent(), "KDB tree is missing"); + checkArgument(!rightPartitionSymbol.isPresent(), "KDB tree is missing"); + this.distributionType = DistributionType.REPLICATED; + } + } + + @JsonProperty("type") + public Type getType() + { + return type; + } + + @JsonProperty("left") + public PlanNode getLeft() + { + return left; + } + + @JsonProperty("right") + public PlanNode getRight() + { + return right; + } + + @JsonProperty("filter") + public Expression getFilter() + { + return filter; + } + + @JsonProperty("leftPartitionSymbol") + public Optional getLeftPartitionSymbol() + { + return leftPartitionSymbol; + } + + @JsonProperty("rightPartitionSymbol") + public Optional getRightPartitionSymbol() + { + return rightPartitionSymbol; + } + + @Override + public List getSources() + { + return ImmutableList.of(left, right); + } + + @Override + @JsonProperty("outputSymbols") + public List getOutputSymbols() + { + return outputSymbols; + } + + @JsonProperty("distributionType") + public DistributionType getDistributionType() + { + return distributionType; + } + + @JsonProperty("kdbTree") + public Optional getKdbTree() + { + return kdbTree; + } + + @Override + public R accept(PlanVisitor visitor, C context) + { + return visitor.visitSpatialJoin(this, context); + } + + @Override + public PlanNode replaceChildren(List newChildren) + { + checkArgument(newChildren.size() == 2, "expected newChildren to contain 2 nodes"); + return new SpatialJoinNode(getId(), type, newChildren.get(0), newChildren.get(1), outputSymbols, filter, leftPartitionSymbol, rightPartitionSymbol, kdbTree); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/TableScanNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/TableScanNode.java index 25e2a3e40215d..5e178e6e94fd2 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/TableScanNode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/plan/TableScanNode.java @@ -18,13 +18,11 @@ import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.sql.planner.Symbol; -import com.facebook.presto.sql.tree.Expression; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import java.util.List; @@ -41,24 +39,16 @@ public class TableScanNode extends PlanNode { private final TableHandle table; - private final Optional tableLayout; private final List outputSymbols; private final Map assignments; // symbol -> column + private final Optional tableLayout; + // Used during predicate refinement over multiple passes of predicate pushdown // TODO: think about how to get rid of this in new planner private final TupleDomain currentConstraint; - // HACK! - // - // This field exists for the sole purpose of being able to print the original predicates (from the query) in - // a human readable way. Predicates that get converted to and from TupleDomains might get more bulky and thus - // more difficult to read when printed. - // For example: - // (ds > '2013-01-01') in the original query could easily become (ds IN ('2013-01-02', '2013-01-03', ...)) after the partitions are generated. - // To make this work, the originalConstraint should be set exactly once after the first predicate push down and never adjusted after that. - // In this way, we are always guaranteed to have a readable predicate that provides some kind of upper bound on the constraints. - private final Expression originalConstraint; + private final TupleDomain enforcedConstraint; @JsonCreator public TableScanNode( @@ -66,10 +56,26 @@ public TableScanNode( @JsonProperty("table") TableHandle table, @JsonProperty("outputSymbols") List outputs, @JsonProperty("assignments") Map assignments, - @JsonProperty("layout") Optional tableLayout, - @JsonProperty("originalConstraint") @Nullable Expression originalConstraint) + @JsonProperty("layout") Optional tableLayout) { - this(id, table, outputs, assignments, tableLayout, null, originalConstraint); + // This constructor is for JSON deserialization only. Do not use. + super(id); + this.table = requireNonNull(table, "table is null"); + this.outputSymbols = ImmutableList.copyOf(requireNonNull(outputs, "outputs is null")); + this.assignments = ImmutableMap.copyOf(requireNonNull(assignments, "assignments is null")); + checkArgument(assignments.keySet().containsAll(outputs), "assignments does not cover all of outputs"); + this.tableLayout = requireNonNull(tableLayout, "tableLayout is null"); + this.currentConstraint = null; + this.enforcedConstraint = null; + } + + public TableScanNode( + PlanNodeId id, + TableHandle table, + List outputs, + Map assignments) + { + this(id, table, outputs, assignments, Optional.empty(), TupleDomain.all(), TupleDomain.all()); } public TableScanNode( @@ -78,25 +84,20 @@ public TableScanNode( List outputs, Map assignments, Optional tableLayout, - @Nullable TupleDomain currentConstraint, - @Nullable Expression originalConstraint) + TupleDomain currentConstraint, + TupleDomain enforcedConstraint) { super(id); - requireNonNull(table, "table is null"); - requireNonNull(outputs, "outputs is null"); - requireNonNull(assignments, "assignments is null"); + this.table = requireNonNull(table, "table is null"); + this.outputSymbols = ImmutableList.copyOf(requireNonNull(outputs, "outputs is null")); + this.assignments = ImmutableMap.copyOf(requireNonNull(assignments, "assignments is null")); checkArgument(assignments.keySet().containsAll(outputs), "assignments does not cover all of outputs"); - requireNonNull(tableLayout, "tableLayout is null"); - if (currentConstraint != null) { - checkArgument(currentConstraint.isAll() || tableLayout.isPresent(), "currentConstraint present without layout"); + this.tableLayout = requireNonNull(tableLayout, "tableLayout is null"); + this.currentConstraint = requireNonNull(currentConstraint, "currentConstraint is null"); + this.enforcedConstraint = requireNonNull(enforcedConstraint, "enforcedConstraint is null"); + if (!currentConstraint.isAll() || !enforcedConstraint.isAll()) { + checkArgument(tableLayout.isPresent(), "tableLayout must be present when currentConstraint or enforcedConstraint is non-trivial"); } - - this.table = table; - this.outputSymbols = ImmutableList.copyOf(outputs); - this.assignments = ImmutableMap.copyOf(assignments); - this.tableLayout = tableLayout; - this.currentConstraint = currentConstraint; - this.originalConstraint = originalConstraint; } @JsonProperty("table") @@ -124,13 +125,13 @@ public Map getAssignments() return assignments; } - @Nullable - @JsonProperty("originalConstraint") - public Expression getOriginalConstraint() - { - return originalConstraint; - } - + /** + * A TupleDomain that represents a predicate that every row this TableScan node + * produces is guaranteed to satisfy. + *

+ * This guarantee can have different origins. + * For example, it may be successful predicate push down, or inherent guarantee provided by the underlying data. + */ public TupleDomain getCurrentConstraint() { // currentConstraint can be pretty complex. As a result, it may incur a significant cost to serialize, store, and transport. @@ -138,6 +139,21 @@ public TupleDomain getCurrentConstraint() return currentConstraint; } + /** + * A TupleDomain that represents a predicate that has been successfully pushed into + * this TableScan node. In other words, predicates that were removed from filters + * above the TableScan node because the TableScan node can guarantee it. + *

+ * This field is used to make sure that predicates which were previously pushed down + * do not get lost in subsequent refinements of the table layout. + */ + public TupleDomain getEnforcedConstraint() + { + // enforcedConstraint can be pretty complex. As a result, it may incur a significant cost to serialize, store, and transport. + checkState(enforcedConstraint != null, "enforcedConstraint should only be used in planner. It is not transported to workers."); + return enforcedConstraint; + } + @Override public List getSources() { @@ -159,7 +175,7 @@ public String toString() .add("outputSymbols", outputSymbols) .add("assignments", assignments) .add("currentConstraint", currentConstraint) - .add("originalConstraint", originalConstraint) + .add("enforcedConstraint", enforcedConstraint) .toString(); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/IOPlanPrinter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/IOPlanPrinter.java index 5733a23d906da..c789cf1261071 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/IOPlanPrinter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/IOPlanPrinter.java @@ -522,7 +522,10 @@ private Set parseConstraints(TableHandle tableHandle, TupleDom ImmutableSet.Builder columnConstraints = ImmutableSet.builder(); for (Map.Entry entry : constraint.getDomains().get().entrySet()) { ColumnMetadata columnMetadata = metadata.getColumnMetadata(session, tableHandle, entry.getKey()); - columnConstraints.add(new ColumnConstraint(columnMetadata.getName(), columnMetadata.getType().getTypeSignature(), parseDomain(entry.getValue()))); + columnConstraints.add(new ColumnConstraint( + columnMetadata.getName(), + columnMetadata.getType().getTypeSignature(), + parseDomain(entry.getValue().simplify()))); } return columnConstraints.build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStats.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStats.java index bafe8a2f89472..2f57aecd3b71d 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStats.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStats.java @@ -38,7 +38,8 @@ public class PlanNodeStats { private final PlanNodeId planNodeId; - private final Duration planNodeWallTime; + private final Duration planNodeScheduledTime; + private final Duration planNodeCpuTime; private final long planNodeInputPositions; private final DataSize planNodeInputDataSize; private final long planNodeOutputPositions; @@ -50,7 +51,8 @@ public class PlanNodeStats PlanNodeStats( PlanNodeId planNodeId, - Duration planNodeWallTime, + Duration planNodeScheduledTime, + Duration planNodeCpuTime, long planNodeInputPositions, DataSize planNodeInputDataSize, long planNodeOutputPositions, @@ -61,7 +63,8 @@ public class PlanNodeStats { this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); - this.planNodeWallTime = requireNonNull(planNodeWallTime, "planNodeWallTime is null"); + this.planNodeScheduledTime = requireNonNull(planNodeScheduledTime, "planNodeScheduledTime is null"); + this.planNodeCpuTime = requireNonNull(planNodeCpuTime, "planNodeCpuTime is null"); this.planNodeInputPositions = planNodeInputPositions; this.planNodeInputDataSize = planNodeInputDataSize; this.planNodeOutputPositions = planNodeOutputPositions; @@ -85,9 +88,14 @@ public PlanNodeId getPlanNodeId() return planNodeId; } - public Duration getPlanNodeWallTime() + public Duration getPlanNodeScheduledTime() { - return planNodeWallTime; + return planNodeScheduledTime; + } + + public Duration getPlanNodeCpuTime() + { + return planNodeCpuTime; } public Set getOperatorTypes() @@ -190,7 +198,8 @@ public PlanNodeStats mergeWith(PlanNodeStats other) return new PlanNodeStats( planNodeId, - new Duration(planNodeWallTime.toMillis() + other.getPlanNodeWallTime().toMillis(), MILLISECONDS), + new Duration(planNodeScheduledTime.toMillis() + other.getPlanNodeScheduledTime().toMillis(), MILLISECONDS), + new Duration(planNodeCpuTime.toMillis() + other.getPlanNodeCpuTime().toMillis(), MILLISECONDS), planNodeInputPositions, planNodeInputDataSize, planNodeOutputPositions, planNodeOutputDataSize, operatorInputStats, diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStatsSummarizer.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStatsSummarizer.java index bc3d32cdfcdb0..4c12ea7b706dc 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStatsSummarizer.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanNodeStatsSummarizer.java @@ -45,10 +45,11 @@ public class PlanNodeStatsSummarizer { private PlanNodeStatsSummarizer() {} - public static Map aggregatePlanNodeStats(StageInfo stageInfo) + public static Map aggregatePlanNodeStats(List stageInfos) { Map aggregatedStats = new HashMap<>(); - List planNodeStats = stageInfo.getTasks().stream() + List planNodeStats = stageInfos.stream() + .flatMap(stageInfo -> stageInfo.getTasks().stream()) .map(TaskInfo::getStats) .flatMap(taskStats -> getPlanNodeStats(taskStats).stream()) .collect(toList()); @@ -65,11 +66,14 @@ private static List getPlanNodeStats(TaskStats taskStats) // it's possible that some or all of them are missing or out of date. // For example, a LIMIT clause can cause a query to finish before stats // are collected from the leaf stages. + Set planNodeIds = new HashSet<>(); + Map planNodeInputPositions = new HashMap<>(); Map planNodeInputBytes = new HashMap<>(); Map planNodeOutputPositions = new HashMap<>(); Map planNodeOutputBytes = new HashMap<>(); - Map planNodeWallMillis = new HashMap<>(); + Map planNodeScheduledMillis = new HashMap<>(); + Map planNodeCpuMillis = new HashMap<>(); Map> operatorInputStats = new HashMap<>(); Map> operatorHashCollisionsStats = new HashMap<>(); @@ -88,9 +92,13 @@ private static List getPlanNodeStats(TaskStats taskStats) // Gather input statistics for (OperatorStats operatorStats : pipelineStats.getOperatorSummaries()) { PlanNodeId planNodeId = operatorStats.getPlanNodeId(); + planNodeIds.add(planNodeId); + + long scheduledMillis = operatorStats.getAddInputWall().toMillis() + operatorStats.getGetOutputWall().toMillis() + operatorStats.getFinishWall().toMillis(); + planNodeScheduledMillis.merge(planNodeId, scheduledMillis, Long::sum); - long wall = operatorStats.getAddInputWall().toMillis() + operatorStats.getGetOutputWall().toMillis() + operatorStats.getFinishWall().toMillis(); - planNodeWallMillis.merge(planNodeId, wall, Long::sum); + long cpuMillis = operatorStats.getAddInputCpu().toMillis() + operatorStats.getGetOutputCpu().toMillis() + operatorStats.getFinishCpu().toMillis(); + planNodeCpuMillis.merge(planNodeId, cpuMillis, Long::sum); // A pipeline like hash build before join might link to another "internal" pipelines which provide actual input for this plan node if (operatorStats.getPlanNodeId().equals(inputPlanNode) && !pipelineStats.isInputPipeline()) { @@ -152,17 +160,20 @@ private static List getPlanNodeStats(TaskStats taskStats) } List stats = new ArrayList<>(); - for (Map.Entry entry : planNodeWallMillis.entrySet()) { - PlanNodeId planNodeId = entry.getKey(); + for (PlanNodeId planNodeId : planNodeIds) { + if (!planNodeInputPositions.containsKey(planNodeId)) { + continue; + } stats.add(new PlanNodeStats( planNodeId, - new Duration(planNodeWallMillis.get(planNodeId), MILLISECONDS), + new Duration(planNodeScheduledMillis.get(planNodeId), MILLISECONDS), + new Duration(planNodeCpuMillis.get(planNodeId), MILLISECONDS), planNodeInputPositions.get(planNodeId), succinctDataSize(planNodeInputBytes.get(planNodeId), BYTE), // It's possible there will be no output stats because all the pipelines that we observed were non-output. // For example in a query like SELECT * FROM a JOIN b ON c = d LIMIT 1 // It's possible to observe stats after the build starts, but before the probe does - // and therefore only have wall time, but no output stats + // and therefore only have scheduled time, but no output stats planNodeOutputPositions.getOrDefault(planNodeId, 0L), succinctDataSize(planNodeOutputBytes.getOrDefault(planNodeId, 0L), BYTE), operatorInputStats.get(planNodeId), diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanPrinter.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanPrinter.java index 0bf0cf60f654b..b1e0abe25340c 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanPrinter.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/planPrinter/PlanPrinter.java @@ -15,14 +15,9 @@ import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; -import com.facebook.presto.cost.CachingCostProvider; -import com.facebook.presto.cost.CachingStatsProvider; -import com.facebook.presto.cost.CostCalculator; -import com.facebook.presto.cost.CostProvider; import com.facebook.presto.cost.PlanNodeCostEstimate; import com.facebook.presto.cost.PlanNodeStatsEstimate; -import com.facebook.presto.cost.StatsCalculator; -import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.execution.StageInfo; import com.facebook.presto.execution.StageStats; import com.facebook.presto.metadata.FunctionRegistry; @@ -82,6 +77,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.StatisticAggregations; import com.facebook.presto.sql.planner.plan.StatisticAggregationsDescriptor; import com.facebook.presto.sql.planner.plan.TableFinishNode; @@ -110,7 +106,6 @@ import io.airlift.slice.Slice; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -120,8 +115,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.execution.StageInfo.getAllStages; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; @@ -131,11 +124,13 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.units.DataSize.succinctBytes; import static java.lang.Double.isFinite; import static java.lang.Double.isNaN; import static java.lang.String.format; +import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; @@ -152,8 +147,7 @@ private PlanPrinter( TypeProvider types, Optional stageExecutionStrategy, FunctionRegistry functionRegistry, - StatsProvider statsProvider, - CostProvider costProvider, + StatsAndCosts estimatedStatsAndCosts, Session session, Optional> stats, int indent, @@ -162,14 +156,13 @@ private PlanPrinter( requireNonNull(plan, "plan is null"); requireNonNull(types, "types is null"); requireNonNull(functionRegistry, "functionRegistry is null"); - requireNonNull(statsProvider, "statsProvider is null"); - requireNonNull(costProvider, "costProvider is null"); + requireNonNull(estimatedStatsAndCosts, "estimatedStatsAndCosts is null"); this.functionRegistry = functionRegistry; this.stats = stats; this.verbose = verbose; - Visitor visitor = new Visitor(stageExecutionStrategy, statsProvider, costProvider, types, session); + Visitor visitor = new Visitor(stageExecutionStrategy, types, estimatedStatsAndCosts, session); plan.accept(visitor, indent); } @@ -179,46 +172,47 @@ public String toString() return output.toString(); } - public static String textLogicalPlan(PlanNode plan, TypeProvider types, FunctionRegistry functionRegistry, StatsProvider statsProvider, CostProvider costProvider, Session session, int indent) + public static String textLogicalPlan(PlanNode plan, TypeProvider types, FunctionRegistry functionRegistry, StatsAndCosts estimatedStatsAndCosts, Session session, int indent) { - return new PlanPrinter(plan, types, Optional.empty(), functionRegistry, statsProvider, costProvider, session, Optional.empty(), indent, false).toString(); + return new PlanPrinter(plan, types, Optional.empty(), functionRegistry, estimatedStatsAndCosts, session, Optional.empty(), indent, false).toString(); } - public static String textLogicalPlan(PlanNode plan, TypeProvider types, FunctionRegistry functionRegistry, StatsCalculator statsCalculator, CostCalculator costCalculator, Session session, int indent, boolean verbose) + public static String textLogicalPlan(PlanNode plan, TypeProvider types, FunctionRegistry functionRegistry, StatsAndCosts estimatedStatsAndCosts, Session session, int indent, boolean verbose) { - return textLogicalPlan(plan, types, Optional.empty(), functionRegistry, statsCalculator, costCalculator, session, Optional.empty(), indent, verbose); + return textLogicalPlan(plan, types, Optional.empty(), functionRegistry, estimatedStatsAndCosts, session, Optional.empty(), indent, verbose); } - public static String textLogicalPlan(PlanNode plan, TypeProvider types, Optional stageExecutionStrategy, FunctionRegistry functionRegistry, StatsCalculator statsCalculator, CostCalculator costCalculator, Session session, Optional> stats, int indent, boolean verbose) + public static String textLogicalPlan(PlanNode plan, TypeProvider types, Optional stageExecutionStrategy, FunctionRegistry functionRegistry, StatsAndCosts estimatedStatsAndCosts, Session session, Optional> stats, int indent, boolean verbose) { - CachingStatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, types); - CachingCostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, session, types); - return new PlanPrinter(plan, types, stageExecutionStrategy, functionRegistry, statsProvider, costProvider, session, stats, indent, verbose).toString(); + return new PlanPrinter(plan, types, stageExecutionStrategy, functionRegistry, estimatedStatsAndCosts, session, stats, indent, verbose).toString(); } - public static String textDistributedPlan(StageInfo outputStageInfo, FunctionRegistry functionRegistry, StatsCalculator statsCalculator, CostCalculator costCalculator, Session session, boolean verbose) + public static String textDistributedPlan(StageInfo outputStageInfo, FunctionRegistry functionRegistry, Session session, boolean verbose) { StringBuilder builder = new StringBuilder(); List allStages = getAllStages(Optional.of(outputStageInfo)); + List allFragments = allStages.stream() + .map(StageInfo::getPlan) + .collect(toImmutableList()); + Map aggregatedStats = aggregatePlanNodeStats(allStages); for (StageInfo stageInfo : allStages) { - Map aggregatedStats = aggregatePlanNodeStats(stageInfo); - builder.append(formatFragment(functionRegistry, statsCalculator, costCalculator, session, stageInfo.getPlan(), Optional.of(stageInfo), Optional.of(aggregatedStats), verbose)); + builder.append(formatFragment(functionRegistry, session, stageInfo.getPlan(), Optional.of(stageInfo), Optional.of(aggregatedStats), verbose, allFragments)); } return builder.toString(); } - public static String textDistributedPlan(SubPlan plan, FunctionRegistry functionRegistry, StatsCalculator statsCalculator, CostCalculator costCalculator, Session session, boolean verbose) + public static String textDistributedPlan(SubPlan plan, FunctionRegistry functionRegistry, Session session, boolean verbose) { StringBuilder builder = new StringBuilder(); for (PlanFragment fragment : plan.getAllFragments()) { - builder.append(formatFragment(functionRegistry, statsCalculator, costCalculator, session, fragment, Optional.empty(), Optional.empty(), verbose)); + builder.append(formatFragment(functionRegistry, session, fragment, Optional.empty(), Optional.empty(), verbose, plan.getAllFragments())); } return builder.toString(); } - private static String formatFragment(FunctionRegistry functionRegistry, StatsCalculator statsCalculator, CostCalculator costCalculator, Session session, PlanFragment fragment, Optional stageInfo, Optional> planNodeStats, boolean verbose) + private static String formatFragment(FunctionRegistry functionRegistry, Session session, PlanFragment fragment, Optional stageInfo, Optional> planNodeStats, boolean verbose, List allFragments) { StringBuilder builder = new StringBuilder(); builder.append(format("Fragment %s [%s]\n", @@ -233,8 +227,9 @@ private static String formatFragment(FunctionRegistry functionRegistry, StatsCal double sdAmongTasks = Math.sqrt(squaredDifferences / stageInfo.get().getTasks().size()); builder.append(indentString(1)) - .append(format("CPU: %s, Input: %s (%s); per task: avg.: %s std.dev.: %s, Output: %s (%s)\n", - stageStats.getTotalCpuTime(), + .append(format("CPU: %s, Scheduled: %s, Input: %s (%s); per task: avg.: %s std.dev.: %s, Output: %s (%s)\n", + stageStats.getTotalCpuTime().convertToMostSuccinctTimeUnit(), + stageStats.getTotalScheduledTime().convertToMostSuccinctTimeUnit(), formatPositions(stageStats.getProcessedInputPositions()), stageStats.getProcessedInputDataSize(), formatDouble(avgPositionsPerTask), @@ -274,14 +269,12 @@ private static String formatFragment(FunctionRegistry functionRegistry, StatsCal } builder.append(indentString(1)).append(format("Grouped Execution: %s\n", fragment.getStageExecutionStrategy().isAnyScanGroupedExecution())); - if (stageInfo.isPresent()) { - builder.append(textLogicalPlan(fragment.getRoot(), TypeProvider.copyOf(fragment.getSymbols()), Optional.of(fragment.getStageExecutionStrategy()), functionRegistry, statsCalculator, costCalculator, session, Optional.of(planNodeStats.get()), 1, verbose)) - .append("\n"); - } - else { - builder.append(textLogicalPlan(fragment.getRoot(), TypeProvider.copyOf(fragment.getSymbols()), Optional.of(fragment.getStageExecutionStrategy()), functionRegistry, statsCalculator, costCalculator, session, Optional.empty(), 1, verbose)) - .append("\n"); - } + TypeProvider typeProvider = TypeProvider.copyOf(allFragments.stream() + .flatMap(f -> f.getSymbols().entrySet().stream()) + .distinct() + .collect(toImmutableMap(Map.Entry::getKey, Map.Entry::getValue))); + builder.append(textLogicalPlan(fragment.getRoot(), typeProvider, Optional.of(fragment.getStageExecutionStrategy()), functionRegistry, fragment.getStatsAndCosts(), session, planNodeStats, 1, verbose)) + .append("\n"); return builder.toString(); } @@ -295,7 +288,8 @@ public static String graphvizLogicalPlan(PlanNode plan, TypeProvider types) SINGLE_DISTRIBUTION, ImmutableList.of(plan.getId()), new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), plan.getOutputSymbols()), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); return GraphvizPrinter.printLogical(ImmutableList.of(fragment)); } @@ -333,8 +327,12 @@ private void printStats(int indent, PlanNodeId planNodeId, boolean printInput, b return; } - long totalMillis = stats.get().values().stream() - .mapToLong(node -> node.getPlanNodeWallTime().toMillis()) + long totalScheduledMillis = stats.get().values().stream() + .mapToLong(node -> node.getPlanNodeScheduledTime().toMillis()) + .sum(); + + long totalCpuMillis = stats.get().values().stream() + .mapToLong(node -> node.getPlanNodeCpuTime().toMillis()) .sum(); PlanNodeStats nodeStats = stats.get().get(planNodeId); @@ -352,10 +350,17 @@ private void printStats(int indent, PlanNodeId planNodeId, boolean printInput, b return; } - double fraction = 100.0d * nodeStats.getPlanNodeWallTime().toMillis() / totalMillis; + double scheduledTimeFraction = 100.0d * nodeStats.getPlanNodeScheduledTime().toMillis() / totalScheduledMillis; + double cpuTimeFraction = 100.0d * nodeStats.getPlanNodeCpuTime().toMillis() / totalCpuMillis; output.append(indentString(indent)); - output.append("CPU fraction: " + formatDouble(fraction) + "%"); + output.append(format( + "CPU: %s (%s%%), Scheduled: %s (%s%%)", + nodeStats.getPlanNodeCpuTime().convertToMostSuccinctTimeUnit(), + formatDouble(cpuTimeFraction), + nodeStats.getPlanNodeScheduledTime().convertToMostSuccinctTimeUnit(), + formatDouble(scheduledTimeFraction))); + if (printInput) { output.append(format(", Input: %s (%s)", formatPositions(nodeStats.getPlanNodeInputPositions()), @@ -508,16 +513,14 @@ private class Visitor { private final Optional stageExecutionStrategy; private final TypeProvider types; - private final StatsProvider statsProvider; - private final CostProvider costProvider; + private final StatsAndCosts estimatedStatsAndCosts; private final Session session; - public Visitor(Optional stageExecutionStrategy, StatsProvider statsProvider, CostProvider costProvider, TypeProvider types, Session session) + public Visitor(Optional stageExecutionStrategy, TypeProvider types, StatsAndCosts estimatedStatsAndCosts, Session session) { this.stageExecutionStrategy = stageExecutionStrategy; this.types = types; - this.statsProvider = statsProvider; - this.costProvider = costProvider; + this.estimatedStatsAndCosts = estimatedStatsAndCosts; this.session = session; } @@ -539,13 +542,7 @@ public Void visitJoin(JoinNode node, Integer indent) } node.getFilter().ifPresent(joinExpressions::add); - if (node.isSpatialJoin()) { - print(indent, "- Spatial%s[%s] => [%s]", - node.getType().getJoinLabel(), - Joiner.on(" AND ").join(joinExpressions), - formatOutputs(node.getOutputSymbols())); - } - else if (node.isCrossJoin()) { + if (node.isCrossJoin()) { checkState(joinExpressions.isEmpty()); print(indent, "- CrossJoin => [%s]", formatOutputs(node.getOutputSymbols())); } @@ -567,6 +564,23 @@ else if (node.isCrossJoin()) { return null; } + @Override + public Void visitSpatialJoin(SpatialJoinNode node, Integer indent) + { + print(indent, "- %s[%s] => [%s]", + node.getType().getJoinLabel(), + node.getFilter(), + formatOutputs(node.getOutputSymbols())); + + print(indent + 2, "Distribution: %s", node.getDistributionType()); + printPlanNodesStatsAndCost(indent + 2, node); + printStats(indent + 2, node.getId()); + node.getLeft().accept(this, indent + 1); + node.getRight().accept(this, indent + 1); + + return null; + } + @Override public Void visitSemiJoin(SemiJoinNode node, Integer indent) { @@ -575,6 +589,7 @@ public Void visitSemiJoin(SemiJoinNode node, Integer indent) node.getFilteringSourceJoinSymbol(), formatHash(node.getSourceHashSymbol(), node.getFilteringSourceHashSymbol()), formatOutputs(node.getOutputSymbols())); + node.getDistributionType().ifPresent(distributionType -> print(indent + 2, "Distribution: %s", distributionType)); printPlanNodesStatsAndCost(indent + 2, node); printStats(indent + 2, node.getId()); node.getSource().accept(this, indent + 1); @@ -818,16 +833,14 @@ public Void visitTableScan(TableScanNode node, Integer indent) { TableHandle table = node.getTable(); if (stageExecutionStrategy.isPresent()) { - print(indent, "- TableScan[%s, grouped = %s, originalConstraint = %s] => [%s]", + print(indent, "- TableScan[%s, grouped = %s] => [%s]", table, stageExecutionStrategy.get().isGroupedExecution(node.getId()), - node.getOriginalConstraint(), formatOutputs(node.getOutputSymbols())); } else { - print(indent, "- TableScan[%s, originalConstraint = %s] => [%s]", + print(indent, "- TableScan[%s] => [%s]", table, - node.getOriginalConstraint(), formatOutputs(node.getOutputSymbols())); } printPlanNodesStatsAndCost(indent + 2, node); @@ -902,8 +915,6 @@ private Void visitScanFilterAndProjectInfo( format += "grouped = %s, "; arguments.add(stageExecutionStrategy.get().isGroupedExecution(scanNode.get().getId())); } - format += "originalConstraint = %s, "; - arguments.add(scanNode.get().getOriginalConstraint()); } if (filterNode.isPresent()) { @@ -913,8 +924,9 @@ private Void visitScanFilterAndProjectInfo( } if (format.length() > 1) { - format = format.substring(0, format.length() - 2) + "] => [%s]"; + format = format.substring(0, format.length() - 2); } + format += "] => [%s]"; if (projectNode.isPresent()) { operatorName += "Project"; @@ -1018,7 +1030,11 @@ public Void visitTopN(TopNNode node, Integer indent) { Iterable keys = Iterables.transform(node.getOrderingScheme().getOrderBy(), input -> input + " " + node.getOrderingScheme().getOrdering(input)); - print(indent, "- TopN[%s by (%s)] => [%s]", node.getCount(), Joiner.on(", ").join(keys), formatOutputs(node.getOutputSymbols())); + print(indent, "- TopN%s[%s by (%s)] => [%s]", + node.getStep() == TopNNode.Step.PARTIAL ? "Partial" : "", + node.getCount(), + Joiner.on(", ").join(keys), + formatOutputs(node.getOutputSymbols())); printPlanNodesStatsAndCost(indent + 2, node); printStats(indent + 2, node.getId()); return processChildren(node, indent + 1); @@ -1231,7 +1247,7 @@ public Void visitMetadataDelete(MetadataDeleteNode node, Integer indent) @Override public Void visitEnforceSingleRow(EnforceSingleRowNode node, Integer indent) { - print(indent, "- Scalar => [%s]", formatOutputs(node.getOutputSymbols())); + print(indent, "- EnforceSingleRow => [%s]", formatOutputs(node.getOutputSymbols())); printPlanNodesStatsAndCost(indent + 2, node); printStats(indent + 2, node.getId()); @@ -1373,25 +1389,24 @@ private String formatDomain(Domain domain) private void printPlanNodesStatsAndCost(int indent, PlanNode... nodes) { - if (Arrays.stream(nodes).anyMatch(this::isKnownPlanNodeStatsOrCost)) { - String formattedStatsAndCost = Joiner.on("/").join(Arrays.stream(nodes) - .map(this::formatPlanNodeStatsAndCost) - .collect(toImmutableList())); - print(indent, "Cost: %s", formattedStatsAndCost); + if (stream(nodes).allMatch(this::isPlanNodeStatsAndCostsUnknown)) { + return; } + print(indent, "Cost: %s", stream(nodes).map(this::formatPlanNodeStatsAndCost).collect(joining("/"))); } - private boolean isKnownPlanNodeStatsOrCost(PlanNode node) + private boolean isPlanNodeStatsAndCostsUnknown(PlanNode node) { - return !UNKNOWN_STATS.equals(statsProvider.getStats(node)) - || !UNKNOWN_COST.equals(costProvider.getCumulativeCost(node)); + PlanNodeStatsEstimate stats = estimatedStatsAndCosts.getStats().getOrDefault(node.getId(), PlanNodeStatsEstimate.unknown()); + PlanNodeCostEstimate cost = estimatedStatsAndCosts.getCosts().getOrDefault(node.getId(), PlanNodeCostEstimate.unknown()); + return stats.isOutputRowCountUnknown() || cost.equals(PlanNodeCostEstimate.unknown()); } private String formatPlanNodeStatsAndCost(PlanNode node) { - PlanNodeStatsEstimate stats = statsProvider.getStats(node); - PlanNodeCostEstimate cost = costProvider.getCumulativeCost(node); - return String.format("{rows: %s (%s), cpu: %s, memory: %s, network: %s}", + PlanNodeStatsEstimate stats = estimatedStatsAndCosts.getStats().getOrDefault(node.getId(), PlanNodeStatsEstimate.unknown()); + PlanNodeCostEstimate cost = estimatedStatsAndCosts.getCosts().getOrDefault(node.getId(), PlanNodeCostEstimate.unknown()); + return format("{rows: %s (%s), cpu: %s, memory: %s, network: %s}", formatAsLong(stats.getOutputRowCount()), formatEstimateAsDataSize(stats.getOutputSizeInBytes(node.getOutputSymbols(), types)), formatDouble(cost.getCpuCost()), @@ -1407,7 +1422,7 @@ private static String formatEstimateAsDataSize(double value) private static String formatHash(Optional... hashes) { - List symbols = Arrays.stream(hashes) + List symbols = stream(hashes) .filter(Optional::isPresent) .map(Optional::get) .collect(toList()); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoDuplicatePlanNodeIdsChecker.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoDuplicatePlanNodeIdsChecker.java index ed5dae90112fd..a594786217b01 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoDuplicatePlanNodeIdsChecker.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoDuplicatePlanNodeIdsChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.TypeProvider; @@ -32,7 +33,7 @@ public class NoDuplicatePlanNodeIdsChecker implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { Map planNodeIds = new HashMap<>(); searchFrom(planNode) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoIdentifierLeftChecker.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoIdentifierLeftChecker.java index 92269479c4751..cec8bb662a817 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoIdentifierLeftChecker.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoIdentifierLeftChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.analyzer.ExpressionTreeUtils; import com.facebook.presto.sql.parser.SqlParser; @@ -28,7 +29,7 @@ public final class NoIdentifierLeftChecker implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { List identifiers = ExpressionTreeUtils.extractExpressions(ExpressionExtractor.extractExpressions(plan), Identifier.class); if (!identifiers.isEmpty()) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoSubqueryExpressionLeftChecker.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoSubqueryExpressionLeftChecker.java index bdfd4934d5c0d..983cdad6d4a71 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoSubqueryExpressionLeftChecker.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/NoSubqueryExpressionLeftChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.ExpressionExtractor; @@ -29,7 +30,7 @@ public final class NoSubqueryExpressionLeftChecker implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { for (Expression expression : ExpressionExtractor.extractExpressions(plan)) { new DefaultTraversalVisitor() diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/PlanSanityChecker.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/PlanSanityChecker.java index 3c0b3208a84e8..a964e2c500813 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/PlanSanityChecker.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/PlanSanityChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.TypeProvider; @@ -55,19 +56,19 @@ public PlanSanityChecker(boolean forceSingleNode) .build(); } - public void validateFinalPlan(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validateFinalPlan(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { - checkers.get(Stage.FINAL).forEach(checker -> checker.validate(planNode, session, metadata, sqlParser, types)); + checkers.get(Stage.FINAL).forEach(checker -> checker.validate(planNode, session, metadata, sqlParser, types, warningCollector)); } - public void validateIntermediatePlan(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validateIntermediatePlan(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { - checkers.get(Stage.INTERMEDIATE).forEach(checker -> checker.validate(planNode, session, metadata, sqlParser, types)); + checkers.get(Stage.INTERMEDIATE).forEach(checker -> checker.validate(planNode, session, metadata, sqlParser, types, warningCollector)); } public interface Checker { - void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types); + void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector); } private enum Stage diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/TypeValidator.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/TypeValidator.java index a077d9a4e4ae0..3c0f05162eed0 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/TypeValidator.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/TypeValidator.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.type.Type; @@ -53,9 +54,9 @@ public final class TypeValidator public TypeValidator() {} @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { - plan.accept(new Visitor(session, metadata, sqlParser, types), null); + plan.accept(new Visitor(session, metadata, sqlParser, types, warningCollector), null); } private static class Visitor @@ -65,13 +66,15 @@ private static class Visitor private final Metadata metadata; private final SqlParser sqlParser; private final TypeProvider types; + private final WarningCollector warningCollector; - public Visitor(Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public Visitor(Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { this.session = requireNonNull(session, "session is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.types = requireNonNull(types, "types is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -116,7 +119,7 @@ public Void visitProject(ProjectNode node, Void context) verifyTypeSignature(entry.getKey(), expectedType.getTypeSignature(), types.get(Symbol.from(symbolReference)).getTypeSignature()); continue; } - Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, types, entry.getValue(), emptyList() /* parameters already replaced */); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, types, entry.getValue(), emptyList(), warningCollector); Type actualType = expressionTypes.get(NodeRef.of(entry.getValue())); verifyTypeSignature(entry.getKey(), expectedType.getTypeSignature(), actualType.getTypeSignature()); } @@ -162,7 +165,7 @@ private void checkSignature(Symbol symbol, Signature signature) private void checkCall(Symbol symbol, FunctionCall call) { Type expectedType = types.get(symbol); - Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, types, call, emptyList() /*parameters already replaced */); + Map, Type> expressionTypes = getExpressionTypes(session, metadata, sqlParser, types, call, emptyList(), warningCollector); Type actualType = expressionTypes.get(NodeRef.of(call)); verifyTypeSignature(symbol, expectedType.getTypeSignature(), actualType.getTypeSignature()); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateAggregationsWithDefaultValues.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateAggregationsWithDefaultValues.java index a36c3f1c796b1..e11653ae3f426 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateAggregationsWithDefaultValues.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateAggregationsWithDefaultValues.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.TypeProvider; @@ -59,7 +60,7 @@ public ValidateAggregationsWithDefaultValues(boolean forceSingleNode) } @Override - public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { planNode.accept(new Visitor(session, metadata, sqlParser, types), null); } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java index 995158c31a792..7e8b61df14b18 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateDependenciesChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.Symbol; @@ -49,6 +50,7 @@ import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SetOperationNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -81,7 +83,7 @@ public final class ValidateDependenciesChecker implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { validate(plan); } @@ -347,28 +349,7 @@ public Void visitJoin(JoinNode node, Set boundSymbols) allInputs); }); - int leftMaxPosition = -1; - Optional rightMinPosition = Optional.empty(); - Set leftSymbols = new HashSet<>(node.getLeft().getOutputSymbols()); - for (int i = 0; i < node.getOutputSymbols().size(); i++) { - Symbol symbol = node.getOutputSymbols().get(i); - if (leftSymbols.contains(symbol)) { - leftMaxPosition = i; - } - else if (!rightMinPosition.isPresent()) { - rightMinPosition = Optional.of(i); - } - } - checkState(!rightMinPosition.isPresent() || rightMinPosition.get() > leftMaxPosition, "Not all left output symbols are before right output symbols"); - - if (node.isCrossJoin()) { - List allInputsOrdered = ImmutableList.builder() - .addAll(node.getLeft().getOutputSymbols()) - .addAll(node.getRight().getOutputSymbols()) - .build(); - checkState(allInputsOrdered.equals(node.getOutputSymbols()), "Outputs symbols (%s) for cross join do not match ordered input symbols (%s)", node.getOutputSymbols(), allInputsOrdered); - } - + checkLeftOutputSymbolsBeforeRight(node.getLeft().getOutputSymbols(), node.getOutputSymbols()); return null; } @@ -391,6 +372,47 @@ public Void visitSemiJoin(SemiJoinNode node, Set boundSymbols) return null; } + @Override + public Void visitSpatialJoin(SpatialJoinNode node, Set boundSymbols) + { + node.getLeft().accept(this, boundSymbols); + node.getRight().accept(this, boundSymbols); + + Set leftInputs = createInputs(node.getLeft(), boundSymbols); + Set rightInputs = createInputs(node.getRight(), boundSymbols); + Set allInputs = ImmutableSet.builder() + .addAll(leftInputs) + .addAll(rightInputs) + .build(); + + Set predicateSymbols = SymbolsExtractor.extractUnique(node.getFilter()); + checkArgument( + allInputs.containsAll(predicateSymbols), + "Symbol from filter (%s) not in sources (%s)", + predicateSymbols, + allInputs); + + checkLeftOutputSymbolsBeforeRight(node.getLeft().getOutputSymbols(), node.getOutputSymbols()); + return null; + } + + private void checkLeftOutputSymbolsBeforeRight(List leftSymbols, List outputSymbols) + { + int leftMaxPosition = -1; + Optional rightMinPosition = Optional.empty(); + Set leftSymbolsSet = new HashSet<>(leftSymbols); + for (int i = 0; i < outputSymbols.size(); i++) { + Symbol symbol = outputSymbols.get(i); + if (leftSymbolsSet.contains(symbol)) { + leftMaxPosition = i; + } + else if (!rightMinPosition.isPresent()) { + rightMinPosition = Optional.of(i); + } + } + checkState(!rightMinPosition.isPresent() || rightMinPosition.get() > leftMaxPosition, "Not all left output symbols are before right output symbols"); + } + @Override public Void visitIndexJoin(IndexJoinNode node, Set boundSymbols) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateStreamingAggregations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateStreamingAggregations.java index 1d017eabd7aed..9da22c754dacf 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateStreamingAggregations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/ValidateStreamingAggregations.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.GroupingProperty; import com.facebook.presto.spi.LocalProperty; @@ -43,9 +44,9 @@ public class ValidateStreamingAggregations implements Checker { @Override - public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode planNode, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { - planNode.accept(new Visitor(session, metadata, sqlParser, types), null); + planNode.accept(new Visitor(session, metadata, sqlParser, types, warningCollector), null); } private static final class Visitor @@ -55,13 +56,15 @@ private static final class Visitor private final Metadata metadata; private final SqlParser sqlParser; private final TypeProvider types; + private final WarningCollector warningCollector; - private Visitor(Session sesstion, Metadata metadata, SqlParser sqlParser, TypeProvider types) + private Visitor(Session sesstion, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { this.sesstion = sesstion; this.metadata = metadata; this.sqlParser = sqlParser; this.types = types; + this.warningCollector = warningCollector; } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyNoFilteredAggregations.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyNoFilteredAggregations.java index e2475158522f7..6b2550a173921 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyNoFilteredAggregations.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyNoFilteredAggregations.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.TypeProvider; @@ -26,7 +27,7 @@ public final class VerifyNoFilteredAggregations implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { searchFrom(plan) .where(AggregationNode.class::isInstance) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyOnlyOneOutputNode.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyOnlyOneOutputNode.java index 518a71f5dbadd..325bb20ca1fb8 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyOnlyOneOutputNode.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/sanity/VerifyOnlyOneOutputNode.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.TypeProvider; @@ -27,7 +28,7 @@ public final class VerifyOnlyOneOutputNode implements PlanSanityChecker.Checker { @Override - public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types) + public void validate(PlanNode plan, Session session, Metadata metadata, SqlParser sqlParser, TypeProvider types, WarningCollector warningCollector) { int outputPlanNodesCount = searchFrom(plan) .where(OutputNode.class::isInstance) diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java index 7c5c027feda21..dcb3cc370c7f6 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeInputRewrite.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.rewrite; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.type.Type; @@ -61,9 +62,10 @@ public Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { - return (Statement) new Visitor(session, parser, metadata, queryExplainer, parameters, accessControl).process(node, null); + return (Statement) new Visitor(session, parser, metadata, queryExplainer, parameters, accessControl, warningCollector).process(node, null); } private static final class Visitor @@ -75,6 +77,7 @@ private static final class Visitor private final Optional queryExplainer; private final List parameters; private final AccessControl accessControl; + private final WarningCollector warningCollector; public Visitor( Session session, @@ -82,7 +85,8 @@ public Visitor( Metadata metadata, Optional queryExplainer, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { this.session = requireNonNull(session, "session is null"); this.parser = parser; @@ -90,6 +94,7 @@ public Visitor( this.queryExplainer = queryExplainer; this.accessControl = accessControl; this.parameters = parameters; + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -100,7 +105,7 @@ protected Node visitDescribeInput(DescribeInput node, Void context) Statement statement = parser.createStatement(sqlString, createParsingOptions(session)); // create analysis for the query we are describing. - Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters); + Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters, warningCollector); Analysis analysis = analyzer.analyze(statement, true); // get all parameters in query diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeOutputRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeOutputRewrite.java index c50fdc4de0211..6821d0de954c1 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeOutputRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/DescribeOutputRewrite.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.rewrite; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.security.AccessControl; @@ -58,9 +59,10 @@ public Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { - return (Statement) new Visitor(session, parser, metadata, queryExplainer, parameters, accessControl).process(node, null); + return (Statement) new Visitor(session, parser, metadata, queryExplainer, parameters, accessControl, warningCollector).process(node, null); } private static final class Visitor @@ -72,6 +74,7 @@ private static final class Visitor private final Optional queryExplainer; private final List parameters; private final AccessControl accessControl; + private final WarningCollector warningCollector; public Visitor( Session session, @@ -79,7 +82,8 @@ public Visitor( Metadata metadata, Optional queryExplainer, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { this.session = requireNonNull(session, "session is null"); this.parser = parser; @@ -87,6 +91,7 @@ public Visitor( this.queryExplainer = queryExplainer; this.parameters = parameters; this.accessControl = accessControl; + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -95,7 +100,7 @@ protected Node visitDescribeOutput(DescribeOutput node, Void context) String sqlString = session.getPreparedStatement(node.getName().getValue()); Statement statement = parser.createStatement(sqlString, createParsingOptions(session)); - Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters); + Analyzer analyzer = new Analyzer(session, metadata, parser, accessControl, queryExplainer, parameters, warningCollector); Analysis analysis = analyzer.analyze(statement, true); Optional limit = Optional.empty(); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java index de0766e6678b2..e1e97144547eb 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java @@ -14,13 +14,15 @@ package com.facebook.presto.sql.rewrite; import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryPreparer; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.sql.analyzer.QueryExplainer; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.tree.AstVisitor; -import com.facebook.presto.sql.tree.Execute; import com.facebook.presto.sql.tree.Explain; import com.facebook.presto.sql.tree.ExplainFormat; import com.facebook.presto.sql.tree.ExplainOption; @@ -32,15 +34,12 @@ import java.util.List; import java.util.Optional; -import static com.facebook.presto.execution.SqlQueryManager.unwrapExecuteStatement; -import static com.facebook.presto.execution.SqlQueryManager.validateParameters; import static com.facebook.presto.sql.QueryUtil.singleValueQuery; import static com.facebook.presto.sql.tree.ExplainFormat.Type.JSON; import static com.facebook.presto.sql.tree.ExplainFormat.Type.TEXT; import static com.facebook.presto.sql.tree.ExplainType.Type.IO; import static com.facebook.presto.sql.tree.ExplainType.Type.LOGICAL; import static com.facebook.presto.sql.tree.ExplainType.Type.VALIDATE; -import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; final class ExplainRewrite @@ -54,26 +53,30 @@ public Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { - return (Statement) new Visitor(session, parser, queryExplainer).process(node, null); + return (Statement) new Visitor(session, parser, queryExplainer, warningCollector).process(node, null); } private static final class Visitor extends AstVisitor { private final Session session; - private final SqlParser parser; + private final QueryPreparer queryPreparer; private final Optional queryExplainer; + private final WarningCollector warningCollector; public Visitor( Session session, SqlParser parser, - Optional queryExplainer) + Optional queryExplainer, + WarningCollector warningCollector) { this.session = requireNonNull(session, "session is null"); - this.parser = parser; + this.queryPreparer = new QueryPreparer(requireNonNull(parser, "queryPreparer is null")); this.queryExplainer = requireNonNull(queryExplainer, "queryExplainer is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -113,26 +116,23 @@ protected Node visitExplain(Explain node, Void context) private Node getQueryPlan(Explain node, ExplainType.Type planType, ExplainFormat.Type planFormat) throws IllegalArgumentException { - Statement wrappedStatement = node.getStatement(); - Statement statement = unwrapExecuteStatement(wrappedStatement, parser, session); - List parameters = wrappedStatement instanceof Execute ? ((Execute) wrappedStatement).getParameters() : emptyList(); - validateParameters(statement, parameters); + PreparedQuery preparedQuery = queryPreparer.prepareQuery(session, node.getStatement()); if (planType == VALIDATE) { - queryExplainer.get().analyze(session, statement, parameters); + queryExplainer.get().analyze(session, preparedQuery.getStatement(), preparedQuery.getParameters(), warningCollector); return singleValueQuery("Valid", true); } String plan; switch (planFormat) { case GRAPHVIZ: - plan = queryExplainer.get().getGraphvizPlan(session, statement, planType, parameters); + plan = queryExplainer.get().getGraphvizPlan(session, preparedQuery.getStatement(), planType, preparedQuery.getParameters(), warningCollector); break; case JSON: - plan = queryExplainer.get().getJsonPlan(session, statement, planType, parameters); + plan = queryExplainer.get().getJsonPlan(session, preparedQuery.getStatement(), planType, preparedQuery.getParameters(), warningCollector); break; case TEXT: - plan = queryExplainer.get().getPlan(session, statement, planType, parameters); + plan = queryExplainer.get().getPlan(session, preparedQuery.getStatement(), planType, preparedQuery.getParameters(), warningCollector); break; default: throw new IllegalArgumentException("Invalid Explain Format: " + planFormat.toString()); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowQueriesRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowQueriesRewrite.java index 1e7526cd0c709..9361995ddce95 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowQueriesRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowQueriesRewrite.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionKind; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; @@ -57,7 +58,6 @@ import com.facebook.presto.sql.tree.ShowCreate; import com.facebook.presto.sql.tree.ShowFunctions; import com.facebook.presto.sql.tree.ShowGrants; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowTables; @@ -72,7 +72,6 @@ import com.google.common.collect.ImmutableSortedMap; import com.google.common.primitives.Primitives; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -91,7 +90,6 @@ import static com.facebook.presto.metadata.MetadataUtil.createQualifiedObjectName; import static com.facebook.presto.spi.StandardErrorCode.INVALID_COLUMN_PROPERTY; import static com.facebook.presto.spi.StandardErrorCode.INVALID_TABLE_PROPERTY; -import static com.facebook.presto.sql.ExpressionFormatter.formatQualifiedName; import static com.facebook.presto.sql.ParsingUtil.createParsingOptions; import static com.facebook.presto.sql.QueryUtil.aliased; import static com.facebook.presto.sql.QueryUtil.aliasedName; @@ -136,9 +134,10 @@ public Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { - return (Statement) new Visitor(metadata, parser, session, parameters, accessControl, queryExplainer).process(node, null); + return (Statement) new Visitor(metadata, parser, session, parameters, accessControl, queryExplainer, warningCollector).process(node, null); } private static class Visitor @@ -149,9 +148,10 @@ private static class Visitor private final SqlParser sqlParser; final List parameters; private final AccessControl accessControl; - private final Optional queryExplainer; + private Optional queryExplainer; + private final WarningCollector warningCollector; - public Visitor(Metadata metadata, SqlParser sqlParser, Session session, List parameters, AccessControl accessControl, Optional queryExplainer) + public Visitor(Metadata metadata, SqlParser sqlParser, Session session, List parameters, AccessControl accessControl, Optional queryExplainer, WarningCollector warningCollector) { this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); @@ -159,6 +159,7 @@ public Visitor(Metadata metadata, SqlParser sqlParser, Session session, List parts = new ArrayList<>(showPartitions.getTable().getParts()); - int last = parts.size() - 1; - parts.set(last, parts.get(last) + "$partitions"); - QualifiedName table = QualifiedName.of(parts); - throw new SemanticException(NOT_SUPPORTED, showPartitions, "SHOW PARTITIONS no longer exists. Use this instead: SELECT * FROM %s", formatQualifiedName(table)); - } - @Override protected Node visitShowCreate(ShowCreate node, Void context) { diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowStatsRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowStatsRewrite.java index 2a7480507d131..0e15494106ae0 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowStatsRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/ShowStatsRewrite.java @@ -14,40 +14,40 @@ package com.facebook.presto.sql.rewrite; import com.facebook.presto.Session; -import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; -import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.TableHandle; +import com.facebook.presto.metadata.TableMetadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.ColumnHandle; +import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.Constraint; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; -import com.facebook.presto.spi.statistics.RangeColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; +import com.facebook.presto.spi.type.BigintType; +import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.DoubleType; +import com.facebook.presto.spi.type.IntegerType; +import com.facebook.presto.spi.type.RealType; +import com.facebook.presto.spi.type.SmallintType; +import com.facebook.presto.spi.type.TinyintType; import com.facebook.presto.spi.type.Type; -import com.facebook.presto.spi.type.VarcharType; -import com.facebook.presto.sql.InterpretedFunctionInvoker; import com.facebook.presto.sql.QueryUtil; import com.facebook.presto.sql.analyzer.QueryExplainer; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.Plan; +import com.facebook.presto.sql.planner.plan.FilterNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.tree.AllColumns; import com.facebook.presto.sql.tree.AstVisitor; import com.facebook.presto.sql.tree.Cast; -import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.DoubleLiteral; import com.facebook.presto.sql.tree.Expression; -import com.facebook.presto.sql.tree.Identifier; -import com.facebook.presto.sql.tree.IsNotNullPredicate; -import com.facebook.presto.sql.tree.IsNullPredicate; -import com.facebook.presto.sql.tree.Literal; -import com.facebook.presto.sql.tree.LogicalBinaryExpression; import com.facebook.presto.sql.tree.Node; -import com.facebook.presto.sql.tree.NotExpression; import com.facebook.presto.sql.tree.NullLiteral; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.Query; @@ -55,20 +55,20 @@ import com.facebook.presto.sql.tree.Row; import com.facebook.presto.sql.tree.SelectItem; import com.facebook.presto.sql.tree.ShowStats; -import com.facebook.presto.sql.tree.SingleColumn; import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.sql.tree.StringLiteral; import com.facebook.presto.sql.tree.Table; import com.facebook.presto.sql.tree.TableSubquery; import com.facebook.presto.sql.tree.Values; import com.google.common.collect.ImmutableList; -import io.airlift.slice.Slice; +import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.Optional; import static com.facebook.presto.metadata.MetadataUtil.createQualifiedObjectName; +import static com.facebook.presto.spi.type.DateType.DATE; import static com.facebook.presto.spi.type.StandardTypes.DOUBLE; import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; import static com.facebook.presto.sql.QueryUtil.aliased; @@ -79,25 +79,19 @@ import static com.facebook.presto.sql.planner.optimizations.PlanNodeSearcher.searchFrom; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; -import static java.util.Collections.singletonList; +import static java.lang.Math.round; import static java.util.Objects.requireNonNull; -import static java.util.function.Function.identity; -import static java.util.stream.Collectors.toMap; public class ShowStatsRewrite implements StatementRewrite.Rewrite { - private static final List> ALLOWED_SHOW_STATS_WHERE_EXPRESSION_TYPES = ImmutableList.of( - Literal.class, Identifier.class, ComparisonExpression.class, LogicalBinaryExpression.class, NotExpression.class, IsNullPredicate.class, IsNotNullPredicate.class); - private static final Expression NULL_DOUBLE = new Cast(new NullLiteral(), DOUBLE); private static final Expression NULL_VARCHAR = new Cast(new NullLiteral(), VARCHAR); - private static final int MAX_LOW_HIGH_LENGTH = 32; @Override - public Statement rewrite(Session session, Metadata metadata, SqlParser parser, Optional queryExplainer, Statement node, List parameters, AccessControl accessControl) + public Statement rewrite(Session session, Metadata metadata, SqlParser parser, Optional queryExplainer, Statement node, List parameters, AccessControl accessControl, WarningCollector warningCollector) { - return (Statement) new Visitor(metadata, session, parameters, queryExplainer).process(node, null); + return (Statement) new Visitor(metadata, session, parameters, queryExplainer, warningCollector).process(node, null); } private static class Visitor @@ -107,25 +101,29 @@ private static class Visitor private final Session session; private final List parameters; private final Optional queryExplainer; + private final WarningCollector warningCollector; - public Visitor(Metadata metadata, Session session, List parameters, Optional queryExplainer) + public Visitor(Metadata metadata, Session session, List parameters, Optional queryExplainer, WarningCollector warningCollector) { this.metadata = requireNonNull(metadata, "metadata is null"); this.session = requireNonNull(session, "session is null"); this.parameters = requireNonNull(parameters, "parameters is null"); this.queryExplainer = requireNonNull(queryExplainer, "queryExplainer is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override protected Node visitShowStats(ShowStats node, Void context) { - validateShowStats(node); checkState(queryExplainer.isPresent(), "Query explainer must be provided for SHOW STATS SELECT"); if (node.getRelation() instanceof TableSubquery) { - QuerySpecification specification = (QuerySpecification) ((TableSubquery) node.getRelation()).getQuery().getQueryBody(); + Query query = ((TableSubquery) node.getRelation()).getQuery(); + QuerySpecification specification = (QuerySpecification) query.getQueryBody(); + Plan plan = queryExplainer.get().getLogicalPlan(session, new Query(Optional.empty(), specification, Optional.empty(), Optional.empty()), parameters, warningCollector); + validateShowStatsSubquery(node, query, specification, plan); Table table = (Table) specification.getFrom().get(); - Constraint constraint = getConstraint(specification); + Constraint constraint = getConstraint(plan); return rewriteShowStats(node, table, constraint); } else if (node.getRelation() instanceof Table) { @@ -137,66 +135,31 @@ else if (node.getRelation() instanceof Table) { } } - private void validateShowStats(ShowStats node) + private void validateShowStatsSubquery(ShowStats node, Query query, QuerySpecification querySpecification, Plan plan) { // The following properties of SELECT subquery are required: // - only one relation in FROM - // - only plain columns in projection - // - only plain columns and constants in WHERE + // - only predicates that can be pushed down can be in the where clause // - no group by // - no having // - no set quantifier - if (!(node.getRelation() instanceof Table || node.getRelation() instanceof TableSubquery)) { - throw new SemanticException(NOT_SUPPORTED, node, "Only table and simple table subquery can be passed as argument to SHOW STATS clause"); - } - - if (node.getRelation() instanceof TableSubquery) { - Query query = ((TableSubquery) node.getRelation()).getQuery(); - check(query.getQueryBody() instanceof QuerySpecification, node, "Only table and simple table subquery can be passed as argument to SHOW STATS clause"); - QuerySpecification querySpecification = (QuerySpecification) query.getQueryBody(); - - check(querySpecification.getFrom().isPresent(), node, "There must be exactly one table in query passed to SHOW STATS SELECT clause"); - check(querySpecification.getFrom().get() instanceof Table, node, "There must be exactly one table in query passed to SHOW STATS SELECT clause"); - check(!query.getWith().isPresent(), node, "WITH is not supported by SHOW STATS SELECT clause"); - check(!querySpecification.getOrderBy().isPresent(), node, "ORDER BY is not supported in SHOW STATS SELECT clause"); - check(!querySpecification.getLimit().isPresent(), node, "LIMIT is not supported by SHOW STATS SELECT clause"); - check(!querySpecification.getHaving().isPresent(), node, "HAVING is not supported in SHOW STATS SELECT clause"); - check(!querySpecification.getGroupBy().isPresent(), node, "GROUP BY is not supported in SHOW STATS SELECT clause"); - check(!querySpecification.getSelect().isDistinct(), node, "DISTINCT is not supported by SHOW STATS SELECT clause"); - for (SelectItem selectItem : querySpecification.getSelect().getSelectItems()) { - if (selectItem instanceof AllColumns) { - continue; - } - check(selectItem instanceof SingleColumn, node, "Only * and column references are supported by SHOW STATS SELECT clause"); - SingleColumn columnSelect = (SingleColumn) selectItem; - check(columnSelect.getExpression() instanceof Identifier, node, "Only * and column references are supported by SHOW STATS SELECT clause"); - } - - querySpecification.getWhere().ifPresent((expression) -> validateShowStatsWhereExpression(expression, node)); - } - } - private void validateShowStatsWhereExpression(Expression expression, ShowStats node) - { - check(ALLOWED_SHOW_STATS_WHERE_EXPRESSION_TYPES.stream().anyMatch(clazz -> clazz.isInstance(expression)), node, "Only literals, column references, comparators, is (not) null and logical operators are allowed in WHERE of SHOW STATS SELECT clause"); + Optional filterNode = searchFrom(plan.getRoot()) + .where(FilterNode.class::isInstance) + .findSingle(); - if (expression instanceof NotExpression) { - validateShowStatsWhereExpression(((NotExpression) expression).getValue(), node); - } - else if (expression instanceof LogicalBinaryExpression) { - validateShowStatsWhereExpression(((LogicalBinaryExpression) expression).getLeft(), node); - validateShowStatsWhereExpression(((LogicalBinaryExpression) expression).getRight(), node); - } - else if (expression instanceof ComparisonExpression) { - validateShowStatsWhereExpression(((ComparisonExpression) expression).getLeft(), node); - validateShowStatsWhereExpression(((ComparisonExpression) expression).getRight(), node); - } - else if (expression instanceof IsNullPredicate) { - validateShowStatsWhereExpression(((IsNullPredicate) expression).getValue(), node); - } - else if (expression instanceof IsNotNullPredicate) { - validateShowStatsWhereExpression(((IsNotNullPredicate) expression).getValue(), node); - } + check(!filterNode.isPresent(), node, "Only predicates that can be pushed down are supported in the SHOW STATS WHERE clause"); + check(querySpecification.getFrom().isPresent(), node, "There must be exactly one table in query passed to SHOW STATS SELECT clause"); + check(querySpecification.getFrom().get() instanceof Table, node, "There must be exactly one table in query passed to SHOW STATS SELECT clause"); + check(!query.getWith().isPresent(), node, "WITH is not supported by SHOW STATS SELECT clause"); + check(!querySpecification.getOrderBy().isPresent(), node, "ORDER BY is not supported in SHOW STATS SELECT clause"); + check(!querySpecification.getLimit().isPresent(), node, "LIMIT is not supported by SHOW STATS SELECT clause"); + check(!querySpecification.getHaving().isPresent(), node, "HAVING is not supported in SHOW STATS SELECT clause"); + check(!querySpecification.getGroupBy().isPresent(), node, "GROUP BY is not supported in SHOW STATS SELECT clause"); + check(!querySpecification.getSelect().isDistinct(), node, "DISTINCT is not supported by SHOW STATS SELECT clause"); + + List selectItems = querySpecification.getSelect().getSelectItems(); + check(selectItems.size() == 1 && selectItems.get(0) instanceof AllColumns, node, "Only SELECT * is supported in SHOW STATS SELECT clause"); } private Node rewriteShowStats(ShowStats node, Table table, Constraint constraint) @@ -205,9 +168,9 @@ private Node rewriteShowStats(ShowStats node, Table table, Constraint statsColumnNames = buildColumnsNames(); List selectItems = buildSelectItems(statsColumnNames); - Map tableColumnNames = getStatisticsColumnNames(tableStatistics, tableHandle); - Map tableColumnTypes = getStatisticsColumnTypes(tableStatistics, tableHandle); - List resultRows = buildStatisticsRows(tableStatistics, tableColumnNames, tableColumnTypes); + TableMetadata tableMetadata = metadata.getTableMetadata(session, tableHandle); + Map columnHandles = metadata.getColumnHandles(session, tableHandle); + List resultRows = buildStatisticsRows(tableMetadata, columnHandles, tableStatistics); return simpleQuery(selectAll(selectItems), aliased(new Values(resultRows), @@ -228,14 +191,8 @@ protected Node visitNode(Node node, Void context) return node; } - private Constraint getConstraint(QuerySpecification specification) + private Constraint getConstraint(Plan plan) { - if (!specification.getWhere().isPresent()) { - return Constraint.alwaysTrue(); - } - - Plan plan = queryExplainer.get().getLogicalPlan(session, new Query(Optional.empty(), specification, Optional.empty(), Optional.empty()), parameters); - Optional scanNode = searchFrom(plan.getRoot()) .where(TableScanNode.class::isInstance) .findSingle(); @@ -247,20 +204,6 @@ private Constraint getConstraint(QuerySpecification specification) return new Constraint<>(scanNode.get().getCurrentConstraint()); } - private Map getStatisticsColumnNames(TableStatistics statistics, TableHandle tableHandle) - { - return statistics.getColumnStatistics() - .keySet().stream() - .collect(toMap(identity(), column -> metadata.getColumnMetadata(session, tableHandle, column).getName())); - } - - private Map getStatisticsColumnTypes(TableStatistics statistics, TableHandle tableHandle) - { - return statistics.getColumnStatistics() - .keySet().stream() - .collect(toMap(identity(), column -> metadata.getColumnMetadata(session, tableHandle, column).getType())); - } - private TableHandle getTableHandle(ShowStats node, QualifiedName table) { QualifiedObjectName qualifiedTableName = createQualifiedObjectName(session, node, table); @@ -288,34 +231,52 @@ private static List buildSelectItems(List columnNames) .collect(toImmutableList()); } - private List buildStatisticsRows(TableStatistics tableStatistics, Map columnNames, Map columnTypes) + private List buildStatisticsRows(TableMetadata tableMetadata, Map columnHandles, TableStatistics tableStatistics) { ImmutableList.Builder rowsBuilder = ImmutableList.builder(); - - // Stats for columns - for (Map.Entry columnStats : tableStatistics.getColumnStatistics().entrySet()) { - ColumnHandle columnHandle = columnStats.getKey(); - rowsBuilder.add(createColumnStatsRow(columnNames.get(columnHandle), columnTypes.get(columnHandle), columnStats.getValue())); + for (ColumnMetadata columnMetadata : tableMetadata.getColumns()) { + if (columnMetadata.isHidden()) { + continue; + } + String columnName = columnMetadata.getName(); + Type columnType = columnMetadata.getType(); + ColumnHandle columnHandle = columnHandles.get(columnName); + ColumnStatistics columnStatistics = tableStatistics.getColumnStatistics().get(columnHandle); + if (columnStatistics != null) { + rowsBuilder.add(createColumnStatsRow(columnName, columnType, columnStatistics)); + } + else { + rowsBuilder.add(createEmptyColumnStatsRow(columnName)); + } } - // Stats for whole table rowsBuilder.add(createTableStatsRow(tableStatistics)); - return rowsBuilder.build(); } private Row createColumnStatsRow(String columnName, Type type, ColumnStatistics columnStatistics) { - RangeColumnStatistics onlyRangeColumnStatistics = columnStatistics.getOnlyRangeColumnStatistics(); + ImmutableList.Builder rowValues = ImmutableList.builder(); + rowValues.add(new StringLiteral(columnName)); + rowValues.add(createEstimateRepresentation(columnStatistics.getDataSize())); + rowValues.add(createEstimateRepresentation(columnStatistics.getDistinctValuesCount())); + rowValues.add(createEstimateRepresentation(columnStatistics.getNullsFraction())); + rowValues.add(NULL_DOUBLE); + rowValues.add(toStringLiteral(type, columnStatistics.getRange().map(DoubleRange::getMin))); + rowValues.add(toStringLiteral(type, columnStatistics.getRange().map(DoubleRange::getMax))); + return new Row(rowValues.build()); + } + private Expression createEmptyColumnStatsRow(String columnName) + { ImmutableList.Builder rowValues = ImmutableList.builder(); rowValues.add(new StringLiteral(columnName)); - rowValues.add(createStatisticValueOrNull(onlyRangeColumnStatistics.getDataSize())); - rowValues.add(createStatisticValueOrNull(onlyRangeColumnStatistics.getDistinctValuesCount())); - rowValues.add(createStatisticValueOrNull(columnStatistics.getNullsFraction())); rowValues.add(NULL_DOUBLE); - rowValues.add(lowHighAsLiteral(type, onlyRangeColumnStatistics.getLowValue())); - rowValues.add(lowHighAsLiteral(type, onlyRangeColumnStatistics.getHighValue())); + rowValues.add(NULL_DOUBLE); + rowValues.add(NULL_DOUBLE); + rowValues.add(NULL_DOUBLE); + rowValues.add(NULL_VARCHAR); + rowValues.add(NULL_VARCHAR); return new Row(rowValues.build()); } @@ -326,34 +287,40 @@ private static Row createTableStatsRow(TableStatistics tableStatistics) rowValues.add(NULL_DOUBLE); rowValues.add(NULL_DOUBLE); rowValues.add(NULL_DOUBLE); - rowValues.add(createStatisticValueOrNull(tableStatistics.getRowCount())); + rowValues.add(createEstimateRepresentation(tableStatistics.getRowCount())); rowValues.add(NULL_VARCHAR); rowValues.add(NULL_VARCHAR); return new Row(rowValues.build()); } - private Expression lowHighAsLiteral(Type valueType, Optional value) + private static Expression createEstimateRepresentation(Estimate estimate) { - if (!value.isPresent()) { - return new Cast(new NullLiteral(), VARCHAR); - } - FunctionRegistry functionRegistry = metadata.getFunctionRegistry(); - InterpretedFunctionInvoker functionInvoker = new InterpretedFunctionInvoker(functionRegistry); - Signature castSignature = functionRegistry.getCoercion(valueType, VarcharType.createUnboundedVarcharType()); - Slice varcharValue = (Slice) functionInvoker.invoke(castSignature, session.toConnectorSession(), singletonList(value.get())); - String stringValue = varcharValue.toStringUtf8(); - if (stringValue.length() > MAX_LOW_HIGH_LENGTH) { - stringValue = stringValue.substring(0, MAX_LOW_HIGH_LENGTH) + "..."; + if (estimate.isUnknown()) { + return NULL_DOUBLE; } - return new StringLiteral(stringValue); + return new DoubleLiteral(Double.toString(estimate.getValue())); } - private static Expression createStatisticValueOrNull(Estimate estimate) + private static Expression toStringLiteral(Type type, Optional optionalValue) { - if (estimate.isValueUnknown()) { - return NULL_DOUBLE; + return optionalValue.map(value -> toStringLiteral(type, value)).orElse(NULL_VARCHAR); + } + + private static Expression toStringLiteral(Type type, double value) + { + if (type.equals(BigintType.BIGINT) || type.equals(IntegerType.INTEGER) || type.equals(SmallintType.SMALLINT) || type.equals(TinyintType.TINYINT)) { + return new StringLiteral(Long.toString(round(value))); } - return new DoubleLiteral(Double.toString(estimate.getValue())); + if (type.equals(DoubleType.DOUBLE) || type instanceof DecimalType) { + return new StringLiteral(Double.toString(value)); + } + if (type.equals(RealType.REAL)) { + return new StringLiteral(Float.toString((float) value)); + } + if (type.equals(DATE)) { + return new StringLiteral(LocalDate.ofEpochDay(round(value)).toString()); + } + throw new IllegalArgumentException("Unexpected type: " + type); } } } diff --git a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/StatementRewrite.java b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/StatementRewrite.java index 508d6217eebe2..cdc96d33ae7f3 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/rewrite/StatementRewrite.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/rewrite/StatementRewrite.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.rewrite; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.sql.analyzer.QueryExplainer; @@ -45,10 +46,11 @@ public static Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl) + AccessControl accessControl, + WarningCollector warningCollector) { for (Rewrite rewrite : REWRITES) { - node = requireNonNull(rewrite.rewrite(session, metadata, parser, queryExplainer, node, parameters, accessControl), "Statement rewrite returned null"); + node = requireNonNull(rewrite.rewrite(session, metadata, parser, queryExplainer, node, parameters, accessControl, warningCollector), "Statement rewrite returned null"); } return node; } @@ -62,6 +64,7 @@ Statement rewrite( Optional queryExplainer, Statement node, List parameters, - AccessControl accessControl); + AccessControl accessControl, + WarningCollector warningCollector); } } diff --git a/presto-main/src/main/java/com/facebook/presto/testing/DateTimeTestingUtils.java b/presto-main/src/main/java/com/facebook/presto/testing/DateTimeTestingUtils.java index 381ac7ff4727e..414a7051af89b 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/DateTimeTestingUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/DateTimeTestingUtils.java @@ -26,6 +26,7 @@ import java.time.LocalTime; import java.time.ZoneId; +import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static java.lang.Math.toIntExact; import static java.time.ZoneOffset.UTC; import static java.util.concurrent.TimeUnit.DAYS; @@ -44,11 +45,19 @@ public static SqlTimestamp sqlTimestampOf( int minuteOfHour, int secondOfMinute, int millisOfSecond, - DateTimeZone baseZone, - TimeZoneKey timestampZone, Session session) { - return sqlTimestampOf(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, baseZone, timestampZone, session.toConnectorSession()); + return sqlTimestampOf( + year, + monthOfYear, + dayOfMonth, + hourOfDay, + minuteOfHour, + secondOfMinute, + millisOfSecond, + getDateTimeZone(session.getTimeZoneKey()), + session.getTimeZoneKey(), + session.toConnectorSession()); } public static SqlTimestamp sqlTimestampOf( @@ -66,9 +75,7 @@ public static SqlTimestamp sqlTimestampOf( if (session.isLegacyTimestamp()) { return new SqlTimestamp(new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, baseZone).getMillis(), timestampZone); } - else { - return new SqlTimestamp(new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, DateTimeZone.UTC).getMillis()); - } + return sqlTimestampOf(LocalDateTime.of(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisToNanos(millisOfSecond))); } /** @@ -106,7 +113,7 @@ public static SqlTime sqlTimeOf( int millisOfSecond, Session session) { - LocalTime time = LocalTime.of(hourOfDay, minuteOfHour, secondOfMinute, toIntExact(MILLISECONDS.toNanos(millisOfSecond))); + LocalTime time = LocalTime.of(hourOfDay, minuteOfHour, secondOfMinute, millisToNanos(millisOfSecond)); return sqlTimeOf(time, session); } @@ -123,4 +130,9 @@ public static SqlTime sqlTimeOf(LocalTime time, Session session) } return new SqlTime(NANOSECONDS.toMillis(time.toNanoOfDay())); } + + private static int millisToNanos(int millisOfSecond) + { + return toIntExact(MILLISECONDS.toNanos(millisOfSecond)); + } } diff --git a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java index ed1af38e4d6e5..c14dda90cb7b7 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/LocalQueryRunner.java @@ -35,6 +35,7 @@ import com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges; import com.facebook.presto.cost.CostComparator; import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.cost.TaskCountEstimator; import com.facebook.presto.eventlistener.EventListenerManager; import com.facebook.presto.execution.CommitTask; import com.facebook.presto.execution.CreateTableTask; @@ -47,6 +48,8 @@ import com.facebook.presto.execution.NodeTaskMap; import com.facebook.presto.execution.PrepareTask; import com.facebook.presto.execution.QueryManagerConfig; +import com.facebook.presto.execution.QueryPreparer; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.RenameColumnTask; import com.facebook.presto.execution.RenameTableTask; import com.facebook.presto.execution.ResetSessionTask; @@ -59,6 +62,7 @@ import com.facebook.presto.execution.scheduler.LegacyNetworkTopology; import com.facebook.presto.execution.scheduler.NodeScheduler; import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.index.IndexManager; import com.facebook.presto.memory.MemoryManagerConfig; import com.facebook.presto.metadata.CatalogManager; @@ -86,9 +90,9 @@ import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.operator.TaskContext; import com.facebook.presto.operator.index.IndexJoinLookupStats; -import com.facebook.presto.server.NoOpSessionSupplier; import com.facebook.presto.server.PluginManager; import com.facebook.presto.server.PluginManagerConfig; +import com.facebook.presto.server.SessionPropertyDefaults; import com.facebook.presto.server.security.PasswordAuthenticatorManager; import com.facebook.presto.spi.PageIndexerFactory; import com.facebook.presto.spi.PageSorter; @@ -136,8 +140,6 @@ import com.facebook.presto.sql.tree.Deallocate; import com.facebook.presto.sql.tree.DropTable; import com.facebook.presto.sql.tree.DropView; -import com.facebook.presto.sql.tree.Execute; -import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.Prepare; import com.facebook.presto.sql.tree.RenameColumn; import com.facebook.presto.sql.tree.RenameTable; @@ -180,8 +182,6 @@ import java.util.function.Function; import static com.facebook.presto.cost.StatsCalculatorModule.createNewStatsCalculator; -import static com.facebook.presto.execution.SqlQueryManager.unwrapExecuteStatement; -import static com.facebook.presto.execution.SqlQueryManager.validateParameters; import static com.facebook.presto.spi.connector.ConnectorSplitManager.SplitSchedulingStrategy.GROUPED_SCHEDULING; import static com.facebook.presto.spi.connector.ConnectorSplitManager.SplitSchedulingStrategy.UNGROUPED_SCHEDULING; import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; @@ -197,7 +197,6 @@ import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static io.airlift.json.JsonCodec.jsonCodec; -import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newScheduledThreadPool; @@ -220,6 +219,7 @@ public class LocalQueryRunner private final StatsCalculator statsCalculator; private final CostCalculator costCalculator; private final CostCalculator estimatedExchangesCostCalculator; + private final TaskCountEstimator taskCountEstimator; private final TestingAccessControlManager accessControl; private final SplitManager splitManager; private final BlockEncodingManager blockEncodingManager; @@ -242,6 +242,7 @@ public class LocalQueryRunner private final boolean alwaysRevokeMemory; private final NodeSpillConfig nodeSpillConfig; + private final NodeSchedulerConfig nodeSchedulerConfig; private boolean printPlan; private final ReadWriteLock lock = new ReentrantReadWriteLock(); @@ -274,15 +275,15 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, finalizerService.start(); this.sqlParser = new SqlParser(); - this.planFragmenter = new PlanFragmenter(new QueryManagerConfig()); this.nodeManager = new InMemoryNodeManager(); this.typeRegistry = new TypeRegistry(); this.pageSorter = new PagesIndexPageSorter(new PagesIndex.TestingFactory(false)); this.indexManager = new IndexManager(); + this.nodeSchedulerConfig = new NodeSchedulerConfig().setIncludeCoordinator(true); NodeScheduler nodeScheduler = new NodeScheduler( new LegacyNetworkTopology(), nodeManager, - new NodeSchedulerConfig().setIncludeCoordinator(true), + nodeSchedulerConfig, new NodeTaskMap(finalizerService)); this.pageSinkManager = new PageSinkManager(); CatalogManager catalogManager = new CatalogManager(); @@ -304,11 +305,13 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, new TablePropertyManager(), new ColumnPropertyManager(), transactionManager); + this.planFragmenter = new PlanFragmenter(this.metadata, this.nodePartitioningManager, new QueryManagerConfig()); this.joinCompiler = new JoinCompiler(metadata, featuresConfig); this.pageIndexerFactory = new GroupByHashPageIndexerFactory(joinCompiler); this.statsCalculator = createNewStatsCalculator(metadata); - this.costCalculator = new CostCalculatorUsingExchanges(() -> nodeCountForStats); - this.estimatedExchangesCostCalculator = new CostCalculatorWithEstimatedExchanges(costCalculator, () -> nodeCountForStats); + this.taskCountEstimator = new TaskCountEstimator(() -> nodeCountForStats); + this.costCalculator = new CostCalculatorUsingExchanges(taskCountEstimator); + this.estimatedExchangesCostCalculator = new CostCalculatorWithEstimatedExchanges(costCalculator, taskCountEstimator); this.accessControl = new TestingAccessControlManager(transactionManager); this.pageSourceManager = new PageSourceManager(); @@ -353,7 +356,7 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, new PasswordAuthenticatorManager(), new EventListenerManager(), blockEncodingManager, - new NoOpSessionSupplier(), + new SessionPropertyDefaults(nodeInfo), typeRegistry); connectorManager.addConnectorFactory(globalSystemConnectorFactory); @@ -584,6 +587,12 @@ public MaterializedResult execute(@Language("SQL") String sql) @Override public MaterializedResult execute(Session session, @Language("SQL") String sql) + { + return executeWithPlan(session, sql, WarningCollector.NOOP).getMaterializedResult(); + } + + @Override + public MaterializedResultWithPlan executeWithPlan(Session session, String sql, WarningCollector warningCollector) { return inTransaction(session, transactionSession -> executeInternal(transactionSession, sql)); } @@ -600,7 +609,7 @@ public T inTransaction(Session session, Function transactionSess .execute(session, transactionSessionConsumer); } - private MaterializedResult executeInternal(Session session, @Language("SQL") String sql) + private MaterializedResultWithPlan executeInternal(Session session, @Language("SQL") String sql) { lock.readLock().lock(); try (Closer closer = Closer.create()) { @@ -615,7 +624,8 @@ private MaterializedResult executeInternal(Session session, @Language("SQL") Str .setQueryMaxSpillSize(nodeSpillConfig.getQueryMaxSpillPerNode()) .build(); - List drivers = createDrivers(session, sql, outputFactory, taskContext); + Plan plan = createPlan(session, sql, WarningCollector.NOOP); + List drivers = createDrivers(session, plan, outputFactory, taskContext); drivers.forEach(closer::register); boolean done = false; @@ -637,7 +647,7 @@ private MaterializedResult executeInternal(Session session, @Language("SQL") Str } verify(builder.get() != null, "Output operator was not created"); - return builder.get().build(); + return new MaterializedResultWithPlan(builder.get().build(), plan); } catch (IOException e) { throw new UncheckedIOException(e); @@ -660,13 +670,17 @@ public List createDrivers(@Language("SQL") String sql, OutputFactory out public List createDrivers(Session session, @Language("SQL") String sql, OutputFactory outputFactory, TaskContext taskContext) { - Plan plan = createPlan(session, sql); + Plan plan = createPlan(session, sql, WarningCollector.NOOP); + return createDrivers(session, plan, outputFactory, taskContext); + } + private List createDrivers(Session session, Plan plan, OutputFactory outputFactory, TaskContext taskContext) + { if (printPlan) { - System.out.println(PlanPrinter.textLogicalPlan(plan.getRoot(), plan.getTypes(), metadata.getFunctionRegistry(), statsCalculator, estimatedExchangesCostCalculator, session, 0, false)); + System.out.println(PlanPrinter.textLogicalPlan(plan.getRoot(), plan.getTypes(), metadata.getFunctionRegistry(), plan.getStatsAndCosts(), session, 0, false)); } - SubPlan subplan = planFragmenter.createSubPlans(session, metadata, nodePartitioningManager, plan, true); + SubPlan subplan = planFragmenter.createSubPlans(session, plan, true); if (!subplan.getChildren().isEmpty()) { throw new AssertionError("Expected subplan to have no children"); } @@ -735,7 +749,7 @@ public List createDrivers(Session session, @Language("SQL") String sql, checkState(driverFactoriesBySource.put(driverFactory.getSourceId().get(), driverFactory) == null); } else { - DriverContext driverContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver()).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver(), false).addDriverContext(); Driver driver = driverFactory.createDriver(driverContext); drivers.add(driver); } @@ -743,11 +757,13 @@ public List createDrivers(Session session, @Language("SQL") String sql, } // add sources to the drivers + ImmutableSet partitionedSources = ImmutableSet.copyOf(subplan.getFragment().getPartitionedSources()); for (TaskSource source : sources) { DriverFactory driverFactory = driverFactoriesBySource.get(source.getPlanNodeId()); checkState(driverFactory != null); + boolean partitioned = partitionedSources.contains(driverFactory.getSourceId().get()); for (ScheduledSplit split : source.getSplits()) { - DriverContext driverContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver()).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(driverFactory.getPipelineId(), driverFactory.isInputDriver(), driverFactory.isOutputDriver(), partitioned).addDriverContext(); Driver driver = driverFactory.createDriver(driverContext); driver.updateSource(new TaskSource(split.getPlanNodeId(), ImmutableSet.of(split), true)); drivers.add(driver); @@ -762,23 +778,23 @@ public List createDrivers(Session session, @Language("SQL") String sql, } @Override - public Plan createPlan(Session session, @Language("SQL") String sql) + public Plan createPlan(Session session, @Language("SQL") String sql, WarningCollector warningCollector) { - return createPlan(session, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED); + return createPlan(session, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, warningCollector); } - public Plan createPlan(Session session, @Language("SQL") String sql, LogicalPlanner.Stage stage) + public Plan createPlan(Session session, @Language("SQL") String sql, LogicalPlanner.Stage stage, WarningCollector warningCollector) { - return createPlan(session, sql, stage, true); + return createPlan(session, sql, stage, true, warningCollector); } - public Plan createPlan(Session session, @Language("SQL") String sql, LogicalPlanner.Stage stage, boolean forceSingleNode) + public Plan createPlan(Session session, @Language("SQL") String sql, LogicalPlanner.Stage stage, boolean forceSingleNode, WarningCollector warningCollector) { - Statement statement = unwrapExecuteStatement(sqlParser.createStatement(sql, createParsingOptions(session)), sqlParser, session); + PreparedQuery preparedQuery = new QueryPreparer(sqlParser).prepareQuery(session, sql); - assertFormattedSql(sqlParser, createParsingOptions(session), statement); + assertFormattedSql(sqlParser, createParsingOptions(session), preparedQuery.getStatement()); - return createPlan(session, sql, getPlanOptimizers(forceSingleNode), stage); + return createPlan(session, sql, getPlanOptimizers(forceSingleNode), stage, warningCollector); } public List getPlanOptimizers(boolean forceSingleNode) @@ -792,29 +808,25 @@ public List getPlanOptimizers(boolean forceSingleNode) featuresConfig, forceSingleNode, new MBeanExporter(new TestingMBeanServer()), + splitManager, + pageSourceManager, statsCalculator, costCalculator, estimatedExchangesCostCalculator, - new CostComparator(featuresConfig)).get(); + new CostComparator(featuresConfig), + taskCountEstimator).get(); } - public Plan createPlan(Session session, @Language("SQL") String sql, List optimizers) + public Plan createPlan(Session session, @Language("SQL") String sql, List optimizers, WarningCollector warningCollector) { - return createPlan(session, sql, optimizers, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED); + return createPlan(session, sql, optimizers, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, warningCollector); } - public Plan createPlan(Session session, @Language("SQL") String sql, List optimizers, LogicalPlanner.Stage stage) + public Plan createPlan(Session session, @Language("SQL") String sql, List optimizers, LogicalPlanner.Stage stage, WarningCollector warningCollector) { - Statement wrapped = sqlParser.createStatement(sql, createParsingOptions(session)); - Statement statement = unwrapExecuteStatement(wrapped, sqlParser, session); + PreparedQuery preparedQuery = new QueryPreparer(sqlParser).prepareQuery(session, sql); - List parameters = emptyList(); - if (wrapped instanceof Execute) { - parameters = ((Execute) wrapped).getParameters(); - } - validateParameters(statement, parameters); - - assertFormattedSql(sqlParser, createParsingOptions(session), statement); + assertFormattedSql(sqlParser, createParsingOptions(session), preparedQuery.getStatement()); PlanNodeIdAllocator idAllocator = new PlanNodeIdAllocator(); @@ -822,17 +834,16 @@ public Plan createPlan(Session session, @Language("SQL") String sql, List properties); Lock getExclusiveLock(); + + class MaterializedResultWithPlan + { + private final MaterializedResult materializedResult; + private final Plan queryPlan; + + public MaterializedResultWithPlan(MaterializedResult materializedResult, Plan queryPlan) + { + this.materializedResult = materializedResult; + this.queryPlan = queryPlan; + } + + public MaterializedResult getMaterializedResult() + { + return materializedResult; + } + + public Plan getQueryPlan() + { + return queryPlan; + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingAccessControlManager.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingAccessControlManager.java index d278708efeb54..68b9bbd7211bd 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/TestingAccessControlManager.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingAccessControlManager.java @@ -102,7 +102,7 @@ public void reset() } @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { if (shouldDenyPrivilege(userName, userName, SET_USER)) { denySetUser(principal, userName); diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingConnectorSession.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingConnectorSession.java index 7f556db403188..713783a210ad5 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/TestingConnectorSession.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingConnectorSession.java @@ -42,7 +42,6 @@ public class TestingConnectorSession public static final ConnectorSession SESSION = new TestingConnectorSession(ImmutableList.of()); private final String queryId; - private final String path; private final Identity identity; private final Optional source; private final TimeZoneKey timeZoneKey; @@ -55,12 +54,11 @@ public class TestingConnectorSession public TestingConnectorSession(List> properties) { - this("user", "path", Optional.of("test"), Optional.empty(), UTC_KEY, ENGLISH, System.currentTimeMillis(), properties, ImmutableMap.of(), new FeaturesConfig().isLegacyTimestamp()); + this("user", Optional.of("test"), Optional.empty(), UTC_KEY, ENGLISH, System.currentTimeMillis(), properties, ImmutableMap.of(), new FeaturesConfig().isLegacyTimestamp()); } public TestingConnectorSession( String user, - String path, Optional source, Optional traceToken, TimeZoneKey timeZoneKey, @@ -71,7 +69,6 @@ public TestingConnectorSession( boolean isLegacyTimestamp) { this.queryId = queryIdGenerator.createNextQueryId().toString(); - this.path = requireNonNull(path, "path is null"); this.identity = new Identity(requireNonNull(user, "user is null"), Optional.empty()); this.source = requireNonNull(source, "source is null"); this.traceToken = requireNonNull(traceToken, "traceToken is null"); @@ -95,12 +92,6 @@ public Optional getSource() return source; } - @Override - public String getPath() - { - return path; - } - @Override public Identity getIdentity() { @@ -137,12 +128,6 @@ public boolean isLegacyTimestamp() return isLegacyTimestamp; } - @Override - public boolean isLegacyRoundNBigint() - { - return false; - } - @Override public T getProperty(String name, Class type) { diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingEnvironment.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingEnvironment.java index b1df2165899df..91c7ab6778380 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/TestingEnvironment.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingEnvironment.java @@ -24,6 +24,7 @@ public class TestingEnvironment private TestingEnvironment() {} public static final TypeManager TYPE_MANAGER = new TypeRegistry(); + static { // wire TYPE_MANAGER with function registry new FunctionRegistry(TYPE_MANAGER, new BlockEncodingManager(TYPE_MANAGER), new FeaturesConfig()); diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingSession.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingSession.java index 73a6300106296..6d764c95a9a99 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/TestingSession.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingSession.java @@ -27,6 +27,7 @@ import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.security.Identity; import com.facebook.presto.spi.transaction.IsolationLevel; +import com.facebook.presto.spi.type.TimeZoneKey; import com.facebook.presto.sql.SqlPath; import com.google.common.collect.ImmutableSet; @@ -34,7 +35,6 @@ import static com.facebook.presto.connector.ConnectorId.createInformationSchemaConnectorId; import static com.facebook.presto.connector.ConnectorId.createSystemTablesConnectorId; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static java.util.Locale.ENGLISH; public final class TestingSession @@ -42,6 +42,15 @@ public final class TestingSession public static final String TESTING_CATALOG = "testing_catalog"; private static final QueryIdGenerator queryIdGenerator = new QueryIdGenerator(); + /* + * Pacific/Apia + * - has DST (e.g. January 2017) + * - had DST change at midnight (on Sunday, 26 September 2010, 00:00:00 clocks were turned forward 1 hour) + * - had offset change since 1970 (offset in January 1970: -11:00, offset in January 2017: +14:00, offset in June 2017: +13:00) + * - a whole day was skipped during policy change (on Friday, 30 December 2011, 00:00:00 clocks were turned forward 24 hours) + */ + public static final TimeZoneKey DEFAULT_TIME_ZONE_KEY = TimeZoneKey.getTimeZoneKey("Pacific/Apia"); + private TestingSession() {} public static SessionBuilder testSessionBuilder() @@ -58,7 +67,7 @@ public static SessionBuilder testSessionBuilder(SessionPropertyManager sessionPr .setCatalog("catalog") .setSchema("schema") .setPath(new SqlPath(Optional.of("path"))) - .setTimeZoneKey(UTC_KEY) + .setTimeZoneKey(DEFAULT_TIME_ZONE_KEY) .setLocale(ENGLISH) .setRemoteUserAddress("address") .setUserAgent("agent"); diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollector.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollector.java new file mode 100644 index 0000000000000..ee4aa6745dc22 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollector.java @@ -0,0 +1,83 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.testing; + +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +@ThreadSafe +public class TestingWarningCollector + implements WarningCollector +{ + @GuardedBy("this") + private final Map warnings = new LinkedHashMap<>(); + private final WarningCollectorConfig config; + + private final boolean addWarnings; + private final AtomicInteger warningCode = new AtomicInteger(); + + public TestingWarningCollector(WarningCollectorConfig config, TestingWarningCollectorConfig testConfig) + { + this.config = requireNonNull(config, "config is null"); + requireNonNull(testConfig, "testConfig is null"); + addWarnings = testConfig.getAddWarnings(); + // Start warning codes at 1 + for (int warningCode = 1; warningCode <= testConfig.getPreloadedWarnings(); warningCode++) { + add(createTestWarning(warningCode)); + } + warningCode.set(testConfig.getPreloadedWarnings()); + } + + @Override + public synchronized void add(PrestoWarning warning) + { + requireNonNull(warning, "warning is null"); + if (warnings.size() < config.getMaxWarnings()) { + warnings.putIfAbsent(warning.getWarningCode(), warning); + } + } + + @Override + public synchronized List getWarnings() + { + if (addWarnings) { + add(createTestWarning(warningCode.incrementAndGet())); + } + return ImmutableList.copyOf(warnings.values()); + } + + @VisibleForTesting + public static PrestoWarning createTestWarning(int code) + { + // format string below is a hack to construct a vendor specific SQLState value + // 01 is the class of warning code and 5 is the first allowed vendor defined prefix character + // See the SQL Standard ISO_IEC_9075-2E_2016 24.1: SQLState for more information + return new PrestoWarning(new WarningCode(code, format("015%02d", code % 100)), "Test warning " + code); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorConfig.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorConfig.java new file mode 100644 index 0000000000000..ff4c02a9f43d7 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorConfig.java @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.testing; + +import io.airlift.configuration.Config; +import io.airlift.configuration.ConfigDescription; + +import static com.google.common.base.Preconditions.checkArgument; + +public class TestingWarningCollectorConfig +{ + private int preloadedWarnings; + private boolean addWarnings; + + @Config("testing-warning-collector.preloaded-warnings") + @ConfigDescription("Preloads warning collector with test warnings") + public TestingWarningCollectorConfig setPreloadedWarnings(int preloadedWarnings) + { + checkArgument(preloadedWarnings >= 0, "preloadedWarnings must be >= 0"); + this.preloadedWarnings = preloadedWarnings; + return this; + } + + public int getPreloadedWarnings() + { + return preloadedWarnings; + } + + @Config("testing-warning-collector.add-warnings") + @ConfigDescription("Adds a warning each time getWarnings is called") + public TestingWarningCollectorConfig setAddWarnings(boolean addWarnings) + { + this.addWarnings = addWarnings; + return this; + } + + public boolean getAddWarnings() + { + return addWarnings; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorFactory.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorFactory.java new file mode 100644 index 0000000000000..09f4ad2e8878f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorFactory.java @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.testing; + +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.execution.warnings.WarningCollectorFactory; + +import static java.util.Objects.requireNonNull; + +public class TestingWarningCollectorFactory + implements WarningCollectorFactory +{ + private final WarningCollectorConfig config; + private final TestingWarningCollectorConfig testConfig; + + public TestingWarningCollectorFactory(WarningCollectorConfig config, TestingWarningCollectorConfig testConfig) + { + this.config = requireNonNull(config, "config is null"); + this.testConfig = requireNonNull(testConfig, "testConfig is null"); + } + + @Override + public WarningCollector create() + { + return new TestingWarningCollector(config, testConfig); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorModule.java b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorModule.java new file mode 100644 index 0000000000000..265d7c1585404 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestingWarningCollectorModule.java @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.testing; + +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.execution.warnings.WarningCollectorFactory; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Provides; +import com.google.inject.Singleton; + +import static io.airlift.configuration.ConfigBinder.configBinder; +import static java.util.Objects.requireNonNull; + +public class TestingWarningCollectorModule + implements Module +{ + @Override + public void configure(Binder binder) + { + configBinder(binder).bindConfig(WarningCollectorConfig.class); + configBinder(binder).bindConfig(TestingWarningCollectorConfig.class); + } + + @Provides + @Singleton + public WarningCollectorFactory createWarningCollectorFactory(WarningCollectorConfig config, TestingWarningCollectorConfig testConfig) + { + requireNonNull(config, "config is null"); + requireNonNull(testConfig, "testConfig is null"); + return () -> new TestingWarningCollector(config, testConfig); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/testing/TestngUtils.java b/presto-main/src/main/java/com/facebook/presto/testing/TestngUtils.java index 6a059b58aa133..1c89e72664956 100644 --- a/presto-main/src/main/java/com/facebook/presto/testing/TestngUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/testing/TestngUtils.java @@ -24,11 +24,11 @@ private TestngUtils() {} { return Collector.of( ArrayList::new, - (builder, entry) -> builder.add(new Object[]{entry}), + (builder, entry) -> builder.add(new Object[] {entry}), (left, right) -> { left.addAll(right); return left; }, - builder -> builder.toArray(new Object[][]{})); + builder -> builder.toArray(new Object[][] {})); } } diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java b/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java index c2f8eebd53bb5..998fdcc880aa0 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/InMemoryTransactionManager.java @@ -249,7 +249,7 @@ public void trySetActive(TransactionId transactionId) @Override public void trySetInactive(TransactionId transactionId) { - tryGetTransactionMetadata(transactionId).ifPresent(TransactionMetadata::setInActive); + tryGetTransactionMetadata(transactionId).ifPresent(TransactionMetadata::setInactive); } private TransactionMetadata getTransactionMetadata(TransactionId transactionId) @@ -344,7 +344,7 @@ public void setActive() idleStartTime.set(null); } - public void setInActive() + public void setInactive() { idleStartTime.set(System.nanoTime()); } diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionBuilder.java b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionBuilder.java index d73d2d611bb0a..9a86569f38f60 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionBuilder.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionBuilder.java @@ -152,7 +152,7 @@ public T execute(Session session, Function callback) return result; } finally { - if (managedTransaction) { + if (managedTransaction && transactionManager.transactionExists(transactionSession.getTransactionId().get())) { if (success) { getFutureValue(transactionManager.asyncCommit(transactionSession.getTransactionId().get())); } diff --git a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java index 2f1905efdf2ed..4483ba2f46e0a 100644 --- a/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java +++ b/presto-main/src/main/java/com/facebook/presto/transaction/TransactionManager.java @@ -13,8 +13,10 @@ */ package com.facebook.presto.transaction; +import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; import com.facebook.presto.metadata.CatalogMetadata; +import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.transaction.IsolationLevel; import com.google.common.util.concurrent.ListenableFuture; @@ -30,6 +32,11 @@ public interface TransactionManager boolean transactionExists(TransactionId transactionId); + default boolean isAutoCommit(TransactionId transactionId) + { + return getTransactionInfo(transactionId).isAutoCommitContext(); + } + TransactionInfo getTransactionInfo(TransactionId transactionId); List getAllTransactionInfos(); @@ -61,4 +68,20 @@ public interface TransactionManager ListenableFuture asyncAbort(TransactionId transactionId); void fail(TransactionId transactionId); + + default void activateTransaction(Session session, boolean transactionControl, AccessControl accessControl) + { + if (!session.getTransactionId().isPresent()) { + return; + } + + // reactivate existing transaction + TransactionId transactionId = session.getTransactionId().get(); + if (transactionControl) { + trySetActive(transactionId); + } + else { + checkAndSetActive(transactionId); + } + } } diff --git a/presto-main/src/main/java/com/facebook/presto/type/BigintOperators.java b/presto-main/src/main/java/com/facebook/presto/type/BigintOperators.java index 63132870586fc..9195b5d3bacf1 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/BigintOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/BigintOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -47,6 +51,7 @@ import static com.facebook.presto.spi.function.OperatorType.SATURATED_FLOOR_CAST; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.BigintType.BIGINT; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Float.floatToRawIntBits; import static java.lang.Math.toIntExact; @@ -135,14 +140,16 @@ public static long negate(@SqlType(StandardTypes.BIGINT) long value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.BIGINT) long left, @SqlType(StandardTypes.BIGINT) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.BIGINT) long left, @SqlType(StandardTypes.BIGINT) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.BIGINT) long left, @SqlType(StandardTypes.BIGINT) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.BIGINT) long left, @SqlType(StandardTypes.BIGINT) long right) { return left != right; } @@ -277,20 +284,39 @@ public static long hashCode(@SqlType(StandardTypes.BIGINT) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.BIGINT) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.BIGINT) long right, - @IsNull boolean rightNull) + public static class BigintDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.BIGINT) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.BIGINT) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.BIGINT, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.BIGINT, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(BIGINT.getLong(left, leftPosition), BIGINT.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(XX_HASH_64) diff --git a/presto-main/src/main/java/com/facebook/presto/type/BooleanOperators.java b/presto-main/src/main/java/com/facebook/presto/type/BooleanOperators.java index 7f1dced74a8a0..e684186460305 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/BooleanOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/BooleanOperators.java @@ -13,10 +13,14 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import io.airlift.slice.Slice; @@ -33,6 +37,7 @@ import static com.facebook.presto.spi.function.OperatorType.LESS_THAN; import static com.facebook.presto.spi.function.OperatorType.LESS_THAN_OR_EQUAL; import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static java.lang.Float.floatToRawIntBits; import static java.nio.charset.StandardCharsets.US_ASCII; @@ -47,14 +52,16 @@ private BooleanOperators() @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.BOOLEAN) boolean left, @SqlType(StandardTypes.BOOLEAN) boolean right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.BOOLEAN) boolean left, @SqlType(StandardTypes.BOOLEAN) boolean right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.BOOLEAN) boolean left, @SqlType(StandardTypes.BOOLEAN) boolean right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.BOOLEAN) boolean left, @SqlType(StandardTypes.BOOLEAN) boolean right) { return left != right; } @@ -166,19 +173,38 @@ public static boolean not(@SqlType(StandardTypes.BOOLEAN) boolean value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.BOOLEAN) boolean left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.BOOLEAN) boolean right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class BooleanDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.BOOLEAN) boolean left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.BOOLEAN) boolean right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.BOOLEAN, nativeContainerType = boolean.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.BOOLEAN, nativeContainerType = boolean.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(BOOLEAN.getBoolean(left, leftPosition), BOOLEAN.getBoolean(right, rightPosition)); } - return notEqual(left, right); } } diff --git a/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java b/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java index 972e390beebd4..88a582752eaf3 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/CharOperators.java @@ -13,9 +13,13 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import io.airlift.slice.Slice; @@ -41,7 +45,8 @@ private CharOperators() {} @LiteralParameters("x") @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType("char(x)") Slice left, @SqlType("char(x)") Slice right) + @SqlNullable + public static Boolean equal(@SqlType("char(x)") Slice left, @SqlType("char(x)") Slice right) { return left.equals(right); } @@ -49,7 +54,8 @@ public static boolean equal(@SqlType("char(x)") Slice left, @SqlType("char(x)") @LiteralParameters("x") @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType("char(x)") Slice left, @SqlType("char(x)") Slice right) + @SqlNullable + public static Boolean notEqual(@SqlType("char(x)") Slice left, @SqlType("char(x)") Slice right) { return !left.equals(right); } @@ -110,22 +116,48 @@ public static long xxHash64(@SqlType("char(x)") Slice slice) return XxHash64.hash(slice); } - @LiteralParameters("x") @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType("char(x)") Slice left, - @IsNull boolean leftNull, - @SqlType("char(x)") Slice right, - @IsNull boolean rightNull) + public static class CharDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @LiteralParameters("x") + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType("char(x)") Slice left, + @IsNull boolean leftNull, + @SqlType("char(x)") Slice right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @LiteralParameters("x") + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = "char(x)", nativeContainerType = Slice.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = "char(x)", nativeContainerType = Slice.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + + int leftLength = left.getSliceLength(leftPosition); + int rightLength = right.getSliceLength(rightPosition); + if (leftLength != rightLength) { + return true; + } + return !left.equals(leftPosition, 0, right, rightPosition, 0, leftLength); } - return notEqual(left, right); } @LiteralParameters("x") diff --git a/presto-main/src/main/java/com/facebook/presto/type/ColorOperators.java b/presto-main/src/main/java/com/facebook/presto/type/ColorOperators.java index 042519eb991a2..2008888025b98 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/ColorOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/ColorOperators.java @@ -13,8 +13,12 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; @@ -23,6 +27,7 @@ import static com.facebook.presto.spi.function.OperatorType.INDETERMINATE; import static com.facebook.presto.spi.function.OperatorType.IS_DISTINCT_FROM; import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; +import static com.facebook.presto.type.ColorType.COLOR; public final class ColorOperators { @@ -32,14 +37,16 @@ private ColorOperators() @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(ColorType.NAME) long left, @SqlType(ColorType.NAME) long right) + @SqlNullable + public static Boolean equal(@SqlType(ColorType.NAME) long left, @SqlType(ColorType.NAME) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(ColorType.NAME) long left, @SqlType(ColorType.NAME) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(ColorType.NAME) long left, @SqlType(ColorType.NAME) long right) { return left != right; } @@ -52,20 +59,39 @@ public static long hashCode(@SqlType(ColorType.NAME) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(ColorType.NAME) long left, - @IsNull boolean leftNull, - @SqlType(ColorType.NAME) long right, - @IsNull boolean rightNull) + public static class ColorDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(ColorType.NAME) long left, + @IsNull boolean leftNull, + @SqlType(ColorType.NAME) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = ColorType.NAME, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = ColorType.NAME, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(COLOR.getLong(left, leftPosition), COLOR.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/DateOperators.java b/presto-main/src/main/java/com/facebook/presto/type/DateOperators.java index ba296079d5507..253f2ee14479b 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DateOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DateOperators.java @@ -15,10 +15,14 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractIntType; import com.facebook.presto.spi.type.StandardTypes; @@ -42,6 +46,7 @@ import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; import static com.facebook.presto.spi.type.DateTimeEncoding.packDateTimeWithZone; +import static com.facebook.presto.spi.type.DateType.DATE; import static com.facebook.presto.util.DateTimeUtils.parseDate; import static com.facebook.presto.util.DateTimeUtils.printDate; import static com.facebook.presto.util.DateTimeZoneIndex.getChronology; @@ -56,14 +61,16 @@ private DateOperators() @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.DATE) long left, @SqlType(StandardTypes.DATE) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.DATE) long left, @SqlType(StandardTypes.DATE) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.DATE) long left, @SqlType(StandardTypes.DATE) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.DATE) long left, @SqlType(StandardTypes.DATE) long right) { return left != right; } @@ -163,20 +170,39 @@ public static long hashCode(@SqlType(StandardTypes.DATE) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.DATE) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.DATE) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class DateDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.DATE) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.DATE) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.DATE, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.DATE, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(DATE.getLong(left, leftPosition), DATE.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/DecimalCasts.java b/presto-main/src/main/java/com/facebook/presto/type/DecimalCasts.java index 65b0499a0a9d0..615811434dfc8 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DecimalCasts.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DecimalCasts.java @@ -14,7 +14,6 @@ package com.facebook.presto.type; import com.facebook.presto.annotation.UsedByGeneratedCode; -import com.facebook.presto.metadata.PolymorphicScalarFunctionBuilder; import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlScalarFunction; import com.facebook.presto.spi.PrestoException; @@ -101,7 +100,7 @@ public final class DecimalCasts public static final SqlScalarFunction DECIMAL_TO_VARCHAR_CAST = castFunctionFromDecimalTo(parseTypeSignature("varchar(x)", ImmutableSet.of("x")), "shortDecimalToVarchar", "longDecimalToVarchar"); public static final SqlScalarFunction VARCHAR_TO_DECIMAL_CAST = castFunctionToDecimalFrom(parseTypeSignature("varchar(x)", ImmutableSet.of("x")), "varcharToShortDecimal", "varcharToLongDecimal"); public static final SqlScalarFunction DECIMAL_TO_JSON_CAST = castFunctionFromDecimalTo(JSON.getTypeSignature(), "shortDecimalToJson", "longDecimalToJson"); - public static final SqlScalarFunction JSON_TO_DECIMAL_CAST = castFunctionToDecimalFromBuilder(JSON.getTypeSignature(), "jsonToShortDecimal", "jsonToLongDecimal").nullableResult(true).build(); + public static final SqlScalarFunction JSON_TO_DECIMAL_CAST = castFunctionToDecimalFromBuilder(JSON.getTypeSignature(), true, "jsonToShortDecimal", "jsonToLongDecimal"); /** * Powers of 10 which can be represented exactly in double. @@ -135,29 +134,30 @@ private static SqlScalarFunction castFunctionFromDecimalTo(TypeSignature to, Str return SqlScalarFunction.builder(DecimalCasts.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods(methodNames) - .withExtraParameters((context) -> { - long precision = context.getLiteral("precision"); - long scale = context.getLiteral("scale"); - Number tenToScale; - if (isShortDecimal(context.getParameterTypes().get(0))) { - tenToScale = longTenToNth(intScale(scale)); - } - else { - tenToScale = bigIntegerTenToNth(intScale(scale)); - } - return ImmutableList.of(precision, scale, tenToScale); - })) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods(methodNames) + .withExtraParameters((context) -> { + long precision = context.getLiteral("precision"); + long scale = context.getLiteral("scale"); + Number tenToScale; + if (isShortDecimal(context.getParameterTypes().get(0))) { + tenToScale = longTenToNth(intScale(scale)); + } + else { + tenToScale = bigIntegerTenToNth(intScale(scale)); + } + return ImmutableList.of(precision, scale, tenToScale); + }))) .build(); } private static SqlScalarFunction castFunctionToDecimalFrom(TypeSignature from, String... methodNames) { - return castFunctionToDecimalFromBuilder(from, methodNames).build(); + return castFunctionToDecimalFromBuilder(from, false, methodNames); } - private static PolymorphicScalarFunctionBuilder castFunctionToDecimalFromBuilder(TypeSignature from, String... methodNames) + private static SqlScalarFunction castFunctionToDecimalFromBuilder(TypeSignature from, boolean nullableResult, String... methodNames) { Signature signature = Signature.builder() .kind(SCALAR) @@ -168,19 +168,21 @@ private static PolymorphicScalarFunctionBuilder castFunctionToDecimalFromBuilder return SqlScalarFunction.builder(DecimalCasts.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods(methodNames) - .withExtraParameters((context) -> { - DecimalType resultType = (DecimalType) context.getReturnType(); - Number tenToScale; - if (isShortDecimal(resultType)) { - tenToScale = longTenToNth(resultType.getScale()); - } - else { - tenToScale = bigIntegerTenToNth(resultType.getScale()); - } - return ImmutableList.of(resultType.getPrecision(), resultType.getScale(), tenToScale); - })); + .choice(choice -> choice + .nullableResult(nullableResult) + .implementation(methodsGroup -> methodsGroup + .methods(methodNames) + .withExtraParameters((context) -> { + DecimalType resultType = (DecimalType) context.getReturnType(); + Number tenToScale; + if (isShortDecimal(resultType)) { + tenToScale = longTenToNth(resultType.getScale()); + } + else { + tenToScale = bigIntegerTenToNth(resultType.getScale()); + } + return ImmutableList.of(resultType.getPrecision(), resultType.getScale(), tenToScale); + }))).build(); } private DecimalCasts() {} diff --git a/presto-main/src/main/java/com/facebook/presto/type/DecimalInequalityOperators.java b/presto-main/src/main/java/com/facebook/presto/type/DecimalInequalityOperators.java index a3f6d0de40845..3e28582039d44 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DecimalInequalityOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DecimalInequalityOperators.java @@ -18,16 +18,20 @@ import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.SqlScalarFunction; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.function.OperatorType; import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic; import com.google.common.collect.ImmutableSet; import io.airlift.slice.Slice; import java.lang.invoke.MethodHandle; +import java.util.Optional; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.PolymorphicScalarFunctionBuilder.constant; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.BLOCK_AND_POSITION; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_NULL_FLAG; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.function.OperatorType.BETWEEN; @@ -43,6 +47,8 @@ import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.compare; import static com.facebook.presto.util.Reflection.methodHandle; import static com.google.common.base.Throwables.throwIfInstanceOf; +import static io.airlift.slice.SizeOf.SIZE_OF_LONG; +import static java.util.Arrays.asList; public class DecimalInequalityOperators { @@ -55,12 +61,12 @@ public class DecimalInequalityOperators private static final MethodHandle IS_RESULT_GREATER_THAN = methodHandle(DecimalInequalityOperators.class, "isResultGreaterThan", int.class); private static final MethodHandle IS_RESULT_GREATER_THAN_OR_EQUAL = methodHandle(DecimalInequalityOperators.class, "isResultGreaterThanOrEqual", int.class); - public static final SqlScalarFunction DECIMAL_EQUAL_OPERATOR = binaryOperator(EQUAL, IS_RESULT_EQUAL); - public static final SqlScalarFunction DECIMAL_NOT_EQUAL_OPERATOR = binaryOperator(NOT_EQUAL, IS_RESULT_NOT_EQUAL); - public static final SqlScalarFunction DECIMAL_LESS_THAN_OPERATOR = binaryOperator(LESS_THAN, IS_RESULT_LESS_THAN); - public static final SqlScalarFunction DECIMAL_LESS_THAN_OR_EQUAL_OPERATOR = binaryOperator(LESS_THAN_OR_EQUAL, IS_RESULT_LESS_THAN_OR_EQUAL); - public static final SqlScalarFunction DECIMAL_GREATER_THAN_OPERATOR = binaryOperator(GREATER_THAN, IS_RESULT_GREATER_THAN); - public static final SqlScalarFunction DECIMAL_GREATER_THAN_OR_EQUAL_OPERATOR = binaryOperator(GREATER_THAN_OR_EQUAL, IS_RESULT_GREATER_THAN_OR_EQUAL); + public static final SqlScalarFunction DECIMAL_EQUAL_OPERATOR = equalityOperator(EQUAL, IS_RESULT_EQUAL); + public static final SqlScalarFunction DECIMAL_NOT_EQUAL_OPERATOR = equalityOperator(NOT_EQUAL, IS_RESULT_NOT_EQUAL); + public static final SqlScalarFunction DECIMAL_LESS_THAN_OPERATOR = comparisonOperator(LESS_THAN, IS_RESULT_LESS_THAN); + public static final SqlScalarFunction DECIMAL_LESS_THAN_OR_EQUAL_OPERATOR = comparisonOperator(LESS_THAN_OR_EQUAL, IS_RESULT_LESS_THAN_OR_EQUAL); + public static final SqlScalarFunction DECIMAL_GREATER_THAN_OPERATOR = comparisonOperator(GREATER_THAN, IS_RESULT_GREATER_THAN); + public static final SqlScalarFunction DECIMAL_GREATER_THAN_OR_EQUAL_OPERATOR = comparisonOperator(GREATER_THAN_OR_EQUAL, IS_RESULT_GREATER_THAN_OR_EQUAL); public static final SqlScalarFunction DECIMAL_BETWEEN_OPERATOR = betweenOperator(); public static final SqlScalarFunction DECIMAL_DISTINCT_FROM_OPERATOR = distinctOperator(); @@ -115,23 +121,49 @@ private static PolymorphicScalarFunctionBuilder makeBinaryOperatorFunctionBuilde .deterministic(true); } - private static SqlScalarFunction binaryOperator(OperatorType operatorType, MethodHandle getResultMethodHandle) + private static SqlScalarFunction equalityOperator(OperatorType operatorType, MethodHandle getResultMethodHandle) { return makeBinaryOperatorFunctionBuilder(operatorType) - .implementation(b -> b - .methods("opShortShort", "opLongLong") - .withExtraParameters(constant(getResultMethodHandle))) + .choice(choice -> choice + .nullableResult(true) + .implementation(methodsGroup -> methodsGroup + .methods("boxedShortShort", "boxedLongLong") + .withExtraParameters(constant(getResultMethodHandle)))) + .build(); + } + + private static SqlScalarFunction comparisonOperator(OperatorType operatorType, MethodHandle getResultMethodHandle) + { + return makeBinaryOperatorFunctionBuilder(operatorType) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("primitiveShortShort", "primitiveLongLong") + .withExtraParameters(constant(getResultMethodHandle)))) .build(); } @UsedByGeneratedCode - public static boolean opShortShort(long a, long b, MethodHandle getResultMethodHandle) + public static Boolean boxedShortShort(long a, long b, MethodHandle getResultMethodHandle) { return invokeGetResult(getResultMethodHandle, Long.compare(a, b)); } @UsedByGeneratedCode - public static boolean opLongLong(Slice left, Slice right, MethodHandle getResultMethodHandle) + public static Boolean boxedLongLong(Slice left, Slice right, MethodHandle getResultMethodHandle) + { + return invokeGetResult(getResultMethodHandle, compare(left, right)); + } + + @UsedByGeneratedCode + //TODO: remove when introducing nullable comparisons (<=, <, >, >=) + public static boolean primitiveShortShort(long a, long b, MethodHandle getResultMethodHandle) + { + return invokeGetResult(getResultMethodHandle, Long.compare(a, b)); + } + + @UsedByGeneratedCode + //TODO: remove when introducing nullable comparisons (<=, <, >, >=) + public static boolean primitiveLongLong(Slice left, Slice right, MethodHandle getResultMethodHandle) { return invokeGetResult(getResultMethodHandle, compare(left, right)); } @@ -139,14 +171,56 @@ public static boolean opLongLong(Slice left, Slice right, MethodHandle getResult private static SqlScalarFunction distinctOperator() { return makeBinaryOperatorFunctionBuilder(IS_DISTINCT_FROM) - .argumentProperties( - valueTypeArgumentProperty(USE_NULL_FLAG), - valueTypeArgumentProperty(USE_NULL_FLAG)) - .implementation(b -> b - .methods("distinctShortShort", "distinctLongLong")) + .choice(choice -> choice + .argumentProperties( + valueTypeArgumentProperty(USE_NULL_FLAG), + valueTypeArgumentProperty(USE_NULL_FLAG)) + .implementation(methodsGroup -> methodsGroup + .methods("distinctShortShort", "distinctLongLong"))) + .choice(choice -> choice + .argumentProperties( + valueTypeArgumentProperty(BLOCK_AND_POSITION), + valueTypeArgumentProperty(BLOCK_AND_POSITION)) + .implementation(methodsGroup -> methodsGroup + .methodWithExplicitJavaTypes("distinctBlockPositionLongLong", + asList(Optional.of(Slice.class), Optional.of(Slice.class))) + .methodWithExplicitJavaTypes("distinctBlockPositionShortShort", + asList(Optional.of(long.class), Optional.of(long.class))))) .build(); } + @UsedByGeneratedCode + public static boolean distinctBlockPositionLongLong(Block left, int leftPosition, Block right, int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + + long leftLow = left.getLong(leftPosition, 0); + long leftHigh = left.getLong(leftPosition, SIZE_OF_LONG); + long rightLow = left.getLong(rightPosition, 0); + long rightHigh = left.getLong(rightPosition, SIZE_OF_LONG); + return UnscaledDecimal128Arithmetic.compare(leftLow, leftHigh, rightLow, rightHigh) != 0; + } + + @UsedByGeneratedCode + public static boolean distinctBlockPositionShortShort(Block left, int leftPosition, Block right, int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + + long leftValue = left.getLong(leftPosition, 0); + long rightValue = right.getLong(rightPosition, 0); + return Long.compare(leftValue, rightValue) != 0; + } + @UsedByGeneratedCode public static boolean distinctShortShort(long left, boolean leftNull, long right, boolean rightNull) { @@ -156,7 +230,7 @@ public static boolean distinctShortShort(long left, boolean leftNull, long right if (leftNull) { return false; } - return opShortShort(left, right, IS_RESULT_NOT_EQUAL); + return primitiveShortShort(left, right, IS_RESULT_NOT_EQUAL); } @UsedByGeneratedCode @@ -168,7 +242,7 @@ public static boolean distinctLongLong(Slice left, boolean leftNull, Slice right if (leftNull) { return false; } - return opLongLong(left, right, IS_RESULT_NOT_EQUAL); + return primitiveLongLong(left, right, IS_RESULT_NOT_EQUAL); } private static boolean invokeGetResult(MethodHandle getResultMethodHandle, int comparisonResult) @@ -194,8 +268,9 @@ private static SqlScalarFunction betweenOperator() return SqlScalarFunction.builder(DecimalInequalityOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods("betweenShortShortShort", "betweenLongLongLong")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("betweenShortShortShort", "betweenLongLongLong"))) .build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/type/DecimalOperators.java b/presto-main/src/main/java/com/facebook/presto/type/DecimalOperators.java index ff93ea0d79376..29d94c36a8338 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DecimalOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DecimalOperators.java @@ -96,12 +96,13 @@ private static SqlScalarFunction decimalAddOperator() return SqlScalarFunction.builder(DecimalOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods("addShortShortShort") - .withExtraParameters(DecimalOperators::calculateShortRescaleParameters)) - .implementation(b -> b - .methods("addShortShortLong", "addLongLongLong", "addShortLongLong", "addLongShortLong") - .withExtraParameters(DecimalOperators::calculateLongRescaleParameters)) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("addShortShortShort") + .withExtraParameters(DecimalOperators::calculateShortRescaleParameters)) + .implementation(methodsGroup -> methodsGroup + .methods("addShortShortLong", "addLongLongLong", "addShortLongLong", "addLongShortLong") + .withExtraParameters(DecimalOperators::calculateLongRescaleParameters))) .build(); } @@ -177,12 +178,13 @@ private static SqlScalarFunction decimalSubtractOperator() return SqlScalarFunction.builder(DecimalOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods("subtractShortShortShort") - .withExtraParameters(DecimalOperators::calculateShortRescaleParameters)) - .implementation(b -> b - .methods("subtractShortShortLong", "subtractLongLongLong", "subtractShortLongLong", "subtractLongShortLong") - .withExtraParameters(DecimalOperators::calculateLongRescaleParameters)) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("subtractShortShortShort") + .withExtraParameters(DecimalOperators::calculateShortRescaleParameters)) + .implementation(methodsGroup -> methodsGroup + .methods("subtractShortShortLong", "subtractLongLongLong", "subtractShortLongLong", "subtractLongShortLong") + .withExtraParameters(DecimalOperators::calculateLongRescaleParameters))) .build(); } @@ -254,7 +256,9 @@ private static SqlScalarFunction decimalMultiplyOperator() return SqlScalarFunction.builder(DecimalOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b.methods("multiplyShortShortShort", "multiplyShortShortLong", "multiplyLongLongLong", "multiplyShortLongLong", "multiplyLongShortLong")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("multiplyShortShortShort", "multiplyShortShortLong", "multiplyLongLongLong", "multiplyShortLongLong", "multiplyLongShortLong"))) .build(); } @@ -317,9 +321,10 @@ private static SqlScalarFunction decimalDivideOperator() return SqlScalarFunction.builder(DecimalOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods("divideShortShortShort", "divideShortLongShort", "divideLongShortShort", "divideShortShortLong", "divideLongLongLong", "divideShortLongLong", "divideLongShortLong") - .withExtraParameters(DecimalOperators::divideRescaleFactor)) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("divideShortShortShort", "divideShortLongShort", "divideLongShortShort", "divideShortShortLong", "divideLongLongLong", "divideShortLongLong", "divideLongShortLong") + .withExtraParameters(DecimalOperators::divideRescaleFactor))) .build(); } @@ -457,9 +462,10 @@ public static SqlScalarFunction modulusScalarFunction(Signature signature) return SqlScalarFunction.builder(DecimalOperators.class) .signature(signature) .deterministic(true) - .implementation(b -> b - .methods("modulusShortShortShort", "modulusLongLongLong", "modulusShortLongLong", "modulusShortLongShort", "modulusLongShortShort", "modulusLongShortLong") - .withExtraParameters(DecimalOperators::modulusRescaleParameters)) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("modulusShortShortShort", "modulusLongLongLong", "modulusShortLongLong", "modulusShortLongShort", "modulusLongShortShort", "modulusLongShortLong") + .withExtraParameters(DecimalOperators::modulusRescaleParameters))) .build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/type/DecimalSaturatedFloorCasts.java b/presto-main/src/main/java/com/facebook/presto/type/DecimalSaturatedFloorCasts.java index d30279fda4b71..0b42f876345c0 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DecimalSaturatedFloorCasts.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DecimalSaturatedFloorCasts.java @@ -50,15 +50,16 @@ private DecimalSaturatedFloorCasts() {} .returnType(parseTypeSignature("decimal(result_precision,result_scale)", ImmutableSet.of("result_precision", "result_scale"))) .build()) .deterministic(true) - .implementation(b -> b - .methods("shortDecimalToShortDecimal", "shortDecimalToLongDecimal", "longDecimalToShortDecimal", "longDecimalToLongDecimal") - .withExtraParameters((context) -> { - int sourcePrecision = toIntExact(context.getLiteral("source_precision")); - int sourceScale = toIntExact(context.getLiteral("source_scale")); - int resultPrecision = toIntExact(context.getLiteral("result_precision")); - int resultScale = toIntExact(context.getLiteral("result_scale")); - return ImmutableList.of(sourcePrecision, sourceScale, resultPrecision, resultScale); - })) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("shortDecimalToShortDecimal", "shortDecimalToLongDecimal", "longDecimalToShortDecimal", "longDecimalToLongDecimal") + .withExtraParameters((context) -> { + int sourcePrecision = toIntExact(context.getLiteral("source_precision")); + int sourceScale = toIntExact(context.getLiteral("source_scale")); + int resultPrecision = toIntExact(context.getLiteral("result_precision")); + int resultScale = toIntExact(context.getLiteral("result_scale")); + return ImmutableList.of(sourcePrecision, sourceScale, resultPrecision, resultScale); + }))) .build(); @UsedByGeneratedCode @@ -120,12 +121,13 @@ private static SqlScalarFunction decimalToGenericIntegerTypeSaturatedFloorCast(T .returnType(type.getTypeSignature()) .build()) .deterministic(true) - .implementation(b -> b - .methods("shortDecimalToGenericIntegerType", "longDecimalToGenericIntegerType") - .withExtraParameters((context) -> { - int sourceScale = toIntExact(context.getLiteral("source_scale")); - return ImmutableList.of(sourceScale, minValue, maxValue); - })) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("shortDecimalToGenericIntegerType", "longDecimalToGenericIntegerType") + .withExtraParameters((context) -> { + int sourceScale = toIntExact(context.getLiteral("source_scale")); + return ImmutableList.of(sourceScale, minValue, maxValue); + }))) .build(); } @@ -169,13 +171,14 @@ private static SqlScalarFunction genericIntegerTypeToDecimalSaturatedFloorCast(T .returnType(parseTypeSignature("decimal(result_precision,result_scale)", ImmutableSet.of("result_precision", "result_scale"))) .build()) .deterministic(true) - .implementation(b -> b - .methods("genericIntegerTypeToShortDecimal", "genericIntegerTypeToLongDecimal") - .withExtraParameters((context) -> { - int resultPrecision = toIntExact(context.getLiteral("result_precision")); - int resultScale = toIntExact(context.getLiteral("result_scale")); - return ImmutableList.of(resultPrecision, resultScale); - })) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("genericIntegerTypeToShortDecimal", "genericIntegerTypeToLongDecimal") + .withExtraParameters((context) -> { + int resultPrecision = toIntExact(context.getLiteral("result_precision")); + int resultScale = toIntExact(context.getLiteral("result_scale")); + return ImmutableList.of(resultPrecision, resultScale); + }))) .build(); } diff --git a/presto-main/src/main/java/com/facebook/presto/type/DecimalToDecimalCasts.java b/presto-main/src/main/java/com/facebook/presto/type/DecimalToDecimalCasts.java index fc80c2b2f1b3e..5fd832c67db93 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DecimalToDecimalCasts.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DecimalToDecimalCasts.java @@ -53,26 +53,27 @@ public final class DecimalToDecimalCasts public static final SqlScalarFunction DECIMAL_TO_DECIMAL_CAST = SqlScalarFunction.builder(DecimalToDecimalCasts.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b - .methods("shortToShortCast") - .withExtraParameters((context) -> { - DecimalType argumentType = (DecimalType) context.getType("F"); - DecimalType resultType = (DecimalType) context.getType("T"); - long rescale = longTenToNth(Math.abs(resultType.getScale() - argumentType.getScale())); - return ImmutableList.of( - argumentType.getPrecision(), argumentType.getScale(), - resultType.getPrecision(), resultType.getScale(), - rescale, rescale / 2); - })) - .implementation(b -> b - .methods("shortToLongCast", "longToShortCast", "longToLongCast") - .withExtraParameters((context) -> { - DecimalType argumentType = (DecimalType) context.getType("F"); - DecimalType resultType = (DecimalType) context.getType("T"); - return ImmutableList.of( - argumentType.getPrecision(), argumentType.getScale(), - resultType.getPrecision(), resultType.getScale()); - })) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .methods("shortToShortCast") + .withExtraParameters((context) -> { + DecimalType argumentType = (DecimalType) context.getType("F"); + DecimalType resultType = (DecimalType) context.getType("T"); + long rescale = longTenToNth(Math.abs(resultType.getScale() - argumentType.getScale())); + return ImmutableList.of( + argumentType.getPrecision(), argumentType.getScale(), + resultType.getPrecision(), resultType.getScale(), + rescale, rescale / 2); + })) + .implementation(methodsGroup -> methodsGroup + .methods("shortToLongCast", "longToShortCast", "longToLongCast") + .withExtraParameters((context) -> { + DecimalType argumentType = (DecimalType) context.getType("F"); + DecimalType resultType = (DecimalType) context.getType("T"); + return ImmutableList.of( + argumentType.getPrecision(), argumentType.getScale(), + resultType.getPrecision(), resultType.getScale()); + }))) .build(); private DecimalToDecimalCasts() {} diff --git a/presto-main/src/main/java/com/facebook/presto/type/DoubleOperators.java b/presto-main/src/main/java/com/facebook/presto/type/DoubleOperators.java index 3223ae8bf881c..9bb6c1d1cd042 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/DoubleOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/DoubleOperators.java @@ -15,9 +15,13 @@ import com.facebook.presto.operator.scalar.MathFunctions; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -49,6 +53,7 @@ import static com.facebook.presto.spi.function.OperatorType.SATURATED_FLOOR_CAST; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.google.common.base.Preconditions.checkState; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Double.doubleToLongBits; @@ -128,7 +133,8 @@ public static double negate(@SqlType(StandardTypes.DOUBLE) double value) @ScalarOperator(EQUAL) @SuppressWarnings("FloatingPointEquality") @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.DOUBLE) double left, @SqlType(StandardTypes.DOUBLE) double right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.DOUBLE) double left, @SqlType(StandardTypes.DOUBLE) double right) { return left == right; } @@ -136,7 +142,8 @@ public static boolean equal(@SqlType(StandardTypes.DOUBLE) double left, @SqlType @ScalarOperator(NOT_EQUAL) @SuppressWarnings("FloatingPointEquality") @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.DOUBLE) double left, @SqlType(StandardTypes.DOUBLE) double right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.DOUBLE) double left, @SqlType(StandardTypes.DOUBLE) double right) { return left != right; } @@ -315,23 +322,47 @@ private static long saturatedFloorCastToLong(double value, long minValue, double } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.DOUBLE) double left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.DOUBLE) double right, - @IsNull boolean rightNull) + public static class DoubleDistinctFromOperator { - if (leftNull != rightNull) { - return true; - } - if (leftNull) { - return false; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.DOUBLE) double left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.DOUBLE) double right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + if (Double.isNaN(left) && Double.isNaN(right)) { + return false; + } + return notEqual(left, right); } - if (Double.isNaN(left) && Double.isNaN(right)) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.DOUBLE, nativeContainerType = double.class) Block leftBlock, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.DOUBLE, nativeContainerType = double.class) Block rightBlock, + @BlockIndex int rightPosition) + { + if (leftBlock.isNull(leftPosition) != rightBlock.isNull(rightPosition)) { + return true; + } + if (leftBlock.isNull(leftPosition)) { + return false; + } + double left = DOUBLE.getDouble(leftBlock, leftPosition); + double right = DOUBLE.getDouble(rightBlock, rightPosition); + if (Double.isNaN(left) && Double.isNaN(right)) { + return false; + } + return notEqual(left, right); } - return notEqual(left, right); } @ScalarOperator(XX_HASH_64) diff --git a/presto-main/src/main/java/com/facebook/presto/type/IntegerOperators.java b/presto-main/src/main/java/com/facebook/presto/type/IntegerOperators.java index da41e0a7c6232..5592502ce384f 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/IntegerOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/IntegerOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractIntType; import com.facebook.presto.spi.type.StandardTypes; @@ -46,6 +50,7 @@ import static com.facebook.presto.spi.function.OperatorType.SATURATED_FLOOR_CAST; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Float.floatToRawIntBits; import static java.lang.String.format; @@ -130,14 +135,16 @@ public static long negate(@SqlType(StandardTypes.INTEGER) long value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.INTEGER) long left, @SqlType(StandardTypes.INTEGER) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.INTEGER) long left, @SqlType(StandardTypes.INTEGER) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.INTEGER) long left, @SqlType(StandardTypes.INTEGER) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.INTEGER) long left, @SqlType(StandardTypes.INTEGER) long right) { return left != right; } @@ -246,20 +253,39 @@ public static long hashCode(@SqlType(StandardTypes.INTEGER) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.INTEGER) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.INTEGER) long right, - @IsNull boolean rightNull) + public static class IntegerDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.INTEGER) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.INTEGER) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.INTEGER, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.INTEGER, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(INTEGER.getLong(left, leftPosition), INTEGER.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(SATURATED_FLOOR_CAST) diff --git a/presto-main/src/main/java/com/facebook/presto/type/IntervalDayTimeOperators.java b/presto-main/src/main/java/com/facebook/presto/type/IntervalDayTimeOperators.java index 4b6a27f9193d1..d56d3e366eecc 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/IntervalDayTimeOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/IntervalDayTimeOperators.java @@ -13,9 +13,13 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -38,6 +42,7 @@ import static com.facebook.presto.spi.function.OperatorType.NEGATION; import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; +import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static io.airlift.slice.Slices.utf8Slice; public final class IntervalDayTimeOperators @@ -104,14 +109,16 @@ public static long negate(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long va @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right) { return left != right; } @@ -170,20 +177,39 @@ public static long hashCode(@SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class IntervalDayTimeDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.INTERVAL_DAY_TO_SECOND) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.INTERVAL_DAY_TO_SECOND, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.INTERVAL_DAY_TO_SECOND, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(INTERVAL_DAY_TIME.getLong(left, leftPosition), INTERVAL_DAY_TIME.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/IntervalYearMonthOperators.java b/presto-main/src/main/java/com/facebook/presto/type/IntervalYearMonthOperators.java index d0d69cf61364a..7987b0af919f8 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/IntervalYearMonthOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/IntervalYearMonthOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.client.IntervalYearMonth; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractIntType; import com.facebook.presto.spi.type.StandardTypes; @@ -38,6 +42,7 @@ import static com.facebook.presto.spi.function.OperatorType.NEGATION; import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; +import static com.facebook.presto.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Math.toIntExact; @@ -105,14 +110,16 @@ public static long negate(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long va @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right) { return left != right; } @@ -171,20 +178,39 @@ public static long hashCode(@SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class IntervalYearMonthDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.INTERVAL_YEAR_TO_MONTH) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.INTERVAL_YEAR_TO_MONTH, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.INTERVAL_YEAR_TO_MONTH, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(INTERVAL_YEAR_MONTH.getLong(left, leftPosition), INTERVAL_YEAR_MONTH.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/IpAddressOperators.java b/presto-main/src/main/java/com/facebook/presto/type/IpAddressOperators.java index 7fde450860777..4d457354b60c5 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/IpAddressOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/IpAddressOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import com.google.common.net.InetAddresses; @@ -40,6 +44,7 @@ import static com.facebook.presto.spi.function.OperatorType.LESS_THAN_OR_EQUAL; import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.type.IpAddressType.IPADDRESS; import static io.airlift.slice.Slices.utf8Slice; import static io.airlift.slice.Slices.wrappedBuffer; import static java.lang.System.arraycopy; @@ -52,14 +57,16 @@ private IpAddressOperators() @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.IPADDRESS) Slice left, @SqlType(StandardTypes.IPADDRESS) Slice right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.IPADDRESS) Slice left, @SqlType(StandardTypes.IPADDRESS) Slice right) { return left.equals(right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.IPADDRESS) Slice left, @SqlType(StandardTypes.IPADDRESS) Slice right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.IPADDRESS) Slice left, @SqlType(StandardTypes.IPADDRESS) Slice right) { return !left.equals(right); } @@ -183,19 +190,39 @@ public static Slice castFromIpAddressToVarbinary(@SqlType(StandardTypes.IPADDRES } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom(@SqlType(StandardTypes.IPADDRESS) Slice left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.IPADDRESS) Slice right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class IpAddressDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.IPADDRESS) Slice left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.IPADDRESS) Slice right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.IPADDRESS, nativeContainerType = Slice.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.IPADDRESS, nativeContainerType = Slice.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return left.compareTo(leftPosition, 0, IPADDRESS.getFixedSize(), right, rightPosition, 0, IPADDRESS.getFixedSize()) != 0; } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/QuantileDigestOperators.java b/presto-main/src/main/java/com/facebook/presto/type/QuantileDigestOperators.java new file mode 100644 index 0000000000000..979d890ddb4fb --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/type/QuantileDigestOperators.java @@ -0,0 +1,68 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.type; + +import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlType; +import com.facebook.presto.spi.type.StandardTypes; +import io.airlift.slice.Slice; + +import static com.facebook.presto.spi.function.OperatorType.CAST; + +public final class QuantileDigestOperators +{ + private QuantileDigestOperators() {} + + @ScalarOperator(CAST) + @SqlType(StandardTypes.VARBINARY) + public static Slice castToBinaryDouble(@SqlType("qdigest(double)") Slice slice) + { + return slice; + } + + @ScalarOperator(CAST) + @SqlType(StandardTypes.VARBINARY) + public static Slice castToBinaryBigint(@SqlType("qdigest(bigint)") Slice slice) + { + return slice; + } + + @ScalarOperator(CAST) + @SqlType(StandardTypes.VARBINARY) + public static Slice castToBinaryReal(@SqlType("qdigest(real)") Slice slice) + { + return slice; + } + + @ScalarOperator(CAST) + @SqlType("qdigest(double)") + public static Slice castFromVarbinaryDouble(@SqlType(StandardTypes.VARBINARY) Slice slice) + { + return slice; + } + + @ScalarOperator(CAST) + @SqlType("qdigest(bigint)") + public static Slice castFromVarbinaryBigint(@SqlType(StandardTypes.VARBINARY) Slice slice) + { + return slice; + } + + @ScalarOperator(CAST) + @SqlType("qdigest(real)") + public static Slice castFromVarbinaryReal(@SqlType(StandardTypes.VARBINARY) Slice slice) + { + return slice; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/type/RealOperators.java b/presto-main/src/main/java/com/facebook/presto/type/RealOperators.java index dc05445801701..594ee8ac380dc 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/RealOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/RealOperators.java @@ -15,9 +15,13 @@ import com.facebook.presto.operator.scalar.MathFunctions; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractIntType; import com.facebook.presto.spi.type.StandardTypes; @@ -47,6 +51,7 @@ import static com.facebook.presto.spi.function.OperatorType.SATURATED_FLOOR_CAST; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.RealType.REAL; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Float.floatToIntBits; import static java.lang.Float.floatToRawIntBits; @@ -113,14 +118,16 @@ public static long negate(@SqlType(StandardTypes.REAL) long value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.REAL) long left, @SqlType(StandardTypes.REAL) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.REAL) long left, @SqlType(StandardTypes.REAL) long right) { return intBitsToFloat((int) left) == intBitsToFloat((int) right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.REAL) long left, @SqlType(StandardTypes.REAL) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.REAL) long left, @SqlType(StandardTypes.REAL) long right) { return intBitsToFloat((int) left) != intBitsToFloat((int) right); } @@ -241,25 +248,44 @@ public static boolean castToBoolean(@SqlType(StandardTypes.REAL) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.REAL) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.REAL) long right, - @IsNull boolean rightNull) + public static class RealDistinctFromOperator { - if (leftNull != rightNull) { - return true; - } - if (leftNull) { - return false; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.REAL) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.REAL) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + float leftFloat = intBitsToFloat((int) left); + float rightFloat = intBitsToFloat((int) right); + if (Float.isNaN(leftFloat) && Float.isNaN(rightFloat)) { + return false; + } + return notEqual(left, right); } - float leftFloat = intBitsToFloat((int) left); - float rightFloat = intBitsToFloat((int) right); - if (Float.isNaN(leftFloat) && Float.isNaN(rightFloat)) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.REAL, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.REAL, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(REAL.getLong(left, leftPosition), REAL.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(SATURATED_FLOOR_CAST) diff --git a/presto-main/src/main/java/com/facebook/presto/type/SmallintOperators.java b/presto-main/src/main/java/com/facebook/presto/type/SmallintOperators.java index 882653c20c5f6..87480ee83bfac 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/SmallintOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/SmallintOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.SmallintType; import com.facebook.presto.spi.type.StandardTypes; @@ -46,6 +50,7 @@ import static com.facebook.presto.spi.function.OperatorType.SATURATED_FLOOR_CAST; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.SmallintType.SMALLINT; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Float.floatToRawIntBits; import static java.lang.String.format; @@ -130,14 +135,16 @@ public static long negate(@SqlType(StandardTypes.SMALLINT) long value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.SMALLINT) long left, @SqlType(StandardTypes.SMALLINT) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.SMALLINT) long left, @SqlType(StandardTypes.SMALLINT) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.SMALLINT) long left, @SqlType(StandardTypes.SMALLINT) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.SMALLINT) long left, @SqlType(StandardTypes.SMALLINT) long right) { return left != right; } @@ -241,20 +248,39 @@ public static long hashCode(@SqlType(StandardTypes.SMALLINT) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.SMALLINT) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.SMALLINT) long right, - @IsNull boolean rightNull) + public static class SmallintDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.SMALLINT) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.SMALLINT) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.SMALLINT, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.SMALLINT, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(SMALLINT.getLong(left, leftPosition), SMALLINT.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(SATURATED_FLOOR_CAST) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TimeOperators.java b/presto-main/src/main/java/com/facebook/presto/type/TimeOperators.java index f5b53008bcae4..86fe1a4a37bd7 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TimeOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TimeOperators.java @@ -15,9 +15,13 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -40,6 +44,7 @@ import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; import static com.facebook.presto.spi.type.DateTimeEncoding.packDateTimeWithZone; +import static com.facebook.presto.spi.type.TimeType.TIME; import static com.facebook.presto.util.DateTimeUtils.parseTimeWithoutTimeZone; import static com.facebook.presto.util.DateTimeUtils.printTimeWithoutTimeZone; import static com.facebook.presto.util.DateTimeZoneIndex.getChronology; @@ -60,14 +65,16 @@ public static long subtract(@SqlType(StandardTypes.TIME) long left, @SqlType(Sta @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.TIME) long left, @SqlType(StandardTypes.TIME) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.TIME) long left, @SqlType(StandardTypes.TIME) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.TIME) long left, @SqlType(StandardTypes.TIME) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.TIME) long left, @SqlType(StandardTypes.TIME) long right) { return left != right; } @@ -186,20 +193,39 @@ public static long xxHash64(@SqlType(StandardTypes.TIME) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.TIME) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.TIME) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class TimeDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.TIME) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.TIME) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.TIME, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.TIME, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(TIME.getLong(left, leftPosition), TIME.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TimeWithTimeZoneOperators.java b/presto-main/src/main/java/com/facebook/presto/type/TimeWithTimeZoneOperators.java index 15b17ccde02d9..a02f2851233ef 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TimeWithTimeZoneOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TimeWithTimeZoneOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -43,6 +47,7 @@ import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; import static com.facebook.presto.spi.type.DateTimeEncoding.unpackMillisUtc; import static com.facebook.presto.spi.type.DateTimeEncoding.unpackZoneKey; +import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; import static com.facebook.presto.util.DateTimeUtils.parseTimeWithTimeZone; import static com.facebook.presto.util.DateTimeUtils.printTimeWithTimeZone; import static com.facebook.presto.util.DateTimeZoneIndex.getChronology; @@ -65,14 +70,16 @@ public static long subtract(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long lef @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right) { return unpackMillisUtc(left) == unpackMillisUtc(right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right) { return unpackMillisUtc(left) != unpackMillisUtc(right); } @@ -177,20 +184,39 @@ public static long xxHash64(@SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long val } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class TimeWithTimeZoneDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.TIME_WITH_TIME_ZONE) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.TIME_WITH_TIME_ZONE, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.TIME_WITH_TIME_ZONE, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) && right.isNull(rightPosition)) { + return false; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(TIME_WITH_TIME_ZONE.getLong(left, leftPosition), TIME_WITH_TIME_ZONE.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TimestampOperators.java b/presto-main/src/main/java/com/facebook/presto/type/TimestampOperators.java index 2200677f25b6e..affc2fc468e8e 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TimestampOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TimestampOperators.java @@ -15,10 +15,14 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -43,6 +47,7 @@ import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; import static com.facebook.presto.spi.type.DateTimeEncoding.packDateTimeWithZone; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.type.DateTimeOperators.modulo24Hour; import static com.facebook.presto.util.DateTimeUtils.parseTimestampWithoutTimeZone; import static com.facebook.presto.util.DateTimeUtils.printTimestampWithoutTimeZone; @@ -65,14 +70,16 @@ public static long subtract(@SqlType(StandardTypes.TIMESTAMP) long left, @SqlTyp @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.TIMESTAMP) long left, @SqlType(StandardTypes.TIMESTAMP) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.TIMESTAMP) long left, @SqlType(StandardTypes.TIMESTAMP) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.TIMESTAMP) long left, @SqlType(StandardTypes.TIMESTAMP) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.TIMESTAMP) long left, @SqlType(StandardTypes.TIMESTAMP) long right) { return left != right; } @@ -222,20 +229,39 @@ public static long hashCode(@SqlType(StandardTypes.TIMESTAMP) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.TIMESTAMP) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.TIMESTAMP) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class TimestampDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.TIMESTAMP) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.TIMESTAMP) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.TIMESTAMP, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.TIMESTAMP, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(TIMESTAMP.getLong(left, leftPosition), TIMESTAMP.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TimestampWithTimeZoneOperators.java b/presto-main/src/main/java/com/facebook/presto/type/TimestampWithTimeZoneOperators.java index 6c7550f241f07..80fe5d559aa0d 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TimestampWithTimeZoneOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TimestampWithTimeZoneOperators.java @@ -15,10 +15,14 @@ import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.AbstractLongType; import com.facebook.presto.spi.type.StandardTypes; @@ -45,6 +49,7 @@ import static com.facebook.presto.spi.type.DateTimeEncoding.packDateTimeWithZone; import static com.facebook.presto.spi.type.DateTimeEncoding.unpackMillisUtc; import static com.facebook.presto.spi.type.DateTimeEncoding.unpackZoneKey; +import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.type.DateTimeOperators.modulo24Hour; import static com.facebook.presto.util.DateTimeUtils.parseTimestampWithTimeZone; import static com.facebook.presto.util.DateTimeUtils.printTimestampWithTimeZone; @@ -68,14 +73,16 @@ public static long subtract(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) lon @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right) { return unpackMillisUtc(left) == unpackMillisUtc(right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right) { return unpackMillisUtc(left) != unpackMillisUtc(right); } @@ -214,20 +221,39 @@ public static long xxHash64(@SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) lon } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right, - @IsNull boolean rightNull) - { - if (leftNull != rightNull) { - return true; + public static class TimestampWithTimeZoneDistinctFromOperator + { + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.TIMESTAMP_WITH_TIME_ZONE) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.TIMESTAMP_WITH_TIME_ZONE, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.TIMESTAMP_WITH_TIME_ZONE, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(TIMESTAMP_WITH_TIME_ZONE.getLong(left, leftPosition), TIMESTAMP_WITH_TIME_ZONE.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TinyintOperators.java b/presto-main/src/main/java/com/facebook/presto/type/TinyintOperators.java index b2ac0d7cf082a..ca557df1a3912 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TinyintOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TinyintOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.TinyintType; @@ -44,6 +48,7 @@ import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL; import static com.facebook.presto.spi.function.OperatorType.SUBTRACT; import static com.facebook.presto.spi.function.OperatorType.XX_HASH_64; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Float.floatToRawIntBits; import static java.lang.String.format; @@ -128,14 +133,16 @@ public static long negate(@SqlType(StandardTypes.TINYINT) long value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.TINYINT) long left, @SqlType(StandardTypes.TINYINT) long right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.TINYINT) long left, @SqlType(StandardTypes.TINYINT) long right) { return left == right; } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.TINYINT) long left, @SqlType(StandardTypes.TINYINT) long right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.TINYINT) long left, @SqlType(StandardTypes.TINYINT) long right) { return left != right; } @@ -241,20 +248,39 @@ public static long xxHash64(@SqlType(StandardTypes.TINYINT) long value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.TINYINT) long left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.TINYINT) long right, - @IsNull boolean rightNull) + public static class TinyintDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.TINYINT) long left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.TINYINT) long right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.TINYINT, nativeContainerType = long.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.TINYINT, nativeContainerType = long.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + return notEqual(TINYINT.getLong(left, leftPosition), TINYINT.getLong(right, rightPosition)); } - return notEqual(left, right); } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/TypeRegistry.java b/presto-main/src/main/java/com/facebook/presto/type/TypeRegistry.java index ee32dc9379be0..8a692885e7ad0 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/TypeRegistry.java +++ b/presto-main/src/main/java/com/facebook/presto/type/TypeRegistry.java @@ -60,6 +60,7 @@ import static com.facebook.presto.spi.type.HyperLogLogType.HYPER_LOG_LOG; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.P4HyperLogLogType.P4_HYPER_LOG_LOG; +import static com.facebook.presto.spi.type.QuantileDigestParametricType.QDIGEST; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.RowType.Field; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; @@ -153,6 +154,7 @@ public TypeRegistry(Set types, FeaturesConfig featuresConfig) addParametricType(ARRAY); addParametricType(MAP); addParametricType(FUNCTION); + addParametricType(QDIGEST); for (Type type : types) { addType(type); diff --git a/presto-main/src/main/java/com/facebook/presto/type/UnknownOperators.java b/presto-main/src/main/java/com/facebook/presto/type/UnknownOperators.java index 32241a65ad96e..f50896fa30006 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/UnknownOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/UnknownOperators.java @@ -13,6 +13,9 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.ScalarOperator; import com.facebook.presto.spi.function.SqlNullable; @@ -93,14 +96,27 @@ public static long hashCode(@SqlType("unknown") boolean value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType("unknown") boolean left, - @IsNull boolean leftNull, - @SqlType("unknown") boolean right, - @IsNull boolean rightNull) + public static class UnknownDistinctFromOperator { - return false; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType("unknown") boolean left, + @IsNull boolean leftNull, + @SqlType("unknown") boolean right, + @IsNull boolean rightNull) + { + return false; + } + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = "unknown", nativeContainerType = boolean.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = "unknown", nativeContainerType = boolean.class) Block right, + @BlockIndex int rightNull) + { + return false; + } } @ScalarOperator(INDETERMINATE) diff --git a/presto-main/src/main/java/com/facebook/presto/type/VarbinaryOperators.java b/presto-main/src/main/java/com/facebook/presto/type/VarbinaryOperators.java index a89631fb045b6..9c9c305ea624d 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/VarbinaryOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/VarbinaryOperators.java @@ -13,8 +13,12 @@ */ package com.facebook.presto.type; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import io.airlift.slice.Slice; @@ -40,14 +44,16 @@ private VarbinaryOperators() @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType(StandardTypes.VARBINARY) Slice left, @SqlType(StandardTypes.VARBINARY) Slice right) + @SqlNullable + public static Boolean equal(@SqlType(StandardTypes.VARBINARY) Slice left, @SqlType(StandardTypes.VARBINARY) Slice right) { return left.equals(right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType(StandardTypes.VARBINARY) Slice left, @SqlType(StandardTypes.VARBINARY) Slice right) + @SqlNullable + public static Boolean notEqual(@SqlType(StandardTypes.VARBINARY) Slice left, @SqlType(StandardTypes.VARBINARY) Slice right) { return !left.equals(right); } @@ -98,20 +104,45 @@ public static long hashCode(@SqlType(StandardTypes.VARBINARY) Slice value) } @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType(StandardTypes.VARBINARY) Slice left, - @IsNull boolean leftNull, - @SqlType(StandardTypes.VARBINARY) Slice right, - @IsNull boolean rightNull) + public static class VarbinaryDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType(StandardTypes.VARBINARY) Slice left, + @IsNull boolean leftNull, + @SqlType(StandardTypes.VARBINARY) Slice right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = StandardTypes.VARBINARY, nativeContainerType = Slice.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = StandardTypes.VARBINARY, nativeContainerType = Slice.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + + int leftLength = left.getSliceLength(leftPosition); + int rightLength = right.getSliceLength(rightPosition); + if (leftLength != rightLength) { + return true; + } + return !left.equals(leftPosition, 0, right, rightPosition, 0, leftLength); } - return notEqual(left, right); } @ScalarOperator(XX_HASH_64) diff --git a/presto-main/src/main/java/com/facebook/presto/type/VarcharOperators.java b/presto-main/src/main/java/com/facebook/presto/type/VarcharOperators.java index e53eb6d12ba5f..c395834cdb6d2 100644 --- a/presto-main/src/main/java/com/facebook/presto/type/VarcharOperators.java +++ b/presto-main/src/main/java/com/facebook/presto/type/VarcharOperators.java @@ -14,9 +14,13 @@ package com.facebook.presto.type; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.function.BlockIndex; +import com.facebook.presto.spi.function.BlockPosition; import com.facebook.presto.spi.function.IsNull; import com.facebook.presto.spi.function.LiteralParameters; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import io.airlift.slice.Slice; @@ -46,7 +50,8 @@ private VarcharOperators() @LiteralParameters("x") @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType("varchar(x)") Slice left, @SqlType("varchar(x)") Slice right) + @SqlNullable + public static Boolean equal(@SqlType("varchar(x)") Slice left, @SqlType("varchar(x)") Slice right) { return left.equals(right); } @@ -54,7 +59,8 @@ public static boolean equal(@SqlType("varchar(x)") Slice left, @SqlType("varchar @LiteralParameters("x") @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType("varchar(x)") Slice left, @SqlType("varchar(x)") Slice right) + @SqlNullable + public static Boolean notEqual(@SqlType("varchar(x)") Slice left, @SqlType("varchar(x)") Slice right) { return !left.equals(right); } @@ -235,22 +241,48 @@ public static long hashCode(@SqlType("varchar(x)") Slice value) return xxHash64(value); } - @LiteralParameters({"x", "y"}) @ScalarOperator(IS_DISTINCT_FROM) - @SqlType(StandardTypes.BOOLEAN) - public static boolean isDistinctFrom( - @SqlType("varchar(x)") Slice left, - @IsNull boolean leftNull, - @SqlType("varchar(y)") Slice right, - @IsNull boolean rightNull) + public static class VarcharDistinctFromOperator { - if (leftNull != rightNull) { - return true; + @LiteralParameters({"x", "y"}) + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @SqlType("varchar(x)") Slice left, + @IsNull boolean leftNull, + @SqlType("varchar(y)") Slice right, + @IsNull boolean rightNull) + { + if (leftNull != rightNull) { + return true; + } + if (leftNull) { + return false; + } + return notEqual(left, right); } - if (leftNull) { - return false; + + @LiteralParameters({"x", "y"}) + @SqlType(StandardTypes.BOOLEAN) + public static boolean isDistinctFrom( + @BlockPosition @SqlType(value = "varchar(x)", nativeContainerType = Slice.class) Block left, + @BlockIndex int leftPosition, + @BlockPosition @SqlType(value = "varchar(y)", nativeContainerType = Slice.class) Block right, + @BlockIndex int rightPosition) + { + if (left.isNull(leftPosition) != right.isNull(rightPosition)) { + return true; + } + if (left.isNull(leftPosition)) { + return false; + } + + int leftLength = left.getSliceLength(leftPosition); + int rightLength = right.getSliceLength(rightPosition); + if (leftLength != rightLength) { + return true; + } + return !left.equals(leftPosition, 0, right, rightPosition, 0, leftLength); } - return notEqual(left, right); } @LiteralParameters("x") diff --git a/presto-main/src/main/java/com/facebook/presto/util/FastutilSetHelper.java b/presto-main/src/main/java/com/facebook/presto/util/FastutilSetHelper.java index 390a24c4640b8..a0c8c1d6415d3 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/FastutilSetHelper.java +++ b/presto-main/src/main/java/com/facebook/presto/util/FastutilSetHelper.java @@ -34,6 +34,8 @@ import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.spi.function.OperatorType.HASH_CODE; import static com.google.common.base.Throwables.throwIfInstanceOf; +import static com.google.common.base.Verify.verify; +import static java.lang.Boolean.TRUE; import static java.lang.Math.toIntExact; public final class FastutilSetHelper @@ -113,7 +115,10 @@ public int hashCode(long value) public boolean equals(long a, long b) { try { - return (boolean) equalsHandle.invokeExact(a, b); + Boolean result = (Boolean) equalsHandle.invokeExact(a, b); + // FastutilHashSet is not intended be used for indeterminate values lookup + verify(result != null, "result is null"); + return TRUE.equals(result); } catch (Throwable t) { throwIfInstanceOf(t, Error.class); @@ -152,7 +157,10 @@ public int hashCode(double value) public boolean equals(double a, double b) { try { - return (boolean) equalsHandle.invokeExact(a, b); + Boolean result = (Boolean) equalsHandle.invokeExact(a, b); + // FastutilHashSet is not intended be used for indeterminate values lookup + verify(result != null, "result is null"); + return TRUE.equals(result); } catch (Throwable t) { throwIfInstanceOf(t, Error.class); @@ -175,7 +183,7 @@ private ObjectStrategy(FunctionRegistry registry, Type type) .asType(MethodType.methodType(long.class, Object.class)); equalsHandle = registry.getScalarFunctionImplementation(registry.resolveOperator(EQUAL, ImmutableList.of(type, type))) .getMethodHandle() - .asType(MethodType.methodType(boolean.class, Object.class, Object.class)); + .asType(MethodType.methodType(Boolean.class, Object.class, Object.class)); } @Override @@ -195,7 +203,10 @@ public int hashCode(Object value) public boolean equals(Object a, Object b) { try { - return (boolean) equalsHandle.invokeExact(a, b); + Boolean result = (Boolean) equalsHandle.invokeExact(a, b); + // FastutilHashSet is not intended be used for indeterminate values lookup + verify(result != null, "result is null"); + return TRUE.equals(result); } catch (Throwable t) { throwIfInstanceOf(t, Error.class); diff --git a/presto-main/src/main/java/com/facebook/presto/util/FinalizerService.java b/presto-main/src/main/java/com/facebook/presto/util/FinalizerService.java index a7331eecab134..e23a587e017d2 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/FinalizerService.java +++ b/presto-main/src/main/java/com/facebook/presto/util/FinalizerService.java @@ -39,7 +39,8 @@ public class FinalizerService private final Set finalizers = Sets.newConcurrentHashSet(); private final ReferenceQueue finalizerQueue = new ReferenceQueue<>(); - private final ExecutorService executor = newSingleThreadExecutor(daemonThreadsNamed("FinalizerService")); + @GuardedBy("this") + private ExecutorService executor; @GuardedBy("this") private Future finalizerTask; @@ -50,6 +51,9 @@ public synchronized void start() if (finalizerTask != null) { return; } + if (executor == null) { + executor = newSingleThreadExecutor(daemonThreadsNamed("FinalizerService")); + } if (executor.isShutdown()) { throw new IllegalStateException("Finalizer service has been destroyed"); } @@ -63,7 +67,10 @@ public synchronized void destroy() finalizerTask.cancel(true); finalizerTask = null; } - executor.shutdownNow(); + if (executor != null) { + executor.shutdownNow(); + executor = null; + } } /** diff --git a/presto-main/src/main/java/com/facebook/presto/util/GraphvizPrinter.java b/presto-main/src/main/java/com/facebook/presto/util/GraphvizPrinter.java index b6f5cbc13de6d..e18789afeb721 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/GraphvizPrinter.java +++ b/presto-main/src/main/java/com/facebook/presto/util/GraphvizPrinter.java @@ -42,6 +42,7 @@ import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; import com.facebook.presto.sql.planner.plan.TableScanNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; @@ -421,7 +422,7 @@ public Void visitLimit(LimitNode node, Void context) @Override public Void visitTableScan(TableScanNode node, Void context) { - printNode(node, format("TableScan[%s]", node.getTable()), format("original constraint=%s", node.getOriginalConstraint()), NODE_COLORS.get(NodeType.TABLESCAN)); + printNode(node, format("TableScan[%s]", node.getTable()), NODE_COLORS.get(NodeType.TABLESCAN)); return null; } @@ -467,6 +468,17 @@ public Void visitSemiJoin(SemiJoinNode node, Void context) return null; } + @Override + public Void visitSpatialJoin(SpatialJoinNode node, Void context) + { + printNode(node, node.getType().getJoinLabel(), node.getFilter().toString(), NODE_COLORS.get(NodeType.JOIN)); + + node.getLeft().accept(this, context); + node.getRight().accept(this, context); + + return null; + } + @Override public Void visitApply(ApplyNode node, Void context) { diff --git a/presto-main/src/main/java/com/facebook/presto/util/MergeSortedPages.java b/presto-main/src/main/java/com/facebook/presto/util/MergeSortedPages.java index a11825e24aaf7..50fd4387ea253 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/MergeSortedPages.java +++ b/presto-main/src/main/java/com/facebook/presto/util/MergeSortedPages.java @@ -18,7 +18,8 @@ import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.PageWithPositionComparator; import com.facebook.presto.operator.WorkProcessor; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; +import com.facebook.presto.operator.WorkProcessor.ProcessState; +import com.facebook.presto.operator.WorkProcessor.TransformationState; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.block.Block; @@ -102,44 +103,42 @@ private static WorkProcessor buildPage( { LocalMemoryContext memoryContext = aggregatedMemoryContext.newLocalMemoryContext(MergeSortedPages.class.getSimpleName()); PageBuilder pageBuilder = new PageBuilder(outputTypes); - return pageWithPositions.transform(pageWithPositionOptional -> { - if (yieldSignal.isSet()) { - return ProcessorState.yield(); - } + return pageWithPositions + .yielding(yieldSignal::isSet) + .transform(pageWithPositionOptional -> { + boolean finished = !pageWithPositionOptional.isPresent(); + if (finished && pageBuilder.isEmpty()) { + memoryContext.close(); + return TransformationState.finished(); + } - boolean finished = !pageWithPositionOptional.isPresent(); - if (finished && pageBuilder.isEmpty()) { - memoryContext.close(); - return ProcessorState.finished(); - } + if (finished || pageBreakPredicate.test(pageBuilder, pageWithPositionOptional.get())) { + if (!updateMemoryAfterEveryPosition) { + // update memory usage just before producing page to cap from top + memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); + } - if (finished || pageBreakPredicate.test(pageBuilder, pageWithPositionOptional.get())) { - if (!updateMemoryAfterEveryPosition) { - // update memory usage just before producing page to cap from top - memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); - } + Page page = pageBuilder.build(); + pageBuilder.reset(); + if (!finished) { + pageWithPositionOptional.get().appendTo(pageBuilder, outputChannels, outputTypes); + } - Page page = pageBuilder.build(); - pageBuilder.reset(); - if (!finished) { - pageWithPositionOptional.get().appendTo(pageBuilder, outputChannels, outputTypes); - } + if (updateMemoryAfterEveryPosition) { + memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); + } - if (updateMemoryAfterEveryPosition) { - memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); - } - - return ProcessorState.ofResult(page, !finished); - } + return TransformationState.ofResult(page, !finished); + } - pageWithPositionOptional.get().appendTo(pageBuilder, outputChannels, outputTypes); + pageWithPositionOptional.get().appendTo(pageBuilder, outputChannels, outputTypes); - if (updateMemoryAfterEveryPosition) { - memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); - } + if (updateMemoryAfterEveryPosition) { + memoryContext.setBytes(pageBuilder.getRetainedSizeInBytes()); + } - return ProcessorState.needsMoreData(); - }); + return TransformationState.needsMoreData(); + }); } private static WorkProcessor pageWithPositions(WorkProcessor pages, AggregatedMemoryContext aggregatedMemoryContext) @@ -153,14 +152,14 @@ private static WorkProcessor pageWithPositions(WorkProcessor

process() + public ProcessState process() { if (position >= page.getPositionCount()) { memoryContext.close(); - return ProcessorState.finished(); + return ProcessState.finished(); } - return ProcessorState.ofResult(new PageWithPosition(page, position++)); + return ProcessState.ofResult(new PageWithPosition(page, position++)); } }); }); diff --git a/presto-main/src/main/java/com/facebook/presto/util/Mergeable.java b/presto-main/src/main/java/com/facebook/presto/util/Mergeable.java index bf904139a7319..ea48388def437 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/Mergeable.java +++ b/presto-main/src/main/java/com/facebook/presto/util/Mergeable.java @@ -19,6 +19,7 @@ public interface Mergeable { /** * Merges the current state with the state of the other instance, and returns the merged state. + * * @throws NullPointerException if other is null */ T mergeWith(T other); diff --git a/presto-main/src/main/java/com/facebook/presto/util/MoreLists.java b/presto-main/src/main/java/com/facebook/presto/util/MoreLists.java index bcca89e809140..7dcf78c6fcd55 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/MoreLists.java +++ b/presto-main/src/main/java/com/facebook/presto/util/MoreLists.java @@ -17,8 +17,11 @@ import java.util.List; import java.util.function.Function; +import java.util.function.IntFunction; import java.util.function.Predicate; +import java.util.stream.IntStream; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; @@ -49,5 +52,14 @@ public static List mappedCopy(List elements, Function mapper) .collect(toImmutableList()); } + public static List nElements(int n, IntFunction function) + { + checkArgument(n >= 0, "n must be greater than or equal to zero"); + requireNonNull(function, "function is null"); + return IntStream.range(0, n) + .mapToObj(function) + .collect(toImmutableList()); + } + private MoreLists() {} } diff --git a/presto-main/src/main/java/com/facebook/presto/util/PrestoIterators.java b/presto-main/src/main/java/com/facebook/presto/util/PrestoIterators.java deleted file mode 100644 index f3bba9bf867c6..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/util/PrestoIterators.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.util; - -import com.google.common.collect.AbstractIterator; - -import java.util.Iterator; - -import static java.util.Objects.requireNonNull; - -public class PrestoIterators -{ - private PrestoIterators() {} - - public static Iterator closeWhenExhausted(Iterator iterator, AutoCloseable resource) - { - requireNonNull(iterator, "iterator is null"); - requireNonNull(resource, "resource is null"); - - return new AbstractIterator() - { - @Override - protected T computeNext() - { - if (iterator.hasNext()) { - return iterator.next(); - } - else { - try { - resource.close(); - } - catch (Exception e) { - throw new RuntimeException(e); - } - return endOfData(); - } - } - }; - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/util/SpatialJoinUtils.java b/presto-main/src/main/java/com/facebook/presto/util/SpatialJoinUtils.java index 121c0051da3c8..ecb2bb5bd1c46 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/SpatialJoinUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/util/SpatialJoinUtils.java @@ -35,6 +35,7 @@ public class SpatialJoinUtils { public static final String ST_CONTAINS = "st_contains"; + public static final String ST_WITHIN = "st_within"; public static final String ST_INTERSECTS = "st_intersects"; public static final String ST_DISTANCE = "st_distance"; @@ -43,8 +44,9 @@ private SpatialJoinUtils() {} /** * Returns a subset of conjuncts matching one of the following shapes: * - ST_Contains(...) + * - ST_Within(...) * - ST_Intersects(...) - * + *

* Doesn't check or guarantee anything about function arguments. */ public static List extractSupportedSpatialFunctions(Expression filterExpression) @@ -59,7 +61,8 @@ public static List extractSupportedSpatialFunctions(Expression fil private static boolean isSupportedSpatialFunction(FunctionCall functionCall) { String functionName = functionCall.getName().toString(); - return functionName.equalsIgnoreCase(ST_CONTAINS) || functionName.equalsIgnoreCase(ST_INTERSECTS); + return functionName.equalsIgnoreCase(ST_CONTAINS) || functionName.equalsIgnoreCase(ST_WITHIN) + || functionName.equalsIgnoreCase(ST_INTERSECTS); } /** @@ -68,7 +71,7 @@ private static boolean isSupportedSpatialFunction(FunctionCall functionCall) * - ST_Distance(...) < ... * - ... >= ST_Distance(...) * - ... > ST_Distance(...) - * + *

* Doesn't check or guarantee anything about ST_Distance functions arguments * or the other side of the comparison. */ diff --git a/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java b/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java index 61f1453a90dd4..c57c6ff9fa7fd 100644 --- a/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java +++ b/presto-main/src/main/java/com/facebook/presto/util/StatementUtils.java @@ -47,7 +47,6 @@ import com.facebook.presto.sql.tree.ShowCreate; import com.facebook.presto.sql.tree.ShowFunctions; import com.facebook.presto.sql.tree.ShowGrants; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowStats; @@ -60,7 +59,7 @@ import java.util.Map; import java.util.Optional; -public class StatementUtils +public final class StatementUtils { private StatementUtils() {} @@ -81,7 +80,6 @@ private StatementUtils() {} builder.put(ShowCreate.class, QueryType.DESCRIBE); builder.put(ShowFunctions.class, QueryType.DESCRIBE); builder.put(ShowGrants.class, QueryType.DESCRIBE); - builder.put(ShowPartitions.class, QueryType.DESCRIBE); builder.put(ShowSchemas.class, QueryType.DESCRIBE); builder.put(ShowSession.class, QueryType.DESCRIBE); builder.put(ShowStats.class, QueryType.DESCRIBE); @@ -125,4 +123,9 @@ public static Optional getQueryType(Class statem { return Optional.ofNullable(STATEMENT_QUERY_TYPES.get(statement)); } + + public static boolean isTransactionControlStatement(Statement statement) + { + return statement instanceof StartTransaction || statement instanceof Commit || statement instanceof Rollback; + } } diff --git a/presto-main/src/main/java/com/facebook/presto/version/EmbedVersion.java b/presto-main/src/main/java/com/facebook/presto/version/EmbedVersion.java new file mode 100644 index 0000000000000..bbdfbabb85808 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/version/EmbedVersion.java @@ -0,0 +1,102 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.version; + +import com.facebook.presto.server.ServerConfig; +import com.google.common.collect.ImmutableMap; +import io.airlift.bytecode.ClassDefinition; +import io.airlift.bytecode.FieldDefinition; +import io.airlift.bytecode.MethodDefinition; +import io.airlift.bytecode.Parameter; + +import javax.inject.Inject; + +import java.lang.invoke.MethodHandle; +import java.util.Objects; + +import static com.facebook.presto.util.CompilerUtils.defineClass; +import static com.facebook.presto.util.CompilerUtils.makeClassName; +import static com.facebook.presto.util.Reflection.constructorMethodHandle; +import static com.google.common.base.Throwables.throwIfUnchecked; +import static io.airlift.bytecode.Access.FINAL; +import static io.airlift.bytecode.Access.PRIVATE; +import static io.airlift.bytecode.Access.PUBLIC; +import static io.airlift.bytecode.Access.a; +import static io.airlift.bytecode.Parameter.arg; +import static io.airlift.bytecode.ParameterizedType.type; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class EmbedVersion +{ + private final MethodHandle runnableConstructor; + + @Inject + public EmbedVersion(ServerConfig serverConfig) + { + ClassDefinition classDefinition = new ClassDefinition( + a(PUBLIC, FINAL), + makeClassName(baseClassName(serverConfig)), + type(Object.class), + type(Runnable.class)); + + FieldDefinition field = classDefinition.declareField(a(PRIVATE), "runnable", Runnable.class); + + Parameter parameter = arg("runnable", type(Runnable.class)); + MethodDefinition constructor = classDefinition.declareConstructor(a(PUBLIC), parameter); + constructor.getBody() + .comment("super(runnable);") + .append(constructor.getThis()) + .invokeConstructor(Object.class) + .append(constructor.getThis()) + .append(parameter) + .putField(field) + .ret(); + + MethodDefinition run = classDefinition.declareMethod(a(PUBLIC), "run", type(void.class)); + run.getBody() + .comment("runnable.run();") + .append(run.getThis()) + .getField(field) + .invokeInterface(Runnable.class, "run", void.class) + .ret(); + + Class generatedClass = defineClass(classDefinition, Runnable.class, ImmutableMap.of(), getClass().getClassLoader()); + this.runnableConstructor = constructorMethodHandle(generatedClass, Runnable.class); + } + + private static String baseClassName(ServerConfig serverConfig) + { + String builtInVersion = new ServerConfig().getPrestoVersion(); + String configuredVersion = serverConfig.getPrestoVersion(); + + String version = configuredVersion; + if (!Objects.equals(builtInVersion, configuredVersion)) { + version = format("%s__%s", builtInVersion, configuredVersion); + } + return format("Presto_%s___", version); + } + + public Runnable embedVersion(Runnable runnable) + { + requireNonNull(runnable, "runnable is null"); + try { + return (Runnable) runnableConstructor.invoke(runnable); + } + catch (Throwable throwable) { + throwIfUnchecked(throwable); + throw new RuntimeException(throwable); + } + } +} diff --git a/presto-main/src/main/resources/com/facebook/presto/server/thread.html b/presto-main/src/main/resources/com/facebook/presto/server/thread.html deleted file mode 100644 index e9c8f4a7025a1..0000000000000 --- a/presto-main/src/main/resources/com/facebook/presto/server/thread.html +++ /dev/null @@ -1,188 +0,0 @@ - - - - - - - - - - - - - - - -

- - - -
- - - diff --git a/presto-main/src/main/resources/webapp/assets/plan.js b/presto-main/src/main/resources/webapp/assets/plan.js deleted file mode 100644 index ee4a5bb8ef946..0000000000000 --- a/presto-main/src/main/resources/webapp/assets/plan.js +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -function flatten(queryInfo) -{ - const stages = new Map(); - flattenStage(queryInfo.outputStage, stages); - - return { - id: queryInfo.queryId, - root: queryInfo.outputStage.plan.id, - stageStats: {}, - stages: stages - } -} - -function flattenStage(stageInfo, result) -{ - stageInfo.subStages.forEach(function(stage) { - flattenStage(stage, result); - }); - - const nodes = new Map(); - flattenNode(result, stageInfo.plan.root, nodes); - - result.set(stageInfo.plan.id, { - stageId: stageInfo.stageId, - id: stageInfo.plan.id, - root: stageInfo.plan.root.id, - distribution: stageInfo.plan.distribution, - stageStats: stageInfo.stageStats, - state: stageInfo.state, - nodes: nodes - }); -} - -function flattenNode(stages, nodeInfo, result) -{ - const allSources = computeSources(nodeInfo); - const sources = allSources[0]; - const remoteSources = allSources[1]; - - result.set(nodeInfo.id, { - id: nodeInfo.id, - type: nodeInfo['@type'], - sources: sources.map(function(node) { return node.id }), - remoteSources: remoteSources, - stats: {} - }); - - sources.forEach(function(child) { - flattenNode(stages, child, result); - }); -} - -let StageStatistics = React.createClass({ - render: function() { - const stage = this.props.stage; - const stats = this.props.stage.stageStats; - return ( -
-
- Output: { stats.outputDataSize + " / " + formatCount(stats.outputPositions) + " rows" } -
- Buffered: { stats.bufferedDataSize } -
- { stage.state } -
- CPU: { stats.totalCpuTime } - { stats.fullyBlocked ? -
Blocked: { stats.totalBlockedTime }
: -
Blocked: { stats.totalBlockedTime }
- } - Memory: { stats.userMemoryReservation } -
- Splits: {"Q:" + stats.queuedDrivers + ", R:" + stats.runningDrivers + ", F:" + stats.completedDrivers } -
- Input: {stats.rawInputDataSize + " / " + formatCount(stats.rawInputPositions) } rows -
-
- ); - } -}); - -let LivePlan = React.createClass({ - getInitialState: function() { - return { - initialized: false, - ended: false, - - graph: initializeGraph(), - svg: initializeSvg("#plan-canvas"), - render: new dagreD3.render(), - }; - }, - resetTimer: function() { - clearTimeout(this.timeoutId); - // stop refreshing when query finishes or fails - if (this.state.query === null || !this.state.ended) { - this.timeoutId = setTimeout(this.refreshLoop, 1000); - } - }, - renderProgressBar: function() { - const query = this.state.query; - const progressBarStyle = { width: getProgressBarPercentage(query) + "%", backgroundColor: getQueryStateColor(query) }; - - if (isQueryComplete(query)) { - return ( -
-
- { getProgressBarTitle(query) } -
-
- ); - } - - return ( -

- - - - - - -
-
-
- { getProgressBarTitle(query) } -
-
-
- $.ajax({url: '/v1/query/' + query.queryId, type: 'DELETE'}) } className="btn btn-warning" target="_blank"> - Kill - -
- ); - }, - refreshLoop: function() { - clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously - const queryId = getFirstParameter(window.location.search); - $.get('/v1/query/' + queryId, function (query) { - this.setState({ - query: query, - - initialized: true, - ended: query.finalQueryInfo, - }); - this.resetTimer(); - }.bind(this)) - .error(function() { - this.setState({ - initialized: true, - }); - this.resetTimer(); - }.bind(this)); - }, - handleStageClick: function(stageCssId) { - window.open("stage.html?" + stageCssId,'_blank'); - }, - componentDidMount: function() { - this.refreshLoop(); - }, - updateD3Stage: function(stage, graph) { - const clusterId = stage.stageId; - const stageRootNodeId = "stage-" + stage.id + "-root"; - const color = getStageStateColor(stage); - - graph.setNode(clusterId, {label: "Stage " + stage.id + " ", clusterLabelPos: 'top-right', style: 'fill: ' + color, labelStyle: 'fill: #fff'}); - - // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React - const html = ReactDOMServer.renderToString(); - - graph.setNode(stageRootNodeId, {class: "stage-stats", label: html, labelType: "html"}); - graph.setParent(stageRootNodeId, clusterId); - graph.setEdge("node-" + stage.root, stageRootNodeId, {style: "visibility: hidden"}); - - stage.nodes.forEach(node => { - const nodeId = "node-" + node.id; - - graph.setNode(nodeId, {label: node.type, style: 'fill: #fff'}); - graph.setParent(nodeId, clusterId); - - node.sources.forEach(source => { - graph.setEdge("node-" + source, nodeId, {arrowheadStyle: "fill: #fff; stroke-width: 0;"}); - }); - - if (node.type === 'remoteSource') { - graph.setNode(nodeId, {label: '', shape: "circle"}); - - node.remoteSources.forEach(sourceId => { - graph.setEdge("stage-" + sourceId + "-root", nodeId, {style: "stroke-width: 5px;", arrowheadStyle: "fill: #fff; stroke-width: 0;"}); - }); - } - }); - }, - updateD3Graph: function() { - if (!this.state.query) { - return; - } - - const graph = this.state.graph; - const stages = flatten(this.state.query).stages; - stages.forEach(stage => { - this.updateD3Stage(stage, graph); - }); - - this.state.render(d3.select("#plan-canvas g"), graph); - - const svg = this.state.svg; - svg.selectAll("g.cluster").on("click", this.handleStageClick); - svg.attr("height", graph.graph().height); - svg.attr("width", graph.graph().width); - }, - findStage: function (stageId, currentStage) { - if (stageId === -1) { - return null; - } - - if (parseInt(currentStage.plan.id) === stageId) { - return currentStage; - } - - for (let i = 0; i < currentStage.subStages.length; i++) { - const stage = this.findStage(stageId, currentStage.subStages[i]); - if (stage !== null) { - return stage; - } - } - - return null; - }, - render: function() { - const query = this.state.query; - - if (query === null || this.state.initialized === false) { - let label = (
Loading...
); - if (this.state.initialized) { - label = "Query not found"; - } - return ( -
-

{ label }

-
- ); - } - - let livePlanGraph = null; - if (!this.state.query.outputStage) { - livePlanGraph = ( -
-
-

Live plan graph will appear automatically when query starts running.

-
Loading...
-
-
- ) - } - else { - this.updateD3Graph(); - const selectedStage = this.findStage(this.state.selectedStageId, this.state.query.outputStage); - if (selectedStage) { - ReactDOM.render( - , - document.getElementById('stage-performance') - ); - } - } - - return ( -
-
-
-

- { query.queryId } - - -

-
-
- - - - - - -
- Overview -   - Live Plan -   - Stage Performance -   - Splits -   - JSON -
-
-
-
-
-
- { this.renderProgressBar() } -
-
-
-
- { livePlanGraph } -
-
-
- ); - } -}); - -ReactDOM.render( - , - document.getElementById('live-plan-header') -); diff --git a/presto-main/src/main/resources/webapp/assets/presto.css b/presto-main/src/main/resources/webapp/assets/presto.css index 30e6882a4539c..4a3fc1ae1408f 100644 --- a/presto-main/src/main/resources/webapp/assets/presto.css +++ b/presto-main/src/main/resources/webapp/assets/presto.css @@ -241,8 +241,8 @@ pre { .status-light { border-radius: 50%; - text-shadow: 0 0 3px #c3b300; - color: #c3b300; + text-shadow: 0 0 3px #250800; + color: #111111; font-size: 16px; } @@ -300,7 +300,7 @@ pre { .stat-text { padding: 0 5px 0 5px; - z-index: 9999; + z-index: 99; } .stat-title { @@ -337,7 +337,7 @@ pre { white-space: nowrap; padding: 0 40px 5px 5px; border: none; - z-index: 10000; + z-index: 100; } /** Query list **/ @@ -372,7 +372,7 @@ pre { } .query-snippet { - height: 116px; + height: 138px; overflow: hidden; } @@ -490,6 +490,22 @@ pre { font-weight: 500; } +#error-type-dropdown { + margin-left: -1px; + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + position: relative; + color: #fff; + background-color: #4ba1bb; + border-color: #46b8da; +} + +.error-type-dropdown-menu { + margin-left: 143px; +} + /** Loading indicators **/ /** ================== **/ @@ -738,7 +754,7 @@ g .cluster:hover { width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.9); - z-index: 10000; + z-index: 100; display: none; align-items: center; justify-content: center; diff --git a/presto-main/src/main/resources/webapp/assets/query.js b/presto-main/src/main/resources/webapp/assets/query.js deleted file mode 100644 index 4b2033a7428e1..0000000000000 --- a/presto-main/src/main/resources/webapp/assets/query.js +++ /dev/null @@ -1,1419 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const Table = Reactable.Table, - Thead = Reactable.Thead, - Th = Reactable.Th, - Tr = Reactable.Tr, - Td = Reactable.Td; - -let TaskList = React.createClass({ - compareTaskId: function(taskA, taskB) { - const taskIdArrA = removeQueryId(taskA).split("."); - const taskIdArrB = removeQueryId(taskB).split("."); - - if (taskIdArrA.length > taskIdArrB.length) { - return 1; - } - for (let i = 0; i < taskIdArrA.length; i++) { - const anum = Number.parseInt(taskIdArrA[i]); - const bnum = Number.parseInt(taskIdArrB[i]); - if (anum !== bnum) return anum > bnum ? 1 : -1; - } - - return 0; - }, - showPortNumbers: function(tasks) { - // check if any host has multiple port numbers - const hostToPortNumber = {}; - for (let i = 0; i < tasks.length; i++) { - const taskUri = tasks[i].taskStatus.self; - const hostname = getHostname(taskUri); - const port = getPort(taskUri); - if ((hostname in hostToPortNumber) && (hostToPortNumber[hostname] !== port)) { - return true; - } - hostToPortNumber[hostname] = port; - } - - return false; - }, - render: function() { - const tasks = this.props.tasks; - - if (tasks === undefined || tasks.length === 0) { - return ( -
-

No threads in the selected group

-
); - } - - const showPortNumbers = this.showPortNumbers(tasks); - - const renderedTasks = tasks.map(task => { - let elapsedTime = parseDuration(task.stats.elapsedTime); - if (elapsedTime === 0) { - elapsedTime = Date.now() - Date.parse(task.stats.createTime); - } - - return ( - - - - { getTaskIdSuffix(task.taskStatus.taskId) } - - - - - { showPortNumbers ? getHostAndPort(task.taskStatus.self) : getHostname(task.taskStatus.self) } - - - - { formatState(task.taskStatus.state, task.stats.fullyBlocked) } - - - { formatCount(task.stats.rawInputPositions) } - - - { formatCount(computeRate(task.stats.rawInputPositions, elapsedTime)) } - - - { formatDataSizeBytes(parseDataSize(task.stats.rawInputDataSize)) } - - - { formatDataSizeBytes(computeRate(parseDataSize(task.stats.rawInputDataSize), elapsedTime)) } - - - { task.stats.queuedDrivers } - - - { task.stats.runningDrivers } - - - { task.stats.blockedDrivers } - - - { task.stats.completedDrivers } - - - { task.stats.elapsedTime } - - - { task.stats.totalCpuTime } - - - { formatDataSizeBytes(task.outputBuffers.totalBufferedBytes) } - - - ); - }); - - return ( - - - - - - - - - - - - - - - - - - { renderedTasks } -
IDHostStateRowsRows/sBytesBytes/sElapsedCPU TimeBuffered
- ); - } -}); - -const BAR_CHART_WIDTH = 800; - -const BAR_CHART_PROPERTIES = { - type: 'bar', - barSpacing: '0', - height: '80px', - barColor: '#747F96', - zeroColor: '#8997B3', - tooltipClassname: 'sparkline-tooltip', - tooltipFormat: 'Task {{offset:offset}} - {{value}}', - disableHiddenCheck: true, -}; - -const HISTOGRAM_WIDTH = 175; - -const HISTOGRAM_PROPERTIES = { - type: 'bar', - barSpacing: '0', - height: '80px', - barColor: '#747F96', - zeroColor: '#747F96', - zeroAxis: true, - tooltipClassname: 'sparkline-tooltip', - tooltipFormat: '{{offset:offset}} -- {{value}} tasks', - disableHiddenCheck: true, -}; - -let StageDetail = React.createClass({ - getInitialState: function() { - return { - expanded: false, - lastRender: null - } - }, - getExpandedIcon: function() { - return this.state.expanded ? "glyphicon-chevron-up" : "glyphicon-chevron-down"; - }, - getExpandedStyle: function() { - return this.state.expanded ? {} : {display: "none"}; - }, - toggleExpanded: function() { - this.setState({ - expanded: !this.state.expanded, - }) - }, - renderHistogram: function(histogramId, inputData, numberFormatter) { - const numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length)); - const dataMin = Math.min.apply(null, inputData); - const dataMax = Math.max.apply(null, inputData); - const bucketSize = (dataMax - dataMin) / numBuckets; - - let histogramData = []; - if (bucketSize === 0) { - histogramData = [inputData.length]; - } - else { - for (let i = 0; i < numBuckets + 1; i++) { - histogramData.push(0); - } - - for (let i in inputData) { - const dataPoint = inputData[i]; - const bucket = Math.floor((dataPoint - dataMin) / bucketSize); - histogramData[bucket] = histogramData[bucket] + 1; - } - } - - const tooltipValueLookups = {'offset' : {}}; - for (let i = 0; i < histogramData.length; i++) { - tooltipValueLookups['offset'][i] = numberFormatter(dataMin + (i * bucketSize)) + "-" + numberFormatter(dataMin + ((i + 1) * bucketSize)); - } - - const stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, {barWidth: (HISTOGRAM_WIDTH / histogramData.length), tooltipValueLookups: tooltipValueLookups}); - $(histogramId).sparkline(histogramData, stageHistogramProperties); - }, - componentDidUpdate: function() { - const stage = this.props.stage; - const numTasks = stage.tasks.length; - - // sort the x-axis - stage.tasks.sort((taskA, taskB) => getTaskIdInStage(taskA.taskStatus.taskId) - getTaskIdInStage(taskB.taskStatus.taskId)); - - const scheduledTimes = stage.tasks.map(task => parseDuration(task.stats.totalScheduledTime)); - const cpuTimes = stage.tasks.map(task => parseDuration(task.stats.totalCpuTime)); - - // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts - if (this.state.lastRender === null || (Date.now() - this.state.lastRender) >= 1000) { - const renderTimestamp = Date.now(); - const stageId = getStageId(stage.stageId); - - this.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, formatDuration); - this.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, formatDuration); - - if (this.state.expanded) { - // this needs to be a string otherwise it will also be passed to numberFormatter - const tooltipValueLookups = {'offset' : {}}; - for (let i = 0; i < numTasks; i++) { - tooltipValueLookups['offset'][i] = getStageId(stage.stageId) + "." + i; - } - - const stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, {barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups}); - - $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, {numberFormatter: formatDuration})); - $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, {numberFormatter: formatDuration})); - } - - this.setState({ - lastRender: renderTimestamp - }); - } - }, - render: function() { - const stage = this.props.stage; - if (stage === undefined || !stage.hasOwnProperty('plan')) { - return ( - - Information about this stage is unavailable. - ); - } - - const totalBufferedBytes = stage.tasks - .map(task => task.outputBuffers.totalBufferedBytes) - .reduce((a, b) => a + b, 0); - - const stageId = getStageId(stage.stageId); - - return ( - - -
{ stageId }
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- Time - -
- Scheduled - - { stage.stageStats.totalScheduledTime } -
- Blocked - - { stage.stageStats.totalBlockedTime } -
- User - - { stage.stageStats.totalUserTime } -
- CPU - - { stage.stageStats.totalCpuTime } -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- Memory - -
- Cumulative - - { formatDataSizeBytes(stage.stageStats.cumulativeUserMemory / 1000) } -
- Current - - { stage.stageStats.userMemoryReservation } -
- Buffers - - { formatDataSize(totalBufferedBytes) } -
- Peak - - { stage.stageStats.peakUserMemoryReservation } -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
- Tasks - -
- Pending - - { stage.tasks.filter(task => task.taskStatus.state === "PLANNED").length } -
- Running - - { stage.tasks.filter(task => task.taskStatus.state === "RUNNING").length } -
- Blocked - - { stage.tasks.filter(task => task.stats.fullyBlocked).length } -
- Total - - { stage.tasks.length } -
-
- - - - - - - - - - - -
- Scheduled Time Skew -
-
-
-
- - - - - - - - - - - -
- CPU Time Skew -
-
-
-
- - - -
- - - - - - - -
- Task Scheduled Time - -
-
-
- - - - - - - -
- Task CPU Time - -
-
-
- - ); - } -}); - -let StageList = React.createClass({ - getStages: function (stage) { - if (stage === undefined || !stage.hasOwnProperty('subStages')) { - return [] - } - - return [].concat.apply(stage, stage.subStages.map(this.getStages)); - }, - render: function() { - const stages = this.getStages(this.props.outputStage); - - if (stages === undefined || stages.length === 0) { - return ( -
-
- No stage information available. -
-
- ); - } - - const renderedStages = stages.map(stage => ); - - return ( -
-
- - - { renderedStages } - -
-
-
- ); - } -}); - -const SMALL_SPARKLINE_PROPERTIES = { - width:'100%', - height: '57px', - fillColor:'#3F4552', - lineColor: '#747F96', - spotColor: '#1EDCFF', - tooltipClassname: 'sparkline-tooltip', - disableHiddenCheck: true, -}; - -const TASK_FILTER = { - ALL: function(state) { return true }, - PLANNED: function(state) { return state === 'PLANNED' }, - RUNNING: function(state) { return state === 'RUNNING' }, - FINISHED: function(state) { return state === 'FINISHED' }, - FAILED: function(state) { return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED' }, -}; - -let QueryDetail = React.createClass({ - getInitialState: function() { - return { - query: null, - lastSnapshotStages: null, - lastSnapshotTasks: null, - - lastScheduledTime: 0, - lastCpuTime: 0, - lastRowInput: 0, - lastByteInput: 0, - - scheduledTimeRate: [], - cpuTimeRate: [], - rowInputRate: [], - byteInputRate: [], - - reservedMemory: [], - - initialized: false, - ended: false, - - lastRefresh: null, - lastRender: null, - - stageRefresh: true, - taskRefresh: true, - - taskFilter: TASK_FILTER.ALL, - }; - }, - resetTimer: function() { - clearTimeout(this.timeoutId); - // stop refreshing when query finishes or fails - if (this.state.query === null || !this.state.ended) { - // task.info-update-interval is set to 3 seconds by default - this.timeoutId = setTimeout(this.refreshLoop, 3000); - } - }, - refreshLoop: function() { - clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously - const queryId = getFirstParameter(window.location.search); - $.get('/v1/query/' + queryId, function (query) { - let lastSnapshotStages = this.state.lastSnapshotStage; - if (this.state.stageRefresh) { - lastSnapshotStages = query.outputStage; - } - let lastSnapshotTasks = this.state.lastSnapshotTasks; - if (this.state.taskRefresh) { - lastSnapshotTasks = query.outputStage; - } - - let lastRefresh = this.state.lastRefresh; - const lastScheduledTime = this.state.lastScheduledTime; - const lastCpuTime = this.state.lastCpuTime; - const lastRowInput = this.state.lastRowInput; - const lastByteInput = this.state.lastByteInput; - const alreadyEnded = this.state.ended; - const nowMillis = Date.now(); - - this.setState({ - query: query, - lastSnapshotStage: lastSnapshotStages, - lastSnapshotTasks: lastSnapshotTasks, - - lastScheduledTime: parseDuration(query.queryStats.totalScheduledTime), - lastCpuTime: parseDuration(query.queryStats.totalCpuTime), - lastRowInput: query.queryStats.processedInputPositions, - lastByteInput: parseDataSize(query.queryStats.processedInputDataSize), - - initialized: true, - ended: query.finalQueryInfo, - - lastRefresh: nowMillis, - }); - - // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement - if (alreadyEnded || (lastRefresh === null && query.state === "RUNNING")) { - this.resetTimer(); - return; - } - - if (lastRefresh === null) { - lastRefresh = nowMillis - parseDuration(query.queryStats.elapsedTime); - } - - const elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0; - if (elapsedSecsSinceLastRefresh >= 0) { - const currentScheduledTimeRate = (parseDuration(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000); - const currentCpuTimeRate = (parseDuration(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000); - const currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh; - const currentByteInputRate = (parseDataSize(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh; - this.setState({ - scheduledTimeRate: addToHistory(currentScheduledTimeRate, this.state.scheduledTimeRate), - cpuTimeRate: addToHistory(currentCpuTimeRate, this.state.cpuTimeRate), - rowInputRate: addToHistory(currentRowInputRate, this.state.rowInputRate), - byteInputRate: addToHistory(currentByteInputRate, this.state.byteInputRate), - reservedMemory: addToHistory(parseDataSize(query.queryStats.userMemoryReservation), this.state.reservedMemory), - }); - } - this.resetTimer(); - }.bind(this)) - .error(() => { - this.setState({ - initialized: true, - }); - this.resetTimer(); - }); - }, - handleTaskRefreshClick: function() { - if (this.state.taskRefresh) { - this.setState({ - taskRefresh: false, - lastSnapshotTasks: this.state.query.outputStage, - }); - } - else { - this.setState({ - taskRefresh: true, - }); - } - }, - renderTaskRefreshButton: function() { - if (this.state.taskRefresh) { - return - } - else { - return - } - }, - handleStageRefreshClick: function() { - if (this.state.stageRefresh) { - this.setState({ - stageRefresh: false, - lastSnapshotStages: this.state.query.outputStage, - }); - } - else { - this.setState({ - stageRefresh: true, - }); - } - }, - renderStageRefreshButton: function() { - if (this.state.stageRefresh) { - return - } - else { - return - } - }, - renderTaskFilterListItem: function(taskFilter, taskFilterText) { - return ( -
  • { taskFilterText }
  • - ); - }, - handleTaskFilterClick: function(filter, event) { - this.setState({ - taskFilter: filter - }); - event.preventDefault(); - }, - getTasksFromStage: function (stage) { - if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) { - return [] - } - - return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage)); - }, - componentDidMount: function() { - this.refreshLoop(); - }, - componentDidUpdate: function() { - // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts - if (this.state.lastRender === null || (Date.now() - this.state.lastRender) >= 1000) { - const renderTimestamp = Date.now(); - $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); - $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); - $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatCount})); - $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatDataSize})); - $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatDataSize})); - - if (this.state.lastRender === null) { - $('#query').each((i, block) => { - hljs.highlightBlock(block); - }); - } - - this.setState({ - lastRender: renderTimestamp, - }); - } - - $('[data-toggle="tooltip"]').tooltip(); - new Clipboard('.copy-button'); - }, - renderTasks: function() { - if (this.state.lastSnapshotTasks === null) { - return; - } - - const tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(task => this.state.taskFilter(task.taskStatus.state), this); - - return ( -
    -
    -
    -

    Tasks

    -
    -
    - - - - - -
    -
    - -
      - { this.renderTaskFilterListItem(TASK_FILTER.ALL, "All") } - { this.renderTaskFilterListItem(TASK_FILTER.PLANNED, "Planned") } - { this.renderTaskFilterListItem(TASK_FILTER.RUNNING, "Running") } - { this.renderTaskFilterListItem(TASK_FILTER.FINISHED, "Finished") } - { this.renderTaskFilterListItem(TASK_FILTER.FAILED, "Aborted/Canceled/Failed") } -
    -
    -
      { this.renderTaskRefreshButton() }
    -
    -
    -
    -
    - -
    -
    -
    - ); - }, - renderStages: function() { - if (this.state.lastSnapshotStage === null) { - return; - } - - return ( -
    -
    -
    -

    Stages

    -
    -
    - - - - -
    - { this.renderStageRefreshButton() } -
    -
    -
    -
    -
    - -
    -
    -
    - ); - }, - renderSessionProperties: function() { - const query = this.state.query; - - const properties = []; - for (let property in query.session.systemProperties) { - if (query.session.systemProperties.hasOwnProperty(property)) { - properties.push( - - { property + "=" + query.session.systemProperties[property] }
    - ); - } - } - - for (let catalog in query.session.catalogProperties) { - if (query.session.catalogProperties.hasOwnProperty(catalog)) { - for (let property in query.session.catalogProperties[catalog]) { - if (query.session.catalogProperties[catalog].hasOwnProperty(property)) { - properties.push( - - { catalog + "." + property + "=" + query.session.catalogProperties[catalog][property] }
    - ); - } - } - } - } - - return properties; - }, - renderResourceEstimates: function() { - const query = this.state.query; - const estimates = query.session.resourceEstimates; - const renderedEstimates = []; - - for (let resource in estimates) { - if (estimates.hasOwnProperty(resource)) { - const upperChars = resource.match(/([A-Z])/g) || []; - let snakeCased = resource; - for (let i = 0, n = upperChars.length; i < n; i++) { - snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase()); - } - - renderedEstimates.push( - - {snakeCased + "=" + query.session.resourceEstimates[resource]}
    - ) - } - } - - return renderedEstimates; - }, - renderProgressBar: function() { - const query = this.state.query; - const progressBarStyle = { width: getProgressBarPercentage(query) + "%", backgroundColor: getQueryStateColor(query) }; - - if (isQueryComplete(query)) { - return ( -
    -
    - { getProgressBarTitle(query) } -
    -
    - ); - } - - return ( - - - - - - - -
    -
    -
    - { getProgressBarTitle(query) } -
    -
    -
    - $.ajax({url: '/v1/query/' + query.queryId + '/killed', type: 'PUT', data: "Killed via web UI"}) } className="btn btn-warning" target="_blank"> - Kill - -
    - ); - }, - renderFailureInfo: function() { - const query = this.state.query; - if (query.failureInfo) { - return ( -
    -
    -

    Error Information

    -
    - - - - - - - - - - - - - - - -
    - Error Type - - { query.errorType } -
    - Error Code - - { query.errorCode.name + " (" + this.state.query.errorCode.code + ")" } -
    - Stack Trace - - - -
    -                                            { formatStackTrace(query.failureInfo) }
    -                                        
    -
    -
    -
    - ); - } - else { - return ""; - } - }, - render: function() { - const query = this.state.query; - - if (query === null || this.state.initialized === false) { - let label = (
    Loading...
    ); - if (this.state.initialized) { - label = "Query not found"; - } - return ( -
    -

    { label }

    -
    - ); - } - - return ( -
    -
    -
    -

    - { query.queryId } - - -

    -
    -
    - - - - - - -
    - Overview -   - Live Plan -   - Stage Performance -   - Splits -   - JSON -
    -
    -
    -
    -
    -
    - { this.renderProgressBar() } -
    -
    -
    -
    -

    Session

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - User - - { query.session.user } -    - - -
    - Principal - - { query.session.principal } -
    - Source - - { query.session.source } -
    - Catalog - - { query.session.catalog } -
    - Schema - - { query.session.schema } -
    - Client Address - - { query.session.remoteUserAddress } -
    - Client Tags - - { query.session.clientTags.join(", ") } -
    - Session Properties - - { this.renderSessionProperties() } -
    - Resource Estimates - - { this.renderResourceEstimates() } -
    -
    -
    -

    Execution

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Resource Group - - { query.resourceGroupId.join(".") } -
    - Submission Time - - { formatShortDateTime(new Date(query.queryStats.createTime)) } -
    - Completion Time - - { query.queryStats.endTime ? formatShortDateTime(new Date(query.queryStats.endTime)) : "" } -
    - Elapsed Time - - { query.queryStats.elapsedTime } -
    - Queued Time - - { query.queryStats.queuedTime } -
    - Execution Time - - { query.queryStats.executionTime } -
    -
    -
    -
    -
    -
    -
    -

    Resource Utilization Summary

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - CPU Time - - { query.queryStats.totalCpuTime } -
    - Scheduled Time - - { query.queryStats.totalScheduledTime } -
    - Blocked Time - - { query.queryStats.totalBlockedTime } -
    - Input Rows - - { formatCount(query.queryStats.processedInputPositions) } -
    - Input Data - - { query.queryStats.processedInputDataSize } -
    - Raw Input Rows - - { formatCount(query.queryStats.rawInputPositions) } -
    - Raw Input Data - - { query.queryStats.rawInputDataSize } -
    - Peak User Memory - - { query.queryStats.peakUserMemoryReservation } -
    - Peak Total Memory - - { query.queryStats.peakTotalMemoryReservation } -
    - Memory Pool - - { query.memoryPool } -
    - Cumulative Memory - - { formatDataSizeBytes(query.queryStats.cumulativeUserMemory / 1000.0, "") + " seconds" } -
    - Output Rows - - { formatCount(query.queryStats.outputPositions) } -
    - Output Data - - { query.queryStats.outputDataSize } -
    - Written Rows - - { formatCount(query.queryStats.writtenPositions) } -
    - Logical Written Data - - { query.queryStats.logicalWrittenDataSize } -
    - Physical Written Data - - { query.queryStats.physicalWrittenDataSize } -
    -
    -
    -

    Timeline

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - Parallelism - -
    -
    Loading ...
    -
    -
    - { formatCount(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1]) } -
    - Scheduled Time/s - -
    -
    Loading ...
    -
    -
    - { formatCount(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1]) } -
    - Input Rows/s - -
    -
    Loading ...
    -
    -
    - { formatCount(this.state.rowInputRate[this.state.rowInputRate.length - 1]) } -
    - Input Bytes/s - -
    -
    Loading ...
    -
    -
    - { formatDataSize(this.state.byteInputRate[this.state.byteInputRate.length - 1]) } -
    - Memory Utilization - -
    -
    Loading ...
    -
    -
    - { formatDataSize(this.state.reservedMemory[this.state.reservedMemory.length - 1]) } -
    -
    -
    -
    -
    - { this.renderFailureInfo() } -
    -
    -

    - Query - - -

    -
    -                            
    -                                { query.query }
    -                            
    -                        
    -
    -
    - { this.renderStages() } - { this.renderTasks() } -
    - ); - } -}); - -ReactDOM.render( - , - document.getElementById('query-detail') -); diff --git a/presto-main/src/main/resources/webapp/dist/embedded_plan.js b/presto-main/src/main/resources/webapp/dist/embedded_plan.js new file mode 100644 index 0000000000000..f4ace44bba695 --- /dev/null +++ b/presto-main/src/main/resources/webapp/dist/embedded_plan.js @@ -0,0 +1,20670 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./embedded_plan.jsx"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./components/LivePlan.jsx": +/*!*********************************!*\ + !*** ./components/LivePlan.jsx ***! + \*********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.LivePlan = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _server = __webpack_require__(/*! react-dom/server */ \"./node_modules/react-dom/server.browser.js\");\n\nvar _server2 = _interopRequireDefault(_server);\n\nvar _dagreD = __webpack_require__(/*! dagre-d3 */ \"./node_modules/dagre-d3/index.js\");\n\nvar dagreD3 = _interopRequireWildcard(_dagreD);\n\nvar _d = __webpack_require__(/*! d3 */ \"./node_modules/d3/index.js\");\n\nvar d3 = _interopRequireWildcard(_d);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nvar _QueryHeader = __webpack_require__(/*! ./QueryHeader */ \"./components/QueryHeader.jsx\");\n\nfunction _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\nvar StageStatistics = function (_React$Component) {\n _inherits(StageStatistics, _React$Component);\n\n function StageStatistics() {\n _classCallCheck(this, StageStatistics);\n\n return _possibleConstructorReturn(this, (StageStatistics.__proto__ || Object.getPrototypeOf(StageStatistics)).apply(this, arguments));\n }\n\n _createClass(StageStatistics, [{\n key: \"render\",\n value: function render() {\n var stage = this.props.stage;\n var stats = this.props.stage.stageStats;\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n null,\n \"Output: \",\n stats.outputDataSize + \" / \" + (0, _utils.formatCount)(stats.outputPositions) + \" rows\",\n _react2.default.createElement(\"br\", null),\n \"Buffered: \",\n stats.bufferedDataSize,\n _react2.default.createElement(\"hr\", null),\n stage.state,\n _react2.default.createElement(\"hr\", null),\n \"CPU: \",\n stats.totalCpuTime,\n stats.fullyBlocked ? _react2.default.createElement(\n \"div\",\n { style: { color: '#ff0000' } },\n \"Blocked: \",\n stats.totalBlockedTime,\n \" \"\n ) : _react2.default.createElement(\n \"div\",\n null,\n \"Blocked: \",\n stats.totalBlockedTime,\n \" \"\n ),\n \"Memory: \",\n stats.userMemoryReservation,\n _react2.default.createElement(\"br\", null),\n \"Splits: \",\n \"Q:\" + stats.queuedDrivers + \", R:\" + stats.runningDrivers + \", F:\" + stats.completedDrivers,\n _react2.default.createElement(\"hr\", null),\n \"Input: \",\n stats.rawInputDataSize + \" / \" + (0, _utils.formatCount)(stats.rawInputPositions),\n \" rows\"\n )\n );\n }\n }], [{\n key: \"flatten\",\n value: function flatten(queryInfo) {\n var stages = new Map();\n StageStatistics.flattenStage(queryInfo.outputStage, stages);\n\n return {\n id: queryInfo.queryId,\n root: queryInfo.outputStage.plan.id,\n stageStats: {},\n stages: stages\n };\n }\n }, {\n key: \"flattenStage\",\n value: function flattenStage(stageInfo, result) {\n stageInfo.subStages.forEach(function (stage) {\n StageStatistics.flattenStage(stage, result);\n });\n\n var nodes = new Map();\n StageStatistics.flattenNode(result, stageInfo.plan.root, nodes);\n\n result.set(stageInfo.plan.id, {\n stageId: stageInfo.stageId,\n id: stageInfo.plan.id,\n root: stageInfo.plan.root.id,\n distribution: stageInfo.plan.distribution,\n stageStats: stageInfo.stageStats,\n state: stageInfo.state,\n nodes: nodes\n });\n }\n }, {\n key: \"flattenNode\",\n value: function flattenNode(stages, nodeInfo, result) {\n var allSources = (0, _utils.computeSources)(nodeInfo);\n var sources = allSources[0];\n var remoteSources = allSources[1];\n\n result.set(nodeInfo.id, {\n id: nodeInfo.id,\n type: nodeInfo['@type'],\n sources: sources.map(function (node) {\n return node.id;\n }),\n remoteSources: remoteSources,\n stats: {}\n });\n\n sources.forEach(function (child) {\n StageStatistics.flattenNode(stages, child, result);\n });\n }\n }]);\n\n return StageStatistics;\n}(_react2.default.Component);\n\nvar LivePlan = exports.LivePlan = function (_React$Component2) {\n _inherits(LivePlan, _React$Component2);\n\n function LivePlan(props) {\n _classCallCheck(this, LivePlan);\n\n var _this2 = _possibleConstructorReturn(this, (LivePlan.__proto__ || Object.getPrototypeOf(LivePlan)).call(this, props));\n\n _this2.state = {\n initialized: false,\n ended: false,\n\n query: null,\n\n graph: (0, _utils.initializeGraph)(),\n svg: (0, _utils.initializeSvg)(\"#plan-canvas\"),\n render: new dagreD3.render()\n };\n return _this2;\n }\n\n _createClass(LivePlan, [{\n key: \"resetTimer\",\n value: function resetTimer() {\n clearTimeout(this.timeoutId);\n // stop refreshing when query finishes or fails\n if (this.state.query === null || !this.state.ended) {\n this.timeoutId = setTimeout(this.refreshLoop.bind(this), 1000);\n }\n }\n }, {\n key: \"refreshLoop\",\n value: function refreshLoop() {\n var _this3 = this;\n\n clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously\n fetch('/v1/query/' + this.props.queryId).then(function (response) {\n return response.json();\n }).then(function (query) {\n _this3.setState({\n query: query,\n\n initialized: true,\n ended: query.finalQueryInfo\n });\n _this3.resetTimer();\n }).catch(function () {\n _this3.setState({\n initialized: true\n });\n _this3.resetTimer();\n });\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n this.refreshLoop.bind(this)();\n }\n }, {\n key: \"updateD3Stage\",\n value: function updateD3Stage(stage, graph) {\n var clusterId = stage.stageId;\n var stageRootNodeId = \"stage-\" + stage.id + \"-root\";\n var color = (0, _utils.getStageStateColor)(stage);\n\n graph.setNode(clusterId, { label: \"Stage \" + stage.id + \" \", clusterLabelPos: 'top-right', style: 'fill: ' + color, labelStyle: 'fill: #fff' });\n\n // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React\n var html = _server2.default.renderToString(_react2.default.createElement(StageStatistics, { key: stage.id, stage: stage }));\n\n graph.setNode(stageRootNodeId, { class: \"stage-stats\", label: html, labelType: \"html\" });\n graph.setParent(stageRootNodeId, clusterId);\n graph.setEdge(\"node-\" + stage.root, stageRootNodeId, { style: \"visibility: hidden\" });\n\n stage.nodes.forEach(function (node) {\n var nodeId = \"node-\" + node.id;\n\n graph.setNode(nodeId, { label: node.type, style: 'fill: #fff' });\n graph.setParent(nodeId, clusterId);\n\n node.sources.forEach(function (source) {\n graph.setEdge(\"node-\" + source, nodeId, { arrowheadStyle: \"fill: #fff; stroke-width: 0;\" });\n });\n\n if (node.type === 'remoteSource') {\n graph.setNode(nodeId, { label: '', shape: \"circle\" });\n\n node.remoteSources.forEach(function (sourceId) {\n graph.setEdge(\"stage-\" + sourceId + \"-root\", nodeId, { style: \"stroke-width: 5px;\", arrowheadStyle: \"fill: #fff; stroke-width: 0;\" });\n });\n }\n });\n }\n }, {\n key: \"updateD3Graph\",\n value: function updateD3Graph() {\n var _this4 = this;\n\n if (!this.state.query) {\n return;\n }\n\n var graph = this.state.graph;\n var stages = StageStatistics.flatten(this.state.query).stages;\n stages.forEach(function (stage) {\n _this4.updateD3Stage(stage, graph);\n });\n\n this.state.render(d3.select(\"#plan-canvas g\"), graph);\n\n var svg = this.state.svg;\n svg.selectAll(\"g.cluster\").on(\"click\", LivePlan.handleStageClick);\n svg.attr(\"height\", graph.graph().height);\n svg.attr(\"width\", graph.graph().width);\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.state.query;\n\n if (query === null || this.state.initialized === false) {\n var label = _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n );\n if (this.state.initialized) {\n label = \"Query not found\";\n }\n return _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n label\n )\n )\n );\n }\n\n var livePlanGraph = null;\n if (query && !query.outputStage) {\n livePlanGraph = _react2.default.createElement(\n \"div\",\n { className: \"row error-message\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n _react2.default.createElement(\n \"h4\",\n null,\n \"Live plan graph will appear automatically when query starts running.\"\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"loader\" },\n \"Loading...\"\n )\n )\n );\n } else {\n this.updateD3Graph();\n }\n\n // TODO: Refactor components to move refreshLoop to parent rather than using this property\n if (this.props.isEmbedded) {\n return _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n livePlanGraph\n )\n );\n }\n\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(_QueryHeader.QueryHeader, { query: query }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n livePlanGraph\n )\n )\n );\n }\n }], [{\n key: \"handleStageClick\",\n value: function handleStageClick(stageCssId) {\n window.open(\"stage.html?\" + stageCssId, '_blank');\n }\n }]);\n\n return LivePlan;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/LivePlan.jsx?"); + +/***/ }), + +/***/ "./components/QueryHeader.jsx": +/*!************************************!*\ + !*** ./components/QueryHeader.jsx ***! + \************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.QueryHeader = undefined;\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _utils = __webpack_require__(/*! ../utils */ \"./utils.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar QueryHeader = exports.QueryHeader = function (_React$Component) {\n _inherits(QueryHeader, _React$Component);\n\n function QueryHeader(props) {\n _classCallCheck(this, QueryHeader);\n\n return _possibleConstructorReturn(this, (QueryHeader.__proto__ || Object.getPrototypeOf(QueryHeader)).call(this, props));\n }\n\n _createClass(QueryHeader, [{\n key: \"renderProgressBar\",\n value: function renderProgressBar() {\n var query = this.props.query;\n var progressBarStyle = { width: (0, _utils.getProgressBarPercentage)(query) + \"%\", backgroundColor: (0, _utils.getQueryStateColor)(query) };\n\n if ((0, _utils.isQueryEnded)(query)) {\n return _react2.default.createElement(\n \"div\",\n { className: \"progress-large\" },\n _react2.default.createElement(\n \"div\",\n { className: \"progress-bar progress-bar-info\", role: \"progressbar\", \"aria-valuenow\": (0, _utils.getProgressBarPercentage)(query), \"aria-valuemin\": \"0\", \"aria-valuemax\": \"100\",\n style: progressBarStyle },\n (0, _utils.getProgressBarTitle)(query)\n )\n );\n }\n\n return _react2.default.createElement(\n \"table\",\n null,\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n { width: \"100%\" },\n _react2.default.createElement(\n \"div\",\n { className: \"progress-large\" },\n _react2.default.createElement(\n \"div\",\n { className: \"progress-bar progress-bar-info\", role: \"progressbar\", \"aria-valuenow\": (0, _utils.getProgressBarPercentage)(query), \"aria-valuemin\": \"0\", \"aria-valuemax\": \"100\",\n style: progressBarStyle },\n (0, _utils.getProgressBarTitle)(query)\n )\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"a\",\n { onClick: function onClick() {\n return $.ajax({ url: '/v1/query/' + query.queryId + '/preempted', type: 'PUT', data: \"Preempted via web UI\" });\n }, className: \"btn btn-warning\",\n target: \"_blank\" },\n \"Preempt\"\n )\n ),\n _react2.default.createElement(\n \"td\",\n null,\n _react2.default.createElement(\n \"a\",\n { onClick: function onClick() {\n return $.ajax({ url: '/v1/query/' + query.queryId + '/killed', type: 'PUT', data: \"Killed via web UI\" });\n }, className: \"btn btn-warning\",\n target: \"_blank\" },\n \"Kill\"\n )\n )\n )\n )\n );\n }\n }, {\n key: \"renderTab\",\n value: function renderTab(path, name) {\n var queryId = this.props.query.queryId;\n if (window.location.pathname.includes(path)) {\n return _react2.default.createElement(\n \"a\",\n { href: path + '?' + queryId, className: \"btn btn-info navbar-btn nav-disabled\" },\n name\n );\n }\n\n return _react2.default.createElement(\n \"a\",\n { href: path + '?' + queryId, className: \"btn btn-info navbar-btn\" },\n name\n );\n }\n }, {\n key: \"render\",\n value: function render() {\n var query = this.props.query;\n return _react2.default.createElement(\n \"div\",\n null,\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"h3\",\n { className: \"query-id\" },\n _react2.default.createElement(\n \"span\",\n { id: \"query-id\" },\n query.queryId\n ),\n _react2.default.createElement(\n \"a\",\n { className: \"btn copy-button\", \"data-clipboard-target\": \"#query-id\", \"data-toggle\": \"tooltip\", \"data-placement\": \"right\", title: \"Copy to clipboard\" },\n _react2.default.createElement(\"span\", { className: \"glyphicon glyphicon-copy\", \"aria-hidden\": \"true\", alt: \"Copy to clipboard\" })\n )\n )\n ),\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-6\" },\n _react2.default.createElement(\n \"table\",\n { className: \"header-inline-links\" },\n _react2.default.createElement(\n \"tbody\",\n null,\n _react2.default.createElement(\n \"tr\",\n null,\n _react2.default.createElement(\n \"td\",\n null,\n this.renderTab(\"query.html\", \"Overview\"),\n \"\\xA0\",\n this.renderTab(\"plan.html\", \"Live Plan\"),\n \"\\xA0\",\n this.renderTab(\"stage.html\", \"Stage Performance\"),\n \"\\xA0\",\n this.renderTab(\"timeline.html\", \"Splits\"),\n \"\\xA0\",\n _react2.default.createElement(\n \"a\",\n { href: \"/v1/query/\" + query.queryId + \"?pretty\", className: \"btn btn-info navbar-btn\", target: \"_blank\" },\n \"JSON\"\n )\n )\n )\n )\n )\n )\n ),\n _react2.default.createElement(\"hr\", { className: \"h2-hr\" }),\n _react2.default.createElement(\n \"div\",\n { className: \"row\" },\n _react2.default.createElement(\n \"div\",\n { className: \"col-xs-12\" },\n this.renderProgressBar()\n )\n )\n );\n }\n }]);\n\n return QueryHeader;\n}(_react2.default.Component);\n\n//# sourceURL=webpack:///./components/QueryHeader.jsx?"); + +/***/ }), + +/***/ "./embedded_plan.jsx": +/*!***************************!*\ + !*** ./embedded_plan.jsx ***! + \***************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _react = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nvar _react2 = _interopRequireDefault(_react);\n\nvar _reactDom = __webpack_require__(/*! react-dom */ \"./node_modules/react-dom/index.js\");\n\nvar _reactDom2 = _interopRequireDefault(_reactDom);\n\nvar _LivePlan = __webpack_require__(/*! ./components/LivePlan */ \"./components/LivePlan.jsx\");\n\nvar _utils = __webpack_require__(/*! ./utils */ \"./utils.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_reactDom2.default.render(_react2.default.createElement(_LivePlan.LivePlan, { queryId: (0, _utils.getFirstParameter)(window.location.search), isEmbedded: true }), document.getElementById('live-plan-header'));\n\n//# sourceURL=webpack:///./embedded_plan.jsx?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/array.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/array.js ***! + \********************************************/ +/*! exports provided: slice, map */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\nvar map = array.map;\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/ascending.js": +/*!************************************************!*\ + !*** ./node_modules/d3-array/src/ascending.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/bisect.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-array/src/bisect.js ***! + \*********************************************/ +/*! exports provided: bisectRight, bisectLeft, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return bisectRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return bisectLeft; });\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisector */ \"./node_modules/d3-array/src/bisector.js\");\n\n\n\nvar ascendingBisect = Object(_bisector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\nvar bisectRight = ascendingBisect.right;\nvar bisectLeft = ascendingBisect.left;\n/* harmony default export */ __webpack_exports__[\"default\"] = (bisectRight);\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/bisect.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/bisector.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-array/src/bisector.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n});\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(f(d), x);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/bisector.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-array/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/cross.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/cross.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pairs */ \"./node_modules/d3-array/src/pairs.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values0, values1, reduce) {\n var n0 = values0.length,\n n1 = values1.length,\n values = new Array(n0 * n1),\n i0,\n i1,\n i,\n value0;\n\n if (reduce == null) reduce = _pairs__WEBPACK_IMPORTED_MODULE_0__[\"pair\"];\n\n for (i0 = i = 0; i0 < n0; ++i0) {\n for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {\n values[i] = reduce(value0, values1[i1]);\n }\n }\n\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/descending.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-array/src/descending.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/deviation.js": +/*!************************************************!*\ + !*** ./node_modules/d3-array/src/deviation.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./variance */ \"./node_modules/d3-array/src/variance.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n var v = Object(_variance__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(array, f);\n return v ? Math.sqrt(v) : v;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/deviation.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/extent.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-array/src/extent.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n return [min, max];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/histogram.js": +/*!************************************************!*\ + !*** ./node_modules/d3-array/src/histogram.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-array/src/array.js\");\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisect */ \"./node_modules/d3-array/src/bisect.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-array/src/constant.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/d3-array/src/extent.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./identity */ \"./node_modules/d3-array/src/identity.js\");\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./range */ \"./node_modules/d3-array/src/range.js\");\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ticks */ \"./node_modules/d3-array/src/ticks.js\");\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/d3-array/src/threshold/sturges.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n domain = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n threshold = _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\n\n function histogram(data) {\n var i,\n n = data.length,\n x,\n values = new Array(n);\n\n for (i = 0; i < n; ++i) {\n values[i] = value(data[i], i, data);\n }\n\n var xz = domain(values),\n x0 = xz[0],\n x1 = xz[1],\n tz = threshold(values, x0, x1);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n tz = Object(_ticks__WEBPACK_IMPORTED_MODULE_6__[\"tickStep\"])(x0, x1, tz);\n tz = Object(_range__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(Math.ceil(x0 / tz) * tz, x1, tz); // exclusive\n }\n\n // Remove any thresholds outside the domain.\n var m = tz.length;\n while (tz[0] <= x0) tz.shift(), --m;\n while (tz[m - 1] > x1) tz.pop(), --m;\n\n var bins = new Array(m + 1),\n bin;\n\n // Initialize bins.\n for (i = 0; i <= m; ++i) {\n bin = bins[i] = [];\n bin.x0 = i > 0 ? tz[i - 1] : x0;\n bin.x1 = i < m ? tz[i] : x1;\n }\n\n // Assign data to bins by value, ignoring any outside the domain.\n for (i = 0; i < n; ++i) {\n x = values[i];\n if (x0 <= x && x <= x1) {\n bins[Object(_bisect__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(tz, x, 0, m)].push(data[i]);\n }\n }\n\n return bins;\n }\n\n histogram.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : value;\n };\n\n histogram.domain = function(_) {\n return arguments.length ? (domain = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])([_[0], _[1]]), histogram) : domain;\n };\n\n histogram.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : threshold;\n };\n\n return histogram;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/histogram.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/identity.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-array/src/identity.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/index.js ***! + \********************************************/ +/*! exports provided: bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./bisect */ \"./node_modules/d3-array/src/bisect.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectLeft\"]; });\n\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-array/src/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return _ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./bisector */ \"./node_modules/d3-array/src/bisector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return _bisector__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./cross */ \"./node_modules/d3-array/src/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return _cross__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./descending */ \"./node_modules/d3-array/src/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return _descending__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./deviation */ \"./node_modules/d3-array/src/deviation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return _deviation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./extent */ \"./node_modules/d3-array/src/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return _extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _histogram__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./histogram */ \"./node_modules/d3-array/src/histogram.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return _histogram__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./threshold/freedmanDiaconis */ \"./node_modules/d3-array/src/threshold/freedmanDiaconis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _threshold_scott__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./threshold/scott */ \"./node_modules/d3-array/src/threshold/scott.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return _threshold_scott__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/d3-array/src/threshold/sturges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _max__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./max */ \"./node_modules/d3-array/src/max.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return _max__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _mean__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./mean */ \"./node_modules/d3-array/src/mean.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return _mean__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _median__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./median */ \"./node_modules/d3-array/src/median.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return _median__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./merge */ \"./node_modules/d3-array/src/merge.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return _merge__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./min */ \"./node_modules/d3-array/src/min.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return _min__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./pairs */ \"./node_modules/d3-array/src/pairs.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return _pairs__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _permute__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./permute */ \"./node_modules/d3-array/src/permute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return _permute__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./quantile */ \"./node_modules/d3-array/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return _quantile__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./range */ \"./node_modules/d3-array/src/range.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return _range__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./scan */ \"./node_modules/d3-array/src/scan.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return _scan__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _shuffle__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./shuffle */ \"./node_modules/d3-array/src/shuffle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return _shuffle__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./sum */ \"./node_modules/d3-array/src/sum.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return _sum__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./ticks */ \"./node_modules/d3-array/src/ticks.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickStep\"]; });\n\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./transpose */ \"./node_modules/d3-array/src/transpose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return _transpose__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./variance */ \"./node_modules/d3-array/src/variance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return _variance__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _zip__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./zip */ \"./node_modules/d3-array/src/zip.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return _zip__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/max.js": +/*!******************************************!*\ + !*** ./node_modules/d3-array/src/max.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n return max;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/max.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/mean.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-array/src/mean.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = n,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) sum += value;\n else --m;\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) sum += value;\n else --m;\n }\n }\n\n if (m) return sum / m;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/mean.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/median.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-array/src/median.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./quantile */ \"./node_modules/d3-array/src/quantile.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n numbers = [];\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(values[i]))) {\n numbers.push(value);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(valueof(values[i], i, values)))) {\n numbers.push(value);\n }\n }\n }\n\n return Object(_quantile__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(numbers.sort(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), 0.5);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/median.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/merge.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/merge.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(arrays) {\n var n = arrays.length,\n m,\n i = -1,\n j = 0,\n merged,\n array;\n\n while (++i < n) j += arrays[i].length;\n merged = new Array(j);\n\n while (--n >= 0) {\n array = arrays[n];\n m = array.length;\n while (--m >= 0) {\n merged[--j] = array[m];\n }\n }\n\n return merged;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/merge.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/min.js": +/*!******************************************!*\ + !*** ./node_modules/d3-array/src/min.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n return min;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/min.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/number.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-array/src/number.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x === null ? NaN : +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/pairs.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/pairs.js ***! + \********************************************/ +/*! exports provided: default, pair */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pair\", function() { return pair; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n if (f == null) f = pair;\n var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);\n while (i < n) pairs[i] = f(p, p = array[++i]);\n return pairs;\n});\n\nfunction pair(a, b) {\n return [a, b];\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/pairs.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/permute.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-array/src/permute.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, indexes) {\n var i = indexes.length, permutes = new Array(i);\n while (i--) permutes[i] = array[indexes[i]];\n return permutes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/permute.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/quantile.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-array/src/quantile.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, p, valueof) {\n if (valueof == null) valueof = _number__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n if (!(n = values.length)) return;\n if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n var n,\n i = (n - 1) * p,\n i0 = Math.floor(i),\n value0 = +valueof(values[i0], i0, values),\n value1 = +valueof(values[i0 + 1], i0 + 1, values);\n return value0 + (value1 - value0) * (i - i0);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/range.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/range.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/range.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/scan.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-array/src/scan.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, compare) {\n if (!(n = values.length)) return;\n var n,\n i = 0,\n j = 0,\n xi,\n xj = values[j];\n\n if (compare == null) compare = _ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n\n while (++i < n) {\n if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {\n xj = xi, j = i;\n }\n }\n\n if (compare(xj, xj) === 0) return j;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/scan.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/shuffle.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-array/src/shuffle.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, i0, i1) {\n var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m + i0];\n array[m + i0] = array[i + i0];\n array[i + i0] = t;\n }\n\n return array;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/shuffle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/sum.js": +/*!******************************************!*\ + !*** ./node_modules/d3-array/src/sum.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (value = +values[i]) sum += value; // Note: zero and null are equivalent.\n }\n }\n\n else {\n while (++i < n) {\n if (value = +valueof(values[i], i, values)) sum += value;\n }\n }\n\n return sum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/sum.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/threshold/freedmanDiaconis.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-array/src/threshold/freedmanDiaconis.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/d3-array/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ascending */ \"./node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../number */ \"./node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../quantile */ \"./node_modules/d3-array/src/quantile.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n values = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(values, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]).sort(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n return Math.ceil((max - min) / (2 * (Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.75) - Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.25)) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/threshold/freedmanDiaconis.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/threshold/scott.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-array/src/threshold/scott.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deviation */ \"./node_modules/d3-array/src/deviation.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n return Math.ceil((max - min) / (3.5 * Object(_deviation__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/threshold/scott.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/threshold/sturges.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-array/src/threshold/sturges.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n return Math.ceil(Math.log(values.length) / Math.LN2) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/threshold/sturges.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/ticks.js": +/*!********************************************!*\ + !*** ./node_modules/d3-array/src/ticks.js ***! + \********************************************/ +/*! exports provided: default, tickIncrement, tickStep */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return tickIncrement; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return tickStep; });\nvar e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n});\n\nfunction tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nfunction tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/ticks.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/transpose.js": +/*!************************************************!*\ + !*** ./node_modules/d3-array/src/transpose.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./min */ \"./node_modules/d3-array/src/min.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(matrix) {\n if (!(n = matrix.length)) return [];\n for (var i = -1, m = Object(_min__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(matrix, length), transpose = new Array(m); ++i < m;) {\n for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n row[j] = matrix[j][i];\n }\n }\n return transpose;\n});\n\nfunction length(d) {\n return d.length;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/transpose.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/variance.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-array/src/variance.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = 0,\n i = -1,\n mean = 0,\n value,\n delta,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n if (m > 1) return sum / (m - 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/variance.js?"); + +/***/ }), + +/***/ "./node_modules/d3-array/src/zip.js": +/*!******************************************!*\ + !*** ./node_modules/d3-array/src/zip.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transpose */ \"./node_modules/d3-array/src/transpose.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_transpose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(arguments);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-array/src/zip.js?"); + +/***/ }), + +/***/ "./node_modules/d3-axis/src/array.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-axis/src/array.js ***! + \*******************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-axis/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-axis/src/axis.js": +/*!******************************************!*\ + !*** ./node_modules/d3-axis/src/axis.js ***! + \******************************************/ +/*! exports provided: axisTop, axisRight, axisBottom, axisLeft */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return axisTop; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return axisRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return axisBottom; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return axisLeft; });\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-axis/src/array.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./identity */ \"./node_modules/d3-axis/src/identity.js\");\n\n\n\nvar top = 1,\n right = 2,\n bottom = 3,\n left = 4,\n epsilon = 1e-6;\n\nfunction translateX(x) {\n return \"translate(\" + (x + 0.5) + \",0)\";\n}\n\nfunction translateY(y) {\n return \"translate(0,\" + (y + 0.5) + \")\";\n}\n\nfunction number(scale) {\n return function(d) {\n return +scale(d);\n };\n}\n\nfunction center(scale) {\n var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.\n if (scale.round()) offset = Math.round(offset);\n return function(d) {\n return +scale(d) + offset;\n };\n}\n\nfunction entering() {\n return !this.__axis;\n}\n\nfunction axis(orient, scale) {\n var tickArguments = [],\n tickValues = null,\n tickFormat = null,\n tickSizeInner = 6,\n tickSizeOuter = 6,\n tickPadding = 3,\n k = orient === top || orient === left ? -1 : 1,\n x = orient === left || orient === right ? \"x\" : \"y\",\n transform = orient === top || orient === bottom ? translateX : translateY;\n\n function axis(context) {\n var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,\n format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : tickFormat,\n spacing = Math.max(tickSizeInner, 0) + tickPadding,\n range = scale.range(),\n range0 = +range[0] + 0.5,\n range1 = +range[range.length - 1] + 0.5,\n position = (scale.bandwidth ? center : number)(scale.copy()),\n selection = context.selection ? context.selection() : context,\n path = selection.selectAll(\".domain\").data([null]),\n tick = selection.selectAll(\".tick\").data(values, scale).order(),\n tickExit = tick.exit(),\n tickEnter = tick.enter().append(\"g\").attr(\"class\", \"tick\"),\n line = tick.select(\"line\"),\n text = tick.select(\"text\");\n\n path = path.merge(path.enter().insert(\"path\", \".tick\")\n .attr(\"class\", \"domain\")\n .attr(\"stroke\", \"currentColor\"));\n\n tick = tick.merge(tickEnter);\n\n line = line.merge(tickEnter.append(\"line\")\n .attr(\"stroke\", \"currentColor\")\n .attr(x + \"2\", k * tickSizeInner));\n\n text = text.merge(tickEnter.append(\"text\")\n .attr(\"fill\", \"currentColor\")\n .attr(x, k * spacing)\n .attr(\"dy\", orient === top ? \"0em\" : orient === bottom ? \"0.71em\" : \"0.32em\"));\n\n if (context !== selection) {\n path = path.transition(context);\n tick = tick.transition(context);\n line = line.transition(context);\n text = text.transition(context);\n\n tickExit = tickExit.transition(context)\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute(\"transform\"); });\n\n tickEnter\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });\n }\n\n tickExit.remove();\n\n path\n .attr(\"d\", orient === left || orient == right\n ? (tickSizeOuter ? \"M\" + k * tickSizeOuter + \",\" + range0 + \"H0.5V\" + range1 + \"H\" + k * tickSizeOuter : \"M0.5,\" + range0 + \"V\" + range1)\n : (tickSizeOuter ? \"M\" + range0 + \",\" + k * tickSizeOuter + \"V0.5H\" + range1 + \"V\" + k * tickSizeOuter : \"M\" + range0 + \",0.5H\" + range1));\n\n tick\n .attr(\"opacity\", 1)\n .attr(\"transform\", function(d) { return transform(position(d)); });\n\n line\n .attr(x + \"2\", k * tickSizeInner);\n\n text\n .attr(x, k * spacing)\n .text(format);\n\n selection.filter(entering)\n .attr(\"fill\", \"none\")\n .attr(\"font-size\", 10)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"text-anchor\", orient === right ? \"start\" : orient === left ? \"end\" : \"middle\");\n\n selection\n .each(function() { this.__axis = position; });\n }\n\n axis.scale = function(_) {\n return arguments.length ? (scale = _, axis) : scale;\n };\n\n axis.ticks = function() {\n return tickArguments = _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(arguments), axis;\n };\n\n axis.tickArguments = function(_) {\n return arguments.length ? (tickArguments = _ == null ? [] : _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_), axis) : tickArguments.slice();\n };\n\n axis.tickValues = function(_) {\n return arguments.length ? (tickValues = _ == null ? null : _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_), axis) : tickValues && tickValues.slice();\n };\n\n axis.tickFormat = function(_) {\n return arguments.length ? (tickFormat = _, axis) : tickFormat;\n };\n\n axis.tickSize = function(_) {\n return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeInner = function(_) {\n return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeOuter = function(_) {\n return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;\n };\n\n axis.tickPadding = function(_) {\n return arguments.length ? (tickPadding = +_, axis) : tickPadding;\n };\n\n return axis;\n}\n\nfunction axisTop(scale) {\n return axis(top, scale);\n}\n\nfunction axisRight(scale) {\n return axis(right, scale);\n}\n\nfunction axisBottom(scale) {\n return axis(bottom, scale);\n}\n\nfunction axisLeft(scale) {\n return axis(left, scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-axis/src/axis.js?"); + +/***/ }), + +/***/ "./node_modules/d3-axis/src/identity.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-axis/src/identity.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-axis/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-axis/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-axis/src/index.js ***! + \*******************************************/ +/*! exports provided: axisTop, axisRight, axisBottom, axisLeft */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _axis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./axis */ \"./node_modules/d3-axis/src/axis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return _axis__WEBPACK_IMPORTED_MODULE_0__[\"axisTop\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return _axis__WEBPACK_IMPORTED_MODULE_0__[\"axisRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return _axis__WEBPACK_IMPORTED_MODULE_0__[\"axisBottom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return _axis__WEBPACK_IMPORTED_MODULE_0__[\"axisLeft\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-axis/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-brush/src/brush.js": +/*!********************************************!*\ + !*** ./node_modules/d3-brush/src/brush.js ***! + \********************************************/ +/*! exports provided: brushSelection, brushX, brushY, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return brushSelection; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return brushX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return brushY; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-drag */ \"./node_modules/d3-drag/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-transition */ \"./node_modules/d3-transition/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-brush/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./event */ \"./node_modules/d3-brush/src/event.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./noevent */ \"./node_modules/d3-brush/src/noevent.js\");\n\n\n\n\n\n\n\n\n\nvar MODE_DRAG = {name: \"drag\"},\n MODE_SPACE = {name: \"space\"},\n MODE_HANDLE = {name: \"handle\"},\n MODE_CENTER = {name: \"center\"};\n\nvar X = {\n name: \"x\",\n handles: [\"e\", \"w\"].map(type),\n input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },\n output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }\n};\n\nvar Y = {\n name: \"y\",\n handles: [\"n\", \"s\"].map(type),\n input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },\n output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }\n};\n\nvar XY = {\n name: \"xy\",\n handles: [\"n\", \"e\", \"s\", \"w\", \"nw\", \"ne\", \"se\", \"sw\"].map(type),\n input: function(xy) { return xy; },\n output: function(xy) { return xy; }\n};\n\nvar cursors = {\n overlay: \"crosshair\",\n selection: \"move\",\n n: \"ns-resize\",\n e: \"ew-resize\",\n s: \"ns-resize\",\n w: \"ew-resize\",\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\"\n};\n\nvar flipX = {\n e: \"w\",\n w: \"e\",\n nw: \"ne\",\n ne: \"nw\",\n se: \"sw\",\n sw: \"se\"\n};\n\nvar flipY = {\n n: \"s\",\n s: \"n\",\n nw: \"sw\",\n ne: \"se\",\n se: \"ne\",\n sw: \"nw\"\n};\n\nvar signsX = {\n overlay: +1,\n selection: +1,\n n: null,\n e: +1,\n s: null,\n w: -1,\n nw: -1,\n ne: +1,\n se: +1,\n sw: -1\n};\n\nvar signsY = {\n overlay: +1,\n selection: +1,\n n: -1,\n e: null,\n s: +1,\n w: null,\n nw: -1,\n ne: -1,\n se: +1,\n sw: +1\n};\n\nfunction type(t) {\n return {type: t};\n}\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].button;\n}\n\nfunction defaultExtent() {\n var svg = this.ownerSVGElement || this;\n return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];\n}\n\n// Like d3.local, but with the name “__brush” rather than auto-generated.\nfunction local(node) {\n while (!node.__brush) if (!(node = node.parentNode)) return;\n return node.__brush;\n}\n\nfunction empty(extent) {\n return extent[0][0] === extent[1][0]\n || extent[0][1] === extent[1][1];\n}\n\nfunction brushSelection(node) {\n var state = node.__brush;\n return state ? state.dim.output(state.selection) : null;\n}\n\nfunction brushX() {\n return brush(X);\n}\n\nfunction brushY() {\n return brush(Y);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return brush(XY);\n});\n\nfunction brush(dim) {\n var extent = defaultExtent,\n filter = defaultFilter,\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(brush, \"start\", \"brush\", \"end\"),\n handleSize = 6,\n touchending;\n\n function brush(group) {\n var overlay = group\n .property(\"__brush\", initialize)\n .selectAll(\".overlay\")\n .data([type(\"overlay\")]);\n\n overlay.enter().append(\"rect\")\n .attr(\"class\", \"overlay\")\n .attr(\"pointer-events\", \"all\")\n .attr(\"cursor\", cursors.overlay)\n .merge(overlay)\n .each(function() {\n var extent = local(this).extent;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this)\n .attr(\"x\", extent[0][0])\n .attr(\"y\", extent[0][1])\n .attr(\"width\", extent[1][0] - extent[0][0])\n .attr(\"height\", extent[1][1] - extent[0][1]);\n });\n\n group.selectAll(\".selection\")\n .data([type(\"selection\")])\n .enter().append(\"rect\")\n .attr(\"class\", \"selection\")\n .attr(\"cursor\", cursors.selection)\n .attr(\"fill\", \"#777\")\n .attr(\"fill-opacity\", 0.3)\n .attr(\"stroke\", \"#fff\")\n .attr(\"shape-rendering\", \"crispEdges\");\n\n var handle = group.selectAll(\".handle\")\n .data(dim.handles, function(d) { return d.type; });\n\n handle.exit().remove();\n\n handle.enter().append(\"rect\")\n .attr(\"class\", function(d) { return \"handle handle--\" + d.type; })\n .attr(\"cursor\", function(d) { return cursors[d.type]; });\n\n group\n .each(redraw)\n .attr(\"fill\", \"none\")\n .attr(\"pointer-events\", \"all\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\")\n .on(\"mousedown.brush touchstart.brush\", started);\n }\n\n brush.move = function(group, selection) {\n if (group.selection) {\n group\n .on(\"start.brush\", function() { emitter(this, arguments).beforestart().start(); })\n .on(\"interrupt.brush end.brush\", function() { emitter(this, arguments).end(); })\n .tween(\"brush\", function() {\n var that = this,\n state = that.__brush,\n emit = emitter(that, arguments),\n selection0 = state.selection,\n selection1 = dim.input(typeof selection === \"function\" ? selection.apply(this, arguments) : selection, state.extent),\n i = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_2__[\"interpolate\"])(selection0, selection1);\n\n function tween(t) {\n state.selection = t === 1 && empty(selection1) ? null : i(t);\n redraw.call(that);\n emit.brush();\n }\n\n return selection0 && selection1 ? tween : tween(1);\n });\n } else {\n group\n .each(function() {\n var that = this,\n args = arguments,\n state = that.__brush,\n selection1 = dim.input(typeof selection === \"function\" ? selection.apply(that, args) : selection, state.extent),\n emit = emitter(that, args).beforestart();\n\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(that);\n state.selection = selection1 == null || empty(selection1) ? null : selection1;\n redraw.call(that);\n emit.start().brush().end();\n });\n }\n };\n\n function redraw() {\n var group = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this),\n selection = local(this).selection;\n\n if (selection) {\n group.selectAll(\".selection\")\n .style(\"display\", null)\n .attr(\"x\", selection[0][0])\n .attr(\"y\", selection[0][1])\n .attr(\"width\", selection[1][0] - selection[0][0])\n .attr(\"height\", selection[1][1] - selection[0][1]);\n\n group.selectAll(\".handle\")\n .style(\"display\", null)\n .attr(\"x\", function(d) { return d.type[d.type.length - 1] === \"e\" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; })\n .attr(\"y\", function(d) { return d.type[0] === \"s\" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; })\n .attr(\"width\", function(d) { return d.type === \"n\" || d.type === \"s\" ? selection[1][0] - selection[0][0] + handleSize : handleSize; })\n .attr(\"height\", function(d) { return d.type === \"e\" || d.type === \"w\" ? selection[1][1] - selection[0][1] + handleSize : handleSize; });\n }\n\n else {\n group.selectAll(\".selection,.handle\")\n .style(\"display\", \"none\")\n .attr(\"x\", null)\n .attr(\"y\", null)\n .attr(\"width\", null)\n .attr(\"height\", null);\n }\n }\n\n function emitter(that, args) {\n return that.__brush.emitter || new Emitter(that, args);\n }\n\n function Emitter(that, args) {\n this.that = that;\n this.args = args;\n this.state = that.__brush;\n this.active = 0;\n }\n\n Emitter.prototype = {\n beforestart: function() {\n if (++this.active === 1) this.state.emitter = this, this.starting = true;\n return this;\n },\n start: function() {\n if (this.starting) this.starting = false, this.emit(\"start\");\n return this;\n },\n brush: function() {\n this.emit(\"brush\");\n return this;\n },\n end: function() {\n if (--this.active === 0) delete this.state.emitter, this.emit(\"end\");\n return this;\n },\n emit: function(type) {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_6__[\"default\"](brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);\n }\n };\n\n function started() {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) { if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches.length < d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches.length) return Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(); }\n else if (touchending) return;\n if (!filter.apply(this, arguments)) return;\n\n var that = this,\n type = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].target.__data__.type,\n mode = (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].metaKey ? type = \"overlay\" : type) === \"selection\" ? MODE_DRAG : (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].altKey ? MODE_CENTER : MODE_HANDLE),\n signX = dim === Y ? null : signsX[type],\n signY = dim === X ? null : signsY[type],\n state = local(that),\n extent = state.extent,\n selection = state.selection,\n W = extent[0][0], w0, w1,\n N = extent[0][1], n0, n1,\n E = extent[1][0], e0, e1,\n S = extent[1][1], s0, s1,\n dx,\n dy,\n moving,\n shifting = signX && signY && d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].shiftKey,\n lockX,\n lockY,\n point0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(that),\n point = point0,\n emit = emitter(that, arguments).beforestart();\n\n if (type === \"overlay\") {\n state.selection = selection = [\n [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],\n [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]\n ];\n } else {\n w0 = selection[0][0];\n n0 = selection[0][1];\n e0 = selection[1][0];\n s0 = selection[1][1];\n }\n\n w1 = w0;\n n1 = n0;\n e1 = e0;\n s1 = s0;\n\n var group = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(that)\n .attr(\"pointer-events\", \"none\");\n\n var overlay = group.selectAll(\".overlay\")\n .attr(\"cursor\", cursors[type]);\n\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) {\n group\n .on(\"touchmove.brush\", moved, true)\n .on(\"touchend.brush touchcancel.brush\", ended, true);\n } else {\n var view = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view)\n .on(\"keydown.brush\", keydowned, true)\n .on(\"keyup.brush\", keyupped, true)\n .on(\"mousemove.brush\", moved, true)\n .on(\"mouseup.brush\", ended, true);\n\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragDisable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view);\n }\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"nopropagation\"])();\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(that);\n redraw.call(that);\n emit.start();\n\n function moved() {\n var point1 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(that);\n if (shifting && !lockX && !lockY) {\n if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true;\n else lockX = true;\n }\n point = point1;\n moving = true;\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n move();\n }\n\n function move() {\n var t;\n\n dx = point[0] - point0[0];\n dy = point[1] - point0[1];\n\n switch (mode) {\n case MODE_SPACE:\n case MODE_DRAG: {\n if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;\n if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;\n break;\n }\n case MODE_HANDLE: {\n if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;\n else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;\n if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;\n else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;\n break;\n }\n case MODE_CENTER: {\n if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));\n if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));\n break;\n }\n }\n\n if (e1 < w1) {\n signX *= -1;\n t = w0, w0 = e0, e0 = t;\n t = w1, w1 = e1, e1 = t;\n if (type in flipX) overlay.attr(\"cursor\", cursors[type = flipX[type]]);\n }\n\n if (s1 < n1) {\n signY *= -1;\n t = n0, n0 = s0, s0 = t;\n t = n1, n1 = s1, s1 = t;\n if (type in flipY) overlay.attr(\"cursor\", cursors[type = flipY[type]]);\n }\n\n if (state.selection) selection = state.selection; // May be set by brush.move!\n if (lockX) w1 = selection[0][0], e1 = selection[1][0];\n if (lockY) n1 = selection[0][1], s1 = selection[1][1];\n\n if (selection[0][0] !== w1\n || selection[0][1] !== n1\n || selection[1][0] !== e1\n || selection[1][1] !== s1) {\n state.selection = [[w1, n1], [e1, s1]];\n redraw.call(that);\n emit.brush();\n }\n }\n\n function ended() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"nopropagation\"])();\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches.length) return;\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n group.on(\"touchmove.brush touchend.brush touchcancel.brush\", null);\n } else {\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragEnable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view, moving);\n view.on(\"keydown.brush keyup.brush mousemove.brush mouseup.brush\", null);\n }\n group.attr(\"pointer-events\", \"all\");\n overlay.attr(\"cursor\", cursors.overlay);\n if (state.selection) selection = state.selection; // May be set by brush.move (on start)!\n if (empty(selection)) state.selection = null, redraw.call(that);\n emit.end();\n }\n\n function keydowned() {\n switch (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].keyCode) {\n case 16: { // SHIFT\n shifting = signX && signY;\n break;\n }\n case 18: { // ALT\n if (mode === MODE_HANDLE) {\n if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;\n if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;\n mode = MODE_CENTER;\n move();\n }\n break;\n }\n case 32: { // SPACE; takes priority over ALT\n if (mode === MODE_HANDLE || mode === MODE_CENTER) {\n if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;\n if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;\n mode = MODE_SPACE;\n overlay.attr(\"cursor\", cursors.selection);\n move();\n }\n break;\n }\n default: return;\n }\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n }\n\n function keyupped() {\n switch (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].keyCode) {\n case 16: { // SHIFT\n if (shifting) {\n lockX = lockY = shifting = false;\n move();\n }\n break;\n }\n case 18: { // ALT\n if (mode === MODE_CENTER) {\n if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;\n if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;\n mode = MODE_HANDLE;\n move();\n }\n break;\n }\n case 32: { // SPACE\n if (mode === MODE_SPACE) {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].altKey) {\n if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;\n if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;\n mode = MODE_CENTER;\n } else {\n if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;\n if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;\n mode = MODE_HANDLE;\n }\n overlay.attr(\"cursor\", cursors[type]);\n move();\n }\n break;\n }\n default: return;\n }\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n }\n }\n\n function initialize() {\n var state = this.__brush || {selection: null};\n state.extent = extent.apply(this, arguments);\n state.dim = dim;\n return state;\n }\n\n brush.extent = function(_) {\n return arguments.length ? (extent = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;\n };\n\n brush.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), brush) : filter;\n };\n\n brush.handleSize = function(_) {\n return arguments.length ? (handleSize = +_, brush) : handleSize;\n };\n\n brush.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? brush : value;\n };\n\n return brush;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-brush/src/brush.js?"); + +/***/ }), + +/***/ "./node_modules/d3-brush/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-brush/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-brush/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-brush/src/event.js": +/*!********************************************!*\ + !*** ./node_modules/d3-brush/src/event.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(target, type, selection) {\n this.target = target;\n this.type = type;\n this.selection = selection;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-brush/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/d3-brush/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-brush/src/index.js ***! + \********************************************/ +/*! exports provided: brush, brushX, brushY, brushSelection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _brush__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./brush */ \"./node_modules/d3-brush/src/brush.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brush\", function() { return _brush__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return _brush__WEBPACK_IMPORTED_MODULE_0__[\"brushX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return _brush__WEBPACK_IMPORTED_MODULE_0__[\"brushY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return _brush__WEBPACK_IMPORTED_MODULE_0__[\"brushSelection\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-brush/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-brush/src/noevent.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-brush/src/noevent.js ***! + \**********************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-brush/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/array.js": +/*!********************************************!*\ + !*** ./node_modules/d3-chord/src/array.js ***! + \********************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/chord.js": +/*!********************************************!*\ + !*** ./node_modules/d3-chord/src/chord.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-chord/src/math.js\");\n\n\n\nfunction compareValue(compare) {\n return function(a, b) {\n return compare(\n a.source.value + a.target.value,\n b.source.value + b.target.value\n );\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var padAngle = 0,\n sortGroups = null,\n sortSubgroups = null,\n sortChords = null;\n\n function chord(matrix) {\n var n = matrix.length,\n groupSums = [],\n groupIndex = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n),\n subgroupIndex = [],\n chords = [],\n groups = chords.groups = new Array(n),\n subgroups = new Array(n * n),\n k,\n x,\n x0,\n dx,\n i,\n j;\n\n // Compute the sum.\n k = 0, i = -1; while (++i < n) {\n x = 0, j = -1; while (++j < n) {\n x += matrix[i][j];\n }\n groupSums.push(x);\n subgroupIndex.push(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n));\n k += x;\n }\n\n // Sort groups…\n if (sortGroups) groupIndex.sort(function(a, b) {\n return sortGroups(groupSums[a], groupSums[b]);\n });\n\n // Sort subgroups…\n if (sortSubgroups) subgroupIndex.forEach(function(d, i) {\n d.sort(function(a, b) {\n return sortSubgroups(matrix[i][a], matrix[i][b]);\n });\n });\n\n // Convert the sum to scaling factor for [0, 2pi].\n // TODO Allow start and end angle to be specified?\n // TODO Allow padding to be specified as percentage?\n k = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"max\"])(0, _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] - padAngle * n) / k;\n dx = k ? padAngle : _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] / n;\n\n // Compute the start and end angle for each group and subgroup.\n // Note: Opera has a bug reordering object literal properties!\n x = 0, i = -1; while (++i < n) {\n x0 = x, j = -1; while (++j < n) {\n var di = groupIndex[i],\n dj = subgroupIndex[di][j],\n v = matrix[di][dj],\n a0 = x,\n a1 = x += v * k;\n subgroups[dj * n + di] = {\n index: di,\n subindex: dj,\n startAngle: a0,\n endAngle: a1,\n value: v\n };\n }\n groups[di] = {\n index: di,\n startAngle: x0,\n endAngle: x,\n value: groupSums[di]\n };\n x += dx;\n }\n\n // Generate chords for each (non-empty) subgroup-subgroup link.\n i = -1; while (++i < n) {\n j = i - 1; while (++j < n) {\n var source = subgroups[j * n + i],\n target = subgroups[i * n + j];\n if (source.value || target.value) {\n chords.push(source.value < target.value\n ? {source: target, target: source}\n : {source: source, target: target});\n }\n }\n }\n\n return sortChords ? chords.sort(sortChords) : chords;\n }\n\n chord.padAngle = function(_) {\n return arguments.length ? (padAngle = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"max\"])(0, _), chord) : padAngle;\n };\n\n chord.sortGroups = function(_) {\n return arguments.length ? (sortGroups = _, chord) : sortGroups;\n };\n\n chord.sortSubgroups = function(_) {\n return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;\n };\n\n chord.sortChords = function(_) {\n return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;\n };\n\n return chord;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/chord.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-chord/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-chord/src/index.js ***! + \********************************************/ +/*! exports provided: chord, ribbon */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _chord__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./chord */ \"./node_modules/d3-chord/src/chord.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"chord\", function() { return _chord__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _ribbon__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ribbon */ \"./node_modules/d3-chord/src/ribbon.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ribbon\", function() { return _ribbon__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/math.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-chord/src/math.js ***! + \*******************************************/ +/*! exports provided: cos, sin, pi, halfPi, tau, max */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return max; });\nvar cos = Math.cos;\nvar sin = Math.sin;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar tau = pi * 2;\nvar max = Math.max;\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/d3-chord/src/ribbon.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-chord/src/ribbon.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-chord/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-chord/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-chord/src/math.js\");\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n\n\n\n\n\nfunction defaultSource(d) {\n return d.source;\n}\n\nfunction defaultTarget(d) {\n return d.target;\n}\n\nfunction defaultRadius(d) {\n return d.radius;\n}\n\nfunction defaultStartAngle(d) {\n return d.startAngle;\n}\n\nfunction defaultEndAngle(d) {\n return d.endAngle;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var source = defaultSource,\n target = defaultTarget,\n radius = defaultRadius,\n startAngle = defaultStartAngle,\n endAngle = defaultEndAngle,\n context = null;\n\n function ribbon() {\n var buffer,\n argv = _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(arguments),\n s = source.apply(this, argv),\n t = target.apply(this, argv),\n sr = +radius.apply(this, (argv[0] = s, argv)),\n sa0 = startAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n sa1 = endAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n sx0 = sr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(sa0),\n sy0 = sr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(sa0),\n tr = +radius.apply(this, (argv[0] = t, argv)),\n ta0 = startAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n ta1 = endAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"];\n\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_3__[\"path\"])();\n\n context.moveTo(sx0, sy0);\n context.arc(0, 0, sr, sa0, sa1);\n if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?\n context.quadraticCurveTo(0, 0, tr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(ta0), tr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ta0));\n context.arc(0, 0, tr, ta0, ta1);\n }\n context.quadraticCurveTo(0, 0, sx0, sy0);\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n ribbon.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : radius;\n };\n\n ribbon.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : startAngle;\n };\n\n ribbon.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : endAngle;\n };\n\n ribbon.source = function(_) {\n return arguments.length ? (source = _, ribbon) : source;\n };\n\n ribbon.target = function(_) {\n return arguments.length ? (target = _, ribbon) : target;\n };\n\n ribbon.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), ribbon) : context;\n };\n\n return ribbon;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-chord/src/ribbon.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/entries.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-collection/src/entries.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var entries = [];\n for (var key in map) entries.push({key: key, value: map[key]});\n return entries;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/entries.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/index.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-collection/src/index.js ***! + \*************************************************/ +/*! exports provided: nest, set, map, keys, values, entries */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./nest */ \"./node_modules/d3-collection/src/nest.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return _nest__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _set__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./set */ \"./node_modules/d3-collection/src/set.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return _set__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./map */ \"./node_modules/d3-collection/src/map.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return _map__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _keys__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./keys */ \"./node_modules/d3-collection/src/keys.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return _keys__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _values__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./values */ \"./node_modules/d3-collection/src/values.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return _values__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _entries__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./entries */ \"./node_modules/d3-collection/src/entries.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return _entries__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/keys.js": +/*!************************************************!*\ + !*** ./node_modules/d3-collection/src/keys.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var keys = [];\n for (var key in map) keys.push(key);\n return keys;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/keys.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/map.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-collection/src/map.js ***! + \***********************************************/ +/*! exports provided: prefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefix\", function() { return prefix; });\nvar prefix = \"$\";\n\nfunction Map() {}\n\nMap.prototype = map.prototype = {\n constructor: Map,\n has: function(key) {\n return (prefix + key) in this;\n },\n get: function(key) {\n return this[prefix + key];\n },\n set: function(key, value) {\n this[prefix + key] = value;\n return this;\n },\n remove: function(key) {\n var property = prefix + key;\n return property in this && delete this[property];\n },\n clear: function() {\n for (var property in this) if (property[0] === prefix) delete this[property];\n },\n keys: function() {\n var keys = [];\n for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));\n return keys;\n },\n values: function() {\n var values = [];\n for (var property in this) if (property[0] === prefix) values.push(this[property]);\n return values;\n },\n entries: function() {\n var entries = [];\n for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});\n return entries;\n },\n size: function() {\n var size = 0;\n for (var property in this) if (property[0] === prefix) ++size;\n return size;\n },\n empty: function() {\n for (var property in this) if (property[0] === prefix) return false;\n return true;\n },\n each: function(f) {\n for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);\n }\n};\n\nfunction map(object, f) {\n var map = new Map;\n\n // Copy constructor.\n if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });\n\n // Index array by numeric index or specified key function.\n else if (Array.isArray(object)) {\n var i = -1,\n n = object.length,\n o;\n\n if (f == null) while (++i < n) map.set(i, object[i]);\n else while (++i < n) map.set(f(o = object[i], i, object), o);\n }\n\n // Convert object to map.\n else if (object) for (var key in object) map.set(key, object[key]);\n\n return map;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (map);\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/map.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/nest.js": +/*!************************************************!*\ + !*** ./node_modules/d3-collection/src/nest.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/d3-collection/src/map.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = [],\n sortKeys = [],\n sortValues,\n rollup,\n nest;\n\n function apply(array, depth, createResult, setResult) {\n if (depth >= keys.length) {\n if (sortValues != null) array.sort(sortValues);\n return rollup != null ? rollup(array) : array;\n }\n\n var i = -1,\n n = array.length,\n key = keys[depth++],\n keyValue,\n value,\n valuesByKey = Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n values,\n result = createResult();\n\n while (++i < n) {\n if (values = valuesByKey.get(keyValue = key(value = array[i]) + \"\")) {\n values.push(value);\n } else {\n valuesByKey.set(keyValue, [value]);\n }\n }\n\n valuesByKey.each(function(values, key) {\n setResult(result, key, apply(values, depth, createResult, setResult));\n });\n\n return result;\n }\n\n function entries(map, depth) {\n if (++depth > keys.length) return map;\n var array, sortKey = sortKeys[depth - 1];\n if (rollup != null && depth >= keys.length) array = map.entries();\n else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });\n return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;\n }\n\n return nest = {\n object: function(array) { return apply(array, 0, createObject, setObject); },\n map: function(array) { return apply(array, 0, createMap, setMap); },\n entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },\n key: function(d) { keys.push(d); return nest; },\n sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },\n sortValues: function(order) { sortValues = order; return nest; },\n rollup: function(f) { rollup = f; return nest; }\n };\n});\n\nfunction createObject() {\n return {};\n}\n\nfunction setObject(object, key, value) {\n object[key] = value;\n}\n\nfunction createMap() {\n return Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n}\n\nfunction setMap(map, key, value) {\n map.set(key, value);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/nest.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/set.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-collection/src/set.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/d3-collection/src/map.js\");\n\n\nfunction Set() {}\n\nvar proto = _map__WEBPACK_IMPORTED_MODULE_0__[\"default\"].prototype;\n\nSet.prototype = set.prototype = {\n constructor: Set,\n has: proto.has,\n add: function(value) {\n value += \"\";\n this[_map__WEBPACK_IMPORTED_MODULE_0__[\"prefix\"] + value] = value;\n return this;\n },\n remove: proto.remove,\n clear: proto.clear,\n values: proto.keys,\n size: proto.size,\n empty: proto.empty,\n each: proto.each\n};\n\nfunction set(object, f) {\n var set = new Set;\n\n // Copy constructor.\n if (object instanceof Set) object.each(function(value) { set.add(value); });\n\n // Otherwise, assume it’s an array.\n else if (object) {\n var i = -1, n = object.length;\n if (f == null) while (++i < n) set.add(object[i]);\n else while (++i < n) set.add(f(object[i], i, object));\n }\n\n return set;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (set);\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/set.js?"); + +/***/ }), + +/***/ "./node_modules/d3-collection/src/values.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-collection/src/values.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var values = [];\n for (var key in map) values.push(map[key]);\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-collection/src/values.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/color.js": +/*!********************************************!*\ + !*** ./node_modules/d3-color/src/color.js ***! + \********************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/cubehelix.js": +/*!************************************************!*\ + !*** ./node_modules/d3-color/src/cubehelix.js ***! + \************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/define.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-color/src/define.js ***! + \*********************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-color/src/index.js ***! + \********************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/lab.js": +/*!******************************************!*\ + !*** ./node_modules/d3-color/src/lab.js ***! + \******************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/d3-color/src/math.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-color/src/math.js ***! + \*******************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/area.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-contour/src/area.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(ring) {\n var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];\n while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];\n return area;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/array.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-contour/src/array.js ***! + \**********************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/ascending.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-contour/src/ascending.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a - b;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/blur.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-contour/src/blur.js ***! + \*********************************************/ +/*! exports provided: blurX, blurY */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"blurX\", function() { return blurX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"blurY\", function() { return blurY; });\n// TODO Optimize edge cases.\n// TODO Optimize index calculation.\n// TODO Optimize arguments.\nfunction blurX(source, target, r) {\n var n = source.width,\n m = source.height,\n w = (r << 1) + 1;\n for (var j = 0; j < m; ++j) {\n for (var i = 0, sr = 0; i < n + r; ++i) {\n if (i < n) {\n sr += source.data[i + j * n];\n }\n if (i >= r) {\n if (i >= w) {\n sr -= source.data[i - w + j * n];\n }\n target.data[i - r + j * n] = sr / Math.min(i + 1, n - 1 + w - i, w);\n }\n }\n }\n}\n\n// TODO Optimize edge cases.\n// TODO Optimize index calculation.\n// TODO Optimize arguments.\nfunction blurY(source, target, r) {\n var n = source.width,\n m = source.height,\n w = (r << 1) + 1;\n for (var i = 0; i < n; ++i) {\n for (var j = 0, sr = 0; j < m + r; ++j) {\n if (j < m) {\n sr += source.data[i + j * n];\n }\n if (j >= r) {\n if (j >= w) {\n sr -= source.data[i + (j - w) * n];\n }\n target.data[i + (j - r) * n] = sr / Math.min(j + 1, m - 1 + w - j, w);\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/blur.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/constant.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-contour/src/constant.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/contains.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-contour/src/contains.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(ring, hole) {\n var i = -1, n = hole.length, c;\n while (++i < n) if (c = ringContains(ring, hole[i])) return c;\n return 0;\n});\n\nfunction ringContains(ring, point) {\n var x = point[0], y = point[1], contains = -1;\n for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {\n var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];\n if (segmentContains(pi, pj, point)) return 0;\n if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains;\n }\n return contains;\n}\n\nfunction segmentContains(a, b, c) {\n var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);\n}\n\nfunction collinear(a, b, c) {\n return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);\n}\n\nfunction within(p, q, r) {\n return p <= q && q <= r || r <= q && q <= p;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/contains.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/contours.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-contour/src/contours.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-contour/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-contour/src/ascending.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-contour/src/area.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-contour/src/constant.js\");\n/* harmony import */ var _contains__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./contains */ \"./node_modules/d3-contour/src/contains.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./noop */ \"./node_modules/d3-contour/src/noop.js\");\n\n\n\n\n\n\n\n\nvar cases = [\n [],\n [[[1.0, 1.5], [0.5, 1.0]]],\n [[[1.5, 1.0], [1.0, 1.5]]],\n [[[1.5, 1.0], [0.5, 1.0]]],\n [[[1.0, 0.5], [1.5, 1.0]]],\n [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]],\n [[[1.0, 0.5], [1.0, 1.5]]],\n [[[1.0, 0.5], [0.5, 1.0]]],\n [[[0.5, 1.0], [1.0, 0.5]]],\n [[[1.0, 1.5], [1.0, 0.5]]],\n [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]],\n [[[1.5, 1.0], [1.0, 0.5]]],\n [[[0.5, 1.0], [1.5, 1.0]]],\n [[[1.0, 1.5], [1.5, 1.0]]],\n [[[0.5, 1.0], [1.0, 1.5]]],\n []\n];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var dx = 1,\n dy = 1,\n threshold = d3_array__WEBPACK_IMPORTED_MODULE_0__[\"thresholdSturges\"],\n smooth = smoothLinear;\n\n function contours(values) {\n var tz = threshold(values);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n var domain = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"extent\"])(values), start = domain[0], stop = domain[1];\n tz = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start, stop, tz);\n tz = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Math.floor(start / tz) * tz, Math.floor(stop / tz) * tz, tz);\n } else {\n tz = tz.slice().sort(_ascending__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n }\n\n return tz.map(function(value) {\n return contour(values, value);\n });\n }\n\n // Accumulate, smooth contour rings, assign holes to exterior rings.\n // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js\n function contour(values, value) {\n var polygons = [],\n holes = [];\n\n isorings(values, value, function(ring) {\n smooth(ring, values, value);\n if (Object(_area__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(ring) > 0) polygons.push([ring]);\n else holes.push(ring);\n });\n\n holes.forEach(function(hole) {\n for (var i = 0, n = polygons.length, polygon; i < n; ++i) {\n if (Object(_contains__WEBPACK_IMPORTED_MODULE_5__[\"default\"])((polygon = polygons[i])[0], hole) !== -1) {\n polygon.push(hole);\n return;\n }\n }\n });\n\n return {\n type: \"MultiPolygon\",\n value: value,\n coordinates: polygons\n };\n }\n\n // Marching squares with isolines stitched into rings.\n // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js\n function isorings(values, value, callback) {\n var fragmentByStart = new Array,\n fragmentByEnd = new Array,\n x, y, t0, t1, t2, t3;\n\n // Special case for the first row (y = -1, t2 = t3 = 0).\n x = y = -1;\n t1 = values[0] >= value;\n cases[t1 << 1].forEach(stitch);\n while (++x < dx - 1) {\n t0 = t1, t1 = values[x + 1] >= value;\n cases[t0 | t1 << 1].forEach(stitch);\n }\n cases[t1 << 0].forEach(stitch);\n\n // General case for the intermediate rows.\n while (++y < dy - 1) {\n x = -1;\n t1 = values[y * dx + dx] >= value;\n t2 = values[y * dx] >= value;\n cases[t1 << 1 | t2 << 2].forEach(stitch);\n while (++x < dx - 1) {\n t0 = t1, t1 = values[y * dx + dx + x + 1] >= value;\n t3 = t2, t2 = values[y * dx + x + 1] >= value;\n cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);\n }\n cases[t1 | t2 << 3].forEach(stitch);\n }\n\n // Special case for the last row (y = dy - 1, t0 = t1 = 0).\n x = -1;\n t2 = values[y * dx] >= value;\n cases[t2 << 2].forEach(stitch);\n while (++x < dx - 1) {\n t3 = t2, t2 = values[y * dx + x + 1] >= value;\n cases[t2 << 2 | t3 << 3].forEach(stitch);\n }\n cases[t2 << 3].forEach(stitch);\n\n function stitch(line) {\n var start = [line[0][0] + x, line[0][1] + y],\n end = [line[1][0] + x, line[1][1] + y],\n startIndex = index(start),\n endIndex = index(end),\n f, g;\n if (f = fragmentByEnd[startIndex]) {\n if (g = fragmentByStart[endIndex]) {\n delete fragmentByEnd[f.end];\n delete fragmentByStart[g.start];\n if (f === g) {\n f.ring.push(end);\n callback(f.ring);\n } else {\n fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)};\n }\n } else {\n delete fragmentByEnd[f.end];\n f.ring.push(end);\n fragmentByEnd[f.end = endIndex] = f;\n }\n } else if (f = fragmentByStart[endIndex]) {\n if (g = fragmentByEnd[startIndex]) {\n delete fragmentByStart[f.start];\n delete fragmentByEnd[g.end];\n if (f === g) {\n f.ring.push(end);\n callback(f.ring);\n } else {\n fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)};\n }\n } else {\n delete fragmentByStart[f.start];\n f.ring.unshift(start);\n fragmentByStart[f.start = startIndex] = f;\n }\n } else {\n fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]};\n }\n }\n }\n\n function index(point) {\n return point[0] * 2 + point[1] * (dx + 1) * 4;\n }\n\n function smoothLinear(ring, values, value) {\n ring.forEach(function(point) {\n var x = point[0],\n y = point[1],\n xt = x | 0,\n yt = y | 0,\n v0,\n v1 = values[yt * dx + xt];\n if (x > 0 && x < dx && xt === x) {\n v0 = values[yt * dx + xt - 1];\n point[0] = x + (value - v0) / (v1 - v0) - 0.5;\n }\n if (y > 0 && y < dy && yt === y) {\n v0 = values[(yt - 1) * dx + xt];\n point[1] = y + (value - v0) / (v1 - v0) - 0.5;\n }\n });\n }\n\n contours.contour = contour;\n\n contours.size = function(_) {\n if (!arguments.length) return [dx, dy];\n var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);\n if (!(_0 > 0) || !(_1 > 0)) throw new Error(\"invalid size\");\n return dx = _0, dy = _1, contours;\n };\n\n contours.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), contours) : threshold;\n };\n\n contours.smooth = function(_) {\n return arguments.length ? (smooth = _ ? smoothLinear : _noop__WEBPACK_IMPORTED_MODULE_6__[\"default\"], contours) : smooth === smoothLinear;\n };\n\n return contours;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/contours.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/density.js": +/*!************************************************!*\ + !*** ./node_modules/d3-contour/src/density.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-contour/src/array.js\");\n/* harmony import */ var _blur__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./blur */ \"./node_modules/d3-contour/src/blur.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-contour/src/constant.js\");\n/* harmony import */ var _contours__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./contours */ \"./node_modules/d3-contour/src/contours.js\");\n\n\n\n\n\n\nfunction defaultX(d) {\n return d[0];\n}\n\nfunction defaultY(d) {\n return d[1];\n}\n\nfunction defaultWeight() {\n return 1;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x = defaultX,\n y = defaultY,\n weight = defaultWeight,\n dx = 960,\n dy = 500,\n r = 20, // blur radius\n k = 2, // log2(grid cell size)\n o = r * 3, // grid offset, to pad for blur\n n = (dx + o * 2) >> k, // grid width\n m = (dy + o * 2) >> k, // grid height\n threshold = Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(20);\n\n function density(data) {\n var values0 = new Float32Array(n * m),\n values1 = new Float32Array(n * m);\n\n data.forEach(function(d, i, data) {\n var xi = (+x(d, i, data) + o) >> k,\n yi = (+y(d, i, data) + o) >> k,\n wi = +weight(d, i, data);\n if (xi >= 0 && xi < n && yi >= 0 && yi < m) {\n values0[xi + yi * n] += wi;\n }\n });\n\n // TODO Optimize.\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurX\"])({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurY\"])({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurX\"])({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurY\"])({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurX\"])({width: n, height: m, data: values0}, {width: n, height: m, data: values1}, r >> k);\n Object(_blur__WEBPACK_IMPORTED_MODULE_2__[\"blurY\"])({width: n, height: m, data: values1}, {width: n, height: m, data: values0}, r >> k);\n\n var tz = threshold(values0);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n var stop = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"max\"])(values0);\n tz = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(0, stop, tz);\n tz = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(0, Math.floor(stop / tz) * tz, tz);\n tz.shift();\n }\n\n return Object(_contours__WEBPACK_IMPORTED_MODULE_4__[\"default\"])()\n .thresholds(tz)\n .size([n, m])\n (values0)\n .map(transform);\n }\n\n function transform(geometry) {\n geometry.value *= Math.pow(2, -2 * k); // Density in points per square pixel.\n geometry.coordinates.forEach(transformPolygon);\n return geometry;\n }\n\n function transformPolygon(coordinates) {\n coordinates.forEach(transformRing);\n }\n\n function transformRing(coordinates) {\n coordinates.forEach(transformPoint);\n }\n\n // TODO Optimize.\n function transformPoint(coordinates) {\n coordinates[0] = coordinates[0] * Math.pow(2, k) - o;\n coordinates[1] = coordinates[1] * Math.pow(2, k) - o;\n }\n\n function resize() {\n o = r * 3;\n n = (dx + o * 2) >> k;\n m = (dy + o * 2) >> k;\n return density;\n }\n\n density.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+_), density) : x;\n };\n\n density.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+_), density) : y;\n };\n\n density.weight = function(_) {\n return arguments.length ? (weight = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+_), density) : weight;\n };\n\n density.size = function(_) {\n if (!arguments.length) return [dx, dy];\n var _0 = Math.ceil(_[0]), _1 = Math.ceil(_[1]);\n if (!(_0 >= 0) && !(_0 >= 0)) throw new Error(\"invalid size\");\n return dx = _0, dy = _1, resize();\n };\n\n density.cellSize = function(_) {\n if (!arguments.length) return 1 << k;\n if (!((_ = +_) >= 1)) throw new Error(\"invalid cell size\");\n return k = Math.floor(Math.log(_) / Math.LN2), resize();\n };\n\n density.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(_), density) : threshold;\n };\n\n density.bandwidth = function(_) {\n if (!arguments.length) return Math.sqrt(r * (r + 1));\n if (!((_ = +_) >= 0)) throw new Error(\"invalid bandwidth\");\n return r = Math.round((Math.sqrt(4 * _ * _ + 1) - 1) / 2), resize();\n };\n\n return density;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/density.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/index.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-contour/src/index.js ***! + \**********************************************/ +/*! exports provided: contours, contourDensity */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _contours__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./contours */ \"./node_modules/d3-contour/src/contours.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"contours\", function() { return _contours__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _density__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./density */ \"./node_modules/d3-contour/src/density.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"contourDensity\", function() { return _density__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-contour/src/noop.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-contour/src/noop.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {});\n\n\n//# sourceURL=webpack:///./node_modules/d3-contour/src/noop.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dispatch/src/dispatch.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-dispatch/src/dispatch.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dispatch/src/index.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-dispatch/src/index.js ***! + \***********************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/constant.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-drag/src/constant.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/drag.js": +/*!******************************************!*\ + !*** ./node_modules/d3-drag/src/drag.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/d3-drag/src/nodrag.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./noevent */ \"./node_modules/d3-drag/src/noevent.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-drag/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./event */ \"./node_modules/d3-drag/src/event.js\");\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].button;\n}\n\nfunction defaultContainer() {\n return this.parentNode;\n}\n\nfunction defaultSubject(d) {\n return d == null ? {x: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].x, y: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].y} : d;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n container = defaultContainer,\n subject = defaultSubject,\n touchable = defaultTouchable,\n gestures = {},\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"drag\", \"end\"),\n active = 0,\n mousedownx,\n mousedowny,\n mousemoving,\n touchending,\n clickDistance2 = 0;\n\n function drag(selection) {\n selection\n .on(\"mousedown.drag\", mousedowned)\n .filter(touchable)\n .on(\"touchstart.drag\", touchstarted)\n .on(\"touchmove.drag\", touchmoved)\n .on(\"touchend.drag touchcancel.drag\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var gesture = beforestart(\"mouse\", container.apply(this, arguments), d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"mouse\"], this, arguments);\n if (!gesture) return;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag\", mousemoved, true).on(\"mouseup.drag\", mouseupped, true);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n mousemoving = false;\n mousedownx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX;\n mousedowny = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY;\n gesture(\"start\");\n }\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n if (!mousemoving) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX - mousedownx, dy = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY - mousedowny;\n mousemoving = dx * dx + dy * dy > clickDistance2;\n }\n gestures.mouse(\"drag\");\n }\n\n function mouseupped() {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag mouseup.drag\", null);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"yesdrag\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view, mousemoving);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gestures.mouse(\"end\");\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n c = container.apply(this, arguments),\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = beforestart(touches[i].identifier, c, d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"touch\"], this, arguments)) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"start\");\n }\n }\n }\n\n function touchmoved() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gesture(\"drag\");\n }\n }\n }\n\n function touchended() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"end\");\n }\n }\n }\n\n function beforestart(id, container, point, that, args) {\n var p = point(container, id), s, dx, dy,\n sublisteners = listeners.copy();\n\n if (!Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, \"beforestart\", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {\n if ((d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].subject = s = subject.apply(that, args)) == null) return false;\n dx = s.x - p[0] || 0;\n dy = s.y - p[1] || 0;\n return true;\n })) return;\n\n return function gesture(type) {\n var p0 = p, n;\n switch (type) {\n case \"start\": gestures[id] = gesture, n = active++; break;\n case \"end\": delete gestures[id], --active; // nobreak\n case \"drag\": p = point(container, id), n = active; break;\n }\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);\n };\n }\n\n drag.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : filter;\n };\n\n drag.container = function(_) {\n return arguments.length ? (container = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : container;\n };\n\n drag.subject = function(_) {\n return arguments.length ? (subject = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : subject;\n };\n\n drag.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : touchable;\n };\n\n drag.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? drag : value;\n };\n\n drag.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);\n };\n\n return drag;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/drag.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/event.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-drag/src/event.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return DragEvent; });\nfunction DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {\n this.target = target;\n this.type = type;\n this.subject = subject;\n this.identifier = id;\n this.active = active;\n this.x = x;\n this.y = y;\n this.dx = dx;\n this.dy = dy;\n this._ = dispatch;\n}\n\nDragEvent.prototype.on = function() {\n var value = this._.on.apply(this._, arguments);\n return value === this._ ? this : value;\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-drag/src/index.js ***! + \*******************************************/ +/*! exports provided: drag, dragDisable, dragEnable */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _drag__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./drag */ \"./node_modules/d3-drag/src/drag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return _drag__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/d3-drag/src/nodrag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"yesdrag\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/nodrag.js": +/*!********************************************!*\ + !*** ./node_modules/d3-drag/src/nodrag.js ***! + \********************************************/ +/*! exports provided: default, yesdrag */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"yesdrag\", function() { return yesdrag; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noevent */ \"./node_modules/d3-drag/src/noevent.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(view) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n } else {\n root.__noselect = root.style.MozUserSelect;\n root.style.MozUserSelect = \"none\";\n }\n});\n\nfunction yesdrag(view, noclick) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", null);\n if (noclick) {\n selection.on(\"click.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n setTimeout(function() { selection.on(\"click.drag\", null); }, 0);\n }\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", null);\n } else {\n root.style.MozUserSelect = root.__noselect;\n delete root.__noselect;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/nodrag.js?"); + +/***/ }), + +/***/ "./node_modules/d3-drag/src/noevent.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-drag/src/noevent.js ***! + \*********************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-drag/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dsv/src/csv.js": +/*!****************************************!*\ + !*** ./node_modules/d3-dsv/src/csv.js ***! + \****************************************/ +/*! exports provided: csvParse, csvParseRows, csvFormat, csvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return csvParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return csvParseRows; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return csvFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return csvFormatRows; });\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-dsv/src/dsv.js\");\n\n\nvar csv = Object(_dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\",\");\n\nvar csvParse = csv.parse;\nvar csvParseRows = csv.parseRows;\nvar csvFormat = csv.format;\nvar csvFormatRows = csv.formatRows;\n\n\n//# sourceURL=webpack:///./node_modules/d3-dsv/src/csv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dsv/src/dsv.js": +/*!****************************************!*\ + !*** ./node_modules/d3-dsv/src/dsv.js ***! + \****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar EOL = {},\n EOF = {},\n QUOTE = 34,\n NEWLINE = 10,\n RETURN = 13;\n\nfunction objectConverter(columns) {\n return new Function(\"d\", \"return {\" + columns.map(function(name, i) {\n return JSON.stringify(name) + \": d[\" + i + \"]\";\n }).join(\",\") + \"}\");\n}\n\nfunction customConverter(columns, f) {\n var object = objectConverter(columns);\n return function(row, i) {\n return f(object(row), i, columns);\n };\n}\n\n// Compute unique columns in order of discovery.\nfunction inferColumns(rows) {\n var columnSet = Object.create(null),\n columns = [];\n\n rows.forEach(function(row) {\n for (var column in row) {\n if (!(column in columnSet)) {\n columns.push(columnSet[column] = column);\n }\n }\n });\n\n return columns;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(delimiter) {\n var reFormat = new RegExp(\"[\\\"\" + delimiter + \"\\n\\r]\"),\n DELIMITER = delimiter.charCodeAt(0);\n\n function parse(text, f) {\n var convert, columns, rows = parseRows(text, function(row, i) {\n if (convert) return convert(row, i - 1);\n columns = row, convert = f ? customConverter(row, f) : objectConverter(row);\n });\n rows.columns = columns || [];\n return rows;\n }\n\n function parseRows(text, f) {\n var rows = [], // output rows\n N = text.length,\n I = 0, // current character index\n n = 0, // current line number\n t, // current token\n eof = N <= 0, // current token followed by EOF?\n eol = false; // current token followed by EOL?\n\n // Strip the trailing newline.\n if (text.charCodeAt(N - 1) === NEWLINE) --N;\n if (text.charCodeAt(N - 1) === RETURN) --N;\n\n function token() {\n if (eof) return EOF;\n if (eol) return eol = false, EOL;\n\n // Unescape quotes.\n var i, j = I, c;\n if (text.charCodeAt(j) === QUOTE) {\n while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);\n if ((i = I) >= N) eof = true;\n else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;\n else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }\n return text.slice(j + 1, i - 1).replace(/\"\"/g, \"\\\"\");\n }\n\n // Find next delimiter or newline.\n while (I < N) {\n if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;\n else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }\n else if (c !== DELIMITER) continue;\n return text.slice(j, i);\n }\n\n // Return last token before EOF.\n return eof = true, text.slice(j, N);\n }\n\n while ((t = token()) !== EOF) {\n var row = [];\n while (t !== EOL && t !== EOF) row.push(t), t = token();\n if (f && (row = f(row, n++)) == null) continue;\n rows.push(row);\n }\n\n return rows;\n }\n\n function format(rows, columns) {\n if (columns == null) columns = inferColumns(rows);\n return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {\n return columns.map(function(column) {\n return formatValue(row[column]);\n }).join(delimiter);\n })).join(\"\\n\");\n }\n\n function formatRows(rows) {\n return rows.map(formatRow).join(\"\\n\");\n }\n\n function formatRow(row) {\n return row.map(formatValue).join(delimiter);\n }\n\n function formatValue(text) {\n return text == null ? \"\"\n : reFormat.test(text += \"\") ? \"\\\"\" + text.replace(/\"/g, \"\\\"\\\"\") + \"\\\"\"\n : text;\n }\n\n return {\n parse: parse,\n parseRows: parseRows,\n format: format,\n formatRows: formatRows\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-dsv/src/dsv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dsv/src/index.js": +/*!******************************************!*\ + !*** ./node_modules/d3-dsv/src/index.js ***! + \******************************************/ +/*! exports provided: dsvFormat, csvParse, csvParseRows, csvFormat, csvFormatRows, tsvParse, tsvParseRows, tsvFormat, tsvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-dsv/src/dsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsvFormat\", function() { return _dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _csv__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./csv */ \"./node_modules/d3-dsv/src/csv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return _csv__WEBPACK_IMPORTED_MODULE_1__[\"csvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return _csv__WEBPACK_IMPORTED_MODULE_1__[\"csvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return _csv__WEBPACK_IMPORTED_MODULE_1__[\"csvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return _csv__WEBPACK_IMPORTED_MODULE_1__[\"csvFormatRows\"]; });\n\n/* harmony import */ var _tsv__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tsv */ \"./node_modules/d3-dsv/src/tsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return _tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return _tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return _tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return _tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvFormatRows\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-dsv/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-dsv/src/tsv.js": +/*!****************************************!*\ + !*** ./node_modules/d3-dsv/src/tsv.js ***! + \****************************************/ +/*! exports provided: tsvParse, tsvParseRows, tsvFormat, tsvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return tsvParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return tsvParseRows; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return tsvFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return tsvFormatRows; });\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-dsv/src/dsv.js\");\n\n\nvar tsv = Object(_dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"\\t\");\n\nvar tsvParse = tsv.parse;\nvar tsvParseRows = tsv.parseRows;\nvar tsvFormat = tsv.format;\nvar tsvFormatRows = tsv.formatRows;\n\n\n//# sourceURL=webpack:///./node_modules/d3-dsv/src/tsv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/back.js": +/*!******************************************!*\ + !*** ./node_modules/d3-ease/src/back.js ***! + \******************************************/ +/*! exports provided: backIn, backOut, backInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backIn\", function() { return backIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backOut\", function() { return backOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backInOut\", function() { return backInOut; });\nvar overshoot = 1.70158;\n\nvar backIn = (function custom(s) {\n s = +s;\n\n function backIn(t) {\n return t * t * ((s + 1) * t - s);\n }\n\n backIn.overshoot = custom;\n\n return backIn;\n})(overshoot);\n\nvar backOut = (function custom(s) {\n s = +s;\n\n function backOut(t) {\n return --t * t * ((s + 1) * t + s) + 1;\n }\n\n backOut.overshoot = custom;\n\n return backOut;\n})(overshoot);\n\nvar backInOut = (function custom(s) {\n s = +s;\n\n function backInOut(t) {\n return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n }\n\n backInOut.overshoot = custom;\n\n return backInOut;\n})(overshoot);\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/back.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/bounce.js": +/*!********************************************!*\ + !*** ./node_modules/d3-ease/src/bounce.js ***! + \********************************************/ +/*! exports provided: bounceIn, bounceOut, bounceInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceIn\", function() { return bounceIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceOut\", function() { return bounceOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceInOut\", function() { return bounceInOut; });\nvar b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\n\nfunction bounceIn(t) {\n return 1 - bounceOut(1 - t);\n}\n\nfunction bounceOut(t) {\n return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n}\n\nfunction bounceInOut(t) {\n return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/bounce.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/circle.js": +/*!********************************************!*\ + !*** ./node_modules/d3-ease/src/circle.js ***! + \********************************************/ +/*! exports provided: circleIn, circleOut, circleInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleIn\", function() { return circleIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleOut\", function() { return circleOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleInOut\", function() { return circleInOut; });\nfunction circleIn(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n\nfunction circleOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n\nfunction circleInOut(t) {\n return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/cubic.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-ease/src/cubic.js ***! + \*******************************************/ +/*! exports provided: cubicIn, cubicOut, cubicInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicIn\", function() { return cubicIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicOut\", function() { return cubicOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicInOut\", function() { return cubicInOut; });\nfunction cubicIn(t) {\n return t * t * t;\n}\n\nfunction cubicOut(t) {\n return --t * t * t + 1;\n}\n\nfunction cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/cubic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/elastic.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-ease/src/elastic.js ***! + \*********************************************/ +/*! exports provided: elasticIn, elasticOut, elasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticIn\", function() { return elasticIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticOut\", function() { return elasticOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticInOut\", function() { return elasticInOut; });\nvar tau = 2 * Math.PI,\n amplitude = 1,\n period = 0.3;\n\nvar elasticIn = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticIn(t) {\n return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n }\n\n elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n elasticIn.period = function(p) { return custom(a, p); };\n\n return elasticIn;\n})(amplitude, period);\n\nvar elasticOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticOut(t) {\n return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n }\n\n elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticOut.period = function(p) { return custom(a, p); };\n\n return elasticOut;\n})(amplitude, period);\n\nvar elasticInOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticInOut(t) {\n return ((t = t * 2 - 1) < 0\n ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n }\n\n elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticInOut.period = function(p) { return custom(a, p); };\n\n return elasticInOut;\n})(amplitude, period);\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/elastic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/exp.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-ease/src/exp.js ***! + \*****************************************/ +/*! exports provided: expIn, expOut, expInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expIn\", function() { return expIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expOut\", function() { return expOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expInOut\", function() { return expInOut; });\nfunction expIn(t) {\n return Math.pow(2, 10 * t - 10);\n}\n\nfunction expOut(t) {\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction expInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/exp.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-ease/src/index.js ***! + \*******************************************/ +/*! exports provided: easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-ease/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return _linear__WEBPACK_IMPORTED_MODULE_0__[\"linear\"]; });\n\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quad */ \"./node_modules/d3-ease/src/quad.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony import */ var _cubic__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubic */ \"./node_modules/d3-ease/src/cubic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony import */ var _poly__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./poly */ \"./node_modules/d3-ease/src/poly.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony import */ var _sin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sin */ \"./node_modules/d3-ease/src/sin.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony import */ var _exp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exp */ \"./node_modules/d3-ease/src/exp.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./circle */ \"./node_modules/d3-ease/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony import */ var _bounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./bounce */ \"./node_modules/d3-ease/src/bounce.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceInOut\"]; });\n\n/* harmony import */ var _back__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./back */ \"./node_modules/d3-ease/src/back.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony import */ var _elastic__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./elastic */ \"./node_modules/d3-ease/src/elastic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticInOut\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/linear.js": +/*!********************************************!*\ + !*** ./node_modules/d3-ease/src/linear.js ***! + \********************************************/ +/*! exports provided: linear */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linear\", function() { return linear; });\nfunction linear(t) {\n return +t;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/poly.js": +/*!******************************************!*\ + !*** ./node_modules/d3-ease/src/poly.js ***! + \******************************************/ +/*! exports provided: polyIn, polyOut, polyInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyIn\", function() { return polyIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyOut\", function() { return polyOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyInOut\", function() { return polyInOut; });\nvar exponent = 3;\n\nvar polyIn = (function custom(e) {\n e = +e;\n\n function polyIn(t) {\n return Math.pow(t, e);\n }\n\n polyIn.exponent = custom;\n\n return polyIn;\n})(exponent);\n\nvar polyOut = (function custom(e) {\n e = +e;\n\n function polyOut(t) {\n return 1 - Math.pow(1 - t, e);\n }\n\n polyOut.exponent = custom;\n\n return polyOut;\n})(exponent);\n\nvar polyInOut = (function custom(e) {\n e = +e;\n\n function polyInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n }\n\n polyInOut.exponent = custom;\n\n return polyInOut;\n})(exponent);\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/poly.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/quad.js": +/*!******************************************!*\ + !*** ./node_modules/d3-ease/src/quad.js ***! + \******************************************/ +/*! exports provided: quadIn, quadOut, quadInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadIn\", function() { return quadIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadOut\", function() { return quadOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadInOut\", function() { return quadInOut; });\nfunction quadIn(t) {\n return t * t;\n}\n\nfunction quadOut(t) {\n return t * (2 - t);\n}\n\nfunction quadInOut(t) {\n return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/d3-ease/src/sin.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-ease/src/sin.js ***! + \*****************************************/ +/*! exports provided: sinIn, sinOut, sinInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinIn\", function() { return sinIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinOut\", function() { return sinOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinInOut\", function() { return sinInOut; });\nvar pi = Math.PI,\n halfPi = pi / 2;\n\nfunction sinIn(t) {\n return 1 - Math.cos(t * halfPi);\n}\n\nfunction sinOut(t) {\n return Math.sin(t * halfPi);\n}\n\nfunction sinInOut(t) {\n return (1 - Math.cos(pi * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-ease/src/sin.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/blob.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-fetch/src/blob.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction responseBlob(response) {\n if (!response.ok) throw new Error(response.status + \" \" + response.statusText);\n return response.blob();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(input, init) {\n return fetch(input, init).then(responseBlob);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/blob.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/buffer.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-fetch/src/buffer.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction responseArrayBuffer(response) {\n if (!response.ok) throw new Error(response.status + \" \" + response.statusText);\n return response.arrayBuffer();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(input, init) {\n return fetch(input, init).then(responseArrayBuffer);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/buffer.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/dsv.js": +/*!******************************************!*\ + !*** ./node_modules/d3-fetch/src/dsv.js ***! + \******************************************/ +/*! exports provided: default, csv, tsv */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return dsv; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csv\", function() { return csv; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsv\", function() { return tsv; });\n/* harmony import */ var d3_dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dsv */ \"./node_modules/d3-dsv/src/index.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./text */ \"./node_modules/d3-fetch/src/text.js\");\n\n\n\nfunction dsvParse(parse) {\n return function(input, init, row) {\n if (arguments.length === 2 && typeof init === \"function\") row = init, init = undefined;\n return Object(_text__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(input, init).then(function(response) {\n return parse(response, row);\n });\n };\n}\n\nfunction dsv(delimiter, input, init, row) {\n if (arguments.length === 3 && typeof init === \"function\") row = init, init = undefined;\n var format = Object(d3_dsv__WEBPACK_IMPORTED_MODULE_0__[\"dsvFormat\"])(delimiter);\n return Object(_text__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(input, init).then(function(response) {\n return format.parse(response, row);\n });\n}\n\nvar csv = dsvParse(d3_dsv__WEBPACK_IMPORTED_MODULE_0__[\"csvParse\"]);\nvar tsv = dsvParse(d3_dsv__WEBPACK_IMPORTED_MODULE_0__[\"tsvParse\"]);\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/dsv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/image.js": +/*!********************************************!*\ + !*** ./node_modules/d3-fetch/src/image.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(input, init) {\n return new Promise(function(resolve, reject) {\n var image = new Image;\n for (var key in init) image[key] = init[key];\n image.onerror = reject;\n image.onload = function() { resolve(image); };\n image.src = input;\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/image.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-fetch/src/index.js ***! + \********************************************/ +/*! exports provided: blob, buffer, dsv, csv, tsv, image, json, text, xml, html, svg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _blob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./blob */ \"./node_modules/d3-fetch/src/blob.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"blob\", function() { return _blob__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _buffer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./buffer */ \"./node_modules/d3-fetch/src/buffer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"buffer\", function() { return _buffer__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-fetch/src/dsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsv\", function() { return _dsv__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csv\", function() { return _dsv__WEBPACK_IMPORTED_MODULE_2__[\"csv\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsv\", function() { return _dsv__WEBPACK_IMPORTED_MODULE_2__[\"tsv\"]; });\n\n/* harmony import */ var _image__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./image */ \"./node_modules/d3-fetch/src/image.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"image\", function() { return _image__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./json */ \"./node_modules/d3-fetch/src/json.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"json\", function() { return _json__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./text */ \"./node_modules/d3-fetch/src/text.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"text\", function() { return _text__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _xml__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./xml */ \"./node_modules/d3-fetch/src/xml.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"xml\", function() { return _xml__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"html\", function() { return _xml__WEBPACK_IMPORTED_MODULE_6__[\"html\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"svg\", function() { return _xml__WEBPACK_IMPORTED_MODULE_6__[\"svg\"]; });\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/json.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-fetch/src/json.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction responseJson(response) {\n if (!response.ok) throw new Error(response.status + \" \" + response.statusText);\n return response.json();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(input, init) {\n return fetch(input, init).then(responseJson);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/json.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/text.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-fetch/src/text.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction responseText(response) {\n if (!response.ok) throw new Error(response.status + \" \" + response.statusText);\n return response.text();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(input, init) {\n return fetch(input, init).then(responseText);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/text.js?"); + +/***/ }), + +/***/ "./node_modules/d3-fetch/src/xml.js": +/*!******************************************!*\ + !*** ./node_modules/d3-fetch/src/xml.js ***! + \******************************************/ +/*! exports provided: default, html, svg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"html\", function() { return html; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"svg\", function() { return svg; });\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./text */ \"./node_modules/d3-fetch/src/text.js\");\n\n\nfunction parser(type) {\n return function(input, init) {\n return Object(_text__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(input, init).then(function(text) {\n return (new DOMParser).parseFromString(text, type);\n });\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (parser(\"application/xml\"));\n\nvar html = parser(\"text/html\");\n\nvar svg = parser(\"image/svg+xml\");\n\n\n//# sourceURL=webpack:///./node_modules/d3-fetch/src/xml.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/center.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-force/src/center.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n var nodes;\n\n if (x == null) x = 0;\n if (y == null) y = 0;\n\n function force() {\n var i,\n n = nodes.length,\n node,\n sx = 0,\n sy = 0;\n\n for (i = 0; i < n; ++i) {\n node = nodes[i], sx += node.x, sy += node.y;\n }\n\n for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {\n node = nodes[i], node.x -= sx, node.y -= sy;\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = +_, force) : x;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = +_, force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/center.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/collide.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-force/src/collide.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/d3-quadtree/src/index.js\");\n\n\n\n\nfunction x(d) {\n return d.x + d.vx;\n}\n\nfunction y(d) {\n return d.y + d.vy;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius) {\n var nodes,\n radii,\n strength = 1,\n iterations = 1;\n\n if (typeof radius !== \"function\") radius = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(radius == null ? 1 : +radius);\n\n function force() {\n var i, n = nodes.length,\n tree,\n node,\n xi,\n yi,\n ri,\n ri2;\n\n for (var k = 0; k < iterations; ++k) {\n tree = Object(d3_quadtree__WEBPACK_IMPORTED_MODULE_2__[\"quadtree\"])(nodes, x, y).visitAfter(prepare);\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n ri = radii[node.index], ri2 = ri * ri;\n xi = node.x + node.vx;\n yi = node.y + node.vy;\n tree.visit(apply);\n }\n }\n\n function apply(quad, x0, y0, x1, y1) {\n var data = quad.data, rj = quad.r, r = ri + rj;\n if (data) {\n if (data.index > node.index) {\n var x = xi - data.x - data.vx,\n y = yi - data.y - data.vy,\n l = x * x + y * y;\n if (l < r * r) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n l = (r - (l = Math.sqrt(l))) / l * strength;\n node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));\n node.vy += (y *= l) * r;\n data.vx -= x * (r = 1 - r);\n data.vy -= y * r;\n }\n }\n return;\n }\n return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;\n }\n }\n\n function prepare(quad) {\n if (quad.data) return quad.r = radii[quad.data.index];\n for (var i = quad.r = 0; i < 4; ++i) {\n if (quad[i] && quad[i].r > quad.r) {\n quad.r = quad[i].r;\n }\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length, node;\n radii = new Array(n);\n for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.iterations = function(_) {\n return arguments.length ? (iterations = +_, force) : iterations;\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = +_, force) : strength;\n };\n\n force.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : radius;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/collide.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-force/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-force/src/index.js ***! + \********************************************/ +/*! exports provided: forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceSimulation, forceX, forceY */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _center__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./center */ \"./node_modules/d3-force/src/center.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCenter\", function() { return _center__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _collide__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./collide */ \"./node_modules/d3-force/src/collide.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCollide\", function() { return _collide__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _link__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./link */ \"./node_modules/d3-force/src/link.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceLink\", function() { return _link__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _manyBody__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./manyBody */ \"./node_modules/d3-force/src/manyBody.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceManyBody\", function() { return _manyBody__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _radial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./radial */ \"./node_modules/d3-force/src/radial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceRadial\", function() { return _radial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _simulation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./simulation */ \"./node_modules/d3-force/src/simulation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceSimulation\", function() { return _simulation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _x__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./x */ \"./node_modules/d3-force/src/x.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceX\", function() { return _x__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _y__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./y */ \"./node_modules/d3-force/src/y.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceY\", function() { return _y__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/jiggle.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-force/src/jiggle.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return (Math.random() - 0.5) * 1e-6;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/jiggle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/link.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-force/src/link.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-collection */ \"./node_modules/d3-collection/src/index.js\");\n\n\n\n\nfunction index(d) {\n return d.index;\n}\n\nfunction find(nodeById, nodeId) {\n var node = nodeById.get(nodeId);\n if (!node) throw new Error(\"missing: \" + nodeId);\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(links) {\n var id = index,\n strength = defaultStrength,\n strengths,\n distance = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(30),\n distances,\n nodes,\n count,\n bias,\n iterations = 1;\n\n if (links == null) links = [];\n\n function defaultStrength(link) {\n return 1 / Math.min(count[link.source.index], count[link.target.index]);\n }\n\n function force(alpha) {\n for (var k = 0, n = links.length; k < iterations; ++k) {\n for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {\n link = links[i], source = link.source, target = link.target;\n x = target.x + target.vx - source.x - source.vx || Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n y = target.y + target.vy - source.y - source.vy || Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n l = Math.sqrt(x * x + y * y);\n l = (l - distances[i]) / l * alpha * strengths[i];\n x *= l, y *= l;\n target.vx -= x * (b = bias[i]);\n target.vy -= y * b;\n source.vx += x * (b = 1 - b);\n source.vy += y * b;\n }\n }\n }\n\n function initialize() {\n if (!nodes) return;\n\n var i,\n n = nodes.length,\n m = links.length,\n nodeById = Object(d3_collection__WEBPACK_IMPORTED_MODULE_2__[\"map\"])(nodes, id),\n link;\n\n for (i = 0, count = new Array(n); i < m; ++i) {\n link = links[i], link.index = i;\n if (typeof link.source !== \"object\") link.source = find(nodeById, link.source);\n if (typeof link.target !== \"object\") link.target = find(nodeById, link.target);\n count[link.source.index] = (count[link.source.index] || 0) + 1;\n count[link.target.index] = (count[link.target.index] || 0) + 1;\n }\n\n for (i = 0, bias = new Array(m); i < m; ++i) {\n link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);\n }\n\n strengths = new Array(m), initializeStrength();\n distances = new Array(m), initializeDistance();\n }\n\n function initializeStrength() {\n if (!nodes) return;\n\n for (var i = 0, n = links.length; i < n; ++i) {\n strengths[i] = +strength(links[i], i, links);\n }\n }\n\n function initializeDistance() {\n if (!nodes) return;\n\n for (var i = 0, n = links.length; i < n; ++i) {\n distances[i] = +distance(links[i], i, links);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.links = function(_) {\n return arguments.length ? (links = _, initialize(), force) : links;\n };\n\n force.id = function(_) {\n return arguments.length ? (id = _, force) : id;\n };\n\n force.iterations = function(_) {\n return arguments.length ? (iterations = +_, force) : iterations;\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initializeStrength(), force) : strength;\n };\n\n force.distance = function(_) {\n return arguments.length ? (distance = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initializeDistance(), force) : distance;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/link.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/manyBody.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-force/src/manyBody.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/d3-quadtree/src/index.js\");\n/* harmony import */ var _simulation__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./simulation */ \"./node_modules/d3-force/src/simulation.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes,\n node,\n alpha,\n strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(-30),\n strengths,\n distanceMin2 = 1,\n distanceMax2 = Infinity,\n theta2 = 0.81;\n\n function force(_) {\n var i, n = nodes.length, tree = Object(d3_quadtree__WEBPACK_IMPORTED_MODULE_2__[\"quadtree\"])(nodes, _simulation__WEBPACK_IMPORTED_MODULE_3__[\"x\"], _simulation__WEBPACK_IMPORTED_MODULE_3__[\"y\"]).visitAfter(accumulate);\n for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length, node;\n strengths = new Array(n);\n for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);\n }\n\n function accumulate(quad) {\n var strength = 0, q, c, weight = 0, x, y, i;\n\n // For internal nodes, accumulate forces from child quadrants.\n if (quad.length) {\n for (x = y = i = 0; i < 4; ++i) {\n if ((q = quad[i]) && (c = Math.abs(q.value))) {\n strength += q.value, weight += c, x += c * q.x, y += c * q.y;\n }\n }\n quad.x = x / weight;\n quad.y = y / weight;\n }\n\n // For leaf nodes, accumulate forces from coincident quadrants.\n else {\n q = quad;\n q.x = q.data.x;\n q.y = q.data.y;\n do strength += strengths[q.data.index];\n while (q = q.next);\n }\n\n quad.value = strength;\n }\n\n function apply(quad, x1, _, x2) {\n if (!quad.value) return true;\n\n var x = quad.x - node.x,\n y = quad.y - node.y,\n w = x2 - x1,\n l = x * x + y * y;\n\n // Apply the Barnes-Hut approximation if possible.\n // Limit forces for very close nodes; randomize direction if coincident.\n if (w * w / theta2 < l) {\n if (l < distanceMax2) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n node.vx += x * quad.value * alpha / l;\n node.vy += y * quad.value * alpha / l;\n }\n return true;\n }\n\n // Otherwise, process points directly.\n else if (quad.length || l >= distanceMax2) return;\n\n // Limit forces for very close nodes; randomize direction if coincident.\n if (quad.data !== node || quad.next) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n }\n\n do if (quad.data !== node) {\n w = strengths[quad.data.index] * alpha / l;\n node.vx += x * w;\n node.vy += y * w;\n } while (quad = quad.next);\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.distanceMin = function(_) {\n return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);\n };\n\n force.distanceMax = function(_) {\n return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);\n };\n\n force.theta = function(_) {\n return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/manyBody.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/radial.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-force/src/radial.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius, x, y) {\n var nodes,\n strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n strengths,\n radiuses;\n\n if (typeof radius !== \"function\") radius = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+radius);\n if (x == null) x = 0;\n if (y == null) y = 0;\n\n function force(alpha) {\n for (var i = 0, n = nodes.length; i < n; ++i) {\n var node = nodes[i],\n dx = node.x - x || 1e-6,\n dy = node.y - y || 1e-6,\n r = Math.sqrt(dx * dx + dy * dy),\n k = (radiuses[i] - r) * strengths[i] * alpha / r;\n node.vx += dx * k;\n node.vy += dy * k;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n radiuses = new Array(n);\n for (i = 0; i < n; ++i) {\n radiuses[i] = +radius(nodes[i], i, nodes);\n strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _, initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : radius;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = +_, force) : x;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = +_, force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/radial.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/simulation.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-force/src/simulation.js ***! + \*************************************************/ +/*! exports provided: x, y, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-collection */ \"./node_modules/d3-collection/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-timer */ \"./node_modules/d3-timer/src/index.js\");\n\n\n\n\nfunction x(d) {\n return d.x;\n}\n\nfunction y(d) {\n return d.y;\n}\n\nvar initialRadius = 10,\n initialAngle = Math.PI * (3 - Math.sqrt(5));\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(nodes) {\n var simulation,\n alpha = 1,\n alphaMin = 0.001,\n alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),\n alphaTarget = 0,\n velocityDecay = 0.6,\n forces = Object(d3_collection__WEBPACK_IMPORTED_MODULE_1__[\"map\"])(),\n stepper = Object(d3_timer__WEBPACK_IMPORTED_MODULE_2__[\"timer\"])(step),\n event = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"tick\", \"end\");\n\n if (nodes == null) nodes = [];\n\n function step() {\n tick();\n event.call(\"tick\", simulation);\n if (alpha < alphaMin) {\n stepper.stop();\n event.call(\"end\", simulation);\n }\n }\n\n function tick() {\n var i, n = nodes.length, node;\n\n alpha += (alphaTarget - alpha) * alphaDecay;\n\n forces.each(function(force) {\n force(alpha);\n });\n\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n if (node.fx == null) node.x += node.vx *= velocityDecay;\n else node.x = node.fx, node.vx = 0;\n if (node.fy == null) node.y += node.vy *= velocityDecay;\n else node.y = node.fy, node.vy = 0;\n }\n }\n\n function initializeNodes() {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.index = i;\n if (isNaN(node.x) || isNaN(node.y)) {\n var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;\n node.x = radius * Math.cos(angle);\n node.y = radius * Math.sin(angle);\n }\n if (isNaN(node.vx) || isNaN(node.vy)) {\n node.vx = node.vy = 0;\n }\n }\n }\n\n function initializeForce(force) {\n if (force.initialize) force.initialize(nodes);\n return force;\n }\n\n initializeNodes();\n\n return simulation = {\n tick: tick,\n\n restart: function() {\n return stepper.restart(step), simulation;\n },\n\n stop: function() {\n return stepper.stop(), simulation;\n },\n\n nodes: function(_) {\n return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;\n },\n\n alpha: function(_) {\n return arguments.length ? (alpha = +_, simulation) : alpha;\n },\n\n alphaMin: function(_) {\n return arguments.length ? (alphaMin = +_, simulation) : alphaMin;\n },\n\n alphaDecay: function(_) {\n return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;\n },\n\n alphaTarget: function(_) {\n return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;\n },\n\n velocityDecay: function(_) {\n return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;\n },\n\n force: function(name, _) {\n return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);\n },\n\n find: function(x, y, radius) {\n var i = 0,\n n = nodes.length,\n dx,\n dy,\n d2,\n node,\n closest;\n\n if (radius == null) radius = Infinity;\n else radius *= radius;\n\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n dx = x - node.x;\n dy = y - node.y;\n d2 = dx * dx + dy * dy;\n if (d2 < radius) closest = node, radius = d2;\n }\n\n return closest;\n },\n\n on: function(name, _) {\n return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/simulation.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/x.js": +/*!****************************************!*\ + !*** ./node_modules/d3-force/src/x.js ***! + \****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n var strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n nodes,\n strengths,\n xz;\n\n if (typeof x !== \"function\") x = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x == null ? 0 : +x);\n\n function force(alpha) {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n xz = new Array(n);\n for (i = 0; i < n; ++i) {\n strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : x;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/x.js?"); + +/***/ }), + +/***/ "./node_modules/d3-force/src/y.js": +/*!****************************************!*\ + !*** ./node_modules/d3-force/src/y.js ***! + \****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(y) {\n var strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n nodes,\n strengths,\n yz;\n\n if (typeof y !== \"function\") y = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(y == null ? 0 : +y);\n\n function force(alpha) {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n yz = new Array(n);\n for (i = 0; i < n; ++i) {\n strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-force/src/y.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/defaultLocale.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-format/src/defaultLocale.js ***! + \*****************************************************/ +/*! exports provided: format, formatPrefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return format; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return formatPrefix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/d3-format/src/locale.js\");\n\n\nvar locale;\nvar format;\nvar formatPrefix;\n\ndefaultLocale({\n decimal: \".\",\n thousands: \",\",\n grouping: [3],\n currency: [\"$\", \"\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n format = locale.format;\n formatPrefix = locale.formatPrefix;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/exponent.js": +/*!************************************************!*\ + !*** ./node_modules/d3-format/src/exponent.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(x)), x ? x[1] : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/exponent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatDecimal.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-format/src/formatDecimal.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Computes the decimal coefficient and exponent of the specified number x with\n// significant digits p, where x is positive and p is in [1, 21] or undefined.\n// For example, formatDecimal(1.23) returns [\"123\", 0].\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf(\"e\")) < 0) return null; // NaN, ±Infinity\n var i, coefficient = x.slice(0, i);\n\n // The string returned by toExponential either has the form \\d\\.\\d+e[-+]\\d+\n // (e.g., 1.2e+3) or the form \\de[-+]\\d+ (e.g., 1e+3).\n return [\n coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,\n +x.slice(i + 1)\n ];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatDecimal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatGroup.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-format/src/formatGroup.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(grouping, thousands) {\n return function(value, width) {\n var i = value.length,\n t = [],\n j = 0,\n g = grouping[0],\n length = 0;\n\n while (i > 0 && g > 0) {\n if (length + g + 1 > width) g = Math.max(1, width - length);\n t.push(value.substring(i -= g, i + g));\n if ((length += g + 1) > width) break;\n g = grouping[j = (j + 1) % grouping.length];\n }\n\n return t.reverse().join(thousands);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatGroup.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatNumerals.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-format/src/formatNumerals.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(numerals) {\n return function(value) {\n return value.replace(/[0-9]/g, function(i) {\n return numerals[+i];\n });\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatNumerals.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatPrefixAuto.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-format/src/formatPrefixAuto.js ***! + \********************************************************/ +/*! exports provided: prefixExponent, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefixExponent\", function() { return prefixExponent; });\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/d3-format/src/formatDecimal.js\");\n\n\nvar prefixExponent;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1],\n i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,\n n = coefficient.length;\n return i === n ? coefficient\n : i > n ? coefficient + new Array(i - n + 1).join(\"0\")\n : i > 0 ? coefficient.slice(0, i) + \".\" + coefficient.slice(i)\n : \"0.\" + new Array(1 - i).join(\"0\") + Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, Math.max(0, p + i - 1))[0]; // less than 1y!\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatPrefixAuto.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatRounded.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-format/src/formatRounded.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1];\n return exponent < 0 ? \"0.\" + new Array(-exponent).join(\"0\") + coefficient\n : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + \".\" + coefficient.slice(exponent + 1)\n : coefficient + new Array(exponent - coefficient.length + 2).join(\"0\");\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatRounded.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatSpecifier.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-format/src/formatSpecifier.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatSpecifier; });\n// [[fill]align][sign][symbol][0][width][,][.precision][~][type]\nvar re = /^(?:(.)?([<>=^]))?([+\\-( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?(~)?([a-z%])?$/i;\n\nfunction formatSpecifier(specifier) {\n return new FormatSpecifier(specifier);\n}\n\nformatSpecifier.prototype = FormatSpecifier.prototype; // instanceof\n\nfunction FormatSpecifier(specifier) {\n if (!(match = re.exec(specifier))) throw new Error(\"invalid format: \" + specifier);\n var match;\n this.fill = match[1] || \" \";\n this.align = match[2] || \">\";\n this.sign = match[3] || \"-\";\n this.symbol = match[4] || \"\";\n this.zero = !!match[5];\n this.width = match[6] && +match[6];\n this.comma = !!match[7];\n this.precision = match[8] && +match[8].slice(1);\n this.trim = !!match[9];\n this.type = match[10] || \"\";\n}\n\nFormatSpecifier.prototype.toString = function() {\n return this.fill\n + this.align\n + this.sign\n + this.symbol\n + (this.zero ? \"0\" : \"\")\n + (this.width == null ? \"\" : Math.max(1, this.width | 0))\n + (this.comma ? \",\" : \"\")\n + (this.precision == null ? \"\" : \".\" + Math.max(0, this.precision | 0))\n + (this.trim ? \"~\" : \"\")\n + this.type;\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatSpecifier.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatTrim.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-format/src/formatTrim.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(s) {\n out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {\n switch (s[i]) {\n case \".\": i0 = i1 = i; break;\n case \"0\": if (i0 === 0) i0 = i; i1 = i; break;\n default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;\n }\n }\n return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatTrim.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/formatTypes.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-format/src/formatTypes.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _formatRounded__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatRounded */ \"./node_modules/d3-format/src/formatRounded.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n \"%\": function(x, p) { return (x * 100).toFixed(p); },\n \"b\": function(x) { return Math.round(x).toString(2); },\n \"c\": function(x) { return x + \"\"; },\n \"d\": function(x) { return Math.round(x).toString(10); },\n \"e\": function(x, p) { return x.toExponential(p); },\n \"f\": function(x, p) { return x.toFixed(p); },\n \"g\": function(x, p) { return x.toPrecision(p); },\n \"o\": function(x) { return Math.round(x).toString(8); },\n \"p\": function(x, p) { return Object(_formatRounded__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(x * 100, p); },\n \"r\": _formatRounded__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n \"s\": _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n \"X\": function(x) { return Math.round(x).toString(16).toUpperCase(); },\n \"x\": function(x) { return Math.round(x).toString(16); }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/formatTypes.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/identity.js": +/*!************************************************!*\ + !*** ./node_modules/d3-format/src/identity.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/index.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-format/src/index.js ***! + \*********************************************/ +/*! exports provided: formatDefaultLocale, format, formatPrefix, formatLocale, formatSpecifier, precisionFixed, precisionPrefix, precisionRound */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/d3-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatDefaultLocale\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"format\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"formatPrefix\"]; });\n\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locale */ \"./node_modules/d3-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatLocale\", function() { return _locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _formatSpecifier__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatSpecifier */ \"./node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatSpecifier\", function() { return _formatSpecifier__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _precisionFixed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./precisionFixed */ \"./node_modules/d3-format/src/precisionFixed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionFixed\", function() { return _precisionFixed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _precisionPrefix__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./precisionPrefix */ \"./node_modules/d3-format/src/precisionPrefix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionPrefix\", function() { return _precisionPrefix__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _precisionRound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./precisionRound */ \"./node_modules/d3-format/src/precisionRound.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionRound\", function() { return _precisionRound__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/locale.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-format/src/locale.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/d3-format/src/exponent.js\");\n/* harmony import */ var _formatGroup__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatGroup */ \"./node_modules/d3-format/src/formatGroup.js\");\n/* harmony import */ var _formatNumerals__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatNumerals */ \"./node_modules/d3-format/src/formatNumerals.js\");\n/* harmony import */ var _formatSpecifier__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./formatSpecifier */ \"./node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony import */ var _formatTrim__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./formatTrim */ \"./node_modules/d3-format/src/formatTrim.js\");\n/* harmony import */ var _formatTypes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./formatTypes */ \"./node_modules/d3-format/src/formatTypes.js\");\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./identity */ \"./node_modules/d3-format/src/identity.js\");\n\n\n\n\n\n\n\n\n\nvar prefixes = [\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"µ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(locale) {\n var group = locale.grouping && locale.thousands ? Object(_formatGroup__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(locale.grouping, locale.thousands) : _identity__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n currency = locale.currency,\n decimal = locale.decimal,\n numerals = locale.numerals ? Object(_formatNumerals__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(locale.numerals) : _identity__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n percent = locale.percent || \"%\";\n\n function newFormat(specifier) {\n specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier);\n\n var fill = specifier.fill,\n align = specifier.align,\n sign = specifier.sign,\n symbol = specifier.symbol,\n zero = specifier.zero,\n width = specifier.width,\n comma = specifier.comma,\n precision = specifier.precision,\n trim = specifier.trim,\n type = specifier.type;\n\n // The \"n\" type is an alias for \",g\".\n if (type === \"n\") comma = true, type = \"g\";\n\n // The \"\" type, and any invalid type, is an alias for \".12~g\".\n else if (!_formatTypes__WEBPACK_IMPORTED_MODULE_5__[\"default\"][type]) precision == null && (precision = 12), trim = true, type = \"g\";\n\n // If zero fill is specified, padding goes after sign and before digits.\n if (zero || (fill === \"0\" && align === \"=\")) zero = true, fill = \"0\", align = \"=\";\n\n // Compute the prefix and suffix.\n // For SI-prefix, the suffix is lazily computed.\n var prefix = symbol === \"$\" ? currency[0] : symbol === \"#\" && /[boxX]/.test(type) ? \"0\" + type.toLowerCase() : \"\",\n suffix = symbol === \"$\" ? currency[1] : /[%p]/.test(type) ? percent : \"\";\n\n // What format function should we use?\n // Is this an integer type?\n // Can this type generate exponential notation?\n var formatType = _formatTypes__WEBPACK_IMPORTED_MODULE_5__[\"default\"][type],\n maybeSuffix = /[defgprs%]/.test(type);\n\n // Set the default precision if not specified,\n // or clamp the specified precision to the supported range.\n // For significant precision, it must be in [1, 21].\n // For fixed precision, it must be in [0, 20].\n precision = precision == null ? 6\n : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))\n : Math.max(0, Math.min(20, precision));\n\n function format(value) {\n var valuePrefix = prefix,\n valueSuffix = suffix,\n i, n, c;\n\n if (type === \"c\") {\n valueSuffix = formatType(value) + valueSuffix;\n value = \"\";\n } else {\n value = +value;\n\n // Perform the initial formatting.\n var valueNegative = value < 0;\n value = formatType(Math.abs(value), precision);\n\n // Trim insignificant zeros.\n if (trim) value = Object(_formatTrim__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(value);\n\n // If a negative value rounds to zero during formatting, treat as positive.\n if (valueNegative && +value === 0) valueNegative = false;\n\n // Compute the prefix and suffix.\n valuePrefix = (valueNegative ? (sign === \"(\" ? sign : \"-\") : sign === \"-\" || sign === \"(\" ? \"\" : sign) + valuePrefix;\n valueSuffix = (type === \"s\" ? prefixes[8 + _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_6__[\"prefixExponent\"] / 3] : \"\") + valueSuffix + (valueNegative && sign === \"(\" ? \")\" : \"\");\n\n // Break the formatted value into the integer “value” part that can be\n // grouped, and fractional or exponential “suffix” part that is not.\n if (maybeSuffix) {\n i = -1, n = value.length;\n while (++i < n) {\n if (c = value.charCodeAt(i), 48 > c || c > 57) {\n valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;\n value = value.slice(0, i);\n break;\n }\n }\n }\n }\n\n // If the fill character is not \"0\", grouping is applied before padding.\n if (comma && !zero) value = group(value, Infinity);\n\n // Compute the padding.\n var length = valuePrefix.length + value.length + valueSuffix.length,\n padding = length < width ? new Array(width - length + 1).join(fill) : \"\";\n\n // If the fill character is \"0\", grouping is applied after padding.\n if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = \"\";\n\n // Reconstruct the final output based on the desired alignment.\n switch (align) {\n case \"<\": value = valuePrefix + value + valueSuffix + padding; break;\n case \"=\": value = valuePrefix + padding + value + valueSuffix; break;\n case \"^\": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;\n default: value = padding + valuePrefix + value + valueSuffix; break;\n }\n\n return numerals(value);\n }\n\n format.toString = function() {\n return specifier + \"\";\n };\n\n return format;\n }\n\n function formatPrefix(specifier, value) {\n var f = newFormat((specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier), specifier.type = \"f\", specifier)),\n e = Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3,\n k = Math.pow(10, -e),\n prefix = prefixes[8 + e / 3];\n return function(value) {\n return f(k * value) + prefix;\n };\n }\n\n return {\n format: newFormat,\n formatPrefix: formatPrefix\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/precisionFixed.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-format/src/precisionFixed.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step) {\n return Math.max(0, -Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/precisionFixed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/precisionPrefix.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-format/src/precisionPrefix.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, value) {\n return Math.max(0, Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3 - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/precisionPrefix.js?"); + +/***/ }), + +/***/ "./node_modules/d3-format/src/precisionRound.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-format/src/precisionRound.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, max) {\n step = Math.abs(step), max = Math.abs(max) - step;\n return Math.max(0, Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(max) - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(step)) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-format/src/precisionRound.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/adder.js": +/*!******************************************!*\ + !*** ./node_modules/d3-geo/src/adder.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Adds floating point numbers with twice the normal precision.\n// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and\n// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)\n// 305–363 (1997).\n// Code adapted from GeographicLib by Charles F. F. Karney,\n// http://geographiclib.sourceforge.net/\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Adder;\n});\n\nfunction Adder() {\n this.reset();\n}\n\nAdder.prototype = {\n constructor: Adder,\n reset: function() {\n this.s = // rounded value\n this.t = 0; // exact error\n },\n add: function(y) {\n add(temp, y, this.t);\n add(this, temp.s, this.s);\n if (this.s) this.t += temp.t;\n else this.s = temp.t;\n },\n valueOf: function() {\n return this.s;\n }\n};\n\nvar temp = new Adder;\n\nfunction add(adder, a, b) {\n var x = adder.s = a + b,\n bv = x - a,\n av = x - bv;\n adder.t = (a - av) + (b - bv);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/adder.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/area.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-geo/src/area.js ***! + \*****************************************/ +/*! exports provided: areaRingSum, areaStream, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"areaRingSum\", function() { return areaRingSum; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"areaStream\", function() { return areaStream; });\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./noop */ \"./node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stream */ \"./node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\nvar areaRingSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n\nvar areaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lambda00,\n phi00,\n lambda0,\n cosPhi0,\n sinPhi0;\n\nvar areaStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: function() {\n areaRingSum.reset();\n areaStream.lineStart = areaRingStart;\n areaStream.lineEnd = areaRingEnd;\n },\n polygonEnd: function() {\n var areaRing = +areaRingSum;\n areaSum.add(areaRing < 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] + areaRing : areaRing);\n this.lineStart = this.lineEnd = this.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n },\n sphere: function() {\n areaSum.add(_math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"]);\n }\n};\n\nfunction areaRingStart() {\n areaStream.point = areaPointFirst;\n}\n\nfunction areaRingEnd() {\n areaPoint(lambda00, phi00);\n}\n\nfunction areaPointFirst(lambda, phi) {\n areaStream.point = areaPoint;\n lambda00 = lambda, phi00 = phi;\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n lambda0 = lambda, cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi = phi / 2 + _math__WEBPACK_IMPORTED_MODULE_1__[\"quarterPi\"]), sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi);\n}\n\nfunction areaPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n phi = phi / 2 + _math__WEBPACK_IMPORTED_MODULE_1__[\"quarterPi\"]; // half the angular distance from south pole\n\n // Spherical excess E for a spherical triangle with vertices: south pole,\n // previous point, current point. Uses a formula derived from Cagnoli’s\n // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).\n var dLambda = lambda - lambda0,\n sdLambda = dLambda >= 0 ? 1 : -1,\n adLambda = sdLambda * dLambda,\n cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n sinPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = sinPhi0 * sinPhi,\n u = cosPhi0 * cosPhi + k * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(adLambda),\n v = k * sdLambda * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(adLambda);\n areaRingSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(v, u));\n\n // Advance the previous points.\n lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n areaSum.reset();\n Object(_stream__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(object, areaStream);\n return areaSum * 2;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/bounds.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-geo/src/bounds.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-geo/src/area.js\");\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./stream */ \"./node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\n\nvar lambda0, phi0, lambda1, phi1, // bounds\n lambda2, // previous lambda-coordinate\n lambda00, phi00, // first point\n p0, // previous 3D point\n deltaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n ranges,\n range;\n\nvar boundsStream = {\n point: boundsPoint,\n lineStart: boundsLineStart,\n lineEnd: boundsLineEnd,\n polygonStart: function() {\n boundsStream.point = boundsRingPoint;\n boundsStream.lineStart = boundsRingStart;\n boundsStream.lineEnd = boundsRingEnd;\n deltaSum.reset();\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].polygonStart();\n },\n polygonEnd: function() {\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].polygonEnd();\n boundsStream.point = boundsPoint;\n boundsStream.lineStart = boundsLineStart;\n boundsStream.lineEnd = boundsLineEnd;\n if (_area__WEBPACK_IMPORTED_MODULE_1__[\"areaRingSum\"] < 0) lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);\n else if (deltaSum > _math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) phi1 = 90;\n else if (deltaSum < -_math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) phi0 = -90;\n range[0] = lambda0, range[1] = lambda1;\n }\n};\n\nfunction boundsPoint(lambda, phi) {\n ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n}\n\nfunction linePoint(lambda, phi) {\n var p = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesian\"])([lambda * _math__WEBPACK_IMPORTED_MODULE_3__[\"radians\"], phi * _math__WEBPACK_IMPORTED_MODULE_3__[\"radians\"]]);\n if (p0) {\n var normal = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianCross\"])(p0, p),\n equatorial = [normal[1], -normal[0], 0],\n inflection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianCross\"])(equatorial, normal);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianNormalizeInPlace\"])(inflection);\n inflection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"spherical\"])(inflection);\n var delta = lambda - lambda2,\n sign = delta > 0 ? 1 : -1,\n lambdai = inflection[0] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"] * sign,\n phii,\n antimeridian = Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(delta) > 180;\n if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {\n phii = inflection[1] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"];\n if (phii > phi1) phi1 = phii;\n } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {\n phii = -inflection[1] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"];\n if (phii < phi0) phi0 = phii;\n } else {\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n }\n if (antimeridian) {\n if (lambda < lambda2) {\n if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;\n } else {\n if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;\n }\n } else {\n if (lambda1 >= lambda0) {\n if (lambda < lambda0) lambda0 = lambda;\n if (lambda > lambda1) lambda1 = lambda;\n } else {\n if (lambda > lambda2) {\n if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;\n } else {\n if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;\n }\n }\n }\n } else {\n ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);\n }\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n p0 = p, lambda2 = lambda;\n}\n\nfunction boundsLineStart() {\n boundsStream.point = linePoint;\n}\n\nfunction boundsLineEnd() {\n range[0] = lambda0, range[1] = lambda1;\n boundsStream.point = boundsPoint;\n p0 = null;\n}\n\nfunction boundsRingPoint(lambda, phi) {\n if (p0) {\n var delta = lambda - lambda2;\n deltaSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);\n } else {\n lambda00 = lambda, phi00 = phi;\n }\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].point(lambda, phi);\n linePoint(lambda, phi);\n}\n\nfunction boundsRingStart() {\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].lineStart();\n}\n\nfunction boundsRingEnd() {\n boundsRingPoint(lambda00, phi00);\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].lineEnd();\n if (Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(deltaSum) > _math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) lambda0 = -(lambda1 = 180);\n range[0] = lambda0, range[1] = lambda1;\n p0 = null;\n}\n\n// Finds the left-right distance between two longitudes.\n// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want\n// the distance between ±180° to be 360°.\nfunction angle(lambda0, lambda1) {\n return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;\n}\n\nfunction rangeCompare(a, b) {\n return a[0] - b[0];\n}\n\nfunction rangeContains(range, x) {\n return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(feature) {\n var i, n, a, b, merged, deltaMax, delta;\n\n phi1 = lambda1 = -(lambda0 = phi0 = Infinity);\n ranges = [];\n Object(_stream__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(feature, boundsStream);\n\n // First, sort ranges by their minimum longitudes.\n if (n = ranges.length) {\n ranges.sort(rangeCompare);\n\n // Then, merge any ranges that overlap.\n for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {\n b = ranges[i];\n if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {\n if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];\n if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];\n } else {\n merged.push(a = b);\n }\n }\n\n // Finally, find the largest gap between the merged ranges.\n // The final bounding box will be the inverse of this gap.\n for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {\n b = merged[i];\n if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0 = b[0], lambda1 = a[1];\n }\n }\n\n ranges = range = null;\n\n return lambda0 === Infinity || phi0 === Infinity\n ? [[NaN, NaN], [NaN, NaN]]\n : [[lambda0, phi0], [lambda1, phi1]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/bounds.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/cartesian.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-geo/src/cartesian.js ***! + \**********************************************/ +/*! exports provided: spherical, cartesian, cartesianDot, cartesianCross, cartesianAddInPlace, cartesianScale, cartesianNormalizeInPlace */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"spherical\", function() { return spherical; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesian\", function() { return cartesian; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianDot\", function() { return cartesianDot; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianCross\", function() { return cartesianCross; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianAddInPlace\", function() { return cartesianAddInPlace; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianScale\", function() { return cartesianScale; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianNormalizeInPlace\", function() { return cartesianNormalizeInPlace; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\nfunction spherical(cartesian) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(cartesian[1], cartesian[0]), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(cartesian[2])];\n}\n\nfunction cartesian(spherical) {\n var lambda = spherical[0], phi = spherical[1], cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n return [cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda), cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi)];\n}\n\nfunction cartesianDot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n\nfunction cartesianCross(a, b) {\n return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];\n}\n\n// TODO return a\nfunction cartesianAddInPlace(a, b) {\n a[0] += b[0], a[1] += b[1], a[2] += b[2];\n}\n\nfunction cartesianScale(vector, k) {\n return [vector[0] * k, vector[1] * k, vector[2] * k];\n}\n\n// TODO return d\nfunction cartesianNormalizeInPlace(d) {\n var l = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);\n d[0] /= l, d[1] /= l, d[2] /= l;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/cartesian.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/centroid.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/centroid.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noop */ \"./node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stream */ \"./node_modules/d3-geo/src/stream.js\");\n\n\n\n\nvar W0, W1,\n X0, Y0, Z0,\n X1, Y1, Z1,\n X2, Y2, Z2,\n lambda00, phi00, // first point\n x0, y0, z0; // previous point\n\nvar centroidStream = {\n sphere: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n point: centroidPoint,\n lineStart: centroidLineStart,\n lineEnd: centroidLineEnd,\n polygonStart: function() {\n centroidStream.lineStart = centroidRingStart;\n centroidStream.lineEnd = centroidRingEnd;\n },\n polygonEnd: function() {\n centroidStream.lineStart = centroidLineStart;\n centroidStream.lineEnd = centroidLineEnd;\n }\n};\n\n// Arithmetic mean of Cartesian vectors.\nfunction centroidPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n centroidPointCartesian(cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda), cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi));\n}\n\nfunction centroidPointCartesian(x, y, z) {\n ++W0;\n X0 += (x - X0) / W0;\n Y0 += (y - Y0) / W0;\n Z0 += (z - Z0) / W0;\n}\n\nfunction centroidLineStart() {\n centroidStream.point = centroidLinePointFirst;\n}\n\nfunction centroidLinePointFirst(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n x0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda);\n y0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda);\n z0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi);\n centroidStream.point = centroidLinePoint;\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidLinePoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi),\n x = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda),\n y = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda),\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi),\n w = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);\n W1 += w;\n X1 += w * (x0 + (x0 = x));\n Y1 += w * (y0 + (y0 = y));\n Z1 += w * (z0 + (z0 = z));\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidLineEnd() {\n centroidStream.point = centroidPoint;\n}\n\n// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,\n// J. Applied Mechanics 42, 239 (1975).\nfunction centroidRingStart() {\n centroidStream.point = centroidRingPointFirst;\n}\n\nfunction centroidRingEnd() {\n centroidRingPoint(lambda00, phi00);\n centroidStream.point = centroidPoint;\n}\n\nfunction centroidRingPointFirst(lambda, phi) {\n lambda00 = lambda, phi00 = phi;\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n centroidStream.point = centroidRingPoint;\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n x0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda);\n y0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda);\n z0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi);\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidRingPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi),\n x = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda),\n y = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda),\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi),\n cx = y0 * z - z0 * y,\n cy = z0 * x - x0 * z,\n cz = x0 * y - y0 * x,\n m = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(cx * cx + cy * cy + cz * cz),\n w = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(m), // line weight = angle\n v = m && -w / m; // area weight multiplier\n X2 += v * cx;\n Y2 += v * cy;\n Z2 += v * cz;\n W1 += w;\n X1 += w * (x0 + (x0 = x));\n Y1 += w * (y0 + (y0 = y));\n Z1 += w * (z0 + (z0 = z));\n centroidPointCartesian(x0, y0, z0);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n W0 = W1 =\n X0 = Y0 = Z0 =\n X1 = Y1 = Z1 =\n X2 = Y2 = Z2 = 0;\n Object(_stream__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(object, centroidStream);\n\n var x = X2,\n y = Y2,\n z = Z2,\n m = x * x + y * y + z * z;\n\n // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.\n if (m < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon2\"]) {\n x = X1, y = Y1, z = Z1;\n // If the feature has zero length, fall back to arithmetic mean of point vectors.\n if (W1 < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) x = X0, y = Y0, z = Z0;\n m = x * x + y * y + z * z;\n // If the feature still has an undefined ccentroid, then return.\n if (m < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon2\"]) return [NaN, NaN];\n }\n\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(y, x) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(m)) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/circle.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-geo/src/circle.js ***! + \*******************************************/ +/*! exports provided: circleStream, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleStream\", function() { return circleStream; });\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-geo/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./rotation */ \"./node_modules/d3-geo/src/rotation.js\");\n\n\n\n\n\n// Generates a circle centered at [0°, 0°], with a given radius and precision.\nfunction circleStream(stream, radius, delta, direction, t0, t1) {\n if (!delta) return;\n var cosRadius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(radius),\n sinRadius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(radius),\n step = direction * delta;\n if (t0 == null) {\n t0 = radius + direction * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n t1 = radius - step / 2;\n } else {\n t0 = circleRadius(cosRadius, t0);\n t1 = circleRadius(cosRadius, t1);\n if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n }\n for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {\n point = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])([cosRadius, -sinRadius * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(t), -sinRadius * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(t)]);\n stream.point(point[0], point[1]);\n }\n}\n\n// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].\nfunction circleRadius(cosRadius, point) {\n point = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(point), point[0] -= cosRadius;\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianNormalizeInPlace\"])(point);\n var radius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"acos\"])(-point[1]);\n return ((-point[2] < 0 ? -radius : radius) + _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) % _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var center = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([0, 0]),\n radius = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(90),\n precision = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(6),\n ring,\n rotate,\n stream = {point: point};\n\n function point(x, y) {\n ring.push(x = rotate(x, y));\n x[0] *= _math__WEBPACK_IMPORTED_MODULE_2__[\"degrees\"], x[1] *= _math__WEBPACK_IMPORTED_MODULE_2__[\"degrees\"];\n }\n\n function circle() {\n var c = center.apply(this, arguments),\n r = radius.apply(this, arguments) * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"],\n p = precision.apply(this, arguments) * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"];\n ring = [];\n rotate = Object(_rotation__WEBPACK_IMPORTED_MODULE_3__[\"rotateRadians\"])(-c[0] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], -c[1] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], 0).invert;\n circleStream(stream, r, p, 1);\n c = {type: \"Polygon\", coordinates: [ring]};\n ring = rotate = null;\n return c;\n }\n\n circle.center = function(_) {\n return arguments.length ? (center = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([+_[0], +_[1]]), circle) : center;\n };\n\n circle.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), circle) : radius;\n };\n\n circle.precision = function(_) {\n return arguments.length ? (precision = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), circle) : precision;\n };\n\n return circle;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/antimeridian.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/antimeridian.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/clip/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\n function() { return true; },\n clipAntimeridianLine,\n clipAntimeridianInterpolate,\n [-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -_math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"]]\n));\n\n// Takes a line and cuts into visible segments. Return values: 0 - there were\n// intersections or the line was empty; 1 - no intersections; 2 - there were\n// intersections, and the first and last segments should be rejoined.\nfunction clipAntimeridianLine(stream) {\n var lambda0 = NaN,\n phi0 = NaN,\n sign0 = NaN,\n clean; // no intersections\n\n return {\n lineStart: function() {\n stream.lineStart();\n clean = 1;\n },\n point: function(lambda1, phi1) {\n var sign1 = lambda1 > 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"],\n delta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda1 - lambda0);\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(delta - _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"]) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) { // line crosses a pole\n stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"]);\n stream.point(sign0, phi0);\n stream.lineEnd();\n stream.lineStart();\n stream.point(sign1, phi0);\n stream.point(lambda1, phi0);\n clean = 0;\n } else if (sign0 !== sign1 && delta >= _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"]) { // line crosses antimeridian\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda0 - sign0) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) lambda0 -= sign0 * _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; // handle degeneracies\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda1 - sign1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) lambda1 -= sign1 * _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"];\n phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);\n stream.point(sign0, phi0);\n stream.lineEnd();\n stream.lineStart();\n stream.point(sign1, phi0);\n clean = 0;\n }\n stream.point(lambda0 = lambda1, phi0 = phi1);\n sign0 = sign1;\n },\n lineEnd: function() {\n stream.lineEnd();\n lambda0 = phi0 = NaN;\n },\n clean: function() {\n return 2 - clean; // if intersections, rejoin first and last segments\n }\n };\n}\n\nfunction clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {\n var cosPhi0,\n cosPhi1,\n sinLambda0Lambda1 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda0 - lambda1);\n return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(sinLambda0Lambda1) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]\n ? Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan\"])((Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi0) * (cosPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi1)) * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda1)\n - Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi1) * (cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi0)) * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda0))\n / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))\n : (phi0 + phi1) / 2;\n}\n\nfunction clipAntimeridianInterpolate(from, to, direction, stream) {\n var phi;\n if (from == null) {\n phi = direction * _math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"];\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n stream.point(0, phi);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], 0);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -phi);\n stream.point(0, -phi);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -phi);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], 0);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n } else if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(from[0] - to[0]) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) {\n var lambda = from[0] < to[0] ? _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"];\n phi = direction * lambda / 2;\n stream.point(-lambda, phi);\n stream.point(0, phi);\n stream.point(lambda, phi);\n } else {\n stream.point(to[0], to[1]);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/antimeridian.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/buffer.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/buffer.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-geo/src/noop.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var lines = [],\n line;\n return {\n point: function(x, y) {\n line.push([x, y]);\n },\n lineStart: function() {\n lines.push(line = []);\n },\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n rejoin: function() {\n if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));\n },\n result: function() {\n var result = lines;\n lines = [];\n line = null;\n return result;\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/buffer.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/circle.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/circle.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../cartesian */ \"./node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../circle */ \"./node_modules/d3-geo/src/circle.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _pointEqual__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../pointEqual */ \"./node_modules/d3-geo/src/pointEqual.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/clip/index.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius) {\n var cr = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(radius),\n delta = 6 * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"],\n smallRadius = cr > 0,\n notHemisphere = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(cr) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]; // TODO optimise for this common case\n\n function interpolate(from, to, direction, stream) {\n Object(_circle__WEBPACK_IMPORTED_MODULE_1__[\"circleStream\"])(stream, radius, delta, direction, from, to);\n }\n\n function visible(lambda, phi) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(lambda) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi) > cr;\n }\n\n // Takes a line and cuts into visible segments. Return values used for polygon\n // clipping: 0 - there were intersections or the line was empty; 1 - no\n // intersections 2 - there were intersections, and the first and last segments\n // should be rejoined.\n function clipLine(stream) {\n var point0, // previous point\n c0, // code for previous point\n v0, // visibility of previous point\n v00, // visibility of first point\n clean; // no intersections\n return {\n lineStart: function() {\n v00 = v0 = false;\n clean = 1;\n },\n point: function(lambda, phi) {\n var point1 = [lambda, phi],\n point2,\n v = visible(lambda, phi),\n c = smallRadius\n ? v ? 0 : code(lambda, phi)\n : v ? code(lambda + (lambda < 0 ? _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]), phi) : 0;\n if (!point0 && (v00 = v0 = v)) stream.lineStart();\n // Handle degeneracies.\n // TODO ignore if not clipping polygons.\n if (v !== v0) {\n point2 = intersect(point0, point1);\n if (!point2 || Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point0, point2) || Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point1, point2)) {\n point1[0] += _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n point1[1] += _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n v = visible(point1[0], point1[1]);\n }\n }\n if (v !== v0) {\n clean = 0;\n if (v) {\n // outside going in\n stream.lineStart();\n point2 = intersect(point1, point0);\n stream.point(point2[0], point2[1]);\n } else {\n // inside going out\n point2 = intersect(point0, point1);\n stream.point(point2[0], point2[1]);\n stream.lineEnd();\n }\n point0 = point2;\n } else if (notHemisphere && point0 && smallRadius ^ v) {\n var t;\n // If the codes for two points are different, or are both zero,\n // and there this segment intersects with the small circle.\n if (!(c & c0) && (t = intersect(point1, point0, true))) {\n clean = 0;\n if (smallRadius) {\n stream.lineStart();\n stream.point(t[0][0], t[0][1]);\n stream.point(t[1][0], t[1][1]);\n stream.lineEnd();\n } else {\n stream.point(t[1][0], t[1][1]);\n stream.lineEnd();\n stream.lineStart();\n stream.point(t[0][0], t[0][1]);\n }\n }\n }\n if (v && (!point0 || !Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point0, point1))) {\n stream.point(point1[0], point1[1]);\n }\n point0 = point1, v0 = v, c0 = c;\n },\n lineEnd: function() {\n if (v0) stream.lineEnd();\n point0 = null;\n },\n // Rejoin first and last segments if there were intersections and the first\n // and last points were visible.\n clean: function() {\n return clean | ((v00 && v0) << 1);\n }\n };\n }\n\n // Intersects the great circle between a and b with the clip circle.\n function intersect(a, b, two) {\n var pa = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(a),\n pb = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(b);\n\n // We have two planes, n1.p = d1 and n2.p = d2.\n // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).\n var n1 = [1, 0, 0], // normal\n n2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianCross\"])(pa, pb),\n n2n2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(n2, n2),\n n1n2 = n2[0], // cartesianDot(n1, n2),\n determinant = n2n2 - n1n2 * n1n2;\n\n // Two polar points.\n if (!determinant) return !two && a;\n\n var c1 = cr * n2n2 / determinant,\n c2 = -cr * n1n2 / determinant,\n n1xn2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianCross\"])(n1, n2),\n A = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(n1, c1),\n B = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(n2, c2);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(A, B);\n\n // Solve |p(t)|^2 = 1.\n var u = n1xn2,\n w = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(A, u),\n uu = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(u, u),\n t2 = w * w - uu * (Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(A, A) - 1);\n\n if (t2 < 0) return;\n\n var t = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(t2),\n q = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(u, (-w - t) / uu);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(q, A);\n q = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])(q);\n\n if (!two) return q;\n\n // Two intersection points.\n var lambda0 = a[0],\n lambda1 = b[0],\n phi0 = a[1],\n phi1 = b[1],\n z;\n\n if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;\n\n var delta = lambda1 - lambda0,\n polar = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(delta - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]) < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"],\n meridian = polar || delta < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n\n if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;\n\n // Check that the first point is between a and b.\n if (meridian\n ? polar\n ? phi0 + phi1 > 0 ^ q[1] < (Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(q[0] - lambda0) < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] ? phi0 : phi1)\n : phi0 <= q[1] && q[1] <= phi1\n : delta > _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] ^ (lambda0 <= q[0] && q[0] <= lambda1)) {\n var q1 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(u, (-w + t) / uu);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(q1, A);\n return [q, Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])(q1)];\n }\n }\n\n // Generates a 4-bit vector representing the location of a point relative to\n // the small circle's bounding box.\n function code(lambda, phi) {\n var r = smallRadius ? radius : _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] - radius,\n code = 0;\n if (lambda < -r) code |= 1; // left\n else if (lambda > r) code |= 2; // right\n if (phi < -r) code |= 4; // below\n else if (phi > r) code |= 8; // above\n return code;\n }\n\n return Object(_index__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-_math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"], radius - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/circle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/extent.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/extent.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rectangle */ \"./node_modules/d3-geo/src/clip/rectangle.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x0 = 0,\n y0 = 0,\n x1 = 960,\n y1 = 500,\n cache,\n cacheStream,\n clip;\n\n return clip = {\n stream: function(stream) {\n return cache && cacheStream === stream ? cache : cache = Object(_rectangle__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x0, y0, x1, y1)(cacheStream = stream);\n },\n extent: function(_) {\n return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/extent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/index.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-geo/src/clip/index.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./buffer */ \"./node_modules/d3-geo/src/clip/buffer.js\");\n/* harmony import */ var _rejoin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rejoin */ \"./node_modules/d3-geo/src/clip/rejoin.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _polygonContains__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../polygonContains */ \"./node_modules/d3-geo/src/polygonContains.js\");\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(pointVisible, clipLine, interpolate, start) {\n return function(sink) {\n var line = clipLine(sink),\n ringBuffer = Object(_buffer__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n ringSink = clipLine(ringBuffer),\n polygonStarted = false,\n polygon,\n segments,\n ring;\n\n var clip = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: function() {\n clip.point = pointRing;\n clip.lineStart = ringStart;\n clip.lineEnd = ringEnd;\n segments = [];\n polygon = [];\n },\n polygonEnd: function() {\n clip.point = point;\n clip.lineStart = lineStart;\n clip.lineEnd = lineEnd;\n segments = Object(d3_array__WEBPACK_IMPORTED_MODULE_4__[\"merge\"])(segments);\n var startInside = Object(_polygonContains__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(polygon, start);\n if (segments.length) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n Object(_rejoin__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(segments, compareIntersection, startInside, interpolate, sink);\n } else if (startInside) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n sink.lineStart();\n interpolate(null, null, 1, sink);\n sink.lineEnd();\n }\n if (polygonStarted) sink.polygonEnd(), polygonStarted = false;\n segments = polygon = null;\n },\n sphere: function() {\n sink.polygonStart();\n sink.lineStart();\n interpolate(null, null, 1, sink);\n sink.lineEnd();\n sink.polygonEnd();\n }\n };\n\n function point(lambda, phi) {\n if (pointVisible(lambda, phi)) sink.point(lambda, phi);\n }\n\n function pointLine(lambda, phi) {\n line.point(lambda, phi);\n }\n\n function lineStart() {\n clip.point = pointLine;\n line.lineStart();\n }\n\n function lineEnd() {\n clip.point = point;\n line.lineEnd();\n }\n\n function pointRing(lambda, phi) {\n ring.push([lambda, phi]);\n ringSink.point(lambda, phi);\n }\n\n function ringStart() {\n ringSink.lineStart();\n ring = [];\n }\n\n function ringEnd() {\n pointRing(ring[0][0], ring[0][1]);\n ringSink.lineEnd();\n\n var clean = ringSink.clean(),\n ringSegments = ringBuffer.result(),\n i, n = ringSegments.length, m,\n segment,\n point;\n\n ring.pop();\n polygon.push(ring);\n ring = null;\n\n if (!n) return;\n\n // No intersections.\n if (clean & 1) {\n segment = ringSegments[0];\n if ((m = segment.length - 1) > 0) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n sink.lineStart();\n for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);\n sink.lineEnd();\n }\n return;\n }\n\n // Rejoin connected segments.\n // TODO reuse ringBuffer.rejoin()?\n if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));\n\n segments.push(ringSegments.filter(validSegment));\n }\n\n return clip;\n };\n});\n\nfunction validSegment(segment) {\n return segment.length > 1;\n}\n\n// Intersections are sorted along the clip edge. For both antimeridian cutting\n// and circle clipping, the same comparison is used.\nfunction compareIntersection(a, b) {\n return ((a = a.x)[0] < 0 ? a[1] - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] : _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - a[1])\n - ((b = b.x)[0] < 0 ? b[1] - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] : _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - b[1]);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/line.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-geo/src/clip/line.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, x0, y0, x1, y1) {\n var ax = a[0],\n ay = a[1],\n bx = b[0],\n by = b[1],\n t0 = 0,\n t1 = 1,\n dx = bx - ax,\n dy = by - ay,\n r;\n\n r = x0 - ax;\n if (!dx && r > 0) return;\n r /= dx;\n if (dx < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dx > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = x1 - ax;\n if (!dx && r < 0) return;\n r /= dx;\n if (dx < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dx > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n r = y0 - ay;\n if (!dy && r > 0) return;\n r /= dy;\n if (dy < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dy > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = y1 - ay;\n if (!dy && r < 0) return;\n r /= dy;\n if (dy < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dy > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;\n if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;\n return true;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/line.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/rectangle.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/rectangle.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return clipRectangle; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _buffer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./buffer */ \"./node_modules/d3-geo/src/clip/buffer.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./line */ \"./node_modules/d3-geo/src/clip/line.js\");\n/* harmony import */ var _rejoin__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./rejoin */ \"./node_modules/d3-geo/src/clip/rejoin.js\");\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n\n\n\n\n\n\nvar clipMax = 1e9, clipMin = -clipMax;\n\n// TODO Use d3-polygon’s polygonContains here for the ring check?\n// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?\n\nfunction clipRectangle(x0, y0, x1, y1) {\n\n function visible(x, y) {\n return x0 <= x && x <= x1 && y0 <= y && y <= y1;\n }\n\n function interpolate(from, to, direction, stream) {\n var a = 0, a1 = 0;\n if (from == null\n || (a = corner(from, direction)) !== (a1 = corner(to, direction))\n || comparePoint(from, to) < 0 ^ direction > 0) {\n do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);\n while ((a = (a + direction + 4) % 4) !== a1);\n } else {\n stream.point(to[0], to[1]);\n }\n }\n\n function corner(p, direction) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[0] - x0) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 0 : 3\n : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[0] - x1) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 2 : 1\n : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[1] - y0) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 1 : 0\n : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon\n }\n\n function compareIntersection(a, b) {\n return comparePoint(a.x, b.x);\n }\n\n function comparePoint(a, b) {\n var ca = corner(a, 1),\n cb = corner(b, 1);\n return ca !== cb ? ca - cb\n : ca === 0 ? b[1] - a[1]\n : ca === 1 ? a[0] - b[0]\n : ca === 2 ? a[1] - b[1]\n : b[0] - a[0];\n }\n\n return function(stream) {\n var activeStream = stream,\n bufferStream = Object(_buffer__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(),\n segments,\n polygon,\n ring,\n x__, y__, v__, // first point\n x_, y_, v_, // previous point\n first,\n clean;\n\n var clipStream = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: polygonStart,\n polygonEnd: polygonEnd\n };\n\n function point(x, y) {\n if (visible(x, y)) activeStream.point(x, y);\n }\n\n function polygonInside() {\n var winding = 0;\n\n for (var i = 0, n = polygon.length; i < n; ++i) {\n for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {\n a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];\n if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }\n else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }\n }\n }\n\n return winding;\n }\n\n // Buffer geometry within a polygon and then clip it en masse.\n function polygonStart() {\n activeStream = bufferStream, segments = [], polygon = [], clean = true;\n }\n\n function polygonEnd() {\n var startInside = polygonInside(),\n cleanInside = clean && startInside,\n visible = (segments = Object(d3_array__WEBPACK_IMPORTED_MODULE_4__[\"merge\"])(segments)).length;\n if (cleanInside || visible) {\n stream.polygonStart();\n if (cleanInside) {\n stream.lineStart();\n interpolate(null, null, 1, stream);\n stream.lineEnd();\n }\n if (visible) {\n Object(_rejoin__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(segments, compareIntersection, startInside, interpolate, stream);\n }\n stream.polygonEnd();\n }\n activeStream = stream, segments = polygon = ring = null;\n }\n\n function lineStart() {\n clipStream.point = linePoint;\n if (polygon) polygon.push(ring = []);\n first = true;\n v_ = false;\n x_ = y_ = NaN;\n }\n\n // TODO rather than special-case polygons, simply handle them separately.\n // Ideally, coincident intersection points should be jittered to avoid\n // clipping issues.\n function lineEnd() {\n if (segments) {\n linePoint(x__, y__);\n if (v__ && v_) bufferStream.rejoin();\n segments.push(bufferStream.result());\n }\n clipStream.point = point;\n if (v_) activeStream.lineEnd();\n }\n\n function linePoint(x, y) {\n var v = visible(x, y);\n if (polygon) ring.push([x, y]);\n if (first) {\n x__ = x, y__ = y, v__ = v;\n first = false;\n if (v) {\n activeStream.lineStart();\n activeStream.point(x, y);\n }\n } else {\n if (v && v_) activeStream.point(x, y);\n else {\n var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],\n b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];\n if (Object(_line__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(a, b, x0, y0, x1, y1)) {\n if (!v_) {\n activeStream.lineStart();\n activeStream.point(a[0], a[1]);\n }\n activeStream.point(b[0], b[1]);\n if (!v) activeStream.lineEnd();\n clean = false;\n } else if (v) {\n activeStream.lineStart();\n activeStream.point(x, y);\n clean = false;\n }\n }\n }\n x_ = x, y_ = y, v_ = v;\n }\n\n return clipStream;\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/rectangle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/clip/rejoin.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/clip/rejoin.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pointEqual__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../pointEqual */ \"./node_modules/d3-geo/src/pointEqual.js\");\n\n\nfunction Intersection(point, points, other, entry) {\n this.x = point;\n this.z = points;\n this.o = other; // another intersection\n this.e = entry; // is an entry?\n this.v = false; // visited\n this.n = this.p = null; // next & previous\n}\n\n// A generalized polygon clipping algorithm: given a polygon that has been cut\n// into its visible line segments, and rejoins the segments by interpolating\n// along the clip edge.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(segments, compareIntersection, startInside, interpolate, stream) {\n var subject = [],\n clip = [],\n i,\n n;\n\n segments.forEach(function(segment) {\n if ((n = segment.length - 1) <= 0) return;\n var n, p0 = segment[0], p1 = segment[n], x;\n\n // If the first and last points of a segment are coincident, then treat as a\n // closed ring. TODO if all rings are closed, then the winding order of the\n // exterior ring should be checked.\n if (Object(_pointEqual__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(p0, p1)) {\n stream.lineStart();\n for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);\n stream.lineEnd();\n return;\n }\n\n subject.push(x = new Intersection(p0, segment, null, true));\n clip.push(x.o = new Intersection(p0, null, x, false));\n subject.push(x = new Intersection(p1, segment, null, false));\n clip.push(x.o = new Intersection(p1, null, x, true));\n });\n\n if (!subject.length) return;\n\n clip.sort(compareIntersection);\n link(subject);\n link(clip);\n\n for (i = 0, n = clip.length; i < n; ++i) {\n clip[i].e = startInside = !startInside;\n }\n\n var start = subject[0],\n points,\n point;\n\n while (1) {\n // Find first unvisited intersection.\n var current = start,\n isSubject = true;\n while (current.v) if ((current = current.n) === start) return;\n points = current.z;\n stream.lineStart();\n do {\n current.v = current.o.v = true;\n if (current.e) {\n if (isSubject) {\n for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);\n } else {\n interpolate(current.x, current.n.x, 1, stream);\n }\n current = current.n;\n } else {\n if (isSubject) {\n points = current.p.z;\n for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);\n } else {\n interpolate(current.x, current.p.x, -1, stream);\n }\n current = current.p;\n }\n current = current.o;\n points = current.z;\n isSubject = !isSubject;\n } while (!current.v);\n stream.lineEnd();\n }\n});\n\nfunction link(array) {\n if (!(n = array.length)) return;\n var n,\n i = 0,\n a = array[0],\n b;\n while (++i < n) {\n a.n = b = array[i];\n b.p = a;\n a = b;\n }\n a.n = b = array[0];\n b.p = a;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/clip/rejoin.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/compose.js": +/*!********************************************!*\ + !*** ./node_modules/d3-geo/src/compose.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n\n function compose(x, y) {\n return x = a(x, y), b(x[0], x[1]);\n }\n\n if (a.invert && b.invert) compose.invert = function(x, y) {\n return x = b.invert(x, y), x && a.invert(x[0], x[1]);\n };\n\n return compose;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/compose.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/constant.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/constant.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/contains.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/contains.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _polygonContains__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./polygonContains */ \"./node_modules/d3-geo/src/polygonContains.js\");\n/* harmony import */ var _distance__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./distance */ \"./node_modules/d3-geo/src/distance.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\n\nvar containsObjectType = {\n Feature: function(object, point) {\n return containsGeometry(object.geometry, point);\n },\n FeatureCollection: function(object, point) {\n var features = object.features, i = -1, n = features.length;\n while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;\n return false;\n }\n};\n\nvar containsGeometryType = {\n Sphere: function() {\n return true;\n },\n Point: function(object, point) {\n return containsPoint(object.coordinates, point);\n },\n MultiPoint: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsPoint(coordinates[i], point)) return true;\n return false;\n },\n LineString: function(object, point) {\n return containsLine(object.coordinates, point);\n },\n MultiLineString: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsLine(coordinates[i], point)) return true;\n return false;\n },\n Polygon: function(object, point) {\n return containsPolygon(object.coordinates, point);\n },\n MultiPolygon: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsPolygon(coordinates[i], point)) return true;\n return false;\n },\n GeometryCollection: function(object, point) {\n var geometries = object.geometries, i = -1, n = geometries.length;\n while (++i < n) if (containsGeometry(geometries[i], point)) return true;\n return false;\n }\n};\n\nfunction containsGeometry(geometry, point) {\n return geometry && containsGeometryType.hasOwnProperty(geometry.type)\n ? containsGeometryType[geometry.type](geometry, point)\n : false;\n}\n\nfunction containsPoint(coordinates, point) {\n return Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates, point) === 0;\n}\n\nfunction containsLine(coordinates, point) {\n var ab = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates[0], coordinates[1]),\n ao = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates[0], point),\n ob = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(point, coordinates[1]);\n return ao + ob <= ab + _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n}\n\nfunction containsPolygon(coordinates, point) {\n return !!Object(_polygonContains__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(coordinates.map(ringRadians), pointRadians(point));\n}\n\nfunction ringRadians(ring) {\n return ring = ring.map(pointRadians), ring.pop(), ring;\n}\n\nfunction pointRadians(point) {\n return [point[0] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"]];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object, point) {\n return (object && containsObjectType.hasOwnProperty(object.type)\n ? containsObjectType[object.type]\n : containsGeometry)(object, point);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/contains.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/distance.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/distance.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _length__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./length */ \"./node_modules/d3-geo/src/length.js\");\n\n\nvar coordinates = [null, null],\n object = {type: \"LineString\", coordinates: coordinates};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n coordinates[0] = a;\n coordinates[1] = b;\n return Object(_length__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(object);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/distance.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/graticule.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-geo/src/graticule.js ***! + \**********************************************/ +/*! exports provided: default, graticule10 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return graticule; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"graticule10\", function() { return graticule10; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\nfunction graticuleX(y0, y1, dy) {\n var y = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(y0, y1 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"], dy).concat(y1);\n return function(x) { return y.map(function(y) { return [x, y]; }); };\n}\n\nfunction graticuleY(x0, x1, dx) {\n var x = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(x0, x1 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"], dx).concat(x1);\n return function(y) { return x.map(function(x) { return [x, y]; }); };\n}\n\nfunction graticule() {\n var x1, x0, X1, X0,\n y1, y0, Y1, Y0,\n dx = 10, dy = dx, DX = 90, DY = 360,\n x, y, X, Y,\n precision = 2.5;\n\n function graticule() {\n return {type: \"MultiLineString\", coordinates: lines()};\n }\n\n function lines() {\n return Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(X0 / DX) * DX, X1, DX).map(X)\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(Y0 / DY) * DY, Y1, DY).map(Y))\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(x0 / dx) * dx, x1, dx).filter(function(x) { return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(x % DX) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; }).map(x))\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(y0 / dy) * dy, y1, dy).filter(function(y) { return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(y % DY) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; }).map(y));\n }\n\n graticule.lines = function() {\n return lines().map(function(coordinates) { return {type: \"LineString\", coordinates: coordinates}; });\n };\n\n graticule.outline = function() {\n return {\n type: \"Polygon\",\n coordinates: [\n X(X0).concat(\n Y(Y1).slice(1),\n X(X1).reverse().slice(1),\n Y(Y0).reverse().slice(1))\n ]\n };\n };\n\n graticule.extent = function(_) {\n if (!arguments.length) return graticule.extentMinor();\n return graticule.extentMajor(_).extentMinor(_);\n };\n\n graticule.extentMajor = function(_) {\n if (!arguments.length) return [[X0, Y0], [X1, Y1]];\n X0 = +_[0][0], X1 = +_[1][0];\n Y0 = +_[0][1], Y1 = +_[1][1];\n if (X0 > X1) _ = X0, X0 = X1, X1 = _;\n if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;\n return graticule.precision(precision);\n };\n\n graticule.extentMinor = function(_) {\n if (!arguments.length) return [[x0, y0], [x1, y1]];\n x0 = +_[0][0], x1 = +_[1][0];\n y0 = +_[0][1], y1 = +_[1][1];\n if (x0 > x1) _ = x0, x0 = x1, x1 = _;\n if (y0 > y1) _ = y0, y0 = y1, y1 = _;\n return graticule.precision(precision);\n };\n\n graticule.step = function(_) {\n if (!arguments.length) return graticule.stepMinor();\n return graticule.stepMajor(_).stepMinor(_);\n };\n\n graticule.stepMajor = function(_) {\n if (!arguments.length) return [DX, DY];\n DX = +_[0], DY = +_[1];\n return graticule;\n };\n\n graticule.stepMinor = function(_) {\n if (!arguments.length) return [dx, dy];\n dx = +_[0], dy = +_[1];\n return graticule;\n };\n\n graticule.precision = function(_) {\n if (!arguments.length) return precision;\n precision = +_;\n x = graticuleX(y0, y1, 90);\n y = graticuleY(x0, x1, precision);\n X = graticuleX(Y0, Y1, 90);\n Y = graticuleY(X0, X1, precision);\n return graticule;\n };\n\n return graticule\n .extentMajor([[-180, -90 + _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]], [180, 90 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]]])\n .extentMinor([[-180, -80 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]], [180, 80 + _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]]]);\n}\n\nfunction graticule10() {\n return graticule()();\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/graticule.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/identity.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/identity.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/index.js": +/*!******************************************!*\ + !*** ./node_modules/d3-geo/src/index.js ***! + \******************************************/ +/*! exports provided: geoArea, geoBounds, geoCentroid, geoCircle, geoClipAntimeridian, geoClipCircle, geoClipExtent, geoClipRectangle, geoContains, geoDistance, geoGraticule, geoGraticule10, geoInterpolate, geoLength, geoPath, geoAlbers, geoAlbersUsa, geoAzimuthalEqualArea, geoAzimuthalEqualAreaRaw, geoAzimuthalEquidistant, geoAzimuthalEquidistantRaw, geoConicConformal, geoConicConformalRaw, geoConicEqualArea, geoConicEqualAreaRaw, geoConicEquidistant, geoConicEquidistantRaw, geoEqualEarth, geoEqualEarthRaw, geoEquirectangular, geoEquirectangularRaw, geoGnomonic, geoGnomonicRaw, geoIdentity, geoProjection, geoProjectionMutator, geoMercator, geoMercatorRaw, geoNaturalEarth1, geoNaturalEarth1Raw, geoOrthographic, geoOrthographicRaw, geoStereographic, geoStereographicRaw, geoTransverseMercator, geoTransverseMercatorRaw, geoRotation, geoStream, geoTransform */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-geo/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoArea\", function() { return _area__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _bounds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bounds */ \"./node_modules/d3-geo/src/bounds.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoBounds\", function() { return _bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _centroid__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./centroid */ \"./node_modules/d3-geo/src/centroid.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCentroid\", function() { return _centroid__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./circle */ \"./node_modules/d3-geo/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCircle\", function() { return _circle__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _clip_antimeridian__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./clip/antimeridian */ \"./node_modules/d3-geo/src/clip/antimeridian.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipAntimeridian\", function() { return _clip_antimeridian__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _clip_circle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./clip/circle */ \"./node_modules/d3-geo/src/clip/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipCircle\", function() { return _clip_circle__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _clip_extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./clip/extent */ \"./node_modules/d3-geo/src/clip/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipExtent\", function() { return _clip_extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _clip_rectangle__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./clip/rectangle */ \"./node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipRectangle\", function() { return _clip_rectangle__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _contains__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./contains */ \"./node_modules/d3-geo/src/contains.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoContains\", function() { return _contains__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _distance__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./distance */ \"./node_modules/d3-geo/src/distance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoDistance\", function() { return _distance__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _graticule__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./graticule */ \"./node_modules/d3-geo/src/graticule.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule\", function() { return _graticule__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule10\", function() { return _graticule__WEBPACK_IMPORTED_MODULE_10__[\"graticule10\"]; });\n\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/d3-geo/src/interpolate.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoInterpolate\", function() { return _interpolate__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _length__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./length */ \"./node_modules/d3-geo/src/length.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoLength\", function() { return _length__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _path_index__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./path/index */ \"./node_modules/d3-geo/src/path/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoPath\", function() { return _path_index__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _projection_albers__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./projection/albers */ \"./node_modules/d3-geo/src/projection/albers.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbers\", function() { return _projection_albers__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _projection_albersUsa__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./projection/albersUsa */ \"./node_modules/d3-geo/src/projection/albersUsa.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbersUsa\", function() { return _projection_albersUsa__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./projection/azimuthalEqualArea */ \"./node_modules/d3-geo/src/projection/azimuthalEqualArea.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualArea\", function() { return _projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualAreaRaw\", function() { return _projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__[\"azimuthalEqualAreaRaw\"]; });\n\n/* harmony import */ var _projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./projection/azimuthalEquidistant */ \"./node_modules/d3-geo/src/projection/azimuthalEquidistant.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistant\", function() { return _projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistantRaw\", function() { return _projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__[\"azimuthalEquidistantRaw\"]; });\n\n/* harmony import */ var _projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./projection/conicConformal */ \"./node_modules/d3-geo/src/projection/conicConformal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformal\", function() { return _projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformalRaw\", function() { return _projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__[\"conicConformalRaw\"]; });\n\n/* harmony import */ var _projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./projection/conicEqualArea */ \"./node_modules/d3-geo/src/projection/conicEqualArea.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualArea\", function() { return _projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualAreaRaw\", function() { return _projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__[\"conicEqualAreaRaw\"]; });\n\n/* harmony import */ var _projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./projection/conicEquidistant */ \"./node_modules/d3-geo/src/projection/conicEquidistant.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistant\", function() { return _projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistantRaw\", function() { return _projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__[\"conicEquidistantRaw\"]; });\n\n/* harmony import */ var _projection_equalEarth__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./projection/equalEarth */ \"./node_modules/d3-geo/src/projection/equalEarth.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEqualEarth\", function() { return _projection_equalEarth__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEqualEarthRaw\", function() { return _projection_equalEarth__WEBPACK_IMPORTED_MODULE_21__[\"equalEarthRaw\"]; });\n\n/* harmony import */ var _projection_equirectangular__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./projection/equirectangular */ \"./node_modules/d3-geo/src/projection/equirectangular.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangular\", function() { return _projection_equirectangular__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangularRaw\", function() { return _projection_equirectangular__WEBPACK_IMPORTED_MODULE_22__[\"equirectangularRaw\"]; });\n\n/* harmony import */ var _projection_gnomonic__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./projection/gnomonic */ \"./node_modules/d3-geo/src/projection/gnomonic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonic\", function() { return _projection_gnomonic__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonicRaw\", function() { return _projection_gnomonic__WEBPACK_IMPORTED_MODULE_23__[\"gnomonicRaw\"]; });\n\n/* harmony import */ var _projection_identity__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./projection/identity */ \"./node_modules/d3-geo/src/projection/identity.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoIdentity\", function() { return _projection_identity__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _projection_index__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./projection/index */ \"./node_modules/d3-geo/src/projection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjection\", function() { return _projection_index__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjectionMutator\", function() { return _projection_index__WEBPACK_IMPORTED_MODULE_25__[\"projectionMutator\"]; });\n\n/* harmony import */ var _projection_mercator__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./projection/mercator */ \"./node_modules/d3-geo/src/projection/mercator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercator\", function() { return _projection_mercator__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercatorRaw\", function() { return _projection_mercator__WEBPACK_IMPORTED_MODULE_26__[\"mercatorRaw\"]; });\n\n/* harmony import */ var _projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./projection/naturalEarth1 */ \"./node_modules/d3-geo/src/projection/naturalEarth1.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1\", function() { return _projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_27__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1Raw\", function() { return _projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_27__[\"naturalEarth1Raw\"]; });\n\n/* harmony import */ var _projection_orthographic__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./projection/orthographic */ \"./node_modules/d3-geo/src/projection/orthographic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographic\", function() { return _projection_orthographic__WEBPACK_IMPORTED_MODULE_28__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographicRaw\", function() { return _projection_orthographic__WEBPACK_IMPORTED_MODULE_28__[\"orthographicRaw\"]; });\n\n/* harmony import */ var _projection_stereographic__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./projection/stereographic */ \"./node_modules/d3-geo/src/projection/stereographic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographic\", function() { return _projection_stereographic__WEBPACK_IMPORTED_MODULE_29__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographicRaw\", function() { return _projection_stereographic__WEBPACK_IMPORTED_MODULE_29__[\"stereographicRaw\"]; });\n\n/* harmony import */ var _projection_transverseMercator__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./projection/transverseMercator */ \"./node_modules/d3-geo/src/projection/transverseMercator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercator\", function() { return _projection_transverseMercator__WEBPACK_IMPORTED_MODULE_30__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercatorRaw\", function() { return _projection_transverseMercator__WEBPACK_IMPORTED_MODULE_30__[\"transverseMercatorRaw\"]; });\n\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./rotation */ \"./node_modules/d3-geo/src/rotation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoRotation\", function() { return _rotation__WEBPACK_IMPORTED_MODULE_31__[\"default\"]; });\n\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./stream */ \"./node_modules/d3-geo/src/stream.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStream\", function() { return _stream__WEBPACK_IMPORTED_MODULE_32__[\"default\"]; });\n\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./transform */ \"./node_modules/d3-geo/src/transform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransform\", function() { return _transform__WEBPACK_IMPORTED_MODULE_33__[\"default\"]; });\n\n\n\n\n\n\n\n // DEPRECATED! Use d3.geoIdentity().clipExtent(…).\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/interpolate.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/interpolate.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var x0 = a[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n y0 = a[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n x1 = b[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n y1 = b[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n sy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0),\n cy1 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1),\n sy1 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y1),\n kx0 = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x0),\n ky0 = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x0),\n kx1 = cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x1),\n ky1 = cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x1),\n d = 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"haversin\"])(y1 - y0) + cy0 * cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"haversin\"])(x1 - x0))),\n k = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(d);\n\n var interpolate = d ? function(t) {\n var B = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(t *= d) / k,\n A = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(d - t) / k,\n x = A * kx0 + B * kx1,\n y = A * ky0 + B * ky1,\n z = A * sy0 + B * sy1;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(y, x) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"],\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(z, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + y * y)) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]\n ];\n } : function() {\n return [x0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], y0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n };\n\n interpolate.distance = d;\n\n return interpolate;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/length.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-geo/src/length.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./noop */ \"./node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stream */ \"./node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\nvar lengthSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lambda0,\n sinPhi0,\n cosPhi0;\n\nvar lengthStream = {\n sphere: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: lengthLineStart,\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n};\n\nfunction lengthLineStart() {\n lengthStream.point = lengthPointFirst;\n lengthStream.lineEnd = lengthLineEnd;\n}\n\nfunction lengthLineEnd() {\n lengthStream.point = lengthStream.lineEnd = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n}\n\nfunction lengthPointFirst(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n lambda0 = lambda, sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi), cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi);\n lengthStream.point = lengthPoint;\n}\n\nfunction lengthPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n var sinPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n delta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda - lambda0),\n cosDelta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(delta),\n sinDelta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(delta),\n x = cosPhi * sinDelta,\n y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,\n z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;\n lengthSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(x * x + y * y), z));\n lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n lengthSum.reset();\n Object(_stream__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(object, lengthStream);\n return +lengthSum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/length.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/math.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-geo/src/math.js ***! + \*****************************************/ +/*! exports provided: epsilon, epsilon2, pi, halfPi, quarterPi, tau, degrees, radians, abs, atan, atan2, cos, ceil, exp, floor, log, pow, sin, sign, sqrt, tan, acos, asin, haversin */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon2\", function() { return epsilon2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quarterPi\", function() { return quarterPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"degrees\", function() { return degrees; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"radians\", function() { return radians; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"abs\", function() { return abs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan\", function() { return atan; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan2\", function() { return atan2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ceil\", function() { return ceil; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"exp\", function() { return exp; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"floor\", function() { return floor; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"log\", function() { return log; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pow\", function() { return pow; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sign\", function() { return sign; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tan\", function() { return tan; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"acos\", function() { return acos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"asin\", function() { return asin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"haversin\", function() { return haversin; });\nvar epsilon = 1e-6;\nvar epsilon2 = 1e-12;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar quarterPi = pi / 4;\nvar tau = pi * 2;\n\nvar degrees = 180 / pi;\nvar radians = pi / 180;\n\nvar abs = Math.abs;\nvar atan = Math.atan;\nvar atan2 = Math.atan2;\nvar cos = Math.cos;\nvar ceil = Math.ceil;\nvar exp = Math.exp;\nvar floor = Math.floor;\nvar log = Math.log;\nvar pow = Math.pow;\nvar sin = Math.sin;\nvar sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };\nvar sqrt = Math.sqrt;\nvar tan = Math.tan;\n\nfunction acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nfunction asin(x) {\n return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);\n}\n\nfunction haversin(x) {\n return (x = sin(x / 2)) * x;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/noop.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-geo/src/noop.js ***! + \*****************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return noop; });\nfunction noop() {}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/noop.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/area.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-geo/src/path/area.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-geo/src/noop.js\");\n\n\n\n\nvar areaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n areaRingSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n x00,\n y00,\n x0,\n y0;\n\nvar areaStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: function() {\n areaStream.lineStart = areaRingStart;\n areaStream.lineEnd = areaRingEnd;\n },\n polygonEnd: function() {\n areaStream.lineStart = areaStream.lineEnd = areaStream.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n areaSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(areaRingSum));\n areaRingSum.reset();\n },\n result: function() {\n var area = areaSum / 2;\n areaSum.reset();\n return area;\n }\n};\n\nfunction areaRingStart() {\n areaStream.point = areaPointFirst;\n}\n\nfunction areaPointFirst(x, y) {\n areaStream.point = areaPoint;\n x00 = x0 = x, y00 = y0 = y;\n}\n\nfunction areaPoint(x, y) {\n areaRingSum.add(y0 * x - x0 * y);\n x0 = x, y0 = y;\n}\n\nfunction areaRingEnd() {\n areaPoint(x00, y00);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (areaStream);\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/area.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/bounds.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/path/bounds.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-geo/src/noop.js\");\n\n\nvar x0 = Infinity,\n y0 = x0,\n x1 = -x0,\n y1 = x1;\n\nvar boundsStream = {\n point: boundsPoint,\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n polygonStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n polygonEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n result: function() {\n var bounds = [[x0, y0], [x1, y1]];\n x1 = y1 = -(y0 = x0 = Infinity);\n return bounds;\n }\n};\n\nfunction boundsPoint(x, y) {\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (boundsStream);\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/bounds.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/centroid.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-geo/src/path/centroid.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n// TODO Enforce positive area for exterior, negative area for interior?\n\nvar X0 = 0,\n Y0 = 0,\n Z0 = 0,\n X1 = 0,\n Y1 = 0,\n Z1 = 0,\n X2 = 0,\n Y2 = 0,\n Z2 = 0,\n x00,\n y00,\n x0,\n y0;\n\nvar centroidStream = {\n point: centroidPoint,\n lineStart: centroidLineStart,\n lineEnd: centroidLineEnd,\n polygonStart: function() {\n centroidStream.lineStart = centroidRingStart;\n centroidStream.lineEnd = centroidRingEnd;\n },\n polygonEnd: function() {\n centroidStream.point = centroidPoint;\n centroidStream.lineStart = centroidLineStart;\n centroidStream.lineEnd = centroidLineEnd;\n },\n result: function() {\n var centroid = Z2 ? [X2 / Z2, Y2 / Z2]\n : Z1 ? [X1 / Z1, Y1 / Z1]\n : Z0 ? [X0 / Z0, Y0 / Z0]\n : [NaN, NaN];\n X0 = Y0 = Z0 =\n X1 = Y1 = Z1 =\n X2 = Y2 = Z2 = 0;\n return centroid;\n }\n};\n\nfunction centroidPoint(x, y) {\n X0 += x;\n Y0 += y;\n ++Z0;\n}\n\nfunction centroidLineStart() {\n centroidStream.point = centroidPointFirstLine;\n}\n\nfunction centroidPointFirstLine(x, y) {\n centroidStream.point = centroidPointLine;\n centroidPoint(x0 = x, y0 = y);\n}\n\nfunction centroidPointLine(x, y) {\n var dx = x - x0, dy = y - y0, z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(dx * dx + dy * dy);\n X1 += z * (x0 + x) / 2;\n Y1 += z * (y0 + y) / 2;\n Z1 += z;\n centroidPoint(x0 = x, y0 = y);\n}\n\nfunction centroidLineEnd() {\n centroidStream.point = centroidPoint;\n}\n\nfunction centroidRingStart() {\n centroidStream.point = centroidPointFirstRing;\n}\n\nfunction centroidRingEnd() {\n centroidPointRing(x00, y00);\n}\n\nfunction centroidPointFirstRing(x, y) {\n centroidStream.point = centroidPointRing;\n centroidPoint(x00 = x0 = x, y00 = y0 = y);\n}\n\nfunction centroidPointRing(x, y) {\n var dx = x - x0,\n dy = y - y0,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(dx * dx + dy * dy);\n\n X1 += z * (x0 + x) / 2;\n Y1 += z * (y0 + y) / 2;\n Z1 += z;\n\n z = y0 * x - x0 * y;\n X2 += z * (x0 + x);\n Y2 += z * (y0 + y);\n Z2 += z * 3;\n centroidPoint(x0 = x, y0 = y);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (centroidStream);\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/context.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-geo/src/path/context.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return PathContext; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-geo/src/noop.js\");\n\n\n\nfunction PathContext(context) {\n this._context = context;\n}\n\nPathContext.prototype = {\n _radius: 4.5,\n pointRadius: function(_) {\n return this._radius = _, this;\n },\n polygonStart: function() {\n this._line = 0;\n },\n polygonEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line === 0) this._context.closePath();\n this._point = NaN;\n },\n point: function(x, y) {\n switch (this._point) {\n case 0: {\n this._context.moveTo(x, y);\n this._point = 1;\n break;\n }\n case 1: {\n this._context.lineTo(x, y);\n break;\n }\n default: {\n this._context.moveTo(x + this._radius, y);\n this._context.arc(x, y, this._radius, 0, _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n break;\n }\n }\n },\n result: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/context.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/index.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-geo/src/path/index.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../identity */ \"./node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../stream */ \"./node_modules/d3-geo/src/stream.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-geo/src/path/area.js\");\n/* harmony import */ var _bounds__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./bounds */ \"./node_modules/d3-geo/src/path/bounds.js\");\n/* harmony import */ var _centroid__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./centroid */ \"./node_modules/d3-geo/src/path/centroid.js\");\n/* harmony import */ var _context__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./context */ \"./node_modules/d3-geo/src/path/context.js\");\n/* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./measure */ \"./node_modules/d3-geo/src/path/measure.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./string */ \"./node_modules/d3-geo/src/path/string.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(projection, context) {\n var pointRadius = 4.5,\n projectionStream,\n contextStream;\n\n function path(object) {\n if (object) {\n if (typeof pointRadius === \"function\") contextStream.pointRadius(+pointRadius.apply(this, arguments));\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(contextStream));\n }\n return contextStream.result();\n }\n\n path.area = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_area__WEBPACK_IMPORTED_MODULE_2__[\"default\"]));\n return _area__WEBPACK_IMPORTED_MODULE_2__[\"default\"].result();\n };\n\n path.measure = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_measure__WEBPACK_IMPORTED_MODULE_6__[\"default\"]));\n return _measure__WEBPACK_IMPORTED_MODULE_6__[\"default\"].result();\n };\n\n path.bounds = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_bounds__WEBPACK_IMPORTED_MODULE_3__[\"default\"]));\n return _bounds__WEBPACK_IMPORTED_MODULE_3__[\"default\"].result();\n };\n\n path.centroid = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_centroid__WEBPACK_IMPORTED_MODULE_4__[\"default\"]));\n return _centroid__WEBPACK_IMPORTED_MODULE_4__[\"default\"].result();\n };\n\n path.projection = function(_) {\n return arguments.length ? (projectionStream = _ == null ? (projection = null, _identity__WEBPACK_IMPORTED_MODULE_0__[\"default\"]) : (projection = _).stream, path) : projection;\n };\n\n path.context = function(_) {\n if (!arguments.length) return context;\n contextStream = _ == null ? (context = null, new _string__WEBPACK_IMPORTED_MODULE_7__[\"default\"]) : new _context__WEBPACK_IMPORTED_MODULE_5__[\"default\"](context = _);\n if (typeof pointRadius !== \"function\") contextStream.pointRadius(pointRadius);\n return path;\n };\n\n path.pointRadius = function(_) {\n if (!arguments.length) return pointRadius;\n pointRadius = typeof _ === \"function\" ? _ : (contextStream.pointRadius(+_), +_);\n return path;\n };\n\n return path.projection(projection).context(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/measure.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-geo/src/path/measure.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-geo/src/noop.js\");\n\n\n\n\nvar lengthSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lengthRing,\n x00,\n y00,\n x0,\n y0;\n\nvar lengthStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: function() {\n lengthStream.point = lengthPointFirst;\n },\n lineEnd: function() {\n if (lengthRing) lengthPoint(x00, y00);\n lengthStream.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n },\n polygonStart: function() {\n lengthRing = true;\n },\n polygonEnd: function() {\n lengthRing = null;\n },\n result: function() {\n var length = +lengthSum;\n lengthSum.reset();\n return length;\n }\n};\n\nfunction lengthPointFirst(x, y) {\n lengthStream.point = lengthPoint;\n x00 = x0 = x, y00 = y0 = y;\n}\n\nfunction lengthPoint(x, y) {\n x0 -= x, y0 -= y;\n lengthSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(x0 * x0 + y0 * y0));\n x0 = x, y0 = y;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (lengthStream);\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/measure.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/path/string.js": +/*!************************************************!*\ + !*** ./node_modules/d3-geo/src/path/string.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return PathString; });\nfunction PathString() {\n this._string = [];\n}\n\nPathString.prototype = {\n _radius: 4.5,\n _circle: circle(4.5),\n pointRadius: function(_) {\n if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;\n return this;\n },\n polygonStart: function() {\n this._line = 0;\n },\n polygonEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line === 0) this._string.push(\"Z\");\n this._point = NaN;\n },\n point: function(x, y) {\n switch (this._point) {\n case 0: {\n this._string.push(\"M\", x, \",\", y);\n this._point = 1;\n break;\n }\n case 1: {\n this._string.push(\"L\", x, \",\", y);\n break;\n }\n default: {\n if (this._circle == null) this._circle = circle(this._radius);\n this._string.push(\"M\", x, \",\", y, this._circle);\n break;\n }\n }\n },\n result: function() {\n if (this._string.length) {\n var result = this._string.join(\"\");\n this._string = [];\n return result;\n } else {\n return null;\n }\n }\n};\n\nfunction circle(radius) {\n return \"m0,\" + radius\n + \"a\" + radius + \",\" + radius + \" 0 1,1 0,\" + -2 * radius\n + \"a\" + radius + \",\" + radius + \" 0 1,1 0,\" + 2 * radius\n + \"z\";\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/path/string.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/pointEqual.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-geo/src/pointEqual.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(a[0] - b[0]) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] && Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(a[1] - b[1]) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/pointEqual.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/polygonContains.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-geo/src/polygonContains.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\n\nvar sum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon, point) {\n var lambda = point[0],\n phi = point[1],\n sinPhi = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(phi),\n normal = [Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(lambda), -Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(lambda), 0],\n angle = 0,\n winding = 0;\n\n sum.reset();\n\n if (sinPhi === 1) phi = _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] + _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n else if (sinPhi === -1) phi = -_math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n\n for (var i = 0, n = polygon.length; i < n; ++i) {\n if (!(m = (ring = polygon[i]).length)) continue;\n var ring,\n m,\n point0 = ring[m - 1],\n lambda0 = point0[0],\n phi0 = point0[1] / 2 + _math__WEBPACK_IMPORTED_MODULE_2__[\"quarterPi\"],\n sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(phi0),\n cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi0);\n\n for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {\n var point1 = ring[j],\n lambda1 = point1[0],\n phi1 = point1[1] / 2 + _math__WEBPACK_IMPORTED_MODULE_2__[\"quarterPi\"],\n sinPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(phi1),\n cosPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi1),\n delta = lambda1 - lambda0,\n sign = delta >= 0 ? 1 : -1,\n absDelta = sign * delta,\n antimeridian = absDelta > _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"],\n k = sinPhi0 * sinPhi1;\n\n sum.add(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(k * sign * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(absDelta), cosPhi0 * cosPhi1 + k * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(absDelta)));\n angle += antimeridian ? delta + sign * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] : delta;\n\n // Are the longitudes either side of the point’s meridian (lambda),\n // and are the latitudes smaller than the parallel (phi)?\n if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {\n var arc = Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianCross\"])(Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesian\"])(point0), Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesian\"])(point1));\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianNormalizeInPlace\"])(arc);\n var intersection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianCross\"])(normal, arc);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianNormalizeInPlace\"])(intersection);\n var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(intersection[2]);\n if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {\n winding += antimeridian ^ delta >= 0 ? 1 : -1;\n }\n }\n }\n }\n\n // First, determine whether the South pole is inside or outside:\n //\n // It is inside if:\n // * the polygon winds around it in a clockwise direction.\n // * the polygon does not (cumulatively) wind around it, but has a negative\n // (counter-clockwise) area.\n //\n // Second, count the (signed) number of times a segment crosses a lambda\n // from the point to the South pole. If it is zero, then the point is the\n // same side as the South pole.\n\n return (angle < -_math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] || angle < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] && sum < -_math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) ^ (winding & 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/polygonContains.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/albers.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/albers.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _conicEqualArea__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./conicEqualArea */ \"./node_modules/d3-geo/src/projection/conicEqualArea.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n .parallels([29.5, 45.5])\n .scale(1070)\n .translate([480, 250])\n .rotate([96, 0])\n .center([-0.6, 38.7]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/albers.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/albersUsa.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/albersUsa.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _albers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./albers */ \"./node_modules/d3-geo/src/projection/albers.js\");\n/* harmony import */ var _conicEqualArea__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./conicEqualArea */ \"./node_modules/d3-geo/src/projection/conicEqualArea.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./fit */ \"./node_modules/d3-geo/src/projection/fit.js\");\n\n\n\n\n\n// The projections must have mutually exclusive clip regions on the sphere,\n// as this will avoid emitting interleaving lines and polygons.\nfunction multiplex(streams) {\n var n = streams.length;\n return {\n point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },\n sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },\n lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },\n lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },\n polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },\n polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }\n };\n}\n\n// A composite projection for the United States, configured by default for\n// 960×500. The projection also works quite well at 960×600 if you change the\n// scale to 1285 and adjust the translate accordingly. The set of standard\n// parallels for each region comes from USGS, which is published here:\n// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var cache,\n cacheStream,\n lower48 = Object(_albers__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), lower48Point,\n alaska = Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"default\"])().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338\n hawaii = Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"default\"])().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007\n point, pointStream = {point: function(x, y) { point = [x, y]; }};\n\n function albersUsa(coordinates) {\n var x = coordinates[0], y = coordinates[1];\n return point = null,\n (lower48Point.point(x, y), point)\n || (alaskaPoint.point(x, y), point)\n || (hawaiiPoint.point(x, y), point);\n }\n\n albersUsa.invert = function(coordinates) {\n var k = lower48.scale(),\n t = lower48.translate(),\n x = (coordinates[0] - t[0]) / k,\n y = (coordinates[1] - t[1]) / k;\n return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska\n : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii\n : lower48).invert(coordinates);\n };\n\n albersUsa.stream = function(stream) {\n return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);\n };\n\n albersUsa.precision = function(_) {\n if (!arguments.length) return lower48.precision();\n lower48.precision(_), alaska.precision(_), hawaii.precision(_);\n return reset();\n };\n\n albersUsa.scale = function(_) {\n if (!arguments.length) return lower48.scale();\n lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);\n return albersUsa.translate(lower48.translate());\n };\n\n albersUsa.translate = function(_) {\n if (!arguments.length) return lower48.translate();\n var k = lower48.scale(), x = +_[0], y = +_[1];\n\n lower48Point = lower48\n .translate(_)\n .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])\n .stream(pointStream);\n\n alaskaPoint = alaska\n .translate([x - 0.307 * k, y + 0.201 * k])\n .clipExtent([[x - 0.425 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.120 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]], [x - 0.214 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.234 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]]])\n .stream(pointStream);\n\n hawaiiPoint = hawaii\n .translate([x - 0.205 * k, y + 0.212 * k])\n .clipExtent([[x - 0.214 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.166 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]], [x - 0.115 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.234 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]]])\n .stream(pointStream);\n\n return reset();\n };\n\n albersUsa.fitExtent = function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitExtent\"])(albersUsa, extent, object);\n };\n\n albersUsa.fitSize = function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitSize\"])(albersUsa, size, object);\n };\n\n albersUsa.fitWidth = function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitWidth\"])(albersUsa, width, object);\n };\n\n albersUsa.fitHeight = function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitHeight\"])(albersUsa, height, object);\n };\n\n function reset() {\n cache = cacheStream = null;\n return albersUsa;\n }\n\n return albersUsa.scale(1070);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/albersUsa.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/azimuthal.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/azimuthal.js ***! + \*********************************************************/ +/*! exports provided: azimuthalRaw, azimuthalInvert */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalRaw\", function() { return azimuthalRaw; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalInvert\", function() { return azimuthalInvert; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n\n\nfunction azimuthalRaw(scale) {\n return function(x, y) {\n var cx = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x),\n cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y),\n k = scale(cx * cy);\n return [\n k * cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x),\n k * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)\n ];\n }\n}\n\nfunction azimuthalInvert(angle) {\n return function(x, y) {\n var z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + y * y),\n c = angle(z),\n sc = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(c),\n cc = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(c);\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x * sc, z * cc),\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z && y * sc / z)\n ];\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/azimuthal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/azimuthalEqualArea.js": +/*!******************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/azimuthalEqualArea.js ***! + \******************************************************************/ +/*! exports provided: azimuthalEqualAreaRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalEqualAreaRaw\", function() { return azimuthalEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nvar azimuthalEqualAreaRaw = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalRaw\"])(function(cxcy) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(2 / (1 + cxcy));\n});\n\nazimuthalEqualAreaRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z / 2);\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(azimuthalEqualAreaRaw)\n .scale(124.75)\n .clipAngle(180 - 1e-3);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/azimuthalEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/azimuthalEquidistant.js": +/*!********************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/azimuthalEquidistant.js ***! + \********************************************************************/ +/*! exports provided: azimuthalEquidistantRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalEquidistantRaw\", function() { return azimuthalEquidistantRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nvar azimuthalEquidistantRaw = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalRaw\"])(function(c) {\n return (c = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"acos\"])(c)) && c / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(c);\n});\n\nazimuthalEquidistantRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return z;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(azimuthalEquidistantRaw)\n .scale(79.4188)\n .clipAngle(180 - 1e-3);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/azimuthalEquidistant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/conic.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/conic.js ***! + \*****************************************************/ +/*! exports provided: conicProjection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicProjection\", function() { return conicProjection; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\nfunction conicProjection(projectAt) {\n var phi0 = 0,\n phi1 = _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 3,\n m = Object(_index__WEBPACK_IMPORTED_MODULE_1__[\"projectionMutator\"])(projectAt),\n p = m(phi0, phi1);\n\n p.parallels = function(_) {\n return arguments.length ? m(phi0 = _[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi1 = _[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"]) : [phi0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], phi1 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n };\n\n return p;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/conic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/conicConformal.js": +/*!**************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/conicConformal.js ***! + \**************************************************************/ +/*! exports provided: conicConformalRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicConformalRaw\", function() { return conicConformalRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _mercator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./mercator */ \"./node_modules/d3-geo/src/projection/mercator.js\");\n\n\n\n\nfunction tany(y) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + y) / 2);\n}\n\nfunction conicConformalRaw(y0, y1) {\n var cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n n = y0 === y1 ? Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0) : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(cy0 / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1)) / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(tany(y1) / tany(y0)),\n f = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(tany(y0), n) / n;\n\n if (!n) return _mercator__WEBPACK_IMPORTED_MODULE_2__[\"mercatorRaw\"];\n\n function project(x, y) {\n if (f > 0) { if (y < -_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) y = -_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]; }\n else { if (y > _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) y = _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]; }\n var r = f / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(tany(y), n);\n return [r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(n * x), f - r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(n * x)];\n }\n\n project.invert = function(x, y) {\n var fy = f - y, r = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(n) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + fy * fy);\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(fy)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(fy), 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(f / r, 1 / n)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicConformalRaw)\n .scale(109.5)\n .parallels([30, 30]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/conicConformal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/conicEqualArea.js": +/*!**************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/conicEqualArea.js ***! + \**************************************************************/ +/*! exports provided: conicEqualAreaRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicEqualAreaRaw\", function() { return conicEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _cylindricalEqualArea__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cylindricalEqualArea */ \"./node_modules/d3-geo/src/projection/cylindricalEqualArea.js\");\n\n\n\n\nfunction conicEqualAreaRaw(y0, y1) {\n var sy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0), n = (sy0 + Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y1)) / 2;\n\n // Are the parallels symmetrical around the Equator?\n if (Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(n) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) return Object(_cylindricalEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"cylindricalEqualAreaRaw\"])(y0);\n\n var c = 1 + sy0 * (2 * n - sy0), r0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(c) / n;\n\n function project(x, y) {\n var r = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(c - 2 * n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)) / n;\n return [r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x *= n), r0 - r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x)];\n }\n\n project.invert = function(x, y) {\n var r0y = r0 - y;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(r0y)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(r0y), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])((c - (x * x + r0y * r0y) * n * n) / (2 * n))];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicEqualAreaRaw)\n .scale(155.424)\n .center([0, 33.6442]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/conicEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/conicEquidistant.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/conicEquidistant.js ***! + \****************************************************************/ +/*! exports provided: conicEquidistantRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicEquidistantRaw\", function() { return conicEquidistantRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _equirectangular__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./equirectangular */ \"./node_modules/d3-geo/src/projection/equirectangular.js\");\n\n\n\n\nfunction conicEquidistantRaw(y0, y1) {\n var cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n n = y0 === y1 ? Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0) : (cy0 - Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1)) / (y1 - y0),\n g = cy0 / n + y0;\n\n if (Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(n) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) return _equirectangular__WEBPACK_IMPORTED_MODULE_2__[\"equirectangularRaw\"];\n\n function project(x, y) {\n var gy = g - y, nx = n * x;\n return [gy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(nx), g - gy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(nx)];\n }\n\n project.invert = function(x, y) {\n var gy = g - y;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(gy)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(gy), g - Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(n) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + gy * gy)];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicEquidistantRaw)\n .scale(131.154)\n .center([0, 13.9389]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/conicEquidistant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/cylindricalEqualArea.js": +/*!********************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/cylindricalEqualArea.js ***! + \********************************************************************/ +/*! exports provided: cylindricalEqualAreaRaw */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cylindricalEqualAreaRaw\", function() { return cylindricalEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n\n\nfunction cylindricalEqualAreaRaw(phi0) {\n var cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi0);\n\n function forward(lambda, phi) {\n return [lambda * cosPhi0, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi) / cosPhi0];\n }\n\n forward.invert = function(x, y) {\n return [x / cosPhi0, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(y * cosPhi0)];\n };\n\n return forward;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/cylindricalEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/equalEarth.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/equalEarth.js ***! + \**********************************************************/ +/*! exports provided: equalEarthRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"equalEarthRaw\", function() { return equalEarthRaw; });\n/* harmony import */ var _index_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index.js */ \"./node_modules/d3-geo/src/projection/index.js\");\n/* harmony import */ var _math_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math.js */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\nvar A1 = 1.340264,\n A2 = -0.081106,\n A3 = 0.000893,\n A4 = 0.003796,\n M = Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(3) / 2,\n iterations = 12;\n\nfunction equalEarthRaw(lambda, phi) {\n var l = Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(M * Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi)), l2 = l * l, l6 = l2 * l2 * l2;\n return [\n lambda * Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(l) / (M * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2))),\n l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2))\n ];\n}\n\nequalEarthRaw.invert = function(x, y) {\n var l = y, l2 = l * l, l6 = l2 * l2 * l2;\n for (var i = 0, delta, fy, fpy; i < iterations; ++i) {\n fy = l * (A1 + A2 * l2 + l6 * (A3 + A4 * l2)) - y;\n fpy = A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2);\n l -= delta = fy / fpy, l2 = l * l, l6 = l2 * l2 * l2;\n if (Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(delta) < _math_js__WEBPACK_IMPORTED_MODULE_1__[\"epsilon2\"]) break;\n }\n return [\n M * x * (A1 + 3 * A2 * l2 + l6 * (7 * A3 + 9 * A4 * l2)) / Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(l),\n Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(Object(_math_js__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(l) / M)\n ];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index_js__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(equalEarthRaw)\n .scale(177.158);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/equalEarth.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/equirectangular.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/equirectangular.js ***! + \***************************************************************/ +/*! exports provided: equirectangularRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"equirectangularRaw\", function() { return equirectangularRaw; });\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\nfunction equirectangularRaw(lambda, phi) {\n return [lambda, phi];\n}\n\nequirectangularRaw.invert = equirectangularRaw;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(equirectangularRaw)\n .scale(152.63);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/equirectangular.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/fit.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/fit.js ***! + \***************************************************/ +/*! exports provided: fitExtent, fitSize, fitWidth, fitHeight */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitExtent\", function() { return fitExtent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitSize\", function() { return fitSize; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitWidth\", function() { return fitWidth; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitHeight\", function() { return fitHeight; });\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../stream */ \"./node_modules/d3-geo/src/stream.js\");\n/* harmony import */ var _path_bounds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../path/bounds */ \"./node_modules/d3-geo/src/path/bounds.js\");\n\n\n\nfunction fit(projection, fitBounds, object) {\n var clip = projection.clipExtent && projection.clipExtent();\n projection.scale(150).translate([0, 0]);\n if (clip != null) projection.clipExtent(null);\n Object(_stream__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(object, projection.stream(_path_bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"]));\n fitBounds(_path_bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"].result());\n if (clip != null) projection.clipExtent(clip);\n return projection;\n}\n\nfunction fitExtent(projection, extent, object) {\n return fit(projection, function(b) {\n var w = extent[1][0] - extent[0][0],\n h = extent[1][1] - extent[0][1],\n k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),\n x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,\n y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\nfunction fitSize(projection, size, object) {\n return fitExtent(projection, [[0, 0], size], object);\n}\n\nfunction fitWidth(projection, width, object) {\n return fit(projection, function(b) {\n var w = +width,\n k = w / (b[1][0] - b[0][0]),\n x = (w - k * (b[1][0] + b[0][0])) / 2,\n y = -k * b[0][1];\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\nfunction fitHeight(projection, height, object) {\n return fit(projection, function(b) {\n var h = +height,\n k = h / (b[1][1] - b[0][1]),\n x = -k * b[0][0],\n y = (h - k * (b[1][1] + b[0][1])) / 2;\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/fit.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/gnomonic.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/gnomonic.js ***! + \********************************************************/ +/*! exports provided: gnomonicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gnomonicRaw\", function() { return gnomonicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction gnomonicRaw(x, y) {\n var cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y), k = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x) * cy;\n return [cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x) / k, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y) / k];\n}\n\ngnomonicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(gnomonicRaw)\n .scale(144.049)\n .clipAngle(60);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/gnomonic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/identity.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/identity.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _clip_rectangle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../clip/rectangle */ \"./node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../identity */ \"./node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../transform */ \"./node_modules/d3-geo/src/transform.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./fit */ \"./node_modules/d3-geo/src/projection/fit.js\");\n\n\n\n\n\nfunction scaleTranslate(kx, ky, tx, ty) {\n return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"] : Object(_transform__WEBPACK_IMPORTED_MODULE_2__[\"transformer\"])({\n point: function(x, y) {\n this.stream.point(x * kx + tx, y * ky + ty);\n }\n });\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform = _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"], // scale, translate and reflect\n x0 = null, y0, x1, y1, // clip extent\n postclip = _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n cache,\n cacheStream,\n projection;\n\n function reset() {\n cache = cacheStream = null;\n return projection;\n }\n\n return projection = {\n stream: function(stream) {\n return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));\n },\n postclip: function(_) {\n return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;\n },\n clipExtent: function(_) {\n return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : Object(_clip_rectangle__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n },\n scale: function(_) {\n return arguments.length ? (transform = scaleTranslate((k = +_) * sx, k * sy, tx, ty), reset()) : k;\n },\n translate: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];\n },\n reflectX: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0;\n },\n reflectY: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0;\n },\n fitExtent: function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitExtent\"])(projection, extent, object);\n },\n fitSize: function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitSize\"])(projection, size, object);\n },\n fitWidth: function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitWidth\"])(projection, width, object);\n },\n fitHeight: function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitHeight\"])(projection, height, object);\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/index.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/index.js ***! + \*****************************************************/ +/*! exports provided: default, projectionMutator */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return projection; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"projectionMutator\", function() { return projectionMutator; });\n/* harmony import */ var _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../clip/antimeridian */ \"./node_modules/d3-geo/src/clip/antimeridian.js\");\n/* harmony import */ var _clip_circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../clip/circle */ \"./node_modules/d3-geo/src/clip/circle.js\");\n/* harmony import */ var _clip_rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../clip/rectangle */ \"./node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony import */ var _compose__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../compose */ \"./node_modules/d3-geo/src/compose.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../identity */ \"./node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../rotation */ \"./node_modules/d3-geo/src/rotation.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../transform */ \"./node_modules/d3-geo/src/transform.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./fit */ \"./node_modules/d3-geo/src/projection/fit.js\");\n/* harmony import */ var _resample__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./resample */ \"./node_modules/d3-geo/src/projection/resample.js\");\n\n\n\n\n\n\n\n\n\n\n\nvar transformRadians = Object(_transform__WEBPACK_IMPORTED_MODULE_7__[\"transformer\"])({\n point: function(x, y) {\n this.stream.point(x * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], y * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]);\n }\n});\n\nfunction transformRotate(rotate) {\n return Object(_transform__WEBPACK_IMPORTED_MODULE_7__[\"transformer\"])({\n point: function(x, y) {\n var r = rotate(x, y);\n return this.stream.point(r[0], r[1]);\n }\n });\n}\n\nfunction scaleTranslate(k, dx, dy) {\n function transform(x, y) {\n return [dx + k * x, dy - k * y];\n }\n transform.invert = function(x, y) {\n return [(x - dx) / k, (dy - y) / k];\n };\n return transform;\n}\n\nfunction scaleTranslateRotate(k, dx, dy, alpha) {\n var cosAlpha = Object(_math__WEBPACK_IMPORTED_MODULE_5__[\"cos\"])(alpha),\n sinAlpha = Object(_math__WEBPACK_IMPORTED_MODULE_5__[\"sin\"])(alpha),\n a = cosAlpha * k,\n b = sinAlpha * k,\n ai = cosAlpha / k,\n bi = sinAlpha / k,\n ci = (sinAlpha * dy - cosAlpha * dx) / k,\n fi = (sinAlpha * dx + cosAlpha * dy) / k;\n function transform(x, y) {\n return [a * x - b * y + dx, dy - b * x - a * y];\n }\n transform.invert = function(x, y) {\n return [ai * x - bi * y + ci, fi - bi * x - ai * y];\n };\n return transform;\n}\n\nfunction projection(project) {\n return projectionMutator(function() { return project; })();\n}\n\nfunction projectionMutator(projectAt) {\n var project,\n k = 150, // scale\n x = 480, y = 250, // translate\n lambda = 0, phi = 0, // center\n deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate\n alpha = 0, // post-rotate\n theta = null, preclip = _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__[\"default\"], // pre-clip angle\n x0 = null, y0, x1, y1, postclip = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"], // post-clip extent\n delta2 = 0.5, // precision\n projectResample,\n projectTransform,\n projectRotateTransform,\n cache,\n cacheStream;\n\n function projection(point) {\n return projectRotateTransform(point[0] * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]);\n }\n\n function invert(point) {\n point = projectRotateTransform.invert(point[0], point[1]);\n return point && [point[0] * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n }\n\n projection.stream = function(stream) {\n return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));\n };\n\n projection.preclip = function(_) {\n return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;\n };\n\n projection.postclip = function(_) {\n return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;\n };\n\n projection.clipAngle = function(_) {\n return arguments.length ? (preclip = +_ ? Object(_clip_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(theta = _ * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]) : (theta = null, _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), reset()) : theta * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"];\n };\n\n projection.clipExtent = function(_) {\n return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"]) : Object(_clip_rectangle__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n };\n\n projection.scale = function(_) {\n return arguments.length ? (k = +_, recenter()) : k;\n };\n\n projection.translate = function(_) {\n return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];\n };\n\n projection.center = function(_) {\n return arguments.length ? (lambda = _[0] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], phi = _[1] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], recenter()) : [lambda * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], phi * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n };\n\n projection.rotate = function(_) {\n return arguments.length ? (deltaLambda = _[0] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], deltaPhi = _[1] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], deltaGamma = _.length > 2 ? _[2] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"] : 0, recenter()) : [deltaLambda * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], deltaPhi * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], deltaGamma * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n };\n\n projection.angle = function(_) {\n return arguments.length ? (alpha = _ % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], recenter()) : alpha * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"];\n };\n\n projection.precision = function(_) {\n return arguments.length ? (projectResample = Object(_resample__WEBPACK_IMPORTED_MODULE_9__[\"default\"])(projectTransform, delta2 = _ * _), reset()) : Object(_math__WEBPACK_IMPORTED_MODULE_5__[\"sqrt\"])(delta2);\n };\n\n projection.fitExtent = function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitExtent\"])(projection, extent, object);\n };\n\n projection.fitSize = function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitSize\"])(projection, size, object);\n };\n\n projection.fitWidth = function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitWidth\"])(projection, width, object);\n };\n\n projection.fitHeight = function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitHeight\"])(projection, height, object);\n };\n\n function recenter() {\n var center = scaleTranslateRotate(k, 0, 0, alpha).apply(null, project(lambda, phi)),\n transform = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], alpha);\n rotate = Object(_rotation__WEBPACK_IMPORTED_MODULE_6__[\"rotateRadians\"])(deltaLambda, deltaPhi, deltaGamma);\n projectTransform = Object(_compose__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(project, transform);\n projectRotateTransform = Object(_compose__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(rotate, projectTransform);\n projectResample = Object(_resample__WEBPACK_IMPORTED_MODULE_9__[\"default\"])(projectTransform, delta2);\n return reset();\n }\n\n function reset() {\n cache = cacheStream = null;\n return projection;\n }\n\n return function() {\n project = projectAt.apply(this, arguments);\n projection.invert = project.invert && invert;\n return recenter();\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/mercator.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/mercator.js ***! + \********************************************************/ +/*! exports provided: mercatorRaw, default, mercatorProjection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mercatorRaw\", function() { return mercatorRaw; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mercatorProjection\", function() { return mercatorProjection; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../rotation */ \"./node_modules/d3-geo/src/rotation.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction mercatorRaw(lambda, phi) {\n return [lambda, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + phi) / 2))];\n}\n\nmercatorRaw.invert = function(x, y) {\n return [x, 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"exp\"])(y)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return mercatorProjection(mercatorRaw)\n .scale(961 / _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n});\n\nfunction mercatorProjection(project) {\n var m = Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(project),\n center = m.center,\n scale = m.scale,\n translate = m.translate,\n clipExtent = m.clipExtent,\n x0 = null, y0, x1, y1; // clip extent\n\n m.scale = function(_) {\n return arguments.length ? (scale(_), reclip()) : scale();\n };\n\n m.translate = function(_) {\n return arguments.length ? (translate(_), reclip()) : translate();\n };\n\n m.center = function(_) {\n return arguments.length ? (center(_), reclip()) : center();\n };\n\n m.clipExtent = function(_) {\n return arguments.length ? ((_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1])), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n };\n\n function reclip() {\n var k = _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] * scale(),\n t = m(Object(_rotation__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(m.rotate()).invert([0, 0]));\n return clipExtent(x0 == null\n ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw\n ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]\n : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);\n }\n\n return reclip();\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/mercator.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/naturalEarth1.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/naturalEarth1.js ***! + \*************************************************************/ +/*! exports provided: naturalEarth1Raw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"naturalEarth1Raw\", function() { return naturalEarth1Raw; });\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\nfunction naturalEarth1Raw(lambda, phi) {\n var phi2 = phi * phi, phi4 = phi2 * phi2;\n return [\n lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))),\n phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))\n ];\n}\n\nnaturalEarth1Raw.invert = function(x, y) {\n var phi = y, i = 25, delta;\n do {\n var phi2 = phi * phi, phi4 = phi2 * phi2;\n phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) /\n (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));\n } while (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(delta) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && --i > 0);\n return [\n x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))),\n phi\n ];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(naturalEarth1Raw)\n .scale(175.295);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/naturalEarth1.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/orthographic.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/orthographic.js ***! + \************************************************************/ +/*! exports provided: orthographicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"orthographicRaw\", function() { return orthographicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction orthographicRaw(x, y) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)];\n}\n\northographicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(orthographicRaw)\n .scale(249.5)\n .clipAngle(90 + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/orthographic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/resample.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/resample.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../cartesian */ \"./node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../transform */ \"./node_modules/d3-geo/src/transform.js\");\n\n\n\n\nvar maxDepth = 16, // maximum depth of subdivision\n cosMinDistance = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(30 * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]); // cos(minimum angular distance)\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(project, delta2) {\n return +delta2 ? resample(project, delta2) : resampleNone(project);\n});\n\nfunction resampleNone(project) {\n return Object(_transform__WEBPACK_IMPORTED_MODULE_2__[\"transformer\"])({\n point: function(x, y) {\n x = project(x, y);\n this.stream.point(x[0], x[1]);\n }\n });\n}\n\nfunction resample(project, delta2) {\n\n function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {\n var dx = x1 - x0,\n dy = y1 - y0,\n d2 = dx * dx + dy * dy;\n if (d2 > 4 * delta2 && depth--) {\n var a = a0 + a1,\n b = b0 + b1,\n c = c0 + c1,\n m = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(a * a + b * b + c * c),\n phi2 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(c /= m),\n lambda2 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(c) - 1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] || Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda0 - lambda1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? (lambda0 + lambda1) / 2 : Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(b, a),\n p = project(lambda2, phi2),\n x2 = p[0],\n y2 = p[1],\n dx2 = x2 - x0,\n dy2 = y2 - y0,\n dz = dy * dx2 - dx * dy2;\n if (dz * dz / d2 > delta2 // perpendicular projected distance\n || Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end\n || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);\n stream.point(x2, y2);\n resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);\n }\n }\n }\n return function(stream) {\n var lambda00, x00, y00, a00, b00, c00, // first point\n lambda0, x0, y0, a0, b0, c0; // previous point\n\n var resampleStream = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },\n polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }\n };\n\n function point(x, y) {\n x = project(x, y);\n stream.point(x[0], x[1]);\n }\n\n function lineStart() {\n x0 = NaN;\n resampleStream.point = linePoint;\n stream.lineStart();\n }\n\n function linePoint(lambda, phi) {\n var c = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])([lambda, phi]), p = project(lambda, phi);\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);\n stream.point(x0, y0);\n }\n\n function lineEnd() {\n resampleStream.point = point;\n stream.lineEnd();\n }\n\n function ringStart() {\n lineStart();\n resampleStream.point = ringPoint;\n resampleStream.lineEnd = ringEnd;\n }\n\n function ringPoint(lambda, phi) {\n linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;\n resampleStream.point = linePoint;\n }\n\n function ringEnd() {\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);\n resampleStream.lineEnd = lineEnd;\n lineEnd();\n }\n\n return resampleStream;\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/resample.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/stereographic.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/stereographic.js ***! + \*************************************************************/ +/*! exports provided: stereographicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stereographicRaw\", function() { return stereographicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction stereographicRaw(x, y) {\n var cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y), k = 1 + Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x) * cy;\n return [cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x) / k, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y) / k];\n}\n\nstereographicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(z);\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(stereographicRaw)\n .scale(250)\n .clipAngle(142);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/stereographic.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/projection/transverseMercator.js": +/*!******************************************************************!*\ + !*** ./node_modules/d3-geo/src/projection/transverseMercator.js ***! + \******************************************************************/ +/*! exports provided: transverseMercatorRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"transverseMercatorRaw\", function() { return transverseMercatorRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _mercator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./mercator */ \"./node_modules/d3-geo/src/projection/mercator.js\");\n\n\n\nfunction transverseMercatorRaw(lambda, phi) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + phi) / 2)), -lambda];\n}\n\ntransverseMercatorRaw.invert = function(x, y) {\n return [-y, 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"exp\"])(x)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var m = Object(_mercator__WEBPACK_IMPORTED_MODULE_1__[\"mercatorProjection\"])(transverseMercatorRaw),\n center = m.center,\n rotate = m.rotate;\n\n m.center = function(_) {\n return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);\n };\n\n m.rotate = function(_) {\n return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);\n };\n\n return rotate([0, 0, 90])\n .scale(159.155);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/projection/transverseMercator.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/rotation.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-geo/src/rotation.js ***! + \*********************************************/ +/*! exports provided: rotateRadians, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rotateRadians\", function() { return rotateRadians; });\n/* harmony import */ var _compose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./compose */ \"./node_modules/d3-geo/src/compose.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-geo/src/math.js\");\n\n\n\nfunction rotationIdentity(lambda, phi) {\n return [lambda > _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda - _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda < -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda + _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda, phi];\n}\n\nrotationIdentity.invert = rotationIdentity;\n\nfunction rotateRadians(deltaLambda, deltaPhi, deltaGamma) {\n return (deltaLambda %= _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"]) ? (deltaPhi || deltaGamma ? Object(_compose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))\n : rotationLambda(deltaLambda))\n : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)\n : rotationIdentity);\n}\n\nfunction forwardRotationLambda(deltaLambda) {\n return function(lambda, phi) {\n return lambda += deltaLambda, [lambda > _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda - _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda < -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda + _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda, phi];\n };\n}\n\nfunction rotationLambda(deltaLambda) {\n var rotation = forwardRotationLambda(deltaLambda);\n rotation.invert = forwardRotationLambda(-deltaLambda);\n return rotation;\n}\n\nfunction rotationPhiGamma(deltaPhi, deltaGamma) {\n var cosDeltaPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(deltaPhi),\n sinDeltaPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(deltaPhi),\n cosDeltaGamma = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(deltaGamma),\n sinDeltaGamma = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(deltaGamma);\n\n function rotation(lambda, phi) {\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n x = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(lambda) * cosPhi,\n y = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda) * cosPhi,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = z * cosDeltaPhi + x * sinDeltaPhi;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(k * cosDeltaGamma + y * sinDeltaGamma)\n ];\n }\n\n rotation.invert = function(lambda, phi) {\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n x = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(lambda) * cosPhi,\n y = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda) * cosPhi,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = z * cosDeltaGamma - y * sinDeltaGamma;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(k * cosDeltaPhi - x * sinDeltaPhi)\n ];\n };\n\n return rotation;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(rotate) {\n rotate = rotateRadians(rotate[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], rotate[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], rotate.length > 2 ? rotate[2] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"] : 0);\n\n function forward(coordinates) {\n coordinates = rotate(coordinates[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], coordinates[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]);\n return coordinates[0] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates[1] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates;\n }\n\n forward.invert = function(coordinates) {\n coordinates = rotate.invert(coordinates[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], coordinates[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]);\n return coordinates[0] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates[1] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates;\n };\n\n return forward;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/rotation.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/stream.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-geo/src/stream.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction streamGeometry(geometry, stream) {\n if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {\n streamGeometryType[geometry.type](geometry, stream);\n }\n}\n\nvar streamObjectType = {\n Feature: function(object, stream) {\n streamGeometry(object.geometry, stream);\n },\n FeatureCollection: function(object, stream) {\n var features = object.features, i = -1, n = features.length;\n while (++i < n) streamGeometry(features[i].geometry, stream);\n }\n};\n\nvar streamGeometryType = {\n Sphere: function(object, stream) {\n stream.sphere();\n },\n Point: function(object, stream) {\n object = object.coordinates;\n stream.point(object[0], object[1], object[2]);\n },\n MultiPoint: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);\n },\n LineString: function(object, stream) {\n streamLine(object.coordinates, stream, 0);\n },\n MultiLineString: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) streamLine(coordinates[i], stream, 0);\n },\n Polygon: function(object, stream) {\n streamPolygon(object.coordinates, stream);\n },\n MultiPolygon: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) streamPolygon(coordinates[i], stream);\n },\n GeometryCollection: function(object, stream) {\n var geometries = object.geometries, i = -1, n = geometries.length;\n while (++i < n) streamGeometry(geometries[i], stream);\n }\n};\n\nfunction streamLine(coordinates, stream, closed) {\n var i = -1, n = coordinates.length - closed, coordinate;\n stream.lineStart();\n while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);\n stream.lineEnd();\n}\n\nfunction streamPolygon(coordinates, stream) {\n var i = -1, n = coordinates.length;\n stream.polygonStart();\n while (++i < n) streamLine(coordinates[i], stream, 1);\n stream.polygonEnd();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object, stream) {\n if (object && streamObjectType.hasOwnProperty(object.type)) {\n streamObjectType[object.type](object, stream);\n } else {\n streamGeometry(object, stream);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/stream.js?"); + +/***/ }), + +/***/ "./node_modules/d3-geo/src/transform.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-geo/src/transform.js ***! + \**********************************************/ +/*! exports provided: default, transformer */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"transformer\", function() { return transformer; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(methods) {\n return {\n stream: transformer(methods)\n };\n});\n\nfunction transformer(methods) {\n return function(stream) {\n var s = new TransformStream;\n for (var key in methods) s[key] = methods[key];\n s.stream = stream;\n return s;\n };\n}\n\nfunction TransformStream() {}\n\nTransformStream.prototype = {\n constructor: TransformStream,\n point: function(x, y) { this.stream.point(x, y); },\n sphere: function() { this.stream.sphere(); },\n lineStart: function() { this.stream.lineStart(); },\n lineEnd: function() { this.stream.lineEnd(); },\n polygonStart: function() { this.stream.polygonStart(); },\n polygonEnd: function() { this.stream.polygonEnd(); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-geo/src/transform.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/accessors.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/accessors.js ***! + \****************************************************/ +/*! exports provided: optional, required */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"optional\", function() { return optional; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"required\", function() { return required; });\nfunction optional(f) {\n return f == null ? null : required(f);\n}\n\nfunction required(f) {\n if (typeof f !== \"function\") throw new Error;\n return f;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/accessors.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/array.js": +/*!************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/array.js ***! + \************************************************/ +/*! exports provided: slice, shuffle */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return shuffle; });\nvar slice = Array.prototype.slice;\n\nfunction shuffle(array) {\n var m = array.length,\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m];\n array[m] = array[i];\n array[i] = t;\n }\n\n return array;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/cluster.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/cluster.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction defaultSeparation(a, b) {\n return a.parent === b.parent ? 1 : 2;\n}\n\nfunction meanX(children) {\n return children.reduce(meanXReduce, 0) / children.length;\n}\n\nfunction meanXReduce(x, c) {\n return x + c.x;\n}\n\nfunction maxY(children) {\n return 1 + children.reduce(maxYReduce, 0);\n}\n\nfunction maxYReduce(y, c) {\n return Math.max(y, c.y);\n}\n\nfunction leafLeft(node) {\n var children;\n while (children = node.children) node = children[0];\n return node;\n}\n\nfunction leafRight(node) {\n var children;\n while (children = node.children) node = children[children.length - 1];\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var separation = defaultSeparation,\n dx = 1,\n dy = 1,\n nodeSize = false;\n\n function cluster(root) {\n var previousNode,\n x = 0;\n\n // First walk, computing the initial x & y values.\n root.eachAfter(function(node) {\n var children = node.children;\n if (children) {\n node.x = meanX(children);\n node.y = maxY(children);\n } else {\n node.x = previousNode ? x += separation(node, previousNode) : 0;\n node.y = 0;\n previousNode = node;\n }\n });\n\n var left = leafLeft(root),\n right = leafRight(root),\n x0 = left.x - separation(left, right) / 2,\n x1 = right.x + separation(right, left) / 2;\n\n // Second walk, normalizing x & y to the desired size.\n return root.eachAfter(nodeSize ? function(node) {\n node.x = (node.x - root.x) * dx;\n node.y = (root.y - node.y) * dy;\n } : function(node) {\n node.x = (node.x - x0) / (x1 - x0) * dx;\n node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;\n });\n }\n\n cluster.separation = function(x) {\n return arguments.length ? (separation = x, cluster) : separation;\n };\n\n cluster.size = function(x) {\n return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);\n };\n\n cluster.nodeSize = function(x) {\n return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);\n };\n\n return cluster;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/cluster.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/constant.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/constant.js ***! + \***************************************************/ +/*! exports provided: constantZero, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"constantZero\", function() { return constantZero; });\nfunction constantZero() {\n return 0;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/ancestors.js": +/*!**************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/ancestors.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var node = this, nodes = [node];\n while (node = node.parent) {\n nodes.push(node);\n }\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/ancestors.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/count.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/count.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction count(node) {\n var sum = 0,\n children = node.children,\n i = children && children.length;\n if (!i) sum = 1;\n else while (--i >= 0) sum += children[i].value;\n node.value = sum;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.eachAfter(count);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/count.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/descendants.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/descendants.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = [];\n this.each(function(node) {\n nodes.push(node);\n });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/descendants.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/each.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/each.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, current, next = [node], children, i, n;\n do {\n current = next.reverse(), next = [];\n while (node = current.pop()) {\n callback(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n next.push(children[i]);\n }\n }\n } while (next.length);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/each.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/eachAfter.js": +/*!**************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/eachAfter.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, nodes = [node], next = [], children, i, n;\n while (node = nodes.pop()) {\n next.push(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n nodes.push(children[i]);\n }\n }\n while (node = next.pop()) {\n callback(node);\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/eachAfter.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/eachBefore.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/eachBefore.js ***! + \***************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, nodes = [node], children, i;\n while (node = nodes.pop()) {\n callback(node), children = node.children;\n if (children) for (i = children.length - 1; i >= 0; --i) {\n nodes.push(children[i]);\n }\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/eachBefore.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/index.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/index.js ***! + \**********************************************************/ +/*! exports provided: default, computeHeight, Node */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return hierarchy; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"computeHeight\", function() { return computeHeight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Node\", function() { return Node; });\n/* harmony import */ var _count__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./count */ \"./node_modules/d3-hierarchy/src/hierarchy/count.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./each */ \"./node_modules/d3-hierarchy/src/hierarchy/each.js\");\n/* harmony import */ var _eachBefore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./eachBefore */ \"./node_modules/d3-hierarchy/src/hierarchy/eachBefore.js\");\n/* harmony import */ var _eachAfter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./eachAfter */ \"./node_modules/d3-hierarchy/src/hierarchy/eachAfter.js\");\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sum */ \"./node_modules/d3-hierarchy/src/hierarchy/sum.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./sort */ \"./node_modules/d3-hierarchy/src/hierarchy/sort.js\");\n/* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./path */ \"./node_modules/d3-hierarchy/src/hierarchy/path.js\");\n/* harmony import */ var _ancestors__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./ancestors */ \"./node_modules/d3-hierarchy/src/hierarchy/ancestors.js\");\n/* harmony import */ var _descendants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./descendants */ \"./node_modules/d3-hierarchy/src/hierarchy/descendants.js\");\n/* harmony import */ var _leaves__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./leaves */ \"./node_modules/d3-hierarchy/src/hierarchy/leaves.js\");\n/* harmony import */ var _links__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./links */ \"./node_modules/d3-hierarchy/src/hierarchy/links.js\");\n\n\n\n\n\n\n\n\n\n\n\n\nfunction hierarchy(data, children) {\n var root = new Node(data),\n valued = +data.value && (root.value = data.value),\n node,\n nodes = [root],\n child,\n childs,\n i,\n n;\n\n if (children == null) children = defaultChildren;\n\n while (node = nodes.pop()) {\n if (valued) node.value = +node.data.value;\n if ((childs = children(node.data)) && (n = childs.length)) {\n node.children = new Array(n);\n for (i = n - 1; i >= 0; --i) {\n nodes.push(child = node.children[i] = new Node(childs[i]));\n child.parent = node;\n child.depth = node.depth + 1;\n }\n }\n }\n\n return root.eachBefore(computeHeight);\n}\n\nfunction node_copy() {\n return hierarchy(this).eachBefore(copyData);\n}\n\nfunction defaultChildren(d) {\n return d.children;\n}\n\nfunction copyData(node) {\n node.data = node.data.data;\n}\n\nfunction computeHeight(node) {\n var height = 0;\n do node.height = height;\n while ((node = node.parent) && (node.height < ++height));\n}\n\nfunction Node(data) {\n this.data = data;\n this.depth =\n this.height = 0;\n this.parent = null;\n}\n\nNode.prototype = hierarchy.prototype = {\n constructor: Node,\n count: _count__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n eachAfter: _eachAfter__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n eachBefore: _eachBefore__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n sum: _sum__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n path: _path__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n ancestors: _ancestors__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n descendants: _descendants__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n leaves: _leaves__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n links: _links__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n copy: node_copy\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/leaves.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/leaves.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var leaves = [];\n this.eachBefore(function(node) {\n if (!node.children) {\n leaves.push(node);\n }\n });\n return leaves;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/leaves.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/links.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/links.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var root = this, links = [];\n root.each(function(node) {\n if (node !== root) { // Don’t include the root’s parent, if any.\n links.push({source: node.parent, target: node});\n }\n });\n return links;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/links.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/path.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/path.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(end) {\n var start = this,\n ancestor = leastCommonAncestor(start, end),\n nodes = [start];\n while (start !== ancestor) {\n start = start.parent;\n nodes.push(start);\n }\n var k = nodes.length;\n while (end !== ancestor) {\n nodes.splice(k, 0, end);\n end = end.parent;\n }\n return nodes;\n});\n\nfunction leastCommonAncestor(a, b) {\n if (a === b) return a;\n var aNodes = a.ancestors(),\n bNodes = b.ancestors(),\n c = null;\n a = aNodes.pop();\n b = bNodes.pop();\n while (a === b) {\n c = a;\n a = aNodes.pop();\n b = bNodes.pop();\n }\n return c;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/path.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/sort.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/sort.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n return this.eachBefore(function(node) {\n if (node.children) {\n node.children.sort(compare);\n }\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/sort.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/hierarchy/sum.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/hierarchy/sum.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.eachAfter(function(node) {\n var sum = +value(node.data) || 0,\n children = node.children,\n i = children && children.length;\n while (--i >= 0) sum += children[i].value;\n node.value = sum;\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/hierarchy/sum.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/index.js": +/*!************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/index.js ***! + \************************************************/ +/*! exports provided: cluster, hierarchy, pack, packSiblings, packEnclose, partition, stratify, tree, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, treemapResquarify */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cluster__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cluster */ \"./node_modules/d3-hierarchy/src/cluster.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cluster\", function() { return _cluster__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hierarchy/index */ \"./node_modules/d3-hierarchy/src/hierarchy/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hierarchy\", function() { return _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _pack_index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./pack/index */ \"./node_modules/d3-hierarchy/src/pack/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pack\", function() { return _pack_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _pack_siblings__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./pack/siblings */ \"./node_modules/d3-hierarchy/src/pack/siblings.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packSiblings\", function() { return _pack_siblings__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _pack_enclose__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./pack/enclose */ \"./node_modules/d3-hierarchy/src/pack/enclose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return _pack_enclose__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _partition__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./partition */ \"./node_modules/d3-hierarchy/src/partition.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"partition\", function() { return _partition__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _stratify__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./stratify */ \"./node_modules/d3-hierarchy/src/stratify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stratify\", function() { return _stratify__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./tree */ \"./node_modules/d3-hierarchy/src/tree.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tree\", function() { return _tree__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _treemap_index__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./treemap/index */ \"./node_modules/d3-hierarchy/src/treemap/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemap\", function() { return _treemap_index__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _treemap_binary__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./treemap/binary */ \"./node_modules/d3-hierarchy/src/treemap/binary.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapBinary\", function() { return _treemap_binary__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _treemap_dice__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./treemap/dice */ \"./node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapDice\", function() { return _treemap_dice__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _treemap_slice__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./treemap/slice */ \"./node_modules/d3-hierarchy/src/treemap/slice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSlice\", function() { return _treemap_slice__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _treemap_sliceDice__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./treemap/sliceDice */ \"./node_modules/d3-hierarchy/src/treemap/sliceDice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSliceDice\", function() { return _treemap_sliceDice__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _treemap_squarify__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./treemap/squarify */ \"./node_modules/d3-hierarchy/src/treemap/squarify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSquarify\", function() { return _treemap_squarify__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _treemap_resquarify__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./treemap/resquarify */ \"./node_modules/d3-hierarchy/src/treemap/resquarify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapResquarify\", function() { return _treemap_resquarify__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/pack/enclose.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/pack/enclose.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/d3-hierarchy/src/array.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(circles) {\n var i = 0, n = (circles = Object(_array__WEBPACK_IMPORTED_MODULE_0__[\"shuffle\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(circles))).length, B = [], p, e;\n\n while (i < n) {\n p = circles[i];\n if (e && enclosesWeak(e, p)) ++i;\n else e = encloseBasis(B = extendBasis(B, p)), i = 0;\n }\n\n return e;\n});\n\nfunction extendBasis(B, p) {\n var i, j;\n\n if (enclosesWeakAll(p, B)) return [p];\n\n // If we get here then B must have at least one element.\n for (i = 0; i < B.length; ++i) {\n if (enclosesNot(p, B[i])\n && enclosesWeakAll(encloseBasis2(B[i], p), B)) {\n return [B[i], p];\n }\n }\n\n // If we get here then B must have at least two elements.\n for (i = 0; i < B.length - 1; ++i) {\n for (j = i + 1; j < B.length; ++j) {\n if (enclosesNot(encloseBasis2(B[i], B[j]), p)\n && enclosesNot(encloseBasis2(B[i], p), B[j])\n && enclosesNot(encloseBasis2(B[j], p), B[i])\n && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {\n return [B[i], B[j], p];\n }\n }\n }\n\n // If we get here then something is very wrong.\n throw new Error;\n}\n\nfunction enclosesNot(a, b) {\n var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y;\n return dr < 0 || dr * dr < dx * dx + dy * dy;\n}\n\nfunction enclosesWeak(a, b) {\n var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y;\n return dr > 0 && dr * dr > dx * dx + dy * dy;\n}\n\nfunction enclosesWeakAll(a, B) {\n for (var i = 0; i < B.length; ++i) {\n if (!enclosesWeak(a, B[i])) {\n return false;\n }\n }\n return true;\n}\n\nfunction encloseBasis(B) {\n switch (B.length) {\n case 1: return encloseBasis1(B[0]);\n case 2: return encloseBasis2(B[0], B[1]);\n case 3: return encloseBasis3(B[0], B[1], B[2]);\n }\n}\n\nfunction encloseBasis1(a) {\n return {\n x: a.x,\n y: a.y,\n r: a.r\n };\n}\n\nfunction encloseBasis2(a, b) {\n var x1 = a.x, y1 = a.y, r1 = a.r,\n x2 = b.x, y2 = b.y, r2 = b.r,\n x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,\n l = Math.sqrt(x21 * x21 + y21 * y21);\n return {\n x: (x1 + x2 + x21 / l * r21) / 2,\n y: (y1 + y2 + y21 / l * r21) / 2,\n r: (l + r1 + r2) / 2\n };\n}\n\nfunction encloseBasis3(a, b, c) {\n var x1 = a.x, y1 = a.y, r1 = a.r,\n x2 = b.x, y2 = b.y, r2 = b.r,\n x3 = c.x, y3 = c.y, r3 = c.r,\n a2 = x1 - x2,\n a3 = x1 - x3,\n b2 = y1 - y2,\n b3 = y1 - y3,\n c2 = r2 - r1,\n c3 = r3 - r1,\n d1 = x1 * x1 + y1 * y1 - r1 * r1,\n d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,\n d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,\n ab = a3 * b2 - a2 * b3,\n xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,\n xb = (b3 * c2 - b2 * c3) / ab,\n ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,\n yb = (a2 * c3 - a3 * c2) / ab,\n A = xb * xb + yb * yb - 1,\n B = 2 * (r1 + xa * xb + ya * yb),\n C = xa * xa + ya * ya - r1 * r1,\n r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);\n return {\n x: x1 + xa + xb * r,\n y: y1 + ya + yb * r,\n r: r\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/pack/enclose.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/pack/index.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/pack/index.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _siblings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./siblings */ \"./node_modules/d3-hierarchy/src/pack/siblings.js\");\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../accessors */ \"./node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/d3-hierarchy/src/constant.js\");\n\n\n\n\nfunction defaultRadius(d) {\n return Math.sqrt(d.value);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var radius = null,\n dx = 1,\n dy = 1,\n padding = _constant__WEBPACK_IMPORTED_MODULE_2__[\"constantZero\"];\n\n function pack(root) {\n root.x = dx / 2, root.y = dy / 2;\n if (radius) {\n root.eachBefore(radiusLeaf(radius))\n .eachAfter(packChildren(padding, 0.5))\n .eachBefore(translateChild(1));\n } else {\n root.eachBefore(radiusLeaf(defaultRadius))\n .eachAfter(packChildren(_constant__WEBPACK_IMPORTED_MODULE_2__[\"constantZero\"], 1))\n .eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))\n .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));\n }\n return root;\n }\n\n pack.radius = function(x) {\n return arguments.length ? (radius = Object(_accessors__WEBPACK_IMPORTED_MODULE_1__[\"optional\"])(x), pack) : radius;\n };\n\n pack.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];\n };\n\n pack.padding = function(x) {\n return arguments.length ? (padding = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+x), pack) : padding;\n };\n\n return pack;\n});\n\nfunction radiusLeaf(radius) {\n return function(node) {\n if (!node.children) {\n node.r = Math.max(0, +radius(node) || 0);\n }\n };\n}\n\nfunction packChildren(padding, k) {\n return function(node) {\n if (children = node.children) {\n var children,\n i,\n n = children.length,\n r = padding(node) * k || 0,\n e;\n\n if (r) for (i = 0; i < n; ++i) children[i].r += r;\n e = Object(_siblings__WEBPACK_IMPORTED_MODULE_0__[\"packEnclose\"])(children);\n if (r) for (i = 0; i < n; ++i) children[i].r -= r;\n node.r = e + r;\n }\n };\n}\n\nfunction translateChild(k) {\n return function(node) {\n var parent = node.parent;\n node.r *= k;\n if (parent) {\n node.x = parent.x + k * node.x;\n node.y = parent.y + k * node.y;\n }\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/pack/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/pack/siblings.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/pack/siblings.js ***! + \********************************************************/ +/*! exports provided: packEnclose, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return packEnclose; });\n/* harmony import */ var _enclose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./enclose */ \"./node_modules/d3-hierarchy/src/pack/enclose.js\");\n\n\nfunction place(b, a, c) {\n var dx = b.x - a.x, x, a2,\n dy = b.y - a.y, y, b2,\n d2 = dx * dx + dy * dy;\n if (d2) {\n a2 = a.r + c.r, a2 *= a2;\n b2 = b.r + c.r, b2 *= b2;\n if (a2 > b2) {\n x = (d2 + b2 - a2) / (2 * d2);\n y = Math.sqrt(Math.max(0, b2 / d2 - x * x));\n c.x = b.x - x * dx - y * dy;\n c.y = b.y - x * dy + y * dx;\n } else {\n x = (d2 + a2 - b2) / (2 * d2);\n y = Math.sqrt(Math.max(0, a2 / d2 - x * x));\n c.x = a.x + x * dx - y * dy;\n c.y = a.y + x * dy + y * dx;\n }\n } else {\n c.x = a.x + c.r;\n c.y = a.y;\n }\n}\n\nfunction intersects(a, b) {\n var dr = a.r + b.r - 1e-6, dx = b.x - a.x, dy = b.y - a.y;\n return dr > 0 && dr * dr > dx * dx + dy * dy;\n}\n\nfunction score(node) {\n var a = node._,\n b = node.next._,\n ab = a.r + b.r,\n dx = (a.x * b.r + b.x * a.r) / ab,\n dy = (a.y * b.r + b.y * a.r) / ab;\n return dx * dx + dy * dy;\n}\n\nfunction Node(circle) {\n this._ = circle;\n this.next = null;\n this.previous = null;\n}\n\nfunction packEnclose(circles) {\n if (!(n = circles.length)) return 0;\n\n var a, b, c, n, aa, ca, i, j, k, sj, sk;\n\n // Place the first circle.\n a = circles[0], a.x = 0, a.y = 0;\n if (!(n > 1)) return a.r;\n\n // Place the second circle.\n b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;\n if (!(n > 2)) return a.r + b.r;\n\n // Place the third circle.\n place(b, a, c = circles[2]);\n\n // Initialize the front-chain using the first three circles a, b and c.\n a = new Node(a), b = new Node(b), c = new Node(c);\n a.next = c.previous = b;\n b.next = a.previous = c;\n c.next = b.previous = a;\n\n // Attempt to place each remaining circle…\n pack: for (i = 3; i < n; ++i) {\n place(a._, b._, c = circles[i]), c = new Node(c);\n\n // Find the closest intersecting circle on the front-chain, if any.\n // “Closeness” is determined by linear distance along the front-chain.\n // “Ahead” or “behind” is likewise determined by linear distance.\n j = b.next, k = a.previous, sj = b._.r, sk = a._.r;\n do {\n if (sj <= sk) {\n if (intersects(j._, c._)) {\n b = j, a.next = b, b.previous = a, --i;\n continue pack;\n }\n sj += j._.r, j = j.next;\n } else {\n if (intersects(k._, c._)) {\n a = k, a.next = b, b.previous = a, --i;\n continue pack;\n }\n sk += k._.r, k = k.previous;\n }\n } while (j !== k.next);\n\n // Success! Insert the new circle c between a and b.\n c.previous = a, c.next = b, a.next = b.previous = b = c;\n\n // Compute the new closest circle pair to the centroid.\n aa = score(a);\n while ((c = c.next) !== b) {\n if ((ca = score(c)) < aa) {\n a = c, aa = ca;\n }\n }\n b = a.next;\n }\n\n // Compute the enclosing circle of the front chain.\n a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = Object(_enclose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a);\n\n // Translate the circles to put the enclosing circle around the origin.\n for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;\n\n return c.r;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(circles) {\n packEnclose(circles);\n return circles;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/pack/siblings.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/partition.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/partition.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _treemap_round__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./treemap/round */ \"./node_modules/d3-hierarchy/src/treemap/round.js\");\n/* harmony import */ var _treemap_dice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./treemap/dice */ \"./node_modules/d3-hierarchy/src/treemap/dice.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var dx = 1,\n dy = 1,\n padding = 0,\n round = false;\n\n function partition(root) {\n var n = root.height + 1;\n root.x0 =\n root.y0 = padding;\n root.x1 = dx;\n root.y1 = dy / n;\n root.eachBefore(positionNode(dy, n));\n if (round) root.eachBefore(_treemap_round__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n return root;\n }\n\n function positionNode(dy, n) {\n return function(node) {\n if (node.children) {\n Object(_treemap_dice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);\n }\n var x0 = node.x0,\n y0 = node.y0,\n x1 = node.x1 - padding,\n y1 = node.y1 - padding;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n };\n }\n\n partition.round = function(x) {\n return arguments.length ? (round = !!x, partition) : round;\n };\n\n partition.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];\n };\n\n partition.padding = function(x) {\n return arguments.length ? (padding = +x, partition) : padding;\n };\n\n return partition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/partition.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/stratify.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/stratify.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./accessors */ \"./node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hierarchy/index */ \"./node_modules/d3-hierarchy/src/hierarchy/index.js\");\n\n\n\nvar keyPrefix = \"$\", // Protect against keys like “__proto__”.\n preroot = {depth: -1},\n ambiguous = {};\n\nfunction defaultId(d) {\n return d.id;\n}\n\nfunction defaultParentId(d) {\n return d.parentId;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var id = defaultId,\n parentId = defaultParentId;\n\n function stratify(data) {\n var d,\n i,\n n = data.length,\n root,\n parent,\n node,\n nodes = new Array(n),\n nodeId,\n nodeKey,\n nodeByKey = {};\n\n for (i = 0; i < n; ++i) {\n d = data[i], node = nodes[i] = new _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"Node\"](d);\n if ((nodeId = id(d, i, data)) != null && (nodeId += \"\")) {\n nodeKey = keyPrefix + (node.id = nodeId);\n nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;\n }\n }\n\n for (i = 0; i < n; ++i) {\n node = nodes[i], nodeId = parentId(data[i], i, data);\n if (nodeId == null || !(nodeId += \"\")) {\n if (root) throw new Error(\"multiple roots\");\n root = node;\n } else {\n parent = nodeByKey[keyPrefix + nodeId];\n if (!parent) throw new Error(\"missing: \" + nodeId);\n if (parent === ambiguous) throw new Error(\"ambiguous: \" + nodeId);\n if (parent.children) parent.children.push(node);\n else parent.children = [node];\n node.parent = parent;\n }\n }\n\n if (!root) throw new Error(\"no root\");\n root.parent = preroot;\n root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(_hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"computeHeight\"]);\n root.parent = null;\n if (n > 0) throw new Error(\"cycle\");\n\n return root;\n }\n\n stratify.id = function(x) {\n return arguments.length ? (id = Object(_accessors__WEBPACK_IMPORTED_MODULE_0__[\"required\"])(x), stratify) : id;\n };\n\n stratify.parentId = function(x) {\n return arguments.length ? (parentId = Object(_accessors__WEBPACK_IMPORTED_MODULE_0__[\"required\"])(x), stratify) : parentId;\n };\n\n return stratify;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/stratify.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/tree.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-hierarchy/src/tree.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hierarchy_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hierarchy/index */ \"./node_modules/d3-hierarchy/src/hierarchy/index.js\");\n\n\nfunction defaultSeparation(a, b) {\n return a.parent === b.parent ? 1 : 2;\n}\n\n// function radialSeparation(a, b) {\n// return (a.parent === b.parent ? 1 : 2) / a.depth;\n// }\n\n// This function is used to traverse the left contour of a subtree (or\n// subforest). It returns the successor of v on this contour. This successor is\n// either given by the leftmost child of v or by the thread of v. The function\n// returns null if and only if v is on the highest level of its subtree.\nfunction nextLeft(v) {\n var children = v.children;\n return children ? children[0] : v.t;\n}\n\n// This function works analogously to nextLeft.\nfunction nextRight(v) {\n var children = v.children;\n return children ? children[children.length - 1] : v.t;\n}\n\n// Shifts the current subtree rooted at w+. This is done by increasing\n// prelim(w+) and mod(w+) by shift.\nfunction moveSubtree(wm, wp, shift) {\n var change = shift / (wp.i - wm.i);\n wp.c -= change;\n wp.s += shift;\n wm.c += change;\n wp.z += shift;\n wp.m += shift;\n}\n\n// All other shifts, applied to the smaller subtrees between w- and w+, are\n// performed by this function. To prepare the shifts, we have to adjust\n// change(w+), shift(w+), and change(w-).\nfunction executeShifts(v) {\n var shift = 0,\n change = 0,\n children = v.children,\n i = children.length,\n w;\n while (--i >= 0) {\n w = children[i];\n w.z += shift;\n w.m += shift;\n shift += w.s + (change += w.c);\n }\n}\n\n// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,\n// returns the specified (default) ancestor.\nfunction nextAncestor(vim, v, ancestor) {\n return vim.a.parent === v.parent ? vim.a : ancestor;\n}\n\nfunction TreeNode(node, i) {\n this._ = node;\n this.parent = null;\n this.children = null;\n this.A = null; // default ancestor\n this.a = this; // ancestor\n this.z = 0; // prelim\n this.m = 0; // mod\n this.c = 0; // change\n this.s = 0; // shift\n this.t = null; // thread\n this.i = i; // number\n}\n\nTreeNode.prototype = Object.create(_hierarchy_index__WEBPACK_IMPORTED_MODULE_0__[\"Node\"].prototype);\n\nfunction treeRoot(root) {\n var tree = new TreeNode(root, 0),\n node,\n nodes = [tree],\n child,\n children,\n i,\n n;\n\n while (node = nodes.pop()) {\n if (children = node._.children) {\n node.children = new Array(n = children.length);\n for (i = n - 1; i >= 0; --i) {\n nodes.push(child = node.children[i] = new TreeNode(children[i], i));\n child.parent = node;\n }\n }\n }\n\n (tree.parent = new TreeNode(null, 0)).children = [tree];\n return tree;\n}\n\n// Node-link tree diagram using the Reingold-Tilford \"tidy\" algorithm\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var separation = defaultSeparation,\n dx = 1,\n dy = 1,\n nodeSize = null;\n\n function tree(root) {\n var t = treeRoot(root);\n\n // Compute the layout using Buchheim et al.’s algorithm.\n t.eachAfter(firstWalk), t.parent.m = -t.z;\n t.eachBefore(secondWalk);\n\n // If a fixed node size is specified, scale x and y.\n if (nodeSize) root.eachBefore(sizeNode);\n\n // If a fixed tree size is specified, scale x and y based on the extent.\n // Compute the left-most, right-most, and depth-most nodes for extents.\n else {\n var left = root,\n right = root,\n bottom = root;\n root.eachBefore(function(node) {\n if (node.x < left.x) left = node;\n if (node.x > right.x) right = node;\n if (node.depth > bottom.depth) bottom = node;\n });\n var s = left === right ? 1 : separation(left, right) / 2,\n tx = s - left.x,\n kx = dx / (right.x + s + tx),\n ky = dy / (bottom.depth || 1);\n root.eachBefore(function(node) {\n node.x = (node.x + tx) * kx;\n node.y = node.depth * ky;\n });\n }\n\n return root;\n }\n\n // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is\n // applied recursively to the children of v, as well as the function\n // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the\n // node v is placed to the midpoint of its outermost children.\n function firstWalk(v) {\n var children = v.children,\n siblings = v.parent.children,\n w = v.i ? siblings[v.i - 1] : null;\n if (children) {\n executeShifts(v);\n var midpoint = (children[0].z + children[children.length - 1].z) / 2;\n if (w) {\n v.z = w.z + separation(v._, w._);\n v.m = v.z - midpoint;\n } else {\n v.z = midpoint;\n }\n } else if (w) {\n v.z = w.z + separation(v._, w._);\n }\n v.parent.A = apportion(v, w, v.parent.A || siblings[0]);\n }\n\n // Computes all real x-coordinates by summing up the modifiers recursively.\n function secondWalk(v) {\n v._.x = v.z + v.parent.m;\n v.m += v.parent.m;\n }\n\n // The core of the algorithm. Here, a new subtree is combined with the\n // previous subtrees. Threads are used to traverse the inside and outside\n // contours of the left and right subtree up to the highest common level. The\n // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the\n // superscript o means outside and i means inside, the subscript - means left\n // subtree and + means right subtree. For summing up the modifiers along the\n // contour, we use respective variables si+, si-, so-, and so+. Whenever two\n // nodes of the inside contours conflict, we compute the left one of the\n // greatest uncommon ancestors using the function ANCESTOR and call MOVE\n // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.\n // Finally, we add a new thread (if necessary).\n function apportion(v, w, ancestor) {\n if (w) {\n var vip = v,\n vop = v,\n vim = w,\n vom = vip.parent.children[0],\n sip = vip.m,\n sop = vop.m,\n sim = vim.m,\n som = vom.m,\n shift;\n while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {\n vom = nextLeft(vom);\n vop = nextRight(vop);\n vop.a = v;\n shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);\n if (shift > 0) {\n moveSubtree(nextAncestor(vim, v, ancestor), v, shift);\n sip += shift;\n sop += shift;\n }\n sim += vim.m;\n sip += vip.m;\n som += vom.m;\n sop += vop.m;\n }\n if (vim && !nextRight(vop)) {\n vop.t = vim;\n vop.m += sim - sop;\n }\n if (vip && !nextLeft(vom)) {\n vom.t = vip;\n vom.m += sip - som;\n ancestor = v;\n }\n }\n return ancestor;\n }\n\n function sizeNode(node) {\n node.x *= dx;\n node.y = node.depth * dy;\n }\n\n tree.separation = function(x) {\n return arguments.length ? (separation = x, tree) : separation;\n };\n\n tree.size = function(x) {\n return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);\n };\n\n tree.nodeSize = function(x) {\n return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);\n };\n\n return tree;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/tree.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/binary.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/binary.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n i, n = nodes.length,\n sum, sums = new Array(n + 1);\n\n for (sums[0] = sum = i = 0; i < n; ++i) {\n sums[i + 1] = sum += nodes[i].value;\n }\n\n partition(0, n, parent.value, x0, y0, x1, y1);\n\n function partition(i, j, value, x0, y0, x1, y1) {\n if (i >= j - 1) {\n var node = nodes[i];\n node.x0 = x0, node.y0 = y0;\n node.x1 = x1, node.y1 = y1;\n return;\n }\n\n var valueOffset = sums[i],\n valueTarget = (value / 2) + valueOffset,\n k = i + 1,\n hi = j - 1;\n\n while (k < hi) {\n var mid = k + hi >>> 1;\n if (sums[mid] < valueTarget) k = mid + 1;\n else hi = mid;\n }\n\n if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k;\n\n var valueLeft = sums[k] - valueOffset,\n valueRight = value - valueLeft;\n\n if ((x1 - x0) > (y1 - y0)) {\n var xk = (x0 * valueRight + x1 * valueLeft) / value;\n partition(i, k, valueLeft, x0, y0, xk, y1);\n partition(k, j, valueRight, xk, y0, x1, y1);\n } else {\n var yk = (y0 * valueRight + y1 * valueLeft) / value;\n partition(i, k, valueLeft, x0, y0, x1, yk);\n partition(k, j, valueRight, x0, yk, x1, y1);\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/binary.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/dice.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/dice.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (x1 - x0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.y0 = y0, node.y1 = y1;\n node.x0 = x0, node.x1 = x0 += node.value * k;\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/dice.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/index.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/index.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./round */ \"./node_modules/d3-hierarchy/src/treemap/round.js\");\n/* harmony import */ var _squarify__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./squarify */ \"./node_modules/d3-hierarchy/src/treemap/squarify.js\");\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../accessors */ \"./node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../constant */ \"./node_modules/d3-hierarchy/src/constant.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var tile = _squarify__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n round = false,\n dx = 1,\n dy = 1,\n paddingStack = [0],\n paddingInner = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingTop = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingRight = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingBottom = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingLeft = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"];\n\n function treemap(root) {\n root.x0 =\n root.y0 = 0;\n root.x1 = dx;\n root.y1 = dy;\n root.eachBefore(positionNode);\n paddingStack = [0];\n if (round) root.eachBefore(_round__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n return root;\n }\n\n function positionNode(node) {\n var p = paddingStack[node.depth],\n x0 = node.x0 + p,\n y0 = node.y0 + p,\n x1 = node.x1 - p,\n y1 = node.y1 - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n if (node.children) {\n p = paddingStack[node.depth + 1] = paddingInner(node) / 2;\n x0 += paddingLeft(node) - p;\n y0 += paddingTop(node) - p;\n x1 -= paddingRight(node) - p;\n y1 -= paddingBottom(node) - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n tile(node, x0, y0, x1, y1);\n }\n }\n\n treemap.round = function(x) {\n return arguments.length ? (round = !!x, treemap) : round;\n };\n\n treemap.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];\n };\n\n treemap.tile = function(x) {\n return arguments.length ? (tile = Object(_accessors__WEBPACK_IMPORTED_MODULE_2__[\"required\"])(x), treemap) : tile;\n };\n\n treemap.padding = function(x) {\n return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();\n };\n\n treemap.paddingInner = function(x) {\n return arguments.length ? (paddingInner = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingInner;\n };\n\n treemap.paddingOuter = function(x) {\n return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();\n };\n\n treemap.paddingTop = function(x) {\n return arguments.length ? (paddingTop = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingTop;\n };\n\n treemap.paddingRight = function(x) {\n return arguments.length ? (paddingRight = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingRight;\n };\n\n treemap.paddingBottom = function(x) {\n return arguments.length ? (paddingBottom = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingBottom;\n };\n\n treemap.paddingLeft = function(x) {\n return arguments.length ? (paddingLeft = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingLeft;\n };\n\n return treemap;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/resquarify.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/resquarify.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/d3-hierarchy/src/treemap/slice.js\");\n/* harmony import */ var _squarify__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./squarify */ \"./node_modules/d3-hierarchy/src/treemap/squarify.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(ratio) {\n\n function resquarify(parent, x0, y0, x1, y1) {\n if ((rows = parent._squarify) && (rows.ratio === ratio)) {\n var rows,\n row,\n nodes,\n i,\n j = -1,\n n,\n m = rows.length,\n value = parent.value;\n\n while (++j < m) {\n row = rows[j], nodes = row.children;\n for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;\n if (row.dice) Object(_dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);\n else Object(_slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);\n value -= row.value;\n }\n } else {\n parent._squarify = rows = Object(_squarify__WEBPACK_IMPORTED_MODULE_2__[\"squarifyRatio\"])(ratio, parent, x0, y0, x1, y1);\n rows.ratio = ratio;\n }\n }\n\n resquarify.ratio = function(x) {\n return custom((x = +x) > 1 ? x : 1);\n };\n\n return resquarify;\n})(_squarify__WEBPACK_IMPORTED_MODULE_2__[\"phi\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/resquarify.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/round.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/round.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n node.x0 = Math.round(node.x0);\n node.y0 = Math.round(node.y0);\n node.x1 = Math.round(node.x1);\n node.y1 = Math.round(node.y1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/round.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/slice.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/slice.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (y1 - y0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.x0 = x0, node.x1 = x1;\n node.y0 = y0, node.y1 = y0 += node.value * k;\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/slice.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/sliceDice.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/sliceDice.js ***! + \************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/d3-hierarchy/src/treemap/slice.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n (parent.depth & 1 ? _slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"] : _dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(parent, x0, y0, x1, y1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/sliceDice.js?"); + +/***/ }), + +/***/ "./node_modules/d3-hierarchy/src/treemap/squarify.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-hierarchy/src/treemap/squarify.js ***! + \***********************************************************/ +/*! exports provided: phi, squarifyRatio, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"phi\", function() { return phi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"squarifyRatio\", function() { return squarifyRatio; });\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/d3-hierarchy/src/treemap/slice.js\");\n\n\n\nvar phi = (1 + Math.sqrt(5)) / 2;\n\nfunction squarifyRatio(ratio, parent, x0, y0, x1, y1) {\n var rows = [],\n nodes = parent.children,\n row,\n nodeValue,\n i0 = 0,\n i1 = 0,\n n = nodes.length,\n dx, dy,\n value = parent.value,\n sumValue,\n minValue,\n maxValue,\n newRatio,\n minRatio,\n alpha,\n beta;\n\n while (i0 < n) {\n dx = x1 - x0, dy = y1 - y0;\n\n // Find the next non-empty node.\n do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);\n minValue = maxValue = sumValue;\n alpha = Math.max(dy / dx, dx / dy) / (value * ratio);\n beta = sumValue * sumValue * alpha;\n minRatio = Math.max(maxValue / beta, beta / minValue);\n\n // Keep adding nodes while the aspect ratio maintains or improves.\n for (; i1 < n; ++i1) {\n sumValue += nodeValue = nodes[i1].value;\n if (nodeValue < minValue) minValue = nodeValue;\n if (nodeValue > maxValue) maxValue = nodeValue;\n beta = sumValue * sumValue * alpha;\n newRatio = Math.max(maxValue / beta, beta / minValue);\n if (newRatio > minRatio) { sumValue -= nodeValue; break; }\n minRatio = newRatio;\n }\n\n // Position and record the row orientation.\n rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});\n if (row.dice) Object(_dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);\n else Object(_slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);\n value -= sumValue, i0 = i1;\n }\n\n return rows;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(ratio) {\n\n function squarify(parent, x0, y0, x1, y1) {\n squarifyRatio(ratio, parent, x0, y0, x1, y1);\n }\n\n squarify.ratio = function(x) {\n return custom((x = +x) > 1 ? x : 1);\n };\n\n return squarify;\n})(phi));\n\n\n//# sourceURL=webpack:///./node_modules/d3-hierarchy/src/treemap/squarify.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/array.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/array.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/basis.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/basis.js ***! + \**************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/basisClosed.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-interpolate/src/basisClosed.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/color.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/color.js ***! + \**************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/constant.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-interpolate/src/constant.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/cubehelix.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-interpolate/src/cubehelix.js ***! + \******************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/date.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-interpolate/src/date.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/discrete.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-interpolate/src/discrete.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/discrete.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/hcl.js": +/*!************************************************!*\ + !*** ./node_modules/d3-interpolate/src/hcl.js ***! + \************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/hsl.js": +/*!************************************************!*\ + !*** ./node_modules/d3-interpolate/src/hsl.js ***! + \************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/hue.js": +/*!************************************************!*\ + !*** ./node_modules/d3-interpolate/src/hue.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = Object(_color__WEBPACK_IMPORTED_MODULE_0__[\"hue\"])(+a, +b);\n return function(t) {\n var x = i(t);\n return x - 360 * Math.floor(x / 360);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/hue.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/index.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/index.js ***! + \**************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./date */ \"./node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _discrete__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discrete */ \"./node_modules/d3-interpolate/src/discrete.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return _discrete__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _hue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hue */ \"./node_modules/d3-interpolate/src/hue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return _hue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _number__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ \"./node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _object__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./round */ \"./node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _round__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./string */ \"./node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _string__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _transform_index__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transform/index */ \"./node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./zoom */ \"./node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./rgb */ \"./node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _hsl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./hsl */ \"./node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"hslLong\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./lab */ \"./node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _hcl__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./hcl */ \"./node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"hclLong\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _piecewise__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./piecewise */ \"./node_modules/d3-interpolate/src/piecewise.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return _piecewise__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./quantize */ \"./node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/lab.js": +/*!************************************************!*\ + !*** ./node_modules/d3-interpolate/src/lab.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/number.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-interpolate/src/number.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/object.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-interpolate/src/object.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/piecewise.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-interpolate/src/piecewise.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return piecewise; });\nfunction piecewise(interpolate, values) {\n var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n while (i < n) I[i] = interpolate(v, v = values[++i]);\n return function(t) {\n var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n return I[i](t - i);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/piecewise.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/quantize.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-interpolate/src/quantize.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/rgb.js": +/*!************************************************!*\ + !*** ./node_modules/d3-interpolate/src/rgb.js ***! + \************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/round.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/round.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/string.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-interpolate/src/string.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/transform/decompose.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-interpolate/src/transform/decompose.js ***! + \****************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/transform/index.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-interpolate/src/transform/index.js ***! + \************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/transform/parse.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-interpolate/src/transform/parse.js ***! + \************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/value.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-interpolate/src/value.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/d3-interpolate/src/zoom.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-interpolate/src/zoom.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/d3-path/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-path/src/index.js ***! + \*******************************************/ +/*! exports provided: path */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./path */ \"./node_modules/d3-path/src/path.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return _path__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-path/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-path/src/path.js": +/*!******************************************!*\ + !*** ./node_modules/d3-path/src/path.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (path);\n\n\n//# sourceURL=webpack:///./node_modules/d3-path/src/path.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/area.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-polygon/src/area.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n a,\n b = polygon[n - 1],\n area = 0;\n\n while (++i < n) {\n a = b;\n b = polygon[i];\n area += a[1] * b[0] - a[0] * b[1];\n }\n\n return area / 2;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/centroid.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-polygon/src/centroid.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n x = 0,\n y = 0,\n a,\n b = polygon[n - 1],\n c,\n k = 0;\n\n while (++i < n) {\n a = b;\n b = polygon[i];\n k += c = a[0] * b[1] - b[0] * a[1];\n x += (a[0] + b[0]) * c;\n y += (a[1] + b[1]) * c;\n }\n\n return k *= 3, [x / k, y / k];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/contains.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-polygon/src/contains.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon, point) {\n var n = polygon.length,\n p = polygon[n - 1],\n x = point[0], y = point[1],\n x0 = p[0], y0 = p[1],\n x1, y1,\n inside = false;\n\n for (var i = 0; i < n; ++i) {\n p = polygon[i], x1 = p[0], y1 = p[1];\n if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;\n x0 = x1, y0 = y1;\n }\n\n return inside;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/contains.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/cross.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-polygon/src/cross.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of\n// the 3D cross product in a quadrant I Cartesian coordinate system (+x is\n// right, +y is up). Returns a positive value if ABC is counter-clockwise,\n// negative if clockwise, and zero if the points are collinear.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c) {\n return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/hull.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-polygon/src/hull.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cross */ \"./node_modules/d3-polygon/src/cross.js\");\n\n\nfunction lexicographicOrder(a, b) {\n return a[0] - b[0] || a[1] - b[1];\n}\n\n// Computes the upper convex hull per the monotone chain algorithm.\n// Assumes points.length >= 3, is sorted by x, unique in y.\n// Returns an array of indices into points in left-to-right order.\nfunction computeUpperHullIndexes(points) {\n var n = points.length,\n indexes = [0, 1],\n size = 2;\n\n for (var i = 2; i < n; ++i) {\n while (size > 1 && Object(_cross__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;\n indexes[size++] = i;\n }\n\n return indexes.slice(0, size); // remove popped points\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(points) {\n if ((n = points.length) < 3) return null;\n\n var i,\n n,\n sortedPoints = new Array(n),\n flippedPoints = new Array(n);\n\n for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];\n sortedPoints.sort(lexicographicOrder);\n for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];\n\n var upperIndexes = computeUpperHullIndexes(sortedPoints),\n lowerIndexes = computeUpperHullIndexes(flippedPoints);\n\n // Construct the hull polygon, removing possible duplicate endpoints.\n var skipLeft = lowerIndexes[0] === upperIndexes[0],\n skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],\n hull = [];\n\n // Add upper hull in right-to-l order.\n // Then add lower hull in left-to-right order.\n for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);\n for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);\n\n return hull;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/hull.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/index.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-polygon/src/index.js ***! + \**********************************************/ +/*! exports provided: polygonArea, polygonCentroid, polygonHull, polygonContains, polygonLength */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-polygon/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonArea\", function() { return _area__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _centroid__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./centroid */ \"./node_modules/d3-polygon/src/centroid.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonCentroid\", function() { return _centroid__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _hull__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./hull */ \"./node_modules/d3-polygon/src/hull.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonHull\", function() { return _hull__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _contains__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./contains */ \"./node_modules/d3-polygon/src/contains.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonContains\", function() { return _contains__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _length__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./length */ \"./node_modules/d3-polygon/src/length.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonLength\", function() { return _length__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-polygon/src/length.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-polygon/src/length.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n b = polygon[n - 1],\n xa,\n ya,\n xb = b[0],\n yb = b[1],\n perimeter = 0;\n\n while (++i < n) {\n xa = xb;\n ya = yb;\n b = polygon[i];\n xb = b[0];\n yb = b[1];\n xa -= xb;\n ya -= yb;\n perimeter += Math.sqrt(xa * xa + ya * ya);\n }\n\n return perimeter;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-polygon/src/length.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/add.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-quadtree/src/add.js ***! + \*********************************************/ +/*! exports provided: default, addAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"addAll\", function() { return addAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n var x = +this._x.call(null, d),\n y = +this._y.call(null, d);\n return add(this.cover(x, y), x, y, d);\n});\n\nfunction add(tree, x, y, d) {\n if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points\n\n var parent,\n node = tree._root,\n leaf = {data: d},\n x0 = tree._x0,\n y0 = tree._y0,\n x1 = tree._x1,\n y1 = tree._y1,\n xm,\n ym,\n xp,\n yp,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return tree._root = leaf, tree;\n\n // Find the existing leaf for the new point, or add it.\n while (node.length) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;\n }\n\n // Is the new point is exactly coincident with the existing point?\n xp = +tree._x.call(null, node.data);\n yp = +tree._y.call(null, node.data);\n if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;\n\n // Otherwise, split the leaf node until the old and new point are separated.\n do {\n parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));\n return parent[j] = node, parent[i] = leaf, tree;\n}\n\nfunction addAll(data) {\n var d, i, n = data.length,\n x,\n y,\n xz = new Array(n),\n yz = new Array(n),\n x0 = Infinity,\n y0 = Infinity,\n x1 = -Infinity,\n y1 = -Infinity;\n\n // Compute the points and their extent.\n for (i = 0; i < n; ++i) {\n if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;\n xz[i] = x;\n yz[i] = y;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n\n // If there were no (valid) points, inherit the existing extent.\n if (x1 < x0) x0 = this._x0, x1 = this._x1;\n if (y1 < y0) y0 = this._y0, y1 = this._y1;\n\n // Expand the tree to cover the new points.\n this.cover(x0, y0).cover(x1, y1);\n\n // Add the new points.\n for (i = 0; i < n; ++i) {\n add(this, xz[i], yz[i], data[i]);\n }\n\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/add.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/cover.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-quadtree/src/cover.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points\n\n var x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1;\n\n // If the quadtree has no extent, initialize them.\n // Integer extent are necessary so that if we later double the extent,\n // the existing quadrant boundaries don’t change due to floating point error!\n if (isNaN(x0)) {\n x1 = (x0 = Math.floor(x)) + 1;\n y1 = (y0 = Math.floor(y)) + 1;\n }\n\n // Otherwise, double repeatedly to cover.\n else if (x0 > x || x > x1 || y0 > y || y > y1) {\n var z = x1 - x0,\n node = this._root,\n parent,\n i;\n\n switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {\n case 0: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);\n break;\n }\n case 1: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);\n break;\n }\n case 2: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);\n break;\n }\n case 3: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);\n break;\n }\n }\n\n if (this._root && this._root.length) this._root = node;\n }\n\n // If the quadtree covers the point already, just return.\n else return this;\n\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/cover.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/data.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-quadtree/src/data.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var data = [];\n this.visit(function(node) {\n if (!node.length) do data.push(node.data); while (node = node.next)\n });\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/data.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/extent.js": +/*!************************************************!*\ + !*** ./node_modules/d3-quadtree/src/extent.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length\n ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])\n : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/find.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-quadtree/src/find.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y, radius) {\n var data,\n x0 = this._x0,\n y0 = this._y0,\n x1,\n y1,\n x2,\n y2,\n x3 = this._x1,\n y3 = this._y1,\n quads = [],\n node = this._root,\n q,\n i;\n\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, x0, y0, x3, y3));\n if (radius == null) radius = Infinity;\n else {\n x0 = x - radius, y0 = y - radius;\n x3 = x + radius, y3 = y + radius;\n radius *= radius;\n }\n\n while (q = quads.pop()) {\n\n // Stop searching if this quadrant can’t contain a closer node.\n if (!(node = q.node)\n || (x1 = q.x0) > x3\n || (y1 = q.y0) > y3\n || (x2 = q.x1) < x0\n || (y2 = q.y1) < y0) continue;\n\n // Bisect the current quadrant.\n if (node.length) {\n var xm = (x1 + x2) / 2,\n ym = (y1 + y2) / 2;\n\n quads.push(\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[3], xm, ym, x2, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[2], x1, ym, xm, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[1], xm, y1, x2, ym),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[0], x1, y1, xm, ym)\n );\n\n // Visit the closest quadrant first.\n if (i = (y >= ym) << 1 | (x >= xm)) {\n q = quads[quads.length - 1];\n quads[quads.length - 1] = quads[quads.length - 1 - i];\n quads[quads.length - 1 - i] = q;\n }\n }\n\n // Visit this point. (Visiting coincident points isn’t necessary!)\n else {\n var dx = x - +this._x.call(null, node.data),\n dy = y - +this._y.call(null, node.data),\n d2 = dx * dx + dy * dy;\n if (d2 < radius) {\n var d = Math.sqrt(radius = d2);\n x0 = x - d, y0 = y - d;\n x3 = x + d, y3 = y + d;\n data = node.data;\n }\n }\n }\n\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/find.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/index.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-quadtree/src/index.js ***! + \***********************************************/ +/*! exports provided: quadtree */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quadtree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quadtree */ \"./node_modules/d3-quadtree/src/quadtree.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quadtree\", function() { return _quadtree__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/quad.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-quadtree/src/quad.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, x0, y0, x1, y1) {\n this.node = node;\n this.x0 = x0;\n this.y0 = y0;\n this.x1 = x1;\n this.y1 = y1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/quadtree.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-quadtree/src/quadtree.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quadtree; });\n/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add */ \"./node_modules/d3-quadtree/src/add.js\");\n/* harmony import */ var _cover__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cover */ \"./node_modules/d3-quadtree/src/cover.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./data */ \"./node_modules/d3-quadtree/src/data.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/d3-quadtree/src/extent.js\");\n/* harmony import */ var _find__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./find */ \"./node_modules/d3-quadtree/src/find.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./remove */ \"./node_modules/d3-quadtree/src/remove.js\");\n/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./root */ \"./node_modules/d3-quadtree/src/root.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./size */ \"./node_modules/d3-quadtree/src/size.js\");\n/* harmony import */ var _visit__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./visit */ \"./node_modules/d3-quadtree/src/visit.js\");\n/* harmony import */ var _visitAfter__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./visitAfter */ \"./node_modules/d3-quadtree/src/visitAfter.js\");\n/* harmony import */ var _x__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./x */ \"./node_modules/d3-quadtree/src/x.js\");\n/* harmony import */ var _y__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./y */ \"./node_modules/d3-quadtree/src/y.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\nfunction quadtree(nodes, x, y) {\n var tree = new Quadtree(x == null ? _x__WEBPACK_IMPORTED_MODULE_10__[\"defaultX\"] : x, y == null ? _y__WEBPACK_IMPORTED_MODULE_11__[\"defaultY\"] : y, NaN, NaN, NaN, NaN);\n return nodes == null ? tree : tree.addAll(nodes);\n}\n\nfunction Quadtree(x, y, x0, y0, x1, y1) {\n this._x = x;\n this._y = y;\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n this._root = undefined;\n}\n\nfunction leaf_copy(leaf) {\n var copy = {data: leaf.data}, next = copy;\n while (leaf = leaf.next) next = next.next = {data: leaf.data};\n return copy;\n}\n\nvar treeProto = quadtree.prototype = Quadtree.prototype;\n\ntreeProto.copy = function() {\n var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),\n node = this._root,\n nodes,\n child;\n\n if (!node) return copy;\n\n if (!node.length) return copy._root = leaf_copy(node), copy;\n\n nodes = [{source: node, target: copy._root = new Array(4)}];\n while (node = nodes.pop()) {\n for (var i = 0; i < 4; ++i) {\n if (child = node.source[i]) {\n if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});\n else node.target[i] = leaf_copy(child);\n }\n }\n }\n\n return copy;\n};\n\ntreeProto.add = _add__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\ntreeProto.addAll = _add__WEBPACK_IMPORTED_MODULE_0__[\"addAll\"];\ntreeProto.cover = _cover__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\ntreeProto.data = _data__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\ntreeProto.extent = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\ntreeProto.find = _find__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\ntreeProto.remove = _remove__WEBPACK_IMPORTED_MODULE_5__[\"default\"];\ntreeProto.removeAll = _remove__WEBPACK_IMPORTED_MODULE_5__[\"removeAll\"];\ntreeProto.root = _root__WEBPACK_IMPORTED_MODULE_6__[\"default\"];\ntreeProto.size = _size__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\ntreeProto.visit = _visit__WEBPACK_IMPORTED_MODULE_8__[\"default\"];\ntreeProto.visitAfter = _visitAfter__WEBPACK_IMPORTED_MODULE_9__[\"default\"];\ntreeProto.x = _x__WEBPACK_IMPORTED_MODULE_10__[\"default\"];\ntreeProto.y = _y__WEBPACK_IMPORTED_MODULE_11__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/quadtree.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/remove.js": +/*!************************************************!*\ + !*** ./node_modules/d3-quadtree/src/remove.js ***! + \************************************************/ +/*! exports provided: default, removeAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeAll\", function() { return removeAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points\n\n var parent,\n node = this._root,\n retainer,\n previous,\n next,\n x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1,\n x,\n y,\n xm,\n ym,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return this;\n\n // Find the leaf node for the point.\n // While descending, also retain the deepest parent with a non-removed sibling.\n if (node.length) while (true) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (!(parent = node, node = node[i = bottom << 1 | right])) return this;\n if (!node.length) break;\n if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;\n }\n\n // Find the point to remove.\n while (node.data !== d) if (!(previous = node, node = node.next)) return this;\n if (next = node.next) delete node.next;\n\n // If there are multiple coincident points, remove just the point.\n if (previous) return (next ? previous.next = next : delete previous.next), this;\n\n // If this is the root point, remove it.\n if (!parent) return this._root = next, this;\n\n // Remove this leaf.\n next ? parent[i] = next : delete parent[i];\n\n // If the parent now contains exactly one leaf, collapse superfluous parents.\n if ((node = parent[0] || parent[1] || parent[2] || parent[3])\n && node === (parent[3] || parent[2] || parent[1] || parent[0])\n && !node.length) {\n if (retainer) retainer[j] = node;\n else this._root = node;\n }\n\n return this;\n});\n\nfunction removeAll(data) {\n for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/remove.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/root.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-quadtree/src/root.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this._root;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/root.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/size.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-quadtree/src/size.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.visit(function(node) {\n if (!node.length) do ++size; while (node = node.next)\n });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/size.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/visit.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-quadtree/src/visit.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], q, node = this._root, child, x0, y0, x1, y1;\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {\n var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n }\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/visit.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/visitAfter.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-quadtree/src/visitAfter.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], next = [], q;\n if (this._root) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this._root, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n var node = q.node;\n if (node.length) {\n var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n }\n next.push(q);\n }\n while (q = next.pop()) {\n callback(q.node, q.x0, q.y0, q.x1, q.y1);\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/visitAfter.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/x.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-quadtree/src/x.js ***! + \*******************************************/ +/*! exports provided: defaultX, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultX\", function() { return defaultX; });\nfunction defaultX(d) {\n return d[0];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._x = _, this) : this._x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/x.js?"); + +/***/ }), + +/***/ "./node_modules/d3-quadtree/src/y.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-quadtree/src/y.js ***! + \*******************************************/ +/*! exports provided: defaultY, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultY\", function() { return defaultY; });\nfunction defaultY(d) {\n return d[1];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._y = _, this) : this._y;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-quadtree/src/y.js?"); + +/***/ }), + +/***/ "./node_modules/d3-queue/index.js": +/*!****************************************!*\ + !*** ./node_modules/d3-queue/index.js ***! + \****************************************/ +/*! exports provided: queue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_queue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/queue */ \"./node_modules/d3-queue/src/queue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"queue\", function() { return _src_queue__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-queue/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-queue/src/array.js": +/*!********************************************!*\ + !*** ./node_modules/d3-queue/src/array.js ***! + \********************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = [].slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-queue/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-queue/src/queue.js": +/*!********************************************!*\ + !*** ./node_modules/d3-queue/src/queue.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return queue; });\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-queue/src/array.js\");\n\n\nvar noabort = {};\n\nfunction Queue(size) {\n this._size = size;\n this._call =\n this._error = null;\n this._tasks = [];\n this._data = [];\n this._waiting =\n this._active =\n this._ended =\n this._start = 0; // inside a synchronous task callback?\n}\n\nQueue.prototype = queue.prototype = {\n constructor: Queue,\n defer: function(callback) {\n if (typeof callback !== \"function\") throw new Error(\"invalid callback\");\n if (this._call) throw new Error(\"defer after await\");\n if (this._error != null) return this;\n var t = _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(arguments, 1);\n t.push(callback);\n ++this._waiting, this._tasks.push(t);\n poke(this);\n return this;\n },\n abort: function() {\n if (this._error == null) abort(this, new Error(\"abort\"));\n return this;\n },\n await: function(callback) {\n if (typeof callback !== \"function\") throw new Error(\"invalid callback\");\n if (this._call) throw new Error(\"multiple await\");\n this._call = function(error, results) { callback.apply(null, [error].concat(results)); };\n maybeNotify(this);\n return this;\n },\n awaitAll: function(callback) {\n if (typeof callback !== \"function\") throw new Error(\"invalid callback\");\n if (this._call) throw new Error(\"multiple await\");\n this._call = callback;\n maybeNotify(this);\n return this;\n }\n};\n\nfunction poke(q) {\n if (!q._start) {\n try { start(q); } // let the current task complete\n catch (e) {\n if (q._tasks[q._ended + q._active - 1]) abort(q, e); // task errored synchronously\n else if (!q._data) throw e; // await callback errored synchronously\n }\n }\n}\n\nfunction start(q) {\n while (q._start = q._waiting && q._active < q._size) {\n var i = q._ended + q._active,\n t = q._tasks[i],\n j = t.length - 1,\n c = t[j];\n t[j] = end(q, i);\n --q._waiting, ++q._active;\n t = c.apply(null, t);\n if (!q._tasks[i]) continue; // task finished synchronously\n q._tasks[i] = t || noabort;\n }\n}\n\nfunction end(q, i) {\n return function(e, r) {\n if (!q._tasks[i]) return; // ignore multiple callbacks\n --q._active, ++q._ended;\n q._tasks[i] = null;\n if (q._error != null) return; // ignore secondary errors\n if (e != null) {\n abort(q, e);\n } else {\n q._data[i] = r;\n if (q._waiting) poke(q);\n else maybeNotify(q);\n }\n };\n}\n\nfunction abort(q, e) {\n var i = q._tasks.length, t;\n q._error = e; // ignore active callbacks\n q._data = undefined; // allow gc\n q._waiting = NaN; // prevent starting\n\n while (--i >= 0) {\n if (t = q._tasks[i]) {\n q._tasks[i] = null;\n if (t.abort) {\n try { t.abort(); }\n catch (e) { /* ignore */ }\n }\n }\n }\n\n q._active = NaN; // allow notification\n maybeNotify(q);\n}\n\nfunction maybeNotify(q) {\n if (!q._active && q._call) {\n var d = q._data;\n q._data = undefined; // allow gc\n q._call(q._error, d);\n }\n}\n\nfunction queue(concurrency) {\n if (concurrency == null) concurrency = Infinity;\n else if (!((concurrency = +concurrency) >= 1)) throw new Error(\"invalid concurrency\");\n return new Queue(concurrency);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-queue/src/queue.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/bates.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-random/src/bates.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n/* harmony import */ var _irwinHall__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./irwinHall */ \"./node_modules/d3-random/src/irwinHall.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomBates(source) {\n function randomBates(n) {\n var randomIrwinHall = _irwinHall__WEBPACK_IMPORTED_MODULE_1__[\"default\"].source(source)(n);\n return function() {\n return randomIrwinHall() / n;\n };\n }\n\n randomBates.source = sourceRandomBates;\n\n return randomBates;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/bates.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/defaultSource.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-random/src/defaultSource.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Math.random();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/defaultSource.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/exponential.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-random/src/exponential.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomExponential(source) {\n function randomExponential(lambda) {\n return function() {\n return -Math.log(1 - source()) / lambda;\n };\n }\n\n randomExponential.source = sourceRandomExponential;\n\n return randomExponential;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/exponential.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/index.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-random/src/index.js ***! + \*********************************************/ +/*! exports provided: randomUniform, randomNormal, randomLogNormal, randomBates, randomIrwinHall, randomExponential */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _uniform__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./uniform */ \"./node_modules/d3-random/src/uniform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomUniform\", function() { return _uniform__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _normal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./normal */ \"./node_modules/d3-random/src/normal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomNormal\", function() { return _normal__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _logNormal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./logNormal */ \"./node_modules/d3-random/src/logNormal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomLogNormal\", function() { return _logNormal__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _bates__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./bates */ \"./node_modules/d3-random/src/bates.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomBates\", function() { return _bates__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _irwinHall__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./irwinHall */ \"./node_modules/d3-random/src/irwinHall.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomIrwinHall\", function() { return _irwinHall__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _exponential__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exponential */ \"./node_modules/d3-random/src/exponential.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomExponential\", function() { return _exponential__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/irwinHall.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-random/src/irwinHall.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomIrwinHall(source) {\n function randomIrwinHall(n) {\n return function() {\n for (var sum = 0, i = 0; i < n; ++i) sum += source();\n return sum;\n };\n }\n\n randomIrwinHall.source = sourceRandomIrwinHall;\n\n return randomIrwinHall;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/irwinHall.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/logNormal.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-random/src/logNormal.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n/* harmony import */ var _normal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./normal */ \"./node_modules/d3-random/src/normal.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomLogNormal(source) {\n function randomLogNormal() {\n var randomNormal = _normal__WEBPACK_IMPORTED_MODULE_1__[\"default\"].source(source).apply(this, arguments);\n return function() {\n return Math.exp(randomNormal());\n };\n }\n\n randomLogNormal.source = sourceRandomLogNormal;\n\n return randomLogNormal;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/logNormal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/normal.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-random/src/normal.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomNormal(source) {\n function randomNormal(mu, sigma) {\n var x, r;\n mu = mu == null ? 0 : +mu;\n sigma = sigma == null ? 1 : +sigma;\n return function() {\n var y;\n\n // If available, use the second previously-generated uniform random.\n if (x != null) y = x, x = null;\n\n // Otherwise, generate a new x and y.\n else do {\n x = source() * 2 - 1;\n y = source() * 2 - 1;\n r = x * x + y * y;\n } while (!r || r > 1);\n\n return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);\n };\n }\n\n randomNormal.source = sourceRandomNormal;\n\n return randomNormal;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/normal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-random/src/uniform.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-random/src/uniform.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomUniform(source) {\n function randomUniform(min, max) {\n min = min == null ? 0 : +min;\n max = max == null ? 1 : +max;\n if (arguments.length === 1) max = min, min = 0;\n else max -= min;\n return function() {\n return source() * max + min;\n };\n }\n\n randomUniform.source = sourceRandomUniform;\n\n return randomUniform;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-random/src/uniform.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/index.js": +/*!******************************************!*\ + !*** ./node_modules/d3-request/index.js ***! + \******************************************/ +/*! exports provided: request, html, json, text, xml, csv, tsv */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_request__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/request */ \"./node_modules/d3-request/src/request.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"request\", function() { return _src_request__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_html__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/html */ \"./node_modules/d3-request/src/html.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"html\", function() { return _src_html__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_json__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/json */ \"./node_modules/d3-request/src/json.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"json\", function() { return _src_json__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_text__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/text */ \"./node_modules/d3-request/src/text.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"text\", function() { return _src_text__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_xml__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/xml */ \"./node_modules/d3-request/src/xml.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"xml\", function() { return _src_xml__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_csv__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/csv */ \"./node_modules/d3-request/src/csv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csv\", function() { return _src_csv__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_tsv__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/tsv */ \"./node_modules/d3-request/src/tsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsv\", function() { return _src_tsv__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/csv.js": +/*!********************************************!*\ + !*** ./node_modules/d3-request/src/csv.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dsv */ \"./node_modules/d3-dsv/src/index.js\");\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-request/src/dsv.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_dsv__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(\"text/csv\", d3_dsv__WEBPACK_IMPORTED_MODULE_0__[\"csvParse\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/csv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/dsv.js": +/*!********************************************!*\ + !*** ./node_modules/d3-request/src/dsv.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _request__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./request */ \"./node_modules/d3-request/src/request.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(defaultMimeType, parse) {\n return function(url, row, callback) {\n if (arguments.length < 3) callback = row, row = null;\n var r = Object(_request__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(url).mimeType(defaultMimeType);\n r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; };\n r.row(row);\n return callback ? r.get(callback) : r;\n };\n});\n\nfunction responseOf(parse, row) {\n return function(request) {\n return parse(request.responseText, row);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/dsv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/html.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-request/src/html.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _type__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./type */ \"./node_modules/d3-request/src/type.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_type__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"text/html\", function(xhr) {\n return document.createRange().createContextualFragment(xhr.responseText);\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/html.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/json.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-request/src/json.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _type__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./type */ \"./node_modules/d3-request/src/type.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_type__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"application/json\", function(xhr) {\n return JSON.parse(xhr.responseText);\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/json.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/request.js": +/*!************************************************!*\ + !*** ./node_modules/d3-request/src/request.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-collection */ \"./node_modules/d3-collection/src/index.js\");\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(url, callback) {\n var request,\n event = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_1__[\"dispatch\"])(\"beforesend\", \"progress\", \"load\", \"error\"),\n mimeType,\n headers = Object(d3_collection__WEBPACK_IMPORTED_MODULE_0__[\"map\"])(),\n xhr = new XMLHttpRequest,\n user = null,\n password = null,\n response,\n responseType,\n timeout = 0;\n\n // If IE does not support CORS, use XDomainRequest.\n if (typeof XDomainRequest !== \"undefined\"\n && !(\"withCredentials\" in xhr)\n && /^(http(s)?:)?\\/\\//.test(url)) xhr = new XDomainRequest;\n\n \"onload\" in xhr\n ? xhr.onload = xhr.onerror = xhr.ontimeout = respond\n : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); };\n\n function respond(o) {\n var status = xhr.status, result;\n if (!status && hasResponse(xhr)\n || status >= 200 && status < 300\n || status === 304) {\n if (response) {\n try {\n result = response.call(request, xhr);\n } catch (e) {\n event.call(\"error\", request, e);\n return;\n }\n } else {\n result = xhr;\n }\n event.call(\"load\", request, result);\n } else {\n event.call(\"error\", request, o);\n }\n }\n\n xhr.onprogress = function(e) {\n event.call(\"progress\", request, e);\n };\n\n request = {\n header: function(name, value) {\n name = (name + \"\").toLowerCase();\n if (arguments.length < 2) return headers.get(name);\n if (value == null) headers.remove(name);\n else headers.set(name, value + \"\");\n return request;\n },\n\n // If mimeType is non-null and no Accept header is set, a default is used.\n mimeType: function(value) {\n if (!arguments.length) return mimeType;\n mimeType = value == null ? null : value + \"\";\n return request;\n },\n\n // Specifies what type the response value should take;\n // for instance, arraybuffer, blob, document, or text.\n responseType: function(value) {\n if (!arguments.length) return responseType;\n responseType = value;\n return request;\n },\n\n timeout: function(value) {\n if (!arguments.length) return timeout;\n timeout = +value;\n return request;\n },\n\n user: function(value) {\n return arguments.length < 1 ? user : (user = value == null ? null : value + \"\", request);\n },\n\n password: function(value) {\n return arguments.length < 1 ? password : (password = value == null ? null : value + \"\", request);\n },\n\n // Specify how to convert the response content to a specific type;\n // changes the callback value on \"load\" events.\n response: function(value) {\n response = value;\n return request;\n },\n\n // Alias for send(\"GET\", …).\n get: function(data, callback) {\n return request.send(\"GET\", data, callback);\n },\n\n // Alias for send(\"POST\", …).\n post: function(data, callback) {\n return request.send(\"POST\", data, callback);\n },\n\n // If callback is non-null, it will be used for error and load events.\n send: function(method, data, callback) {\n xhr.open(method, url, true, user, password);\n if (mimeType != null && !headers.has(\"accept\")) headers.set(\"accept\", mimeType + \",*/*\");\n if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); });\n if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType);\n if (responseType != null) xhr.responseType = responseType;\n if (timeout > 0) xhr.timeout = timeout;\n if (callback == null && typeof data === \"function\") callback = data, data = null;\n if (callback != null && callback.length === 1) callback = fixCallback(callback);\n if (callback != null) request.on(\"error\", callback).on(\"load\", function(xhr) { callback(null, xhr); });\n event.call(\"beforesend\", request, xhr);\n xhr.send(data == null ? null : data);\n return request;\n },\n\n abort: function() {\n xhr.abort();\n return request;\n },\n\n on: function() {\n var value = event.on.apply(event, arguments);\n return value === event ? request : value;\n }\n };\n\n if (callback != null) {\n if (typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n return request.get(callback);\n }\n\n return request;\n});\n\nfunction fixCallback(callback) {\n return function(error, xhr) {\n callback(error == null ? xhr : null);\n };\n}\n\nfunction hasResponse(xhr) {\n var type = xhr.responseType;\n return type && type !== \"text\"\n ? xhr.response // null on error\n : xhr.responseText; // \"\" on error\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/request.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/text.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-request/src/text.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _type__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./type */ \"./node_modules/d3-request/src/type.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_type__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"text/plain\", function(xhr) {\n return xhr.responseText;\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/text.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/tsv.js": +/*!********************************************!*\ + !*** ./node_modules/d3-request/src/tsv.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dsv */ \"./node_modules/d3-dsv/src/index.js\");\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./dsv */ \"./node_modules/d3-request/src/dsv.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_dsv__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(\"text/tab-separated-values\", d3_dsv__WEBPACK_IMPORTED_MODULE_0__[\"tsvParse\"]));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/tsv.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/type.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-request/src/type.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _request__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./request */ \"./node_modules/d3-request/src/request.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(defaultMimeType, response) {\n return function(url, callback) {\n var r = Object(_request__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(url).mimeType(defaultMimeType).response(response);\n if (callback != null) {\n if (typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n return r.get(callback);\n }\n return r;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/type.js?"); + +/***/ }), + +/***/ "./node_modules/d3-request/src/xml.js": +/*!********************************************!*\ + !*** ./node_modules/d3-request/src/xml.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _type__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./type */ \"./node_modules/d3-request/src/type.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_type__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"application/xml\", function(xhr) {\n var xml = xhr.responseXML;\n if (!xml) throw new Error(\"parse error\");\n return xml;\n}));\n\n\n//# sourceURL=webpack:///./node_modules/d3-request/src/xml.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Accent.js": +/*!*******************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Accent.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Accent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Dark2.js": +/*!******************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Dark2.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Dark2.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Paired.js": +/*!*******************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Paired.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Paired.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Pastel1.js": +/*!********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Pastel1.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Pastel1.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Pastel2.js": +/*!********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Pastel2.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Pastel2.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Set1.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Set1.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Set1.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Set2.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Set2.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Set2.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/Set3.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/Set3.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/Set3.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/categorical/category10.js": +/*!***********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/categorical/category10.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/categorical/category10.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/colors.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/colors.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(specifier) {\n var n = specifier.length / 6 | 0, colors = new Array(n), i = 0;\n while (i < n) colors[i] = \"#\" + specifier.slice(i * 6, ++i * 6);\n return colors;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/colors.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/BrBG.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/BrBG.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"d8b365f5f5f55ab4ac\",\n \"a6611adfc27d80cdc1018571\",\n \"a6611adfc27df5f5f580cdc1018571\",\n \"8c510ad8b365f6e8c3c7eae55ab4ac01665e\",\n \"8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e\",\n \"8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e\",\n \"8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e\",\n \"5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30\",\n \"5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/BrBG.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/PRGn.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/PRGn.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"af8dc3f7f7f77fbf7b\",\n \"7b3294c2a5cfa6dba0008837\",\n \"7b3294c2a5cff7f7f7a6dba0008837\",\n \"762a83af8dc3e7d4e8d9f0d37fbf7b1b7837\",\n \"762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837\",\n \"762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837\",\n \"762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837\",\n \"40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b\",\n \"40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/PRGn.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/PiYG.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/PiYG.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e9a3c9f7f7f7a1d76a\",\n \"d01c8bf1b6dab8e1864dac26\",\n \"d01c8bf1b6daf7f7f7b8e1864dac26\",\n \"c51b7de9a3c9fde0efe6f5d0a1d76a4d9221\",\n \"c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221\",\n \"c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221\",\n \"c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221\",\n \"8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419\",\n \"8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/PiYG.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/PuOr.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/PuOr.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"998ec3f7f7f7f1a340\",\n \"5e3c99b2abd2fdb863e66101\",\n \"5e3c99b2abd2f7f7f7fdb863e66101\",\n \"542788998ec3d8daebfee0b6f1a340b35806\",\n \"542788998ec3d8daebf7f7f7fee0b6f1a340b35806\",\n \"5427888073acb2abd2d8daebfee0b6fdb863e08214b35806\",\n \"5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806\",\n \"2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08\",\n \"2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/PuOr.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/RdBu.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/RdBu.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"ef8a62f7f7f767a9cf\",\n \"ca0020f4a58292c5de0571b0\",\n \"ca0020f4a582f7f7f792c5de0571b0\",\n \"b2182bef8a62fddbc7d1e5f067a9cf2166ac\",\n \"b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac\",\n \"b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac\",\n \"b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac\",\n \"67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061\",\n \"67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/RdBu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/RdGy.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/RdGy.js ***! + \***************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"ef8a62ffffff999999\",\n \"ca0020f4a582bababa404040\",\n \"ca0020f4a582ffffffbababa404040\",\n \"b2182bef8a62fddbc7e0e0e09999994d4d4d\",\n \"b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d\",\n \"b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d\",\n \"b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d\",\n \"67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a\",\n \"67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/RdGy.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/RdYlBu.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/RdYlBu.js ***! + \*****************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fc8d59ffffbf91bfdb\",\n \"d7191cfdae61abd9e92c7bb6\",\n \"d7191cfdae61ffffbfabd9e92c7bb6\",\n \"d73027fc8d59fee090e0f3f891bfdb4575b4\",\n \"d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4\",\n \"d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4\",\n \"d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4\",\n \"a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695\",\n \"a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/RdYlBu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/RdYlGn.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/RdYlGn.js ***! + \*****************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fc8d59ffffbf91cf60\",\n \"d7191cfdae61a6d96a1a9641\",\n \"d7191cfdae61ffffbfa6d96a1a9641\",\n \"d73027fc8d59fee08bd9ef8b91cf601a9850\",\n \"d73027fc8d59fee08bffffbfd9ef8b91cf601a9850\",\n \"d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850\",\n \"d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850\",\n \"a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837\",\n \"a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/RdYlGn.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/diverging/Spectral.js": +/*!*******************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/diverging/Spectral.js ***! + \*******************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fc8d59ffffbf99d594\",\n \"d7191cfdae61abdda42b83ba\",\n \"d7191cfdae61ffffbfabdda42b83ba\",\n \"d53e4ffc8d59fee08be6f59899d5943288bd\",\n \"d53e4ffc8d59fee08bffffbfe6f59899d5943288bd\",\n \"d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd\",\n \"d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd\",\n \"9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2\",\n \"9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/diverging/Spectral.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/index.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/index.js ***! + \******************************************************/ +/*! exports provided: schemeCategory10, schemeAccent, schemeDark2, schemePaired, schemePastel1, schemePastel2, schemeSet1, schemeSet2, schemeSet3, interpolateBrBG, schemeBrBG, interpolatePRGn, schemePRGn, interpolatePiYG, schemePiYG, interpolatePuOr, schemePuOr, interpolateRdBu, schemeRdBu, interpolateRdGy, schemeRdGy, interpolateRdYlBu, schemeRdYlBu, interpolateRdYlGn, schemeRdYlGn, interpolateSpectral, schemeSpectral, interpolateBuGn, schemeBuGn, interpolateBuPu, schemeBuPu, interpolateGnBu, schemeGnBu, interpolateOrRd, schemeOrRd, interpolatePuBuGn, schemePuBuGn, interpolatePuBu, schemePuBu, interpolatePuRd, schemePuRd, interpolateRdPu, schemeRdPu, interpolateYlGnBu, schemeYlGnBu, interpolateYlGn, schemeYlGn, interpolateYlOrBr, schemeYlOrBr, interpolateYlOrRd, schemeYlOrRd, interpolateBlues, schemeBlues, interpolateGreens, schemeGreens, interpolateGreys, schemeGreys, interpolatePurples, schemePurples, interpolateReds, schemeReds, interpolateOranges, schemeOranges, interpolateCubehelixDefault, interpolateRainbow, interpolateWarm, interpolateCool, interpolateSinebow, interpolateViridis, interpolateMagma, interpolateInferno, interpolatePlasma */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _categorical_category10__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./categorical/category10 */ \"./node_modules/d3-scale-chromatic/src/categorical/category10.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory10\", function() { return _categorical_category10__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _categorical_Accent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./categorical/Accent */ \"./node_modules/d3-scale-chromatic/src/categorical/Accent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeAccent\", function() { return _categorical_Accent__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _categorical_Dark2__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./categorical/Dark2 */ \"./node_modules/d3-scale-chromatic/src/categorical/Dark2.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeDark2\", function() { return _categorical_Dark2__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _categorical_Paired__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./categorical/Paired */ \"./node_modules/d3-scale-chromatic/src/categorical/Paired.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePaired\", function() { return _categorical_Paired__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _categorical_Pastel1__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./categorical/Pastel1 */ \"./node_modules/d3-scale-chromatic/src/categorical/Pastel1.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePastel1\", function() { return _categorical_Pastel1__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _categorical_Pastel2__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./categorical/Pastel2 */ \"./node_modules/d3-scale-chromatic/src/categorical/Pastel2.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePastel2\", function() { return _categorical_Pastel2__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _categorical_Set1__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./categorical/Set1 */ \"./node_modules/d3-scale-chromatic/src/categorical/Set1.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet1\", function() { return _categorical_Set1__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _categorical_Set2__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./categorical/Set2 */ \"./node_modules/d3-scale-chromatic/src/categorical/Set2.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet2\", function() { return _categorical_Set2__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _categorical_Set3__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./categorical/Set3 */ \"./node_modules/d3-scale-chromatic/src/categorical/Set3.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet3\", function() { return _categorical_Set3__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _diverging_BrBG__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./diverging/BrBG */ \"./node_modules/d3-scale-chromatic/src/diverging/BrBG.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBrBG\", function() { return _diverging_BrBG__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBrBG\", function() { return _diverging_BrBG__WEBPACK_IMPORTED_MODULE_9__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_PRGn__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./diverging/PRGn */ \"./node_modules/d3-scale-chromatic/src/diverging/PRGn.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePRGn\", function() { return _diverging_PRGn__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePRGn\", function() { return _diverging_PRGn__WEBPACK_IMPORTED_MODULE_10__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_PiYG__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./diverging/PiYG */ \"./node_modules/d3-scale-chromatic/src/diverging/PiYG.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePiYG\", function() { return _diverging_PiYG__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePiYG\", function() { return _diverging_PiYG__WEBPACK_IMPORTED_MODULE_11__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_PuOr__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./diverging/PuOr */ \"./node_modules/d3-scale-chromatic/src/diverging/PuOr.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuOr\", function() { return _diverging_PuOr__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuOr\", function() { return _diverging_PuOr__WEBPACK_IMPORTED_MODULE_12__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_RdBu__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./diverging/RdBu */ \"./node_modules/d3-scale-chromatic/src/diverging/RdBu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdBu\", function() { return _diverging_RdBu__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdBu\", function() { return _diverging_RdBu__WEBPACK_IMPORTED_MODULE_13__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_RdGy__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./diverging/RdGy */ \"./node_modules/d3-scale-chromatic/src/diverging/RdGy.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdGy\", function() { return _diverging_RdGy__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdGy\", function() { return _diverging_RdGy__WEBPACK_IMPORTED_MODULE_14__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_RdYlBu__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./diverging/RdYlBu */ \"./node_modules/d3-scale-chromatic/src/diverging/RdYlBu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdYlBu\", function() { return _diverging_RdYlBu__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdYlBu\", function() { return _diverging_RdYlBu__WEBPACK_IMPORTED_MODULE_15__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_RdYlGn__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./diverging/RdYlGn */ \"./node_modules/d3-scale-chromatic/src/diverging/RdYlGn.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdYlGn\", function() { return _diverging_RdYlGn__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdYlGn\", function() { return _diverging_RdYlGn__WEBPACK_IMPORTED_MODULE_16__[\"scheme\"]; });\n\n/* harmony import */ var _diverging_Spectral__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./diverging/Spectral */ \"./node_modules/d3-scale-chromatic/src/diverging/Spectral.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateSpectral\", function() { return _diverging_Spectral__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSpectral\", function() { return _diverging_Spectral__WEBPACK_IMPORTED_MODULE_17__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_BuGn__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./sequential-multi/BuGn */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/BuGn.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBuGn\", function() { return _sequential_multi_BuGn__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBuGn\", function() { return _sequential_multi_BuGn__WEBPACK_IMPORTED_MODULE_18__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_BuPu__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./sequential-multi/BuPu */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/BuPu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBuPu\", function() { return _sequential_multi_BuPu__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBuPu\", function() { return _sequential_multi_BuPu__WEBPACK_IMPORTED_MODULE_19__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_GnBu__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./sequential-multi/GnBu */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/GnBu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGnBu\", function() { return _sequential_multi_GnBu__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGnBu\", function() { return _sequential_multi_GnBu__WEBPACK_IMPORTED_MODULE_20__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_OrRd__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./sequential-multi/OrRd */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/OrRd.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateOrRd\", function() { return _sequential_multi_OrRd__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeOrRd\", function() { return _sequential_multi_OrRd__WEBPACK_IMPORTED_MODULE_21__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_PuBuGn__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./sequential-multi/PuBuGn */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/PuBuGn.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuBuGn\", function() { return _sequential_multi_PuBuGn__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuBuGn\", function() { return _sequential_multi_PuBuGn__WEBPACK_IMPORTED_MODULE_22__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_PuBu__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./sequential-multi/PuBu */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/PuBu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuBu\", function() { return _sequential_multi_PuBu__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuBu\", function() { return _sequential_multi_PuBu__WEBPACK_IMPORTED_MODULE_23__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_PuRd__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./sequential-multi/PuRd */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/PuRd.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuRd\", function() { return _sequential_multi_PuRd__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuRd\", function() { return _sequential_multi_PuRd__WEBPACK_IMPORTED_MODULE_24__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_RdPu__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./sequential-multi/RdPu */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/RdPu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdPu\", function() { return _sequential_multi_RdPu__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdPu\", function() { return _sequential_multi_RdPu__WEBPACK_IMPORTED_MODULE_25__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_YlGnBu__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./sequential-multi/YlGnBu */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/YlGnBu.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlGnBu\", function() { return _sequential_multi_YlGnBu__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlGnBu\", function() { return _sequential_multi_YlGnBu__WEBPACK_IMPORTED_MODULE_26__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_YlGn__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./sequential-multi/YlGn */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/YlGn.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlGn\", function() { return _sequential_multi_YlGn__WEBPACK_IMPORTED_MODULE_27__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlGn\", function() { return _sequential_multi_YlGn__WEBPACK_IMPORTED_MODULE_27__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_YlOrBr__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./sequential-multi/YlOrBr */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrBr.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlOrBr\", function() { return _sequential_multi_YlOrBr__WEBPACK_IMPORTED_MODULE_28__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlOrBr\", function() { return _sequential_multi_YlOrBr__WEBPACK_IMPORTED_MODULE_28__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_YlOrRd__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./sequential-multi/YlOrRd */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrRd.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlOrRd\", function() { return _sequential_multi_YlOrRd__WEBPACK_IMPORTED_MODULE_29__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlOrRd\", function() { return _sequential_multi_YlOrRd__WEBPACK_IMPORTED_MODULE_29__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Blues__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./sequential-single/Blues */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Blues.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBlues\", function() { return _sequential_single_Blues__WEBPACK_IMPORTED_MODULE_30__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBlues\", function() { return _sequential_single_Blues__WEBPACK_IMPORTED_MODULE_30__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Greens__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./sequential-single/Greens */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Greens.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGreens\", function() { return _sequential_single_Greens__WEBPACK_IMPORTED_MODULE_31__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGreens\", function() { return _sequential_single_Greens__WEBPACK_IMPORTED_MODULE_31__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Greys__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./sequential-single/Greys */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Greys.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGreys\", function() { return _sequential_single_Greys__WEBPACK_IMPORTED_MODULE_32__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGreys\", function() { return _sequential_single_Greys__WEBPACK_IMPORTED_MODULE_32__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Purples__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./sequential-single/Purples */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Purples.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePurples\", function() { return _sequential_single_Purples__WEBPACK_IMPORTED_MODULE_33__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePurples\", function() { return _sequential_single_Purples__WEBPACK_IMPORTED_MODULE_33__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Reds__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! ./sequential-single/Reds */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Reds.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateReds\", function() { return _sequential_single_Reds__WEBPACK_IMPORTED_MODULE_34__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeReds\", function() { return _sequential_single_Reds__WEBPACK_IMPORTED_MODULE_34__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_single_Oranges__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! ./sequential-single/Oranges */ \"./node_modules/d3-scale-chromatic/src/sequential-single/Oranges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateOranges\", function() { return _sequential_single_Oranges__WEBPACK_IMPORTED_MODULE_35__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeOranges\", function() { return _sequential_single_Oranges__WEBPACK_IMPORTED_MODULE_35__[\"scheme\"]; });\n\n/* harmony import */ var _sequential_multi_cubehelix__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! ./sequential-multi/cubehelix */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixDefault\", function() { return _sequential_multi_cubehelix__WEBPACK_IMPORTED_MODULE_36__[\"default\"]; });\n\n/* harmony import */ var _sequential_multi_rainbow__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! ./sequential-multi/rainbow */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/rainbow.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRainbow\", function() { return _sequential_multi_rainbow__WEBPACK_IMPORTED_MODULE_37__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateWarm\", function() { return _sequential_multi_rainbow__WEBPACK_IMPORTED_MODULE_37__[\"warm\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCool\", function() { return _sequential_multi_rainbow__WEBPACK_IMPORTED_MODULE_37__[\"cool\"]; });\n\n/* harmony import */ var _sequential_multi_sinebow__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! ./sequential-multi/sinebow */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/sinebow.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateSinebow\", function() { return _sequential_multi_sinebow__WEBPACK_IMPORTED_MODULE_38__[\"default\"]; });\n\n/* harmony import */ var _sequential_multi_viridis__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! ./sequential-multi/viridis */ \"./node_modules/d3-scale-chromatic/src/sequential-multi/viridis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateViridis\", function() { return _sequential_multi_viridis__WEBPACK_IMPORTED_MODULE_39__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateMagma\", function() { return _sequential_multi_viridis__WEBPACK_IMPORTED_MODULE_39__[\"magma\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateInferno\", function() { return _sequential_multi_viridis__WEBPACK_IMPORTED_MODULE_39__[\"inferno\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePlasma\", function() { return _sequential_multi_viridis__WEBPACK_IMPORTED_MODULE_39__[\"plasma\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/ramp.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/ramp.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(scheme) {\n return Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateRgbBasis\"])(scheme[scheme.length - 1]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/ramp.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/BuGn.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/BuGn.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e5f5f999d8c92ca25f\",\n \"edf8fbb2e2e266c2a4238b45\",\n \"edf8fbb2e2e266c2a42ca25f006d2c\",\n \"edf8fbccece699d8c966c2a42ca25f006d2c\",\n \"edf8fbccece699d8c966c2a441ae76238b45005824\",\n \"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824\",\n \"f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/BuGn.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/BuPu.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/BuPu.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e0ecf49ebcda8856a7\",\n \"edf8fbb3cde38c96c688419d\",\n \"edf8fbb3cde38c96c68856a7810f7c\",\n \"edf8fbbfd3e69ebcda8c96c68856a7810f7c\",\n \"edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b\",\n \"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b\",\n \"f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/BuPu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/GnBu.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/GnBu.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e0f3dba8ddb543a2ca\",\n \"f0f9e8bae4bc7bccc42b8cbe\",\n \"f0f9e8bae4bc7bccc443a2ca0868ac\",\n \"f0f9e8ccebc5a8ddb57bccc443a2ca0868ac\",\n \"f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e\",\n \"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e\",\n \"f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/GnBu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/OrRd.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/OrRd.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fee8c8fdbb84e34a33\",\n \"fef0d9fdcc8afc8d59d7301f\",\n \"fef0d9fdcc8afc8d59e34a33b30000\",\n \"fef0d9fdd49efdbb84fc8d59e34a33b30000\",\n \"fef0d9fdd49efdbb84fc8d59ef6548d7301f990000\",\n \"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000\",\n \"fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/OrRd.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/PuBu.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/PuBu.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"ece7f2a6bddb2b8cbe\",\n \"f1eef6bdc9e174a9cf0570b0\",\n \"f1eef6bdc9e174a9cf2b8cbe045a8d\",\n \"f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d\",\n \"f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b\",\n \"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b\",\n \"fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/PuBu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/PuBuGn.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/PuBuGn.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"ece2f0a6bddb1c9099\",\n \"f6eff7bdc9e167a9cf02818a\",\n \"f6eff7bdc9e167a9cf1c9099016c59\",\n \"f6eff7d0d1e6a6bddb67a9cf1c9099016c59\",\n \"f6eff7d0d1e6a6bddb67a9cf3690c002818a016450\",\n \"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450\",\n \"fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/PuBuGn.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/PuRd.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/PuRd.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e7e1efc994c7dd1c77\",\n \"f1eef6d7b5d8df65b0ce1256\",\n \"f1eef6d7b5d8df65b0dd1c77980043\",\n \"f1eef6d4b9dac994c7df65b0dd1c77980043\",\n \"f1eef6d4b9dac994c7df65b0e7298ace125691003f\",\n \"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f\",\n \"f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/PuRd.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/RdPu.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/RdPu.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fde0ddfa9fb5c51b8a\",\n \"feebe2fbb4b9f768a1ae017e\",\n \"feebe2fbb4b9f768a1c51b8a7a0177\",\n \"feebe2fcc5c0fa9fb5f768a1c51b8a7a0177\",\n \"feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177\",\n \"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177\",\n \"fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/RdPu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/YlGn.js": +/*!**********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/YlGn.js ***! + \**********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"f7fcb9addd8e31a354\",\n \"ffffccc2e69978c679238443\",\n \"ffffccc2e69978c67931a354006837\",\n \"ffffccd9f0a3addd8e78c67931a354006837\",\n \"ffffccd9f0a3addd8e78c67941ab5d238443005a32\",\n \"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32\",\n \"ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/YlGn.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/YlGnBu.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/YlGnBu.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"edf8b17fcdbb2c7fb8\",\n \"ffffcca1dab441b6c4225ea8\",\n \"ffffcca1dab441b6c42c7fb8253494\",\n \"ffffccc7e9b47fcdbb41b6c42c7fb8253494\",\n \"ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84\",\n \"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84\",\n \"ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/YlGnBu.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrBr.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrBr.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fff7bcfec44fd95f0e\",\n \"ffffd4fed98efe9929cc4c02\",\n \"ffffd4fed98efe9929d95f0e993404\",\n \"ffffd4fee391fec44ffe9929d95f0e993404\",\n \"ffffd4fee391fec44ffe9929ec7014cc4c028c2d04\",\n \"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04\",\n \"ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrBr.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrRd.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrRd.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"ffeda0feb24cf03b20\",\n \"ffffb2fecc5cfd8d3ce31a1c\",\n \"ffffb2fecc5cfd8d3cf03b20bd0026\",\n \"ffffb2fed976feb24cfd8d3cf03b20bd0026\",\n \"ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026\",\n \"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026\",\n \"ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/YlOrRd.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/cubehelix.js": +/*!***************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/cubehelix.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(300, 0.5, 0.0), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(-240, 0.5, 1.0)));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/rainbow.js": +/*!*************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/rainbow.js ***! + \*************************************************************************/ +/*! exports provided: warm, cool, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"warm\", function() { return warm; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cool\", function() { return cool; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n\n\n\nvar warm = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(-100, 0.75, 0.35), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(80, 1.50, 0.8));\n\nvar cool = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(260, 0.75, 0.35), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(80, 1.50, 0.8));\n\nvar c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])();\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(t) {\n if (t < 0 || t > 1) t -= Math.floor(t);\n var ts = Math.abs(t - 0.5);\n c.h = 360 * t - 100;\n c.s = 1.5 - 1.5 * ts;\n c.l = 0.8 - 0.9 * ts;\n return c + \"\";\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/rainbow.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/sinebow.js": +/*!*************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/sinebow.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n\n\nvar c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(),\n pi_1_3 = Math.PI / 3,\n pi_2_3 = Math.PI * 2 / 3;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(t) {\n var x;\n t = (0.5 - t) * Math.PI;\n c.r = 255 * (x = Math.sin(t)) * x;\n c.g = 255 * (x = Math.sin(t + pi_1_3)) * x;\n c.b = 255 * (x = Math.sin(t + pi_2_3)) * x;\n return c + \"\";\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/sinebow.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-multi/viridis.js": +/*!*************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-multi/viridis.js ***! + \*************************************************************************/ +/*! exports provided: default, magma, inferno, plasma */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"magma\", function() { return magma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"inferno\", function() { return inferno; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"plasma\", function() { return plasma; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n\n\nfunction ramp(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725\")));\n\nvar magma = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf\"));\n\nvar inferno = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4\"));\n\nvar plasma = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921\"));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-multi/viridis.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Blues.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Blues.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"deebf79ecae13182bd\",\n \"eff3ffbdd7e76baed62171b5\",\n \"eff3ffbdd7e76baed63182bd08519c\",\n \"eff3ffc6dbef9ecae16baed63182bd08519c\",\n \"eff3ffc6dbef9ecae16baed64292c62171b5084594\",\n \"f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594\",\n \"f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Blues.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Greens.js": +/*!*************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Greens.js ***! + \*************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"e5f5e0a1d99b31a354\",\n \"edf8e9bae4b374c476238b45\",\n \"edf8e9bae4b374c47631a354006d2c\",\n \"edf8e9c7e9c0a1d99b74c47631a354006d2c\",\n \"edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32\",\n \"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32\",\n \"f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Greens.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Greys.js": +/*!************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Greys.js ***! + \************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"f0f0f0bdbdbd636363\",\n \"f7f7f7cccccc969696525252\",\n \"f7f7f7cccccc969696636363252525\",\n \"f7f7f7d9d9d9bdbdbd969696636363252525\",\n \"f7f7f7d9d9d9bdbdbd969696737373525252252525\",\n \"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525\",\n \"fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Greys.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Oranges.js": +/*!**************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Oranges.js ***! + \**************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fee6cefdae6be6550d\",\n \"feeddefdbe85fd8d3cd94701\",\n \"feeddefdbe85fd8d3ce6550da63603\",\n \"feeddefdd0a2fdae6bfd8d3ce6550da63603\",\n \"feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04\",\n \"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04\",\n \"fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Oranges.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Purples.js": +/*!**************************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Purples.js ***! + \**************************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"efedf5bcbddc756bb1\",\n \"f2f0f7cbc9e29e9ac86a51a3\",\n \"f2f0f7cbc9e29e9ac8756bb154278f\",\n \"f2f0f7dadaebbcbddc9e9ac8756bb154278f\",\n \"f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486\",\n \"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486\",\n \"fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Purples.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale-chromatic/src/sequential-single/Reds.js": +/*!***********************************************************************!*\ + !*** ./node_modules/d3-scale-chromatic/src/sequential-single/Reds.js ***! + \***********************************************************************/ +/*! exports provided: scheme, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scheme\", function() { return scheme; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../colors */ \"./node_modules/d3-scale-chromatic/src/colors.js\");\n/* harmony import */ var _ramp__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ramp */ \"./node_modules/d3-scale-chromatic/src/ramp.js\");\n\n\n\nvar scheme = new Array(3).concat(\n \"fee0d2fc9272de2d26\",\n \"fee5d9fcae91fb6a4acb181d\",\n \"fee5d9fcae91fb6a4ade2d26a50f15\",\n \"fee5d9fcbba1fc9272fb6a4ade2d26a50f15\",\n \"fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d\",\n \"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d\",\n \"fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d\"\n).map(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_ramp__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(scheme));\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale-chromatic/src/sequential-single/Reds.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/array.js": +/*!********************************************!*\ + !*** ./node_modules/d3-scale/src/array.js ***! + \********************************************/ +/*! exports provided: map, slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar array = Array.prototype;\n\nvar map = array.map;\nvar slice = array.slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/band.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-scale/src/band.js ***! + \*******************************************/ +/*! exports provided: default, point */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return band; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _ordinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ordinal */ \"./node_modules/d3-scale/src/ordinal.js\");\n\n\n\nfunction band() {\n var scale = Object(_ordinal__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().unknown(undefined),\n domain = scale.domain,\n ordinalRange = scale.range,\n range = [0, 1],\n step,\n bandwidth,\n round = false,\n paddingInner = 0,\n paddingOuter = 0,\n align = 0.5;\n\n delete scale.unknown;\n\n function rescale() {\n var n = domain().length,\n reverse = range[1] < range[0],\n start = range[reverse - 0],\n stop = range[1 - reverse];\n step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);\n if (round) step = Math.floor(step);\n start += (stop - start - step * (n - paddingInner)) * align;\n bandwidth = step * (1 - paddingInner);\n if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);\n var values = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n).map(function(i) { return start + step * i; });\n return ordinalRange(reverse ? values.reverse() : values);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (domain(_), rescale()) : domain();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = [+_[0], +_[1]], rescale()) : range.slice();\n };\n\n scale.rangeRound = function(_) {\n return range = [+_[0], +_[1]], round = true, rescale();\n };\n\n scale.bandwidth = function() {\n return bandwidth;\n };\n\n scale.step = function() {\n return step;\n };\n\n scale.round = function(_) {\n return arguments.length ? (round = !!_, rescale()) : round;\n };\n\n scale.padding = function(_) {\n return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;\n };\n\n scale.paddingInner = function(_) {\n return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;\n };\n\n scale.paddingOuter = function(_) {\n return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;\n };\n\n scale.align = function(_) {\n return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;\n };\n\n scale.copy = function() {\n return band()\n .domain(domain())\n .range(range)\n .round(round)\n .paddingInner(paddingInner)\n .paddingOuter(paddingOuter)\n .align(align);\n };\n\n return rescale();\n}\n\nfunction pointish(scale) {\n var copy = scale.copy;\n\n scale.padding = scale.paddingOuter;\n delete scale.paddingInner;\n delete scale.paddingOuter;\n\n scale.copy = function() {\n return pointish(copy());\n };\n\n return scale;\n}\n\nfunction point() {\n return pointish(band().paddingInner(1));\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/band.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-scale/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/continuous.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-scale/src/continuous.js ***! + \*************************************************/ +/*! exports provided: deinterpolateLinear, copy, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deinterpolateLinear\", function() { return deinterpolateLinear; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"copy\", function() { return copy; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return continuous; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-scale/src/number.js\");\n\n\n\n\n\n\nvar unit = [0, 1];\n\nfunction deinterpolateLinear(a, b) {\n return (b -= (a = +a))\n ? function(x) { return (x - a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(b);\n}\n\nfunction deinterpolateClamp(deinterpolate) {\n return function(a, b) {\n var d = deinterpolate(a = +a, b = +b);\n return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };\n };\n}\n\nfunction reinterpolateClamp(reinterpolate) {\n return function(a, b) {\n var r = reinterpolate(a = +a, b = +b);\n return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };\n };\n}\n\nfunction bimap(domain, range, deinterpolate, reinterpolate) {\n var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];\n if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);\n else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);\n return function(x) { return r0(d0(x)); };\n}\n\nfunction polymap(domain, range, deinterpolate, reinterpolate) {\n var j = Math.min(domain.length, range.length) - 1,\n d = new Array(j),\n r = new Array(j),\n i = -1;\n\n // Reverse descending domains.\n if (domain[j] < domain[0]) {\n domain = domain.slice().reverse();\n range = range.slice().reverse();\n }\n\n while (++i < j) {\n d[i] = deinterpolate(domain[i], domain[i + 1]);\n r[i] = reinterpolate(range[i], range[i + 1]);\n }\n\n return function(x) {\n var i = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 1, j) - 1;\n return r[i](d[i](x));\n };\n}\n\nfunction copy(source, target) {\n return target\n .domain(source.domain())\n .range(source.range())\n .interpolate(source.interpolate())\n .clamp(source.clamp());\n}\n\n// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].\n// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].\nfunction continuous(deinterpolate, reinterpolate) {\n var domain = unit,\n range = unit,\n interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolate\"],\n clamp = false,\n piecewise,\n output,\n input;\n\n function rescale() {\n piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;\n output = input = null;\n return scale;\n }\n\n function scale(x) {\n return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);\n }\n\n scale.invert = function(y) {\n return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);\n };\n\n scale.domain = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_2__[\"map\"].call(_, _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]), rescale()) : domain.slice();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_2__[\"slice\"].call(_), rescale()) : range.slice();\n };\n\n scale.rangeRound = function(_) {\n return range = _array__WEBPACK_IMPORTED_MODULE_2__[\"slice\"].call(_), interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRound\"], rescale();\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = !!_, rescale()) : clamp;\n };\n\n scale.interpolate = function(_) {\n return arguments.length ? (interpolate = _, rescale()) : interpolate;\n };\n\n return rescale();\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/continuous.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/diverging.js": +/*!************************************************!*\ + !*** ./node_modules/d3-scale/src/diverging.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return diverging; });\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n\n\nfunction diverging(interpolator) {\n var x0 = 0,\n x1 = 0.5,\n x2 = 1,\n k10 = 1,\n k21 = 1,\n clamp = false;\n\n function scale(x) {\n var t = 0.5 + ((x = +x) - x1) * (x < x1 ? k10 : k21);\n return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (x0 = +_[0], x1 = +_[1], x2 = +_[2], k10 = x0 === x1 ? 0 : 0.5 / (x1 - x0), k21 = x1 === x2 ? 0 : 0.5 / (x2 - x1), scale) : [x0, x1, x2];\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = !!_, scale) : clamp;\n };\n\n scale.interpolator = function(_) {\n return arguments.length ? (interpolator = _, scale) : interpolator;\n };\n\n scale.copy = function() {\n return diverging(interpolator).domain([x0, x1, x2]).clamp(clamp);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_0__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/diverging.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/identity.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-scale/src/identity.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return identity; });\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./number */ \"./node_modules/d3-scale/src/number.js\");\n\n\n\n\nfunction identity() {\n var domain = [0, 1];\n\n function scale(x) {\n return +x;\n }\n\n scale.invert = scale;\n\n scale.domain = scale.range = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(_, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]), scale) : domain.slice();\n };\n\n scale.copy = function() {\n return identity().domain(domain);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_1__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-scale/src/index.js ***! + \********************************************/ +/*! exports provided: scaleBand, scalePoint, scaleIdentity, scaleLinear, scaleLog, scaleOrdinal, scaleImplicit, scalePow, scaleSqrt, scaleQuantile, scaleQuantize, scaleThreshold, scaleTime, scaleUtc, scaleSequential, scaleDiverging */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _band__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./band */ \"./node_modules/d3-scale/src/band.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleBand\", function() { return _band__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePoint\", function() { return _band__WEBPACK_IMPORTED_MODULE_0__[\"point\"]; });\n\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./identity */ \"./node_modules/d3-scale/src/identity.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleIdentity\", function() { return _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLinear\", function() { return _linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./log */ \"./node_modules/d3-scale/src/log.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLog\", function() { return _log__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _ordinal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ordinal */ \"./node_modules/d3-scale/src/ordinal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleOrdinal\", function() { return _ordinal__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleImplicit\", function() { return _ordinal__WEBPACK_IMPORTED_MODULE_4__[\"implicit\"]; });\n\n/* harmony import */ var _pow__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./pow */ \"./node_modules/d3-scale/src/pow.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePow\", function() { return _pow__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSqrt\", function() { return _pow__WEBPACK_IMPORTED_MODULE_5__[\"sqrt\"]; });\n\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./quantile */ \"./node_modules/d3-scale/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantile\", function() { return _quantile__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./quantize */ \"./node_modules/d3-scale/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _threshold__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./threshold */ \"./node_modules/d3-scale/src/threshold.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleThreshold\", function() { return _threshold__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _time__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./time */ \"./node_modules/d3-scale/src/time.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleTime\", function() { return _time__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _utcTime__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./utcTime */ \"./node_modules/d3-scale/src/utcTime.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleUtc\", function() { return _utcTime__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _sequential__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./sequential */ \"./node_modules/d3-scale/src/sequential.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSequential\", function() { return _sequential__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _diverging__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./diverging */ \"./node_modules/d3-scale/src/diverging.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleDiverging\", function() { return _diverging__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/linear.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-scale/src/linear.js ***! + \*********************************************/ +/*! exports provided: linearish, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linearish\", function() { return linearish; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return linear; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./continuous */ \"./node_modules/d3-scale/src/continuous.js\");\n/* harmony import */ var _tickFormat__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./tickFormat */ \"./node_modules/d3-scale/src/tickFormat.js\");\n\n\n\n\n\nfunction linearish(scale) {\n var domain = scale.domain;\n\n scale.ticks = function(count) {\n var d = domain();\n return Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ticks\"])(d[0], d[d.length - 1], count == null ? 10 : count);\n };\n\n scale.tickFormat = function(count, specifier) {\n return Object(_tickFormat__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(domain(), count, specifier);\n };\n\n scale.nice = function(count) {\n if (count == null) count = 10;\n\n var d = domain(),\n i0 = 0,\n i1 = d.length - 1,\n start = d[i0],\n stop = d[i1],\n step;\n\n if (stop < start) {\n step = start, start = stop, stop = step;\n step = i0, i0 = i1, i1 = step;\n }\n\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n\n if (step > 0) {\n start = Math.floor(start / step) * step;\n stop = Math.ceil(stop / step) * step;\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n } else if (step < 0) {\n start = Math.ceil(start * step) / step;\n stop = Math.floor(stop * step) / step;\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n }\n\n if (step > 0) {\n d[i0] = Math.floor(start / step) * step;\n d[i1] = Math.ceil(stop / step) * step;\n domain(d);\n } else if (step < 0) {\n d[i0] = Math.ceil(start * step) / step;\n d[i1] = Math.floor(stop * step) / step;\n domain(d);\n }\n\n return scale;\n };\n\n return scale;\n}\n\nfunction linear() {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"deinterpolateLinear\"], d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]);\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"copy\"])(scale, linear());\n };\n\n return linearish(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/log.js": +/*!******************************************!*\ + !*** ./node_modules/d3-scale/src/log.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return log; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-format */ \"./node_modules/d3-format/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _nice__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./nice */ \"./node_modules/d3-scale/src/nice.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./continuous */ \"./node_modules/d3-scale/src/continuous.js\");\n\n\n\n\n\n\nfunction deinterpolate(a, b) {\n return (b = Math.log(b / a))\n ? function(x) { return Math.log(x / a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(b);\n}\n\nfunction reinterpolate(a, b) {\n return a < 0\n ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }\n : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };\n}\n\nfunction pow10(x) {\n return isFinite(x) ? +(\"1e\" + x) : x < 0 ? 0 : x;\n}\n\nfunction powp(base) {\n return base === 10 ? pow10\n : base === Math.E ? Math.exp\n : function(x) { return Math.pow(base, x); };\n}\n\nfunction logp(base) {\n return base === Math.E ? Math.log\n : base === 10 && Math.log10\n || base === 2 && Math.log2\n || (base = Math.log(base), function(x) { return Math.log(x) / base; });\n}\n\nfunction reflect(f) {\n return function(x) {\n return -f(-x);\n };\n}\n\nfunction log() {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(deinterpolate, reinterpolate).domain([1, 10]),\n domain = scale.domain,\n base = 10,\n logs = logp(10),\n pows = powp(10);\n\n function rescale() {\n logs = logp(base), pows = powp(base);\n if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);\n return scale;\n }\n\n scale.base = function(_) {\n return arguments.length ? (base = +_, rescale()) : base;\n };\n\n scale.domain = function(_) {\n return arguments.length ? (domain(_), rescale()) : domain();\n };\n\n scale.ticks = function(count) {\n var d = domain(),\n u = d[0],\n v = d[d.length - 1],\n r;\n\n if (r = v < u) i = u, u = v, v = i;\n\n var i = logs(u),\n j = logs(v),\n p,\n k,\n t,\n n = count == null ? 10 : +count,\n z = [];\n\n if (!(base % 1) && j - i < n) {\n i = Math.round(i) - 1, j = Math.round(j) + 1;\n if (u > 0) for (; i < j; ++i) {\n for (k = 1, p = pows(i); k < base; ++k) {\n t = p * k;\n if (t < u) continue;\n if (t > v) break;\n z.push(t);\n }\n } else for (; i < j; ++i) {\n for (k = base - 1, p = pows(i); k >= 1; --k) {\n t = p * k;\n if (t < u) continue;\n if (t > v) break;\n z.push(t);\n }\n }\n } else {\n z = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ticks\"])(i, j, Math.min(j - i, n)).map(pows);\n }\n\n return r ? z.reverse() : z;\n };\n\n scale.tickFormat = function(count, specifier) {\n if (specifier == null) specifier = base === 10 ? \".0e\" : \",\";\n if (typeof specifier !== \"function\") specifier = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"format\"])(specifier);\n if (count === Infinity) return specifier;\n if (count == null) count = 10;\n var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?\n return function(d) {\n var i = d / pows(Math.round(logs(d)));\n if (i * base < base - 0.5) i *= base;\n return i <= k ? specifier(d) : \"\";\n };\n };\n\n scale.nice = function() {\n return domain(Object(_nice__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(domain(), {\n floor: function(x) { return pows(Math.floor(logs(x))); },\n ceil: function(x) { return pows(Math.ceil(logs(x))); }\n }));\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_4__[\"copy\"])(scale, log().base(base));\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/log.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/nice.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-scale/src/nice.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(domain, interval) {\n domain = domain.slice();\n\n var i0 = 0,\n i1 = domain.length - 1,\n x0 = domain[i0],\n x1 = domain[i1],\n t;\n\n if (x1 < x0) {\n t = i0, i0 = i1, i1 = t;\n t = x0, x0 = x1, x1 = t;\n }\n\n domain[i0] = interval.floor(x0);\n domain[i1] = interval.ceil(x1);\n return domain;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/nice.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/number.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-scale/src/number.js ***! + \*********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/ordinal.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-scale/src/ordinal.js ***! + \**********************************************/ +/*! exports provided: implicit, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"implicit\", function() { return implicit; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return ordinal; });\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-collection */ \"./node_modules/d3-collection/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n\n\n\nvar implicit = {name: \"implicit\"};\n\nfunction ordinal(range) {\n var index = Object(d3_collection__WEBPACK_IMPORTED_MODULE_0__[\"map\"])(),\n domain = [],\n unknown = implicit;\n\n range = range == null ? [] : _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(range);\n\n function scale(d) {\n var key = d + \"\", i = index.get(key);\n if (!i) {\n if (unknown !== implicit) return unknown;\n index.set(key, i = domain.push(d));\n }\n return range[(i - 1) % range.length];\n }\n\n scale.domain = function(_) {\n if (!arguments.length) return domain.slice();\n domain = [], index = Object(d3_collection__WEBPACK_IMPORTED_MODULE_0__[\"map\"])();\n var i = -1, n = _.length, d, key;\n while (++i < n) if (!index.has(key = (d = _[i]) + \"\")) index.set(key, domain.push(d));\n return scale;\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), scale) : range.slice();\n };\n\n scale.unknown = function(_) {\n return arguments.length ? (unknown = _, scale) : unknown;\n };\n\n scale.copy = function() {\n return ordinal()\n .domain(domain)\n .range(range)\n .unknown(unknown);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/ordinal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/pow.js": +/*!******************************************!*\ + !*** ./node_modules/d3-scale/src/pow.js ***! + \******************************************/ +/*! exports provided: default, sqrt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return pow; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./continuous */ \"./node_modules/d3-scale/src/continuous.js\");\n\n\n\n\nfunction raise(x, exponent) {\n return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);\n}\n\nfunction pow() {\n var exponent = 1,\n scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(deinterpolate, reinterpolate),\n domain = scale.domain;\n\n function deinterpolate(a, b) {\n return (b = raise(b, exponent) - (a = raise(a, exponent)))\n ? function(x) { return (raise(x, exponent) - a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(b);\n }\n\n function reinterpolate(a, b) {\n b = raise(b, exponent) - (a = raise(a, exponent));\n return function(t) { return raise(a + b * t, 1 / exponent); };\n }\n\n scale.exponent = function(_) {\n return arguments.length ? (exponent = +_, domain(domain())) : exponent;\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"copy\"])(scale, pow().exponent(exponent));\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_1__[\"linearish\"])(scale);\n}\n\nfunction sqrt() {\n return pow().exponent(0.5);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/pow.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/quantile.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-scale/src/quantile.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quantile; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n\n\n\nfunction quantile() {\n var domain = [],\n range = [],\n thresholds = [];\n\n function rescale() {\n var i = 0, n = Math.max(1, range.length);\n thresholds = new Array(n - 1);\n while (++i < n) thresholds[i - 1] = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"quantile\"])(domain, i / n);\n return scale;\n }\n\n function scale(x) {\n if (!isNaN(x = +x)) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(thresholds, x)];\n }\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return i < 0 ? [NaN, NaN] : [\n i > 0 ? thresholds[i - 1] : domain[0],\n i < thresholds.length ? thresholds[i] : domain[domain.length - 1]\n ];\n };\n\n scale.domain = function(_) {\n if (!arguments.length) return domain.slice();\n domain = [];\n for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);\n domain.sort(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ascending\"]);\n return rescale();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), rescale()) : range.slice();\n };\n\n scale.quantiles = function() {\n return thresholds.slice();\n };\n\n scale.copy = function() {\n return quantile()\n .domain(domain)\n .range(range);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/quantize.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-scale/src/quantize.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quantize; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n\n\n\n\nfunction quantize() {\n var x0 = 0,\n x1 = 1,\n n = 1,\n domain = [0.5],\n range = [0, 1];\n\n function scale(x) {\n if (x <= x) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 0, n)];\n }\n\n function rescale() {\n var i = -1;\n domain = new Array(n);\n while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);\n return scale;\n }\n\n scale.domain = function(_) {\n return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];\n };\n\n scale.range = function(_) {\n return arguments.length ? (n = (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_)).length - 1, rescale()) : range.slice();\n };\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return i < 0 ? [NaN, NaN]\n : i < 1 ? [x0, domain[0]]\n : i >= n ? [domain[n - 1], x1]\n : [domain[i - 1], domain[i]];\n };\n\n scale.copy = function() {\n return quantize()\n .domain([x0, x1])\n .range(range);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_2__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/sequential.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-scale/src/sequential.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return sequential; });\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-scale/src/linear.js\");\n\n\nfunction sequential(interpolator) {\n var x0 = 0,\n x1 = 1,\n k10 = 1,\n clamp = false;\n\n function scale(x) {\n var t = (x - x0) * k10;\n return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (x0 = +_[0], x1 = +_[1], k10 = x0 === x1 ? 0 : 1 / (x1 - x0), scale) : [x0, x1];\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = !!_, scale) : clamp;\n };\n\n scale.interpolator = function(_) {\n return arguments.length ? (interpolator = _, scale) : interpolator;\n };\n\n scale.copy = function() {\n return sequential(interpolator).domain([x0, x1]).clamp(clamp);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_0__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/sequential.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/threshold.js": +/*!************************************************!*\ + !*** ./node_modules/d3-scale/src/threshold.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return threshold; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n\n\n\nfunction threshold() {\n var domain = [0.5],\n range = [0, 1],\n n = 1;\n\n function scale(x) {\n if (x <= x) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 0, n)];\n }\n\n scale.domain = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();\n };\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return [domain[i - 1], domain[i]];\n };\n\n scale.copy = function() {\n return threshold()\n .domain(domain)\n .range(range);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/threshold.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/tickFormat.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-scale/src/tickFormat.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-format */ \"./node_modules/d3-format/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(domain, count, specifier) {\n var start = domain[0],\n stop = domain[domain.length - 1],\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start, stop, count == null ? 10 : count),\n precision;\n specifier = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"formatSpecifier\"])(specifier == null ? \",f\" : specifier);\n switch (specifier.type) {\n case \"s\": {\n var value = Math.max(Math.abs(start), Math.abs(stop));\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionPrefix\"])(step, value))) specifier.precision = precision;\n return Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"formatPrefix\"])(specifier, value);\n }\n case \"\":\n case \"e\":\n case \"g\":\n case \"p\":\n case \"r\": {\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionRound\"])(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === \"e\");\n break;\n }\n case \"f\":\n case \"%\": {\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionFixed\"])(step))) specifier.precision = precision - (specifier.type === \"%\") * 2;\n break;\n }\n }\n return Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"format\"])(specifier);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/tickFormat.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/time.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-scale/src/time.js ***! + \*******************************************/ +/*! exports provided: calendar, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"calendar\", function() { return calendar; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-time */ \"./node_modules/d3-time/src/index.js\");\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/d3-time-format/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./continuous */ \"./node_modules/d3-scale/src/continuous.js\");\n/* harmony import */ var _nice__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./nice */ \"./node_modules/d3-scale/src/nice.js\");\n\n\n\n\n\n\n\n\nvar durationSecond = 1000,\n durationMinute = durationSecond * 60,\n durationHour = durationMinute * 60,\n durationDay = durationHour * 24,\n durationWeek = durationDay * 7,\n durationMonth = durationDay * 30,\n durationYear = durationDay * 365;\n\nfunction date(t) {\n return new Date(t);\n}\n\nfunction number(t) {\n return t instanceof Date ? +t : +new Date(+t);\n}\n\nfunction calendar(year, month, week, day, hour, minute, second, millisecond, format) {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"deinterpolateLinear\"], d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]),\n invert = scale.invert,\n domain = scale.domain;\n\n var formatMillisecond = format(\".%L\"),\n formatSecond = format(\":%S\"),\n formatMinute = format(\"%I:%M\"),\n formatHour = format(\"%I %p\"),\n formatDay = format(\"%a %d\"),\n formatWeek = format(\"%b %d\"),\n formatMonth = format(\"%B\"),\n formatYear = format(\"%Y\");\n\n var tickIntervals = [\n [second, 1, durationSecond],\n [second, 5, 5 * durationSecond],\n [second, 15, 15 * durationSecond],\n [second, 30, 30 * durationSecond],\n [minute, 1, durationMinute],\n [minute, 5, 5 * durationMinute],\n [minute, 15, 15 * durationMinute],\n [minute, 30, 30 * durationMinute],\n [ hour, 1, durationHour ],\n [ hour, 3, 3 * durationHour ],\n [ hour, 6, 6 * durationHour ],\n [ hour, 12, 12 * durationHour ],\n [ day, 1, durationDay ],\n [ day, 2, 2 * durationDay ],\n [ week, 1, durationWeek ],\n [ month, 1, durationMonth ],\n [ month, 3, 3 * durationMonth ],\n [ year, 1, durationYear ]\n ];\n\n function tickFormat(date) {\n return (second(date) < date ? formatMillisecond\n : minute(date) < date ? formatSecond\n : hour(date) < date ? formatMinute\n : day(date) < date ? formatHour\n : month(date) < date ? (week(date) < date ? formatDay : formatWeek)\n : year(date) < date ? formatMonth\n : formatYear)(date);\n }\n\n function tickInterval(interval, start, stop, step) {\n if (interval == null) interval = 10;\n\n // If a desired tick count is specified, pick a reasonable tick interval\n // based on the extent of the domain and a rough estimate of tick size.\n // Otherwise, assume interval is already a time interval and use it.\n if (typeof interval === \"number\") {\n var target = Math.abs(stop - start) / interval,\n i = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisector\"])(function(i) { return i[2]; }).right(tickIntervals, target);\n if (i === tickIntervals.length) {\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start / durationYear, stop / durationYear, interval);\n interval = year;\n } else if (i) {\n i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];\n step = i[1];\n interval = i[0];\n } else {\n step = Math.max(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start, stop, interval), 1);\n interval = millisecond;\n }\n }\n\n return step == null ? interval : interval.every(step);\n }\n\n scale.invert = function(y) {\n return new Date(invert(y));\n };\n\n scale.domain = function(_) {\n return arguments.length ? domain(_array__WEBPACK_IMPORTED_MODULE_4__[\"map\"].call(_, number)) : domain().map(date);\n };\n\n scale.ticks = function(interval, step) {\n var d = domain(),\n t0 = d[0],\n t1 = d[d.length - 1],\n r = t1 < t0,\n t;\n if (r) t = t0, t0 = t1, t1 = t;\n t = tickInterval(interval, t0, t1, step);\n t = t ? t.range(t0, t1 + 1) : []; // inclusive stop\n return r ? t.reverse() : t;\n };\n\n scale.tickFormat = function(count, specifier) {\n return specifier == null ? tickFormat : format(specifier);\n };\n\n scale.nice = function(interval, step) {\n var d = domain();\n return (interval = tickInterval(interval, d[0], d[d.length - 1], step))\n ? domain(Object(_nice__WEBPACK_IMPORTED_MODULE_6__[\"default\"])(d, interval))\n : scale;\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"copy\"])(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));\n };\n\n return scale;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return calendar(d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeYear\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMonth\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeWeek\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeDay\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeHour\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMinute\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeSecond\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMillisecond\"], d3_time_format__WEBPACK_IMPORTED_MODULE_3__[\"timeFormat\"]).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/time.js?"); + +/***/ }), + +/***/ "./node_modules/d3-scale/src/utcTime.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-scale/src/utcTime.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _time__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./time */ \"./node_modules/d3-scale/src/time.js\");\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/d3-time-format/src/index.js\");\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-time */ \"./node_modules/d3-time/src/index.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_time__WEBPACK_IMPORTED_MODULE_0__[\"calendar\"])(d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcYear\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMonth\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcWeek\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcDay\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcHour\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMinute\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcSecond\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMillisecond\"], d3_time_format__WEBPACK_IMPORTED_MODULE_1__[\"utcFormat\"]).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-scale/src/utcTime.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/constant.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-selection/src/constant.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/create.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-selection/src/create.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/creator.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-selection/src/creator.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/index.js": +/*!************************************************!*\ + !*** ./node_modules/d3-selection/src/index.js ***! + \************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./create */ \"./node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./creator */ \"./node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./local */ \"./node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./matcher */ \"./node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mouse */ \"./node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./namespace */ \"./node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./select */ \"./node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selector */ \"./node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selectorAll */ \"./node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./selection/style */ \"./node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./touch */ \"./node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./touches */ \"./node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./window */ \"./node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/local.js": +/*!************************************************!*\ + !*** ./node_modules/d3-selection/src/local.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/matcher.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-selection/src/matcher.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/mouse.js": +/*!************************************************!*\ + !*** ./node_modules/d3-selection/src/mouse.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/namespace.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-selection/src/namespace.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/namespaces.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-selection/src/namespaces.js ***! + \*****************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/point.js": +/*!************************************************!*\ + !*** ./node_modules/d3-selection/src/point.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/select.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-selection/src/select.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selectAll.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-selection/src/selectAll.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/append.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/append.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/attr.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/attr.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/call.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/call.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/classed.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/classed.js ***! + \************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/clone.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/clone.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/data.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/data.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/datum.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/datum.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/dispatch.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/dispatch.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/each.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/each.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/empty.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/empty.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/enter.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/enter.js ***! + \**********************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/exit.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/exit.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/filter.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/filter.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/html.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/html.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/index.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/index.js ***! + \**********************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/insert.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/insert.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/lower.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/lower.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/merge.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/merge.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/node.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/node.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/nodes.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/nodes.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/on.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/on.js ***! + \*******************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/order.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/order.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/property.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/property.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/raise.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/raise.js ***! + \**********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/remove.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/remove.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/select.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/select.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/selectAll.js": +/*!**************************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/selectAll.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/size.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/size.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/sort.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/sort.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/sparse.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/sparse.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/style.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/style.js ***! + \**********************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selection/text.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-selection/src/selection/text.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selector.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-selection/src/selector.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/selectorAll.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-selection/src/selectorAll.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/sourceEvent.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-selection/src/sourceEvent.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/touch.js": +/*!************************************************!*\ + !*** ./node_modules/d3-selection/src/touch.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/touches.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-selection/src/touches.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/d3-selection/src/window.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-selection/src/window.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/arc.js": +/*!******************************************!*\ + !*** ./node_modules/d3-shape/src/arc.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-shape/src/math.js\");\n\n\n\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"max\"])(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null;\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n a1 = endAngle.apply(this, arguments) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n da = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n context.moveTo(r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a0), r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n context.moveTo(r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a1), r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) && (padRadius ? +padRadius.apply(this, arguments) : Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(r0 * r0 + r1 * r1)),\n rc = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n var p0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(rp / r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ap)),\n p1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(rp / r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ap));\n if ((da0 -= p0 * 2) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a01),\n y01 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a01),\n x10 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a10),\n y10 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a10);\n\n // Apply rounded corners?\n if (rc > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n var x11 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a11),\n y11 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a11),\n x00 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a00),\n y00 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a00);\n\n // Restrict the corner radius according to the sector angle.\n if (da < _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]) {\n var oc = da0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],\n ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"acos\"])((ax * bx + ay * by) / (Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(ax * ax + ay * ay) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(bx * bx + by * by))) / 2),\n lc = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(rc, (r0 - lc) / (kc - 1));\n rc1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(rc, (r1 - lc) / (kc + 1));\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.cy + t0.y11, t0.cx + t0.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y11, t1.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) || !(da0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.cy + t0.y11, t0.cx + t0.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y11, t1.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] / 2;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a) * r, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/arc.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/area.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-shape/src/area.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _curve_linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./curve/linear */ \"./node_modules/d3-shape/src/curve/linear.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./line */ \"./node_modules/d3-shape/src/line.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-shape/src/point.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x0 = _point__WEBPACK_IMPORTED_MODULE_4__[\"x\"],\n x1 = null,\n y0 = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(0),\n y1 = _point__WEBPACK_IMPORTED_MODULE_4__[\"y\"],\n defined = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(true),\n context = null,\n curve = _curve_linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n output = null;\n\n function area(data) {\n var i,\n j,\n k,\n n = data.length,\n d,\n defined0 = false,\n buffer,\n x0z = new Array(n),\n y0z = new Array(n);\n\n if (context == null) output = curve(buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])());\n\n for (i = 0; i <= n; ++i) {\n if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n if (defined0 = !defined0) {\n j = i;\n output.areaStart();\n output.lineStart();\n } else {\n output.lineEnd();\n output.lineStart();\n for (k = i - 1; k >= j; --k) {\n output.point(x0z[k], y0z[k]);\n }\n output.lineEnd();\n output.areaEnd();\n }\n }\n if (defined0) {\n x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);\n output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);\n }\n }\n\n if (buffer) return output = null, buffer + \"\" || null;\n }\n\n function arealine() {\n return Object(_line__WEBPACK_IMPORTED_MODULE_3__[\"default\"])().defined(defined).curve(curve).context(context);\n }\n\n area.x = function(_) {\n return arguments.length ? (x0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), x1 = null, area) : x0;\n };\n\n area.x0 = function(_) {\n return arguments.length ? (x0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : x0;\n };\n\n area.x1 = function(_) {\n return arguments.length ? (x1 = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : x1;\n };\n\n area.y = function(_) {\n return arguments.length ? (y0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), y1 = null, area) : y0;\n };\n\n area.y0 = function(_) {\n return arguments.length ? (y0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : y0;\n };\n\n area.y1 = function(_) {\n return arguments.length ? (y1 = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : y1;\n };\n\n area.lineX0 =\n area.lineY0 = function() {\n return arealine().x(x0).y(y0);\n };\n\n area.lineY1 = function() {\n return arealine().x(x0).y(y1);\n };\n\n area.lineX1 = function() {\n return arealine().x(x1).y(y0);\n };\n\n area.defined = function(_) {\n return arguments.length ? (defined = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(!!_), area) : defined;\n };\n\n area.curve = function(_) {\n return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;\n };\n\n area.context = function(_) {\n return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;\n };\n\n return area;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/areaRadial.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/areaRadial.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _curve_radial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./curve/radial */ \"./node_modules/d3-shape/src/curve/radial.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-shape/src/area.js\");\n/* harmony import */ var _lineRadial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./lineRadial */ \"./node_modules/d3-shape/src/lineRadial.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var a = Object(_area__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().curve(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"curveRadialLinear\"]),\n c = a.curve,\n x0 = a.lineX0,\n x1 = a.lineX1,\n y0 = a.lineY0,\n y1 = a.lineY1;\n\n a.angle = a.x, delete a.x;\n a.startAngle = a.x0, delete a.x0;\n a.endAngle = a.x1, delete a.x1;\n a.radius = a.y, delete a.y;\n a.innerRadius = a.y0, delete a.y0;\n a.outerRadius = a.y1, delete a.y1;\n a.lineStartAngle = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(x0()); }, delete a.lineX0;\n a.lineEndAngle = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(x1()); }, delete a.lineX1;\n a.lineInnerRadius = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(y0()); }, delete a.lineY0;\n a.lineOuterRadius = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(y1()); }, delete a.lineY1;\n\n a.curve = function(_) {\n return arguments.length ? c(Object(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_)) : c()._curve;\n };\n\n return a;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/areaRadial.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/array.js": +/*!********************************************!*\ + !*** ./node_modules/d3-shape/src/array.js ***! + \********************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/constant.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-shape/src/constant.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function constant() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/basis.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/basis.js ***! + \**************************************************/ +/*! exports provided: point, Basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Basis\", function() { return Basis; });\nfunction point(that, x, y) {\n that._context.bezierCurveTo(\n (2 * that._x0 + that._x1) / 3,\n (2 * that._y0 + that._y1) / 3,\n (that._x0 + 2 * that._x1) / 3,\n (that._y0 + 2 * that._y1) / 3,\n (that._x0 + 4 * that._x1 + x) / 6,\n (that._y0 + 4 * that._y1 + y) / 6\n );\n}\n\nfunction Basis(context) {\n this._context = context;\n}\n\nBasis.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 3: point(this, this._x1, this._y1); // proceed\n case 2: this._context.lineTo(this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Basis(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/basis.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/basisClosed.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/basisClosed.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-shape/src/curve/basis.js\");\n\n\n\nfunction BasisClosed(context) {\n this._context = context;\n}\n\nBasisClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x2, this._y2);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);\n this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x2, this._y2);\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x2 = x, this._y2 = y; break;\n case 1: this._point = 2; this._x3 = x, this._y3 = y; break;\n case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;\n default: Object(_basis__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new BasisClosed(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/basisOpen.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/basisOpen.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-shape/src/curve/basis.js\");\n\n\nfunction BasisOpen(context) {\n this._context = context;\n}\n\nBasisOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;\n case 3: this._point = 4; // proceed\n default: Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new BasisOpen(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/basisOpen.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/bundle.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/bundle.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/d3-shape/src/curve/basis.js\");\n\n\nfunction Bundle(context, beta) {\n this._basis = new _basis__WEBPACK_IMPORTED_MODULE_0__[\"Basis\"](context);\n this._beta = beta;\n}\n\nBundle.prototype = {\n lineStart: function() {\n this._x = [];\n this._y = [];\n this._basis.lineStart();\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n j = x.length - 1;\n\n if (j > 0) {\n var x0 = x[0],\n y0 = y[0],\n dx = x[j] - x0,\n dy = y[j] - y0,\n i = -1,\n t;\n\n while (++i <= j) {\n t = i / j;\n this._basis.point(\n this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),\n this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)\n );\n }\n }\n\n this._x = this._y = null;\n this._basis.lineEnd();\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(beta) {\n\n function bundle(context) {\n return beta === 1 ? new _basis__WEBPACK_IMPORTED_MODULE_0__[\"Basis\"](context) : new Bundle(context, beta);\n }\n\n bundle.beta = function(beta) {\n return custom(+beta);\n };\n\n return bundle;\n})(0.85));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/bundle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/cardinal.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/cardinal.js ***! + \*****************************************************/ +/*! exports provided: point, Cardinal, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cardinal\", function() { return Cardinal; });\nfunction point(that, x, y) {\n that._context.bezierCurveTo(\n that._x1 + that._k * (that._x2 - that._x0),\n that._y1 + that._k * (that._y2 - that._y0),\n that._x2 + that._k * (that._x1 - x),\n that._y2 + that._k * (that._y1 - y),\n that._x2,\n that._y2\n );\n}\n\nfunction Cardinal(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinal.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: point(this, this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; this._x1 = x, this._y1 = y; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new Cardinal(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/cardinal.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/cardinalClosed.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/cardinalClosed.js ***! + \***********************************************************/ +/*! exports provided: CardinalClosed, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CardinalClosed\", function() { return CardinalClosed; });\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/d3-shape/src/curve/cardinal.js\");\n\n\n\nfunction CardinalClosed(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: Object(_cardinal__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new CardinalClosed(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/cardinalClosed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/cardinalOpen.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/cardinalOpen.js ***! + \*********************************************************/ +/*! exports provided: CardinalOpen, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CardinalOpen\", function() { return CardinalOpen; });\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/d3-shape/src/curve/cardinal.js\");\n\n\nfunction CardinalOpen(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: Object(_cardinal__WEBPACK_IMPORTED_MODULE_0__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new CardinalOpen(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/cardinalOpen.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/catmullRom.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/catmullRom.js ***! + \*******************************************************/ +/*! exports provided: point, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-shape/src/math.js\");\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/d3-shape/src/curve/cardinal.js\");\n\n\n\nfunction point(that, x, y) {\n var x1 = that._x1,\n y1 = that._y1,\n x2 = that._x2,\n y2 = that._y2;\n\n if (that._l01_a > _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) {\n var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,\n n = 3 * that._l01_a * (that._l01_a + that._l12_a);\n x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;\n y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;\n }\n\n if (that._l23_a > _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) {\n var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,\n m = 3 * that._l23_a * (that._l23_a + that._l12_a);\n x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;\n y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;\n }\n\n that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);\n}\n\nfunction CatmullRom(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRom.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: this.point(this._x2, this._y2); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRom(context, alpha) : new _cardinal__WEBPACK_IMPORTED_MODULE_1__[\"Cardinal\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/catmullRom.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/catmullRomClosed.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/catmullRomClosed.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cardinalClosed__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinalClosed */ \"./node_modules/d3-shape/src/curve/cardinalClosed.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _catmullRom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./catmullRom */ \"./node_modules/d3-shape/src/curve/catmullRom.js\");\n\n\n\n\nfunction CatmullRomClosed(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: Object(_catmullRom__WEBPACK_IMPORTED_MODULE_2__[\"point\"])(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomClosed(context, alpha) : new _cardinalClosed__WEBPACK_IMPORTED_MODULE_0__[\"CardinalClosed\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/catmullRomClosed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/catmullRomOpen.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/catmullRomOpen.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cardinalOpen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinalOpen */ \"./node_modules/d3-shape/src/curve/cardinalOpen.js\");\n/* harmony import */ var _catmullRom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./catmullRom */ \"./node_modules/d3-shape/src/curve/catmullRom.js\");\n\n\n\nfunction CatmullRomOpen(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: Object(_catmullRom__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomOpen(context, alpha) : new _cardinalOpen__WEBPACK_IMPORTED_MODULE_0__[\"CardinalOpen\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/catmullRomOpen.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/linear.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/linear.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction Linear(context) {\n this._context = context;\n}\n\nLinear.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: this._context.lineTo(x, y); break;\n }\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Linear(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/linear.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/linearClosed.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/linearClosed.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/d3-shape/src/noop.js\");\n\n\nfunction LinearClosed(context) {\n this._context = context;\n}\n\nLinearClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._point) this._context.closePath();\n },\n point: function(x, y) {\n x = +x, y = +y;\n if (this._point) this._context.lineTo(x, y);\n else this._point = 1, this._context.moveTo(x, y);\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new LinearClosed(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/linearClosed.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/monotone.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/monotone.js ***! + \*****************************************************/ +/*! exports provided: monotoneX, monotoneY */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monotoneX\", function() { return monotoneX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monotoneY\", function() { return monotoneY; });\nfunction sign(x) {\n return x < 0 ? -1 : 1;\n}\n\n// Calculate the slopes of the tangents (Hermite-type interpolation) based on\n// the following paper: Steffen, M. 1990. A Simple Method for Monotonic\n// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.\n// NOV(II), P. 443, 1990.\nfunction slope3(that, x2, y2) {\n var h0 = that._x1 - that._x0,\n h1 = x2 - that._x1,\n s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),\n s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),\n p = (s0 * h1 + s1 * h0) / (h0 + h1);\n return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;\n}\n\n// Calculate a one-sided slope.\nfunction slope2(that, t) {\n var h = that._x1 - that._x0;\n return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;\n}\n\n// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations\n// \"you can express cubic Hermite interpolation in terms of cubic Bézier curves\n// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1\".\nfunction point(that, t0, t1) {\n var x0 = that._x0,\n y0 = that._y0,\n x1 = that._x1,\n y1 = that._y1,\n dx = (x1 - x0) / 3;\n that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);\n}\n\nfunction MonotoneX(context) {\n this._context = context;\n}\n\nMonotoneX.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 =\n this._t0 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x1, this._y1); break;\n case 3: point(this, this._t0, slope2(this, this._t0)); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n var t1 = NaN;\n\n x = +x, y = +y;\n if (x === this._x1 && y === this._y1) return; // Ignore coincident points.\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; point(this, slope2(this, t1 = slope3(this, x, y)), t1); break;\n default: point(this, this._t0, t1 = slope3(this, x, y)); break;\n }\n\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n this._t0 = t1;\n }\n}\n\nfunction MonotoneY(context) {\n this._context = new ReflectContext(context);\n}\n\n(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {\n MonotoneX.prototype.point.call(this, y, x);\n};\n\nfunction ReflectContext(context) {\n this._context = context;\n}\n\nReflectContext.prototype = {\n moveTo: function(x, y) { this._context.moveTo(y, x); },\n closePath: function() { this._context.closePath(); },\n lineTo: function(x, y) { this._context.lineTo(y, x); },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }\n};\n\nfunction monotoneX(context) {\n return new MonotoneX(context);\n}\n\nfunction monotoneY(context) {\n return new MonotoneY(context);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/monotone.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/natural.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/natural.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction Natural(context) {\n this._context = context;\n}\n\nNatural.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = [];\n this._y = [];\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n n = x.length;\n\n if (n) {\n this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);\n if (n === 2) {\n this._context.lineTo(x[1], y[1]);\n } else {\n var px = controlPoints(x),\n py = controlPoints(y);\n for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {\n this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);\n }\n }\n }\n\n if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();\n this._line = 1 - this._line;\n this._x = this._y = null;\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n// See https://www.particleincell.com/2012/bezier-splines/ for derivation.\nfunction controlPoints(x) {\n var i,\n n = x.length - 1,\n m,\n a = new Array(n),\n b = new Array(n),\n r = new Array(n);\n a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];\n for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];\n a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];\n for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];\n a[n - 1] = r[n - 1] / b[n - 1];\n for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];\n b[n - 1] = (x[n] + a[n - 1]) / 2;\n for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];\n return [a, b];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Natural(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/natural.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/radial.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/radial.js ***! + \***************************************************/ +/*! exports provided: curveRadialLinear, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"curveRadialLinear\", function() { return curveRadialLinear; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return curveRadial; });\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/d3-shape/src/curve/linear.js\");\n\n\nvar curveRadialLinear = curveRadial(_linear__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\nfunction Radial(curve) {\n this._curve = curve;\n}\n\nRadial.prototype = {\n areaStart: function() {\n this._curve.areaStart();\n },\n areaEnd: function() {\n this._curve.areaEnd();\n },\n lineStart: function() {\n this._curve.lineStart();\n },\n lineEnd: function() {\n this._curve.lineEnd();\n },\n point: function(a, r) {\n this._curve.point(r * Math.sin(a), r * -Math.cos(a));\n }\n};\n\nfunction curveRadial(curve) {\n\n function radial(context) {\n return new Radial(curve(context));\n }\n\n radial._curve = curve;\n\n return radial;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/radial.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/curve/step.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/curve/step.js ***! + \*************************************************/ +/*! exports provided: default, stepBefore, stepAfter */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stepBefore\", function() { return stepBefore; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stepAfter\", function() { return stepAfter; });\nfunction Step(context, t) {\n this._context = context;\n this._t = t;\n}\n\nStep.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = this._y = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: {\n if (this._t <= 0) {\n this._context.lineTo(this._x, y);\n this._context.lineTo(x, y);\n } else {\n var x1 = this._x * (1 - this._t) + x * this._t;\n this._context.lineTo(x1, this._y);\n this._context.lineTo(x1, y);\n }\n break;\n }\n }\n this._x = x, this._y = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Step(context, 0.5);\n});\n\nfunction stepBefore(context) {\n return new Step(context, 0);\n}\n\nfunction stepAfter(context) {\n return new Step(context, 1);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/curve/step.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/descending.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/descending.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/identity.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-shape/src/identity.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n return d;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-shape/src/index.js ***! + \********************************************/ +/*! exports provided: arc, area, line, pie, areaRadial, radialArea, lineRadial, radialLine, pointRadial, linkHorizontal, linkVertical, linkRadial, symbol, symbols, symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye, curveBasisClosed, curveBasisOpen, curveBasis, curveBundle, curveCardinalClosed, curveCardinalOpen, curveCardinal, curveCatmullRomClosed, curveCatmullRomOpen, curveCatmullRom, curveLinearClosed, curveLinear, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, stack, stackOffsetExpand, stackOffsetDiverging, stackOffsetNone, stackOffsetSilhouette, stackOffsetWiggle, stackOrderAscending, stackOrderDescending, stackOrderInsideOut, stackOrderNone, stackOrderReverse */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _arc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./arc */ \"./node_modules/d3-shape/src/arc.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"arc\", function() { return _arc__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./area */ \"./node_modules/d3-shape/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"area\", function() { return _area__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./line */ \"./node_modules/d3-shape/src/line.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"line\", function() { return _line__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _pie__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./pie */ \"./node_modules/d3-shape/src/pie.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pie\", function() { return _pie__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _areaRadial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./areaRadial */ \"./node_modules/d3-shape/src/areaRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"areaRadial\", function() { return _areaRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialArea\", function() { return _areaRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _lineRadial__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./lineRadial */ \"./node_modules/d3-shape/src/lineRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return _lineRadial__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialLine\", function() { return _lineRadial__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _pointRadial__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./pointRadial */ \"./node_modules/d3-shape/src/pointRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pointRadial\", function() { return _pointRadial__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _link_index__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./link/index */ \"./node_modules/d3-shape/src/link/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return _link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkHorizontal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return _link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkVertical\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return _link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkRadial\"]; });\n\n/* harmony import */ var _symbol__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./symbol */ \"./node_modules/d3-shape/src/symbol.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbol\", function() { return _symbol__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return _symbol__WEBPACK_IMPORTED_MODULE_8__[\"symbols\"]; });\n\n/* harmony import */ var _symbol_circle__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./symbol/circle */ \"./node_modules/d3-shape/src/symbol/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCircle\", function() { return _symbol_circle__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _symbol_cross__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./symbol/cross */ \"./node_modules/d3-shape/src/symbol/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCross\", function() { return _symbol_cross__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _symbol_diamond__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./symbol/diamond */ \"./node_modules/d3-shape/src/symbol/diamond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolDiamond\", function() { return _symbol_diamond__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _symbol_square__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./symbol/square */ \"./node_modules/d3-shape/src/symbol/square.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolSquare\", function() { return _symbol_square__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _symbol_star__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./symbol/star */ \"./node_modules/d3-shape/src/symbol/star.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolStar\", function() { return _symbol_star__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _symbol_triangle__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./symbol/triangle */ \"./node_modules/d3-shape/src/symbol/triangle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolTriangle\", function() { return _symbol_triangle__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _symbol_wye__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./symbol/wye */ \"./node_modules/d3-shape/src/symbol/wye.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolWye\", function() { return _symbol_wye__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _curve_basisClosed__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./curve/basisClosed */ \"./node_modules/d3-shape/src/curve/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisClosed\", function() { return _curve_basisClosed__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _curve_basisOpen__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./curve/basisOpen */ \"./node_modules/d3-shape/src/curve/basisOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisOpen\", function() { return _curve_basisOpen__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _curve_basis__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./curve/basis */ \"./node_modules/d3-shape/src/curve/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasis\", function() { return _curve_basis__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _curve_bundle__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./curve/bundle */ \"./node_modules/d3-shape/src/curve/bundle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBundle\", function() { return _curve_bundle__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _curve_cardinalClosed__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./curve/cardinalClosed */ \"./node_modules/d3-shape/src/curve/cardinalClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalClosed\", function() { return _curve_cardinalClosed__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _curve_cardinalOpen__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./curve/cardinalOpen */ \"./node_modules/d3-shape/src/curve/cardinalOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalOpen\", function() { return _curve_cardinalOpen__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _curve_cardinal__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./curve/cardinal */ \"./node_modules/d3-shape/src/curve/cardinal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinal\", function() { return _curve_cardinal__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _curve_catmullRomClosed__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./curve/catmullRomClosed */ \"./node_modules/d3-shape/src/curve/catmullRomClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomClosed\", function() { return _curve_catmullRomClosed__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony import */ var _curve_catmullRomOpen__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./curve/catmullRomOpen */ \"./node_modules/d3-shape/src/curve/catmullRomOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomOpen\", function() { return _curve_catmullRomOpen__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _curve_catmullRom__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./curve/catmullRom */ \"./node_modules/d3-shape/src/curve/catmullRom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRom\", function() { return _curve_catmullRom__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _curve_linearClosed__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./curve/linearClosed */ \"./node_modules/d3-shape/src/curve/linearClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinearClosed\", function() { return _curve_linearClosed__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n/* harmony import */ var _curve_linear__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./curve/linear */ \"./node_modules/d3-shape/src/curve/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinear\", function() { return _curve_linear__WEBPACK_IMPORTED_MODULE_27__[\"default\"]; });\n\n/* harmony import */ var _curve_monotone__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./curve/monotone */ \"./node_modules/d3-shape/src/curve/monotone.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneX\", function() { return _curve_monotone__WEBPACK_IMPORTED_MODULE_28__[\"monotoneX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneY\", function() { return _curve_monotone__WEBPACK_IMPORTED_MODULE_28__[\"monotoneY\"]; });\n\n/* harmony import */ var _curve_natural__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./curve/natural */ \"./node_modules/d3-shape/src/curve/natural.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveNatural\", function() { return _curve_natural__WEBPACK_IMPORTED_MODULE_29__[\"default\"]; });\n\n/* harmony import */ var _curve_step__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./curve/step */ \"./node_modules/d3-shape/src/curve/step.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStep\", function() { return _curve_step__WEBPACK_IMPORTED_MODULE_30__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepAfter\", function() { return _curve_step__WEBPACK_IMPORTED_MODULE_30__[\"stepAfter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepBefore\", function() { return _curve_step__WEBPACK_IMPORTED_MODULE_30__[\"stepBefore\"]; });\n\n/* harmony import */ var _stack__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./stack */ \"./node_modules/d3-shape/src/stack.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stack\", function() { return _stack__WEBPACK_IMPORTED_MODULE_31__[\"default\"]; });\n\n/* harmony import */ var _offset_expand__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./offset/expand */ \"./node_modules/d3-shape/src/offset/expand.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetExpand\", function() { return _offset_expand__WEBPACK_IMPORTED_MODULE_32__[\"default\"]; });\n\n/* harmony import */ var _offset_diverging__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./offset/diverging */ \"./node_modules/d3-shape/src/offset/diverging.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetDiverging\", function() { return _offset_diverging__WEBPACK_IMPORTED_MODULE_33__[\"default\"]; });\n\n/* harmony import */ var _offset_none__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! ./offset/none */ \"./node_modules/d3-shape/src/offset/none.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetNone\", function() { return _offset_none__WEBPACK_IMPORTED_MODULE_34__[\"default\"]; });\n\n/* harmony import */ var _offset_silhouette__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! ./offset/silhouette */ \"./node_modules/d3-shape/src/offset/silhouette.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetSilhouette\", function() { return _offset_silhouette__WEBPACK_IMPORTED_MODULE_35__[\"default\"]; });\n\n/* harmony import */ var _offset_wiggle__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! ./offset/wiggle */ \"./node_modules/d3-shape/src/offset/wiggle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetWiggle\", function() { return _offset_wiggle__WEBPACK_IMPORTED_MODULE_36__[\"default\"]; });\n\n/* harmony import */ var _order_ascending__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! ./order/ascending */ \"./node_modules/d3-shape/src/order/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderAscending\", function() { return _order_ascending__WEBPACK_IMPORTED_MODULE_37__[\"default\"]; });\n\n/* harmony import */ var _order_descending__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! ./order/descending */ \"./node_modules/d3-shape/src/order/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderDescending\", function() { return _order_descending__WEBPACK_IMPORTED_MODULE_38__[\"default\"]; });\n\n/* harmony import */ var _order_insideOut__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! ./order/insideOut */ \"./node_modules/d3-shape/src/order/insideOut.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderInsideOut\", function() { return _order_insideOut__WEBPACK_IMPORTED_MODULE_39__[\"default\"]; });\n\n/* harmony import */ var _order_none__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! ./order/none */ \"./node_modules/d3-shape/src/order/none.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderNone\", function() { return _order_none__WEBPACK_IMPORTED_MODULE_40__[\"default\"]; });\n\n/* harmony import */ var _order_reverse__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! ./order/reverse */ \"./node_modules/d3-shape/src/order/reverse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderReverse\", function() { return _order_reverse__WEBPACK_IMPORTED_MODULE_41__[\"default\"]; });\n\n\n\n\n\n // Note: radialArea is deprecated!\n // Note: radialLine is deprecated!\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/line.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-shape/src/line.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _curve_linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./curve/linear */ \"./node_modules/d3-shape/src/curve/linear.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-shape/src/point.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x = _point__WEBPACK_IMPORTED_MODULE_3__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_3__[\"y\"],\n defined = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(true),\n context = null,\n curve = _curve_linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n output = null;\n\n function line(data) {\n var i,\n n = data.length,\n d,\n defined0 = false,\n buffer;\n\n if (context == null) output = curve(buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])());\n\n for (i = 0; i <= n; ++i) {\n if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n if (defined0 = !defined0) output.lineStart();\n else output.lineEnd();\n }\n if (defined0) output.point(+x(d, i, data), +y(d, i, data));\n }\n\n if (buffer) return output = null, buffer + \"\" || null;\n }\n\n line.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), line) : x;\n };\n\n line.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), line) : y;\n };\n\n line.defined = function(_) {\n return arguments.length ? (defined = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(!!_), line) : defined;\n };\n\n line.curve = function(_) {\n return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;\n };\n\n line.context = function(_) {\n return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;\n };\n\n return line;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/line.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/lineRadial.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/lineRadial.js ***! + \*************************************************/ +/*! exports provided: lineRadial, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return lineRadial; });\n/* harmony import */ var _curve_radial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./curve/radial */ \"./node_modules/d3-shape/src/curve/radial.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./line */ \"./node_modules/d3-shape/src/line.js\");\n\n\n\nfunction lineRadial(l) {\n var c = l.curve;\n\n l.angle = l.x, delete l.x;\n l.radius = l.y, delete l.y;\n\n l.curve = function(_) {\n return arguments.length ? c(Object(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_)) : c()._curve;\n };\n\n return l;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return lineRadial(Object(_line__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().curve(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"curveRadialLinear\"]));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/lineRadial.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/link/index.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/link/index.js ***! + \*************************************************/ +/*! exports provided: linkHorizontal, linkVertical, linkRadial */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return linkHorizontal; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return linkVertical; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return linkRadial; });\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../array */ \"./node_modules/d3-shape/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../point */ \"./node_modules/d3-shape/src/point.js\");\n/* harmony import */ var _pointRadial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../pointRadial */ \"./node_modules/d3-shape/src/pointRadial.js\");\n\n\n\n\n\n\nfunction linkSource(d) {\n return d.source;\n}\n\nfunction linkTarget(d) {\n return d.target;\n}\n\nfunction link(curve) {\n var source = linkSource,\n target = linkTarget,\n x = _point__WEBPACK_IMPORTED_MODULE_3__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_3__[\"y\"],\n context = null;\n\n function link() {\n var buffer, argv = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n curve(context, +x.apply(this, (argv[0] = s, argv)), +y.apply(this, argv), +x.apply(this, (argv[0] = t, argv)), +y.apply(this, argv));\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n link.source = function(_) {\n return arguments.length ? (source = _, link) : source;\n };\n\n link.target = function(_) {\n return arguments.length ? (target = _, link) : target;\n };\n\n link.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+_), link) : x;\n };\n\n link.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+_), link) : y;\n };\n\n link.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), link) : context;\n };\n\n return link;\n}\n\nfunction curveHorizontal(context, x0, y0, x1, y1) {\n context.moveTo(x0, y0);\n context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);\n}\n\nfunction curveVertical(context, x0, y0, x1, y1) {\n context.moveTo(x0, y0);\n context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);\n}\n\nfunction curveRadial(context, x0, y0, x1, y1) {\n var p0 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x0, y0),\n p1 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x0, y0 = (y0 + y1) / 2),\n p2 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x1, y0),\n p3 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x1, y1);\n context.moveTo(p0[0], p0[1]);\n context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);\n}\n\nfunction linkHorizontal() {\n return link(curveHorizontal);\n}\n\nfunction linkVertical() {\n return link(curveVertical);\n}\n\nfunction linkRadial() {\n var l = link(curveRadial);\n l.angle = l.x, delete l.x;\n l.radius = l.y, delete l.y;\n return l;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/link/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/math.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-shape/src/math.js ***! + \*******************************************/ +/*! exports provided: abs, atan2, cos, max, min, sin, sqrt, epsilon, pi, halfPi, tau, acos, asin */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"abs\", function() { return abs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan2\", function() { return atan2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return max; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return min; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"acos\", function() { return acos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"asin\", function() { return asin; });\nvar abs = Math.abs;\nvar atan2 = Math.atan2;\nvar cos = Math.cos;\nvar max = Math.max;\nvar min = Math.min;\nvar sin = Math.sin;\nvar sqrt = Math.sqrt;\n\nvar epsilon = 1e-12;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar tau = 2 * pi;\n\nfunction acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nfunction asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/noop.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-shape/src/noop.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/noop.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/offset/diverging.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-shape/src/offset/diverging.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 1)) return;\n for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {\n for (yp = yn = 0, i = 0; i < n; ++i) {\n if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {\n d[0] = yp, d[1] = yp += dy;\n } else if (dy < 0) {\n d[1] = yn, d[0] = yn += dy;\n } else {\n d[0] = yp;\n }\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/offset/diverging.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/offset/expand.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/offset/expand.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0)) return;\n for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {\n for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;\n if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;\n }\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/offset/expand.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/offset/none.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-shape/src/offset/none.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 1)) return;\n for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {\n s0 = s1, s1 = series[order[i]];\n for (j = 0; j < m; ++j) {\n s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/offset/none.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/offset/silhouette.js": +/*!********************************************************!*\ + !*** ./node_modules/d3-shape/src/offset/silhouette.js ***! + \********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0)) return;\n for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {\n for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;\n s0[j][1] += s0[j][0] = -y / 2;\n }\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/offset/silhouette.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/offset/wiggle.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/offset/wiggle.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;\n for (var y = 0, j = 1, s0, m, n; j < m; ++j) {\n for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {\n var si = series[order[i]],\n sij0 = si[j][1] || 0,\n sij1 = si[j - 1][1] || 0,\n s3 = (sij0 - sij1) / 2;\n for (var k = 0; k < i; ++k) {\n var sk = series[order[k]],\n skj0 = sk[j][1] || 0,\n skj1 = sk[j - 1][1] || 0;\n s3 += skj0 - skj1;\n }\n s1 += sij0, s2 += s3 * sij0;\n }\n s0[j - 1][1] += s0[j - 1][0] = y;\n if (s1) y -= s2 / s1;\n }\n s0[j - 1][1] += s0[j - 1][0] = y;\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/offset/wiggle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/order/ascending.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-shape/src/order/ascending.js ***! + \******************************************************/ +/*! exports provided: default, sum */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return sum; });\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/order/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var sums = series.map(sum);\n return Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).sort(function(a, b) { return sums[a] - sums[b]; });\n});\n\nfunction sum(series) {\n var s = 0, i = -1, n = series.length, v;\n while (++i < n) if (v = +series[i][1]) s += v;\n return s;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/order/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/order/descending.js": +/*!*******************************************************!*\ + !*** ./node_modules/d3-shape/src/order/descending.js ***! + \*******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-shape/src/order/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).reverse();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/order/descending.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/order/insideOut.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-shape/src/order/insideOut.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/order/none.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/d3-shape/src/order/ascending.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var n = series.length,\n i,\n j,\n sums = series.map(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"sum\"]),\n order = Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).sort(function(a, b) { return sums[b] - sums[a]; }),\n top = 0,\n bottom = 0,\n tops = [],\n bottoms = [];\n\n for (i = 0; i < n; ++i) {\n j = order[i];\n if (top < bottom) {\n top += sums[j];\n tops.push(j);\n } else {\n bottom += sums[j];\n bottoms.push(j);\n }\n }\n\n return bottoms.reverse().concat(tops);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/order/insideOut.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/order/none.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/order/none.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var n = series.length, o = new Array(n);\n while (--n >= 0) o[n] = n;\n return o;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/order/none.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/order/reverse.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/order/reverse.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/d3-shape/src/order/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n return Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).reverse();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/order/reverse.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/pie.js": +/*!******************************************!*\ + !*** ./node_modules/d3-shape/src/pie.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./descending */ \"./node_modules/d3-shape/src/descending.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./identity */ \"./node_modules/d3-shape/src/identity.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./math */ \"./node_modules/d3-shape/src/math.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n sortValues = _descending__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n sort = null,\n startAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0),\n endAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"]),\n padAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0);\n\n function pie(data) {\n var i,\n n = data.length,\n j,\n k,\n sum = 0,\n index = new Array(n),\n arcs = new Array(n),\n a0 = +startAngle.apply(this, arguments),\n da = Math.min(_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"], Math.max(-_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"], endAngle.apply(this, arguments) - a0)),\n a1,\n p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),\n pa = p * (da < 0 ? -1 : 1),\n v;\n\n for (i = 0; i < n; ++i) {\n if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {\n sum += v;\n }\n }\n\n // Optionally sort the arcs by previously-computed values or by data.\n if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });\n else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });\n\n // Compute the arcs! They are stored in the original data's order.\n for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {\n j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {\n data: data[j],\n index: i,\n value: v,\n startAngle: a0,\n endAngle: a1,\n padAngle: p\n };\n }\n\n return arcs;\n }\n\n pie.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : value;\n };\n\n pie.sortValues = function(_) {\n return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;\n };\n\n pie.sort = function(_) {\n return arguments.length ? (sort = _, sortValues = null, pie) : sort;\n };\n\n pie.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : startAngle;\n };\n\n pie.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : endAngle;\n };\n\n pie.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : padAngle;\n };\n\n return pie;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/pie.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/point.js": +/*!********************************************!*\ + !*** ./node_modules/d3-shape/src/point.js ***! + \********************************************/ +/*! exports provided: x, y */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\nfunction x(p) {\n return p[0];\n}\n\nfunction y(p) {\n return p[1];\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/pointRadial.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-shape/src/pointRadial.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/pointRadial.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/stack.js": +/*!********************************************!*\ + !*** ./node_modules/d3-shape/src/stack.js ***! + \********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/d3-shape/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _offset_none__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./offset/none */ \"./node_modules/d3-shape/src/offset/none.js\");\n/* harmony import */ var _order_none__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./order/none */ \"./node_modules/d3-shape/src/order/none.js\");\n\n\n\n\n\nfunction stackValue(d, key) {\n return d[key];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([]),\n order = _order_none__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n offset = _offset_none__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n value = stackValue;\n\n function stack(data) {\n var kz = keys.apply(this, arguments),\n i,\n m = data.length,\n n = kz.length,\n sz = new Array(n),\n oz;\n\n for (i = 0; i < n; ++i) {\n for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {\n si[j] = sij = [0, +value(data[j], ki, j, data)];\n sij.data = data[j];\n }\n si.key = ki;\n }\n\n for (i = 0, oz = order(sz); i < n; ++i) {\n sz[oz[i]].index = i;\n }\n\n offset(sz, oz);\n return sz;\n }\n\n stack.keys = function(_) {\n return arguments.length ? (keys = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)), stack) : keys;\n };\n\n stack.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), stack) : value;\n };\n\n stack.order = function(_) {\n return arguments.length ? (order = _ == null ? _order_none__WEBPACK_IMPORTED_MODULE_3__[\"default\"] : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)), stack) : order;\n };\n\n stack.offset = function(_) {\n return arguments.length ? (offset = _ == null ? _offset_none__WEBPACK_IMPORTED_MODULE_2__[\"default\"] : _, stack) : offset;\n };\n\n return stack;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/stack.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-shape/src/symbol.js ***! + \*********************************************/ +/*! exports provided: symbols, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return symbols; });\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony import */ var _symbol_circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./symbol/circle */ \"./node_modules/d3-shape/src/symbol/circle.js\");\n/* harmony import */ var _symbol_cross__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./symbol/cross */ \"./node_modules/d3-shape/src/symbol/cross.js\");\n/* harmony import */ var _symbol_diamond__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./symbol/diamond */ \"./node_modules/d3-shape/src/symbol/diamond.js\");\n/* harmony import */ var _symbol_star__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./symbol/star */ \"./node_modules/d3-shape/src/symbol/star.js\");\n/* harmony import */ var _symbol_square__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./symbol/square */ \"./node_modules/d3-shape/src/symbol/square.js\");\n/* harmony import */ var _symbol_triangle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./symbol/triangle */ \"./node_modules/d3-shape/src/symbol/triangle.js\");\n/* harmony import */ var _symbol_wye__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./symbol/wye */ \"./node_modules/d3-shape/src/symbol/wye.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-shape/src/constant.js\");\n\n\n\n\n\n\n\n\n\n\nvar symbols = [\n _symbol_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _symbol_cross__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n _symbol_diamond__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n _symbol_square__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n _symbol_star__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n _symbol_triangle__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n _symbol_wye__WEBPACK_IMPORTED_MODULE_7__[\"default\"]\n];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var type = Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(_symbol_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"]),\n size = Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(64),\n context = null;\n\n function symbol() {\n var buffer;\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n type.apply(this, arguments).draw(context, +size.apply(this, arguments));\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n symbol.type = function(_) {\n return arguments.length ? (type = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(_), symbol) : type;\n };\n\n symbol.size = function(_) {\n return arguments.length ? (size = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(+_), symbol) : size;\n };\n\n symbol.context = function(_) {\n return arguments.length ? (context = _ == null ? null : _, symbol) : context;\n };\n\n return symbol;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/circle.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/circle.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-shape/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"]);\n context.moveTo(r, 0);\n context.arc(0, 0, r, 0, _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/circle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/cross.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/cross.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / 5) / 2;\n context.moveTo(-3 * r, -r);\n context.lineTo(-r, -r);\n context.lineTo(-r, -3 * r);\n context.lineTo(r, -3 * r);\n context.lineTo(r, -r);\n context.lineTo(3 * r, -r);\n context.lineTo(3 * r, r);\n context.lineTo(r, r);\n context.lineTo(r, 3 * r);\n context.lineTo(-r, 3 * r);\n context.lineTo(-r, r);\n context.lineTo(-3 * r, r);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/cross.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/diamond.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/diamond.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar tan30 = Math.sqrt(1 / 3),\n tan30_2 = tan30 * 2;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var y = Math.sqrt(size / tan30_2),\n x = y * tan30;\n context.moveTo(0, -y);\n context.lineTo(x, 0);\n context.lineTo(0, y);\n context.lineTo(-x, 0);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/diamond.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/square.js": +/*!****************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/square.js ***! + \****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var w = Math.sqrt(size),\n x = -w / 2;\n context.rect(x, x, w, w);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/square.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/star.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/star.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/d3-shape/src/math.js\");\n\n\nvar ka = 0.89081309152928522810,\n kr = Math.sin(_math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 10) / Math.sin(7 * _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 10),\n kx = Math.sin(_math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] / 10) * kr,\n ky = -Math.cos(_math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] / 10) * kr;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size * ka),\n x = kx * r,\n y = ky * r;\n context.moveTo(0, -r);\n context.lineTo(x, y);\n for (var i = 1; i < 5; ++i) {\n var a = _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] * i / 5,\n c = Math.cos(a),\n s = Math.sin(a);\n context.lineTo(s * r, -c * r);\n context.lineTo(c * x - s * y, s * x + c * y);\n }\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/star.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/triangle.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/triangle.js ***! + \******************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar sqrt3 = Math.sqrt(3);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var y = -Math.sqrt(size / (sqrt3 * 3));\n context.moveTo(0, y * 2);\n context.lineTo(-sqrt3 * y, -y);\n context.lineTo(sqrt3 * y, -y);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/triangle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-shape/src/symbol/wye.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-shape/src/symbol/wye.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar c = -0.5,\n s = Math.sqrt(3) / 2,\n k = 1 / Math.sqrt(12),\n a = (k / 2 + 1) * 3;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / a),\n x0 = r / 2,\n y0 = r * k,\n x1 = x0,\n y1 = r * k + r,\n x2 = -x1,\n y2 = y1;\n context.moveTo(x0, y0);\n context.lineTo(x1, y1);\n context.lineTo(x2, y2);\n context.lineTo(c * x0 - s * y0, s * x0 + c * y0);\n context.lineTo(c * x1 - s * y1, s * x1 + c * y1);\n context.lineTo(c * x2 - s * y2, s * x2 + c * y2);\n context.lineTo(c * x0 + s * y0, c * y0 - s * x0);\n context.lineTo(c * x1 + s * y1, c * y1 - s * x1);\n context.lineTo(c * x2 + s * y2, c * y2 - s * x2);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-shape/src/symbol/wye.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time-format/src/defaultLocale.js": +/*!**********************************************************!*\ + !*** ./node_modules/d3-time-format/src/defaultLocale.js ***! + \**********************************************************/ +/*! exports provided: timeFormat, timeParse, utcFormat, utcParse, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return timeFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return timeParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return utcFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return utcParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/d3-time-format/src/locale.js\");\n\n\nvar locale;\nvar timeFormat;\nvar timeParse;\nvar utcFormat;\nvar utcParse;\n\ndefaultLocale({\n dateTime: \"%x, %X\",\n date: \"%-m/%-d/%Y\",\n time: \"%-I:%M:%S %p\",\n periods: [\"AM\", \"PM\"],\n days: [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n shortDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n months: [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"],\n shortMonths: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n timeFormat = locale.format;\n timeParse = locale.parse;\n utcFormat = locale.utcFormat;\n utcParse = locale.utcParse;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-time-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time-format/src/index.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-time-format/src/index.js ***! + \**************************************************/ +/*! exports provided: timeFormatDefaultLocale, timeFormat, timeParse, utcFormat, utcParse, timeFormatLocale, isoFormat, isoParse */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/d3-time-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatDefaultLocale\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcParse\"]; });\n\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locale */ \"./node_modules/d3-time-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatLocale\", function() { return _locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _isoFormat__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./isoFormat */ \"./node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoFormat\", function() { return _isoFormat__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _isoParse__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./isoParse */ \"./node_modules/d3-time-format/src/isoParse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoParse\", function() { return _isoParse__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-time-format/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time-format/src/isoFormat.js": +/*!******************************************************!*\ + !*** ./node_modules/d3-time-format/src/isoFormat.js ***! + \******************************************************/ +/*! exports provided: isoSpecifier, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isoSpecifier\", function() { return isoSpecifier; });\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/d3-time-format/src/defaultLocale.js\");\n\n\nvar isoSpecifier = \"%Y-%m-%dT%H:%M:%S.%LZ\";\n\nfunction formatIsoNative(date) {\n return date.toISOString();\n}\n\nvar formatIso = Date.prototype.toISOString\n ? formatIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"])(isoSpecifier);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (formatIso);\n\n\n//# sourceURL=webpack:///./node_modules/d3-time-format/src/isoFormat.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time-format/src/isoParse.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-time-format/src/isoParse.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _isoFormat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isoFormat */ \"./node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/d3-time-format/src/defaultLocale.js\");\n\n\n\nfunction parseIsoNative(string) {\n var date = new Date(string);\n return isNaN(date) ? null : date;\n}\n\nvar parseIso = +new Date(\"2000-01-01T00:00:00.000Z\")\n ? parseIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_1__[\"utcParse\"])(_isoFormat__WEBPACK_IMPORTED_MODULE_0__[\"isoSpecifier\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (parseIso);\n\n\n//# sourceURL=webpack:///./node_modules/d3-time-format/src/isoParse.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time-format/src/locale.js": +/*!***************************************************!*\ + !*** ./node_modules/d3-time-format/src/locale.js ***! + \***************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatLocale; });\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-time */ \"./node_modules/d3-time/src/index.js\");\n\n\nfunction localDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);\n date.setFullYear(d.y);\n return date;\n }\n return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);\n}\n\nfunction utcDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));\n date.setUTCFullYear(d.y);\n return date;\n }\n return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));\n}\n\nfunction newYear(y) {\n return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};\n}\n\nfunction formatLocale(locale) {\n var locale_dateTime = locale.dateTime,\n locale_date = locale.date,\n locale_time = locale.time,\n locale_periods = locale.periods,\n locale_weekdays = locale.days,\n locale_shortWeekdays = locale.shortDays,\n locale_months = locale.months,\n locale_shortMonths = locale.shortMonths;\n\n var periodRe = formatRe(locale_periods),\n periodLookup = formatLookup(locale_periods),\n weekdayRe = formatRe(locale_weekdays),\n weekdayLookup = formatLookup(locale_weekdays),\n shortWeekdayRe = formatRe(locale_shortWeekdays),\n shortWeekdayLookup = formatLookup(locale_shortWeekdays),\n monthRe = formatRe(locale_months),\n monthLookup = formatLookup(locale_months),\n shortMonthRe = formatRe(locale_shortMonths),\n shortMonthLookup = formatLookup(locale_shortMonths);\n\n var formats = {\n \"a\": formatShortWeekday,\n \"A\": formatWeekday,\n \"b\": formatShortMonth,\n \"B\": formatMonth,\n \"c\": null,\n \"d\": formatDayOfMonth,\n \"e\": formatDayOfMonth,\n \"f\": formatMicroseconds,\n \"H\": formatHour24,\n \"I\": formatHour12,\n \"j\": formatDayOfYear,\n \"L\": formatMilliseconds,\n \"m\": formatMonthNumber,\n \"M\": formatMinutes,\n \"p\": formatPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatSeconds,\n \"u\": formatWeekdayNumberMonday,\n \"U\": formatWeekNumberSunday,\n \"V\": formatWeekNumberISO,\n \"w\": formatWeekdayNumberSunday,\n \"W\": formatWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatYear,\n \"Y\": formatFullYear,\n \"Z\": formatZone,\n \"%\": formatLiteralPercent\n };\n\n var utcFormats = {\n \"a\": formatUTCShortWeekday,\n \"A\": formatUTCWeekday,\n \"b\": formatUTCShortMonth,\n \"B\": formatUTCMonth,\n \"c\": null,\n \"d\": formatUTCDayOfMonth,\n \"e\": formatUTCDayOfMonth,\n \"f\": formatUTCMicroseconds,\n \"H\": formatUTCHour24,\n \"I\": formatUTCHour12,\n \"j\": formatUTCDayOfYear,\n \"L\": formatUTCMilliseconds,\n \"m\": formatUTCMonthNumber,\n \"M\": formatUTCMinutes,\n \"p\": formatUTCPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatUTCSeconds,\n \"u\": formatUTCWeekdayNumberMonday,\n \"U\": formatUTCWeekNumberSunday,\n \"V\": formatUTCWeekNumberISO,\n \"w\": formatUTCWeekdayNumberSunday,\n \"W\": formatUTCWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatUTCYear,\n \"Y\": formatUTCFullYear,\n \"Z\": formatUTCZone,\n \"%\": formatLiteralPercent\n };\n\n var parses = {\n \"a\": parseShortWeekday,\n \"A\": parseWeekday,\n \"b\": parseShortMonth,\n \"B\": parseMonth,\n \"c\": parseLocaleDateTime,\n \"d\": parseDayOfMonth,\n \"e\": parseDayOfMonth,\n \"f\": parseMicroseconds,\n \"H\": parseHour24,\n \"I\": parseHour24,\n \"j\": parseDayOfYear,\n \"L\": parseMilliseconds,\n \"m\": parseMonthNumber,\n \"M\": parseMinutes,\n \"p\": parsePeriod,\n \"Q\": parseUnixTimestamp,\n \"s\": parseUnixTimestampSeconds,\n \"S\": parseSeconds,\n \"u\": parseWeekdayNumberMonday,\n \"U\": parseWeekNumberSunday,\n \"V\": parseWeekNumberISO,\n \"w\": parseWeekdayNumberSunday,\n \"W\": parseWeekNumberMonday,\n \"x\": parseLocaleDate,\n \"X\": parseLocaleTime,\n \"y\": parseYear,\n \"Y\": parseFullYear,\n \"Z\": parseZone,\n \"%\": parseLiteralPercent\n };\n\n // These recursive directive definitions must be deferred.\n formats.x = newFormat(locale_date, formats);\n formats.X = newFormat(locale_time, formats);\n formats.c = newFormat(locale_dateTime, formats);\n utcFormats.x = newFormat(locale_date, utcFormats);\n utcFormats.X = newFormat(locale_time, utcFormats);\n utcFormats.c = newFormat(locale_dateTime, utcFormats);\n\n function newFormat(specifier, formats) {\n return function(date) {\n var string = [],\n i = -1,\n j = 0,\n n = specifier.length,\n c,\n pad,\n format;\n\n if (!(date instanceof Date)) date = new Date(+date);\n\n while (++i < n) {\n if (specifier.charCodeAt(i) === 37) {\n string.push(specifier.slice(j, i));\n if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);\n else pad = c === \"e\" ? \" \" : \"0\";\n if (format = formats[c]) c = format(date, pad);\n string.push(c);\n j = i + 1;\n }\n }\n\n string.push(specifier.slice(j, i));\n return string.join(\"\");\n };\n }\n\n function newParse(specifier, newDate) {\n return function(string) {\n var d = newYear(1900),\n i = parseSpecifier(d, specifier, string += \"\", 0),\n week, day;\n if (i != string.length) return null;\n\n // If a UNIX timestamp is specified, return it.\n if (\"Q\" in d) return new Date(d.Q);\n\n // The am-pm flag is 0 for AM, and 1 for PM.\n if (\"p\" in d) d.H = d.H % 12 + d.p * 12;\n\n // Convert day-of-week and week-of-year to day-of-year.\n if (\"V\" in d) {\n if (d.V < 1 || d.V > 53) return null;\n if (!(\"w\" in d)) d.w = 1;\n if (\"Z\" in d) {\n week = utcDate(newYear(d.y)), day = week.getUTCDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getUTCFullYear();\n d.m = week.getUTCMonth();\n d.d = week.getUTCDate() + (d.w + 6) % 7;\n } else {\n week = newDate(newYear(d.y)), day = week.getDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getFullYear();\n d.m = week.getMonth();\n d.d = week.getDate() + (d.w + 6) % 7;\n }\n } else if (\"W\" in d || \"U\" in d) {\n if (!(\"w\" in d)) d.w = \"u\" in d ? d.u % 7 : \"W\" in d ? 1 : 0;\n day = \"Z\" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();\n d.m = 0;\n d.d = \"W\" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;\n }\n\n // If a time zone is specified, all fields are interpreted as UTC and then\n // offset according to the specified time zone.\n if (\"Z\" in d) {\n d.H += d.Z / 100 | 0;\n d.M += d.Z % 100;\n return utcDate(d);\n }\n\n // Otherwise, all fields are in local time.\n return newDate(d);\n };\n }\n\n function parseSpecifier(d, specifier, string, j) {\n var i = 0,\n n = specifier.length,\n m = string.length,\n c,\n parse;\n\n while (i < n) {\n if (j >= m) return -1;\n c = specifier.charCodeAt(i++);\n if (c === 37) {\n c = specifier.charAt(i++);\n parse = parses[c in pads ? specifier.charAt(i++) : c];\n if (!parse || ((j = parse(d, string, j)) < 0)) return -1;\n } else if (c != string.charCodeAt(j++)) {\n return -1;\n }\n }\n\n return j;\n }\n\n function parsePeriod(d, string, i) {\n var n = periodRe.exec(string.slice(i));\n return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortWeekday(d, string, i) {\n var n = shortWeekdayRe.exec(string.slice(i));\n return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseWeekday(d, string, i) {\n var n = weekdayRe.exec(string.slice(i));\n return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortMonth(d, string, i) {\n var n = shortMonthRe.exec(string.slice(i));\n return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseMonth(d, string, i) {\n var n = monthRe.exec(string.slice(i));\n return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseLocaleDateTime(d, string, i) {\n return parseSpecifier(d, locale_dateTime, string, i);\n }\n\n function parseLocaleDate(d, string, i) {\n return parseSpecifier(d, locale_date, string, i);\n }\n\n function parseLocaleTime(d, string, i) {\n return parseSpecifier(d, locale_time, string, i);\n }\n\n function formatShortWeekday(d) {\n return locale_shortWeekdays[d.getDay()];\n }\n\n function formatWeekday(d) {\n return locale_weekdays[d.getDay()];\n }\n\n function formatShortMonth(d) {\n return locale_shortMonths[d.getMonth()];\n }\n\n function formatMonth(d) {\n return locale_months[d.getMonth()];\n }\n\n function formatPeriod(d) {\n return locale_periods[+(d.getHours() >= 12)];\n }\n\n function formatUTCShortWeekday(d) {\n return locale_shortWeekdays[d.getUTCDay()];\n }\n\n function formatUTCWeekday(d) {\n return locale_weekdays[d.getUTCDay()];\n }\n\n function formatUTCShortMonth(d) {\n return locale_shortMonths[d.getUTCMonth()];\n }\n\n function formatUTCMonth(d) {\n return locale_months[d.getUTCMonth()];\n }\n\n function formatUTCPeriod(d) {\n return locale_periods[+(d.getUTCHours() >= 12)];\n }\n\n return {\n format: function(specifier) {\n var f = newFormat(specifier += \"\", formats);\n f.toString = function() { return specifier; };\n return f;\n },\n parse: function(specifier) {\n var p = newParse(specifier += \"\", localDate);\n p.toString = function() { return specifier; };\n return p;\n },\n utcFormat: function(specifier) {\n var f = newFormat(specifier += \"\", utcFormats);\n f.toString = function() { return specifier; };\n return f;\n },\n utcParse: function(specifier) {\n var p = newParse(specifier, utcDate);\n p.toString = function() { return specifier; };\n return p;\n }\n };\n}\n\nvar pads = {\"-\": \"\", \"_\": \" \", \"0\": \"0\"},\n numberRe = /^\\s*\\d+/, // note: ignores next directive\n percentRe = /^%/,\n requoteRe = /[\\\\^$*+?|[\\]().{}]/g;\n\nfunction pad(value, fill, width) {\n var sign = value < 0 ? \"-\" : \"\",\n string = (sign ? -value : value) + \"\",\n length = string.length;\n return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);\n}\n\nfunction requote(s) {\n return s.replace(requoteRe, \"\\\\$&\");\n}\n\nfunction formatRe(names) {\n return new RegExp(\"^(?:\" + names.map(requote).join(\"|\") + \")\", \"i\");\n}\n\nfunction formatLookup(names) {\n var map = {}, i = -1, n = names.length;\n while (++i < n) map[names[i].toLowerCase()] = i;\n return map;\n}\n\nfunction parseWeekdayNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.w = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekdayNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.u = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.U = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberISO(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.V = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.W = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseFullYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 4));\n return n ? (d.y = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;\n}\n\nfunction parseZone(d, string, i) {\n var n = /^(Z)|([+-]\\d\\d)(?::?(\\d\\d))?/.exec(string.slice(i, i + 6));\n return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || \"00\")), i + n[0].length) : -1;\n}\n\nfunction parseMonthNumber(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.m = n[0] - 1, i + n[0].length) : -1;\n}\n\nfunction parseDayOfMonth(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseDayOfYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseHour24(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.H = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMinutes(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.M = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.S = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMilliseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.L = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMicroseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 6));\n return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;\n}\n\nfunction parseLiteralPercent(d, string, i) {\n var n = percentRe.exec(string.slice(i, i + 1));\n return n ? i + n[0].length : -1;\n}\n\nfunction parseUnixTimestamp(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseUnixTimestampSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;\n}\n\nfunction formatDayOfMonth(d, p) {\n return pad(d.getDate(), p, 2);\n}\n\nfunction formatHour24(d, p) {\n return pad(d.getHours(), p, 2);\n}\n\nfunction formatHour12(d, p) {\n return pad(d.getHours() % 12 || 12, p, 2);\n}\n\nfunction formatDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 3);\n}\n\nfunction formatMilliseconds(d, p) {\n return pad(d.getMilliseconds(), p, 3);\n}\n\nfunction formatMicroseconds(d, p) {\n return formatMilliseconds(d, p) + \"000\";\n}\n\nfunction formatMonthNumber(d, p) {\n return pad(d.getMonth() + 1, p, 2);\n}\n\nfunction formatMinutes(d, p) {\n return pad(d.getMinutes(), p, 2);\n}\n\nfunction formatSeconds(d, p) {\n return pad(d.getSeconds(), p, 2);\n}\n\nfunction formatWeekdayNumberMonday(d) {\n var day = d.getDay();\n return day === 0 ? 7 : day;\n}\n\nfunction formatWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatWeekNumberISO(d, p) {\n var day = d.getDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d).getDay() === 4), p, 2);\n}\n\nfunction formatWeekdayNumberSunday(d) {\n return d.getDay();\n}\n\nfunction formatWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatYear(d, p) {\n return pad(d.getFullYear() % 100, p, 2);\n}\n\nfunction formatFullYear(d, p) {\n return pad(d.getFullYear() % 10000, p, 4);\n}\n\nfunction formatZone(d) {\n var z = d.getTimezoneOffset();\n return (z > 0 ? \"-\" : (z *= -1, \"+\"))\n + pad(z / 60 | 0, \"0\", 2)\n + pad(z % 60, \"0\", 2);\n}\n\nfunction formatUTCDayOfMonth(d, p) {\n return pad(d.getUTCDate(), p, 2);\n}\n\nfunction formatUTCHour24(d, p) {\n return pad(d.getUTCHours(), p, 2);\n}\n\nfunction formatUTCHour12(d, p) {\n return pad(d.getUTCHours() % 12 || 12, p, 2);\n}\n\nfunction formatUTCDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 3);\n}\n\nfunction formatUTCMilliseconds(d, p) {\n return pad(d.getUTCMilliseconds(), p, 3);\n}\n\nfunction formatUTCMicroseconds(d, p) {\n return formatUTCMilliseconds(d, p) + \"000\";\n}\n\nfunction formatUTCMonthNumber(d, p) {\n return pad(d.getUTCMonth() + 1, p, 2);\n}\n\nfunction formatUTCMinutes(d, p) {\n return pad(d.getUTCMinutes(), p, 2);\n}\n\nfunction formatUTCSeconds(d, p) {\n return pad(d.getUTCSeconds(), p, 2);\n}\n\nfunction formatUTCWeekdayNumberMonday(d) {\n var dow = d.getUTCDay();\n return dow === 0 ? 7 : dow;\n}\n\nfunction formatUTCWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCWeekNumberISO(d, p) {\n var day = d.getUTCDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d).getUTCDay() === 4), p, 2);\n}\n\nfunction formatUTCWeekdayNumberSunday(d) {\n return d.getUTCDay();\n}\n\nfunction formatUTCWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCYear(d, p) {\n return pad(d.getUTCFullYear() % 100, p, 2);\n}\n\nfunction formatUTCFullYear(d, p) {\n return pad(d.getUTCFullYear() % 10000, p, 4);\n}\n\nfunction formatUTCZone() {\n return \"+0000\";\n}\n\nfunction formatLiteralPercent() {\n return \"%\";\n}\n\nfunction formatUnixTimestamp(d) {\n return +d;\n}\n\nfunction formatUnixTimestampSeconds(d) {\n return Math.floor(+d / 1000);\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-time-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/day.js": +/*!*****************************************!*\ + !*** ./node_modules/d3-time/src/day.js ***! + \*****************************************/ +/*! exports provided: default, days */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"days\", function() { return days; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar day = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setDate(date.getDate() + step);\n}, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (day);\nvar days = day.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/day.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/duration.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-time/src/duration.js ***! + \**********************************************/ +/*! exports provided: durationSecond, durationMinute, durationHour, durationDay, durationWeek */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationSecond\", function() { return durationSecond; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationMinute\", function() { return durationMinute; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationHour\", function() { return durationHour; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationDay\", function() { return durationDay; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationWeek\", function() { return durationWeek; });\nvar durationSecond = 1e3;\nvar durationMinute = 6e4;\nvar durationHour = 36e5;\nvar durationDay = 864e5;\nvar durationWeek = 6048e5;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/duration.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/hour.js": +/*!******************************************!*\ + !*** ./node_modules/d3-time/src/hour.js ***! + \******************************************/ +/*! exports provided: default, hours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hours\", function() { return hours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar hour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n var offset = date.getTimezoneOffset() * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"] % _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n if (offset < 0) offset += _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n date.setTime(Math.floor((+date - offset) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"] + offset);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hour);\nvar hours = hour.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/hour.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-time/src/index.js ***! + \*******************************************/ +/*! exports provided: timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _millisecond__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./millisecond */ \"./node_modules/d3-time/src/millisecond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony import */ var _second__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./second */ \"./node_modules/d3-time/src/second.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony import */ var _minute__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./minute */ \"./node_modules/d3-time/src/minute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"minutes\"]; });\n\n/* harmony import */ var _hour__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./hour */ \"./node_modules/d3-time/src/hour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"hours\"]; });\n\n/* harmony import */ var _day__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./day */ \"./node_modules/d3-time/src/day.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"days\"]; });\n\n/* harmony import */ var _week__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./week */ \"./node_modules/d3-time/src/week.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"monday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"mondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"friday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"fridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturdays\"]; });\n\n/* harmony import */ var _month__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./month */ \"./node_modules/d3-time/src/month.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"months\"]; });\n\n/* harmony import */ var _year__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./year */ \"./node_modules/d3-time/src/year.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"years\"]; });\n\n/* harmony import */ var _utcMinute__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./utcMinute */ \"./node_modules/d3-time/src/utcMinute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"utcMinutes\"]; });\n\n/* harmony import */ var _utcHour__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./utcHour */ \"./node_modules/d3-time/src/utcHour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"utcHours\"]; });\n\n/* harmony import */ var _utcDay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./utcDay */ \"./node_modules/d3-time/src/utcDay.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"utcDays\"]; });\n\n/* harmony import */ var _utcWeek__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./utcWeek */ \"./node_modules/d3-time/src/utcWeek.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturdays\"]; });\n\n/* harmony import */ var _utcMonth__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./utcMonth */ \"./node_modules/d3-time/src/utcMonth.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"utcMonths\"]; });\n\n/* harmony import */ var _utcYear__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./utcYear */ \"./node_modules/d3-time/src/utcYear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"utcYears\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/interval.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-time/src/interval.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return newInterval; });\nvar t0 = new Date,\n t1 = new Date;\n\nfunction newInterval(floori, offseti, count, field) {\n\n function interval(date) {\n return floori(date = new Date(+date)), date;\n }\n\n interval.floor = interval;\n\n interval.ceil = function(date) {\n return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n };\n\n interval.round = function(date) {\n var d0 = interval(date),\n d1 = interval.ceil(date);\n return date - d0 < d1 - date ? d0 : d1;\n };\n\n interval.offset = function(date, step) {\n return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n };\n\n interval.range = function(start, stop, step) {\n var range = [], previous;\n start = interval.ceil(start);\n step = step == null ? 1 : Math.floor(step);\n if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n while (previous < start && start < stop);\n return range;\n };\n\n interval.filter = function(test) {\n return newInterval(function(date) {\n if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n }, function(date, step) {\n if (date >= date) {\n if (step < 0) while (++step <= 0) {\n while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n } else while (--step >= 0) {\n while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n }\n }\n });\n };\n\n if (count) {\n interval.count = function(start, end) {\n t0.setTime(+start), t1.setTime(+end);\n floori(t0), floori(t1);\n return Math.floor(count(t0, t1));\n };\n\n interval.every = function(step) {\n step = Math.floor(step);\n return !isFinite(step) || !(step > 0) ? null\n : !(step > 1) ? interval\n : interval.filter(field\n ? function(d) { return field(d) % step === 0; }\n : function(d) { return interval.count(0, d) % step === 0; });\n };\n }\n\n return interval;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/millisecond.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-time/src/millisecond.js ***! + \*************************************************/ +/*! exports provided: default, milliseconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"milliseconds\", function() { return milliseconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n\n\nvar millisecond = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function() {\n // noop\n}, function(date, step) {\n date.setTime(+date + step);\n}, function(start, end) {\n return end - start;\n});\n\n// An optimized implementation for this simple case.\nmillisecond.every = function(k) {\n k = Math.floor(k);\n if (!isFinite(k) || !(k > 0)) return null;\n if (!(k > 1)) return millisecond;\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / k) * k);\n }, function(date, step) {\n date.setTime(+date + step * k);\n }, function(start, end) {\n return (end - start) / k;\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (millisecond);\nvar milliseconds = millisecond.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/millisecond.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/minute.js": +/*!********************************************!*\ + !*** ./node_modules/d3-time/src/minute.js ***! + \********************************************/ +/*! exports provided: default, minutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"minutes\", function() { return minutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar minute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (minute);\nvar minutes = minute.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/minute.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/month.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-time/src/month.js ***! + \*******************************************/ +/*! exports provided: default, months */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"months\", function() { return months; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n\n\nvar month = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setMonth(date.getMonth() + step);\n}, function(start, end) {\n return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;\n}, function(date) {\n return date.getMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (month);\nvar months = month.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/month.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/second.js": +/*!********************************************!*\ + !*** ./node_modules/d3-time/src/second.js ***! + \********************************************/ +/*! exports provided: default, seconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"seconds\", function() { return seconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar second = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"];\n}, function(date) {\n return date.getUTCSeconds();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (second);\nvar seconds = second.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/second.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcDay.js": +/*!********************************************!*\ + !*** ./node_modules/d3-time/src/utcDay.js ***! + \********************************************/ +/*! exports provided: default, utcDays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return utcDays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcDay = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getUTCDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcDay);\nvar utcDays = utcDay.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcDay.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcHour.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-time/src/utcHour.js ***! + \*********************************************/ +/*! exports provided: default, utcHours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return utcHours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcHour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMinutes(0, 0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getUTCHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcHour);\nvar utcHours = utcHour.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcHour.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcMinute.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-time/src/utcMinute.js ***! + \***********************************************/ +/*! exports provided: default, utcMinutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return utcMinutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcMinute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCSeconds(0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getUTCMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMinute);\nvar utcMinutes = utcMinute.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcMinute.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcMonth.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-time/src/utcMonth.js ***! + \**********************************************/ +/*! exports provided: default, utcMonths */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return utcMonths; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n\n\nvar utcMonth = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCMonth(date.getUTCMonth() + step);\n}, function(start, end) {\n return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n}, function(date) {\n return date.getUTCMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMonth);\nvar utcMonths = utcMonth.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcMonth.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcWeek.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-time/src/utcWeek.js ***! + \*********************************************/ +/*! exports provided: utcSunday, utcMonday, utcTuesday, utcWednesday, utcThursday, utcFriday, utcSaturday, utcSundays, utcMondays, utcTuesdays, utcWednesdays, utcThursdays, utcFridays, utcSaturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return utcSunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return utcMonday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return utcTuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return utcWednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return utcThursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return utcFriday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return utcSaturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return utcSundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return utcMondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return utcTuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return utcWednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return utcThursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return utcFridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return utcSaturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nfunction utcWeekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step * 7);\n }, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar utcSunday = utcWeekday(0);\nvar utcMonday = utcWeekday(1);\nvar utcTuesday = utcWeekday(2);\nvar utcWednesday = utcWeekday(3);\nvar utcThursday = utcWeekday(4);\nvar utcFriday = utcWeekday(5);\nvar utcSaturday = utcWeekday(6);\n\nvar utcSundays = utcSunday.range;\nvar utcMondays = utcMonday.range;\nvar utcTuesdays = utcTuesday.range;\nvar utcWednesdays = utcWednesday.range;\nvar utcThursdays = utcThursday.range;\nvar utcFridays = utcFriday.range;\nvar utcSaturdays = utcSaturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcWeek.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/utcYear.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-time/src/utcYear.js ***! + \*********************************************/ +/*! exports provided: default, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return utcYears; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n\n\nvar utcYear = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step);\n}, function(start, end) {\n return end.getUTCFullYear() - start.getUTCFullYear();\n}, function(date) {\n return date.getUTCFullYear();\n});\n\n// An optimized implementation for this simple case.\nutcYear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcYear);\nvar utcYears = utcYear.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/utcYear.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/week.js": +/*!******************************************!*\ + !*** ./node_modules/d3-time/src/week.js ***! + \******************************************/ +/*! exports provided: sunday, monday, tuesday, wednesday, thursday, friday, saturday, sundays, mondays, tuesdays, wednesdays, thursdays, fridays, saturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sunday\", function() { return sunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monday\", function() { return monday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesday\", function() { return tuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesday\", function() { return wednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursday\", function() { return thursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"friday\", function() { return friday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturday\", function() { return saturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sundays\", function() { return sundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mondays\", function() { return mondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesdays\", function() { return tuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesdays\", function() { return wednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursdays\", function() { return thursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fridays\", function() { return fridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturdays\", function() { return saturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-time/src/duration.js\");\n\n\n\nfunction weekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setDate(date.getDate() + step * 7);\n }, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar sunday = weekday(0);\nvar monday = weekday(1);\nvar tuesday = weekday(2);\nvar wednesday = weekday(3);\nvar thursday = weekday(4);\nvar friday = weekday(5);\nvar saturday = weekday(6);\n\nvar sundays = sunday.range;\nvar mondays = monday.range;\nvar tuesdays = tuesday.range;\nvar wednesdays = wednesday.range;\nvar thursdays = thursday.range;\nvar fridays = friday.range;\nvar saturdays = saturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/week.js?"); + +/***/ }), + +/***/ "./node_modules/d3-time/src/year.js": +/*!******************************************!*\ + !*** ./node_modules/d3-time/src/year.js ***! + \******************************************/ +/*! exports provided: default, years */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"years\", function() { return years; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-time/src/interval.js\");\n\n\nvar year = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setFullYear(date.getFullYear() + step);\n}, function(start, end) {\n return end.getFullYear() - start.getFullYear();\n}, function(date) {\n return date.getFullYear();\n});\n\n// An optimized implementation for this simple case.\nyear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setFullYear(date.getFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (year);\nvar years = year.range;\n\n\n//# sourceURL=webpack:///./node_modules/d3-time/src/year.js?"); + +/***/ }), + +/***/ "./node_modules/d3-timer/src/index.js": +/*!********************************************!*\ + !*** ./node_modules/d3-timer/src/index.js ***! + \********************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./timeout */ \"./node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interval */ \"./node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-timer/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-timer/src/interval.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-timer/src/interval.js ***! + \***********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/d3-timer/src/timeout.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-timer/src/timeout.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/d3-timer/src/timer.js": +/*!********************************************!*\ + !*** ./node_modules/d3-timer/src/timer.js ***! + \********************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/active.js": +/*!**************************************************!*\ + !*** ./node_modules/d3-transition/src/active.js ***! + \**************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\nvar root = [null];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n i;\n\n if (schedules) {\n name = name == null ? null : name + \"\";\n for (i in schedules) {\n if ((schedule = schedules[i]).state > _transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"SCHEDULED\"] && schedule.name === name) {\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]([[node]], root, name, +i);\n }\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/active.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/index.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-transition/src/index.js ***! + \*************************************************/ +/*! exports provided: transition, active, interrupt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/d3-transition/src/selection/index.js\");\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return _transition_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _active__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./active */ \"./node_modules/d3-transition/src/active.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return _active__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/d3-transition/src/interrupt.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return _interrupt__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/interrupt.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-transition/src/interrupt.js ***! + \*****************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n active,\n empty = true,\n i;\n\n if (!schedules) return;\n\n name = name == null ? null : name + \"\";\n\n for (i in schedules) {\n if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n active = schedule.state > _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"STARTING\"] && schedule.state < _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDING\"];\n schedule.state = _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDED\"];\n schedule.timer.stop();\n if (active) schedule.on.call(\"interrupt\", node, node.__data__, schedule.index, schedule.group);\n delete schedules[i];\n }\n\n if (empty) delete node.__transition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/selection/index.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-transition/src/selection/index.js ***! + \***********************************************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/d3-transition/src/selection/interrupt.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./transition */ \"./node_modules/d3-transition/src/selection/transition.js\");\n\n\n\n\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.interrupt = _interrupt__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.transition = _transition__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/selection/interrupt.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-transition/src/selection/interrupt.js ***! + \***************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../interrupt */ \"./node_modules/d3-transition/src/interrupt.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return this.each(function() {\n Object(_interrupt__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(this, name);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/selection/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/selection/transition.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-transition/src/selection/transition.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../transition/index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-ease */ \"./node_modules/d3-ease/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-timer */ \"./node_modules/d3-timer/src/index.js\");\n\n\n\n\n\nvar defaultTiming = {\n time: null, // Set on use.\n delay: 0,\n duration: 250,\n ease: d3_ease__WEBPACK_IMPORTED_MODULE_2__[\"easeCubicInOut\"]\n};\n\nfunction inherit(node, id) {\n var timing;\n while (!(timing = node.__transition) || !(timing = timing[id])) {\n if (!(node = node.parentNode)) {\n return defaultTiming.time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), defaultTiming;\n }\n }\n return timing;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var id,\n timing;\n\n if (name instanceof _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]) {\n id = name._id, name = name._name;\n } else {\n id = Object(_transition_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])(), (timing = defaultTiming).time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), name = name == null ? null : name + \"\";\n }\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n Object(_transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id, i, group, timing || inherit(node, id));\n }\n }\n }\n\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/selection/transition.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/attr.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/attr.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrConstantNS(fullname, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttribute(name);\n value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction attrFunctionNS(fullname, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"namespace\"])(name), i = fullname === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformSvg\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return this.attrTween(name, typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"attr.\" + name, value))\n : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/attr.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/attrTween.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/attrTween.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n\n\nfunction attrTweenNS(fullname, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttributeNS(fullname.space, fullname.local, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\nfunction attrTween(name, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttribute(name, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var key = \"attr.\" + name;\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"namespace\"])(name);\n return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/attrTween.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/delay.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/delay.js ***! + \************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction delayFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = +value.apply(this, arguments);\n };\n}\n\nfunction delayConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? delayFunction\n : delayConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).delay;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/delay.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/duration.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/duration.js ***! + \***************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction durationFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = +value.apply(this, arguments);\n };\n}\n\nfunction durationConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? durationFunction\n : durationConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).duration;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/duration.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/ease.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/ease.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new Error;\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).ease = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each(easeConstant(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).ease;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/ease.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/filter.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/filter.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-transition/src/transition/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"matcher\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/filter.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/index.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/index.js ***! + \************************************************************/ +/*! exports provided: Transition, default, newId */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transition\", function() { return Transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"newId\", function() { return newId; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attr */ \"./node_modules/d3-transition/src/transition/attr.js\");\n/* harmony import */ var _attrTween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./attrTween */ \"./node_modules/d3-transition/src/transition/attrTween.js\");\n/* harmony import */ var _delay__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./delay */ \"./node_modules/d3-transition/src/transition/delay.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./duration */ \"./node_modules/d3-transition/src/transition/duration.js\");\n/* harmony import */ var _ease__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ease */ \"./node_modules/d3-transition/src/transition/ease.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./filter */ \"./node_modules/d3-transition/src/transition/filter.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./merge */ \"./node_modules/d3-transition/src/transition/merge.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./on */ \"./node_modules/d3-transition/src/transition/on.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./remove */ \"./node_modules/d3-transition/src/transition/remove.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./select */ \"./node_modules/d3-transition/src/transition/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/d3-transition/src/transition/selectAll.js\");\n/* harmony import */ var _selection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selection */ \"./node_modules/d3-transition/src/transition/selection.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./style */ \"./node_modules/d3-transition/src/transition/style.js\");\n/* harmony import */ var _styleTween__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./styleTween */ \"./node_modules/d3-transition/src/transition/styleTween.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./text */ \"./node_modules/d3-transition/src/transition/text.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./transition */ \"./node_modules/d3-transition/src/transition/transition.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./tween */ \"./node_modules/d3-transition/src/transition/tween.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar id = 0;\n\nfunction Transition(groups, parents, name, id) {\n this._groups = groups;\n this._parents = parents;\n this._name = name;\n this._id = id;\n}\n\nfunction transition(name) {\n return Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"])().transition(name);\n}\n\nfunction newId() {\n return ++id;\n}\n\nvar selection_prototype = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype;\n\nTransition.prototype = transition.prototype = {\n constructor: Transition,\n select: _select__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n selection: _selection__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n transition: _transition__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n call: selection_prototype.call,\n nodes: selection_prototype.nodes,\n node: selection_prototype.node,\n size: selection_prototype.size,\n empty: selection_prototype.empty,\n each: selection_prototype.each,\n on: _on__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n attrTween: _attrTween__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n styleTween: _styleTween__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n tween: _tween__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n delay: _delay__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n duration: _duration__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n ease: _ease__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/interpolate.js": +/*!******************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/interpolate.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var c;\n return (typeof b === \"number\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"]\n : (c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"])\n : d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateString\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/merge.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/merge.js ***! + \************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-transition/src/transition/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(transition) {\n if (transition._id !== this._id) throw new Error;\n\n for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](merges, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/merge.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/on.js": +/*!*********************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/on.js ***! + \*********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n var i = t.indexOf(\".\");\n if (i >= 0) t = t.slice(0, i);\n return !t || t === \"start\";\n });\n}\n\nfunction onFunction(id, name, listener) {\n var on0, on1, sit = start(name) ? _schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"] : _schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"];\n return function() {\n var schedule = sit(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n\n schedule.on = on1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, listener) {\n var id = this._id;\n\n return arguments.length < 2\n ? Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).on.on(name)\n : this.each(onFunction(id, name, listener));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/on.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/remove.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/remove.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transition) if (+i !== id) return;\n if (parent) parent.removeChild(this);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.on(\"end.remove\", removeFunction(this._id));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/remove.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/schedule.js": +/*!***************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/schedule.js ***! + \***************************************************************/ +/*! exports provided: CREATED, SCHEDULED, STARTING, STARTED, RUNNING, ENDING, ENDED, default, init, set, get */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CREATED\", function() { return CREATED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SCHEDULED\", function() { return SCHEDULED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTING\", function() { return STARTING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTED\", function() { return STARTED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RUNNING\", function() { return RUNNING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDING\", function() { return ENDING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDED\", function() { return ENDED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return set; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"get\", function() { return get; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-timer */ \"./node_modules/d3-timer/src/index.js\");\n\n\n\nvar emptyOn = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"end\", \"interrupt\");\nvar emptyTween = [];\n\nvar CREATED = 0;\nvar SCHEDULED = 1;\nvar STARTING = 2;\nvar STARTED = 3;\nvar RUNNING = 4;\nvar ENDING = 5;\nvar ENDED = 6;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name, id, index, group, timing) {\n var schedules = node.__transition;\n if (!schedules) node.__transition = {};\n else if (id in schedules) return;\n create(node, id, {\n name: name,\n index: index, // For context during callback.\n group: group, // For context during callback.\n on: emptyOn,\n tween: emptyTween,\n time: timing.time,\n delay: timing.delay,\n duration: timing.duration,\n ease: timing.ease,\n timer: null,\n state: CREATED\n });\n});\n\nfunction init(node, id) {\n var schedule = get(node, id);\n if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n return schedule;\n}\n\nfunction set(node, id) {\n var schedule = get(node, id);\n if (schedule.state > STARTING) throw new Error(\"too late; already started\");\n return schedule;\n}\n\nfunction get(node, id) {\n var schedule = node.__transition;\n if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n return schedule;\n}\n\nfunction create(node, id, self) {\n var schedules = node.__transition,\n tween;\n\n // Initialize the self timer when the transition is created.\n // Note the actual delay is not known until the first callback!\n schedules[id] = self;\n self.timer = Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timer\"])(schedule, 0, self.time);\n\n function schedule(elapsed) {\n self.state = SCHEDULED;\n self.timer.restart(start, self.delay, self.time);\n\n // If the elapsed delay is less than our first sleep, start immediately.\n if (self.delay <= elapsed) start(elapsed - self.delay);\n }\n\n function start(elapsed) {\n var i, j, n, o;\n\n // If the state is not SCHEDULED, then we previously errored on start.\n if (self.state !== SCHEDULED) return stop();\n\n for (i in schedules) {\n o = schedules[i];\n if (o.name !== self.name) continue;\n\n // While this element already has a starting transition during this frame,\n // defer starting an interrupting transition until that transition has a\n // chance to tick (and possibly end); see d3/d3-transition#54!\n if (o.state === STARTED) return Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(start);\n\n // Interrupt the active transition, if any.\n // Dispatch the interrupt event.\n if (o.state === RUNNING) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n\n // Cancel any pre-empted transitions. No interrupt event is dispatched\n // because the cancelled transitions never started. Note that this also\n // removes this transition from the pending list!\n else if (+i < id) {\n o.state = ENDED;\n o.timer.stop();\n delete schedules[i];\n }\n }\n\n // Defer the first tick to end of the current frame; see d3/d3#1576.\n // Note the transition may be canceled after start and before the first tick!\n // Note this must be scheduled before the start event; see d3/d3-transition#16!\n // Assuming this is successful, subsequent callbacks go straight to tick.\n Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(function() {\n if (self.state === STARTED) {\n self.state = RUNNING;\n self.timer.restart(tick, self.delay, self.time);\n tick(elapsed);\n }\n });\n\n // Dispatch the start event.\n // Note this must be done before the tween are initialized.\n self.state = STARTING;\n self.on.call(\"start\", node, node.__data__, self.index, self.group);\n if (self.state !== STARTING) return; // interrupted\n self.state = STARTED;\n\n // Initialize the tween, deleting null tween.\n tween = new Array(n = self.tween.length);\n for (i = 0, j = -1; i < n; ++i) {\n if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n tween[++j] = o;\n }\n }\n tween.length = j + 1;\n }\n\n function tick(elapsed) {\n var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n i = -1,\n n = tween.length;\n\n while (++i < n) {\n tween[i].call(null, t);\n }\n\n // Dispatch the end event.\n if (self.state === ENDING) {\n self.on.call(\"end\", node, node.__data__, self.index, self.group);\n stop();\n }\n }\n\n function stop() {\n self.state = ENDED;\n self.timer.stop();\n delete schedules[id];\n for (var i in schedules) return; // eslint-disable-line no-unused-vars\n delete node.__transition;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/schedule.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/select.js": +/*!*************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/select.js ***! + \*************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selector\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(subgroup[i], name, id, i, subgroup, Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id));\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/select.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/selectAll.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/selectAll.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selectorAll\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n for (var children = select.call(node, node.__data__, i, group), child, inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id), k = 0, l = children.length; k < l; ++k) {\n if (child = children[k]) {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(child, name, id, k, children, inherit);\n }\n }\n subgroups.push(children);\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/selection.js": +/*!****************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/selection.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n\n\nvar Selection = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.constructor;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Selection(this._groups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/selection.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/style.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/style.js ***! + \************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction styleRemove(name, interpolate) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction styleRemoveEnd(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction styleFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = value(this);\n if (value1 == null) value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var i = (name += \"\") === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformCss\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return value == null ? this\n .styleTween(name, styleRemove(name, i))\n .on(\"end.style.\" + name, styleRemoveEnd(name))\n : this.styleTween(name, typeof value === \"function\"\n ? styleFunction(name, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"style.\" + name, value))\n : styleConstant(name, i, value + \"\"), priority);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/style.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/styleTween.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/styleTween.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction styleTween(name, value, priority) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.style.setProperty(name, i(t), priority);\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var key = \"style.\" + (name += \"\");\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/styleTween.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/text.js": +/*!***********************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/text.js ***! + \***********************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tween */ \"./node_modules/d3-transition/src/transition/tween.js\");\n\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var value1 = value(this);\n this.textContent = value1 == null ? \"\" : value1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.tween(\"text\", typeof value === \"function\"\n ? textFunction(Object(_tween__WEBPACK_IMPORTED_MODULE_0__[\"tweenValue\"])(this, \"text\", value))\n : textConstant(value == null ? \"\" : value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/text.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/transition.js": +/*!*****************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/transition.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var name = this._name,\n id0 = this._id,\n id1 = Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])();\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n var inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"get\"])(node, id0);\n Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id1, i, group, {\n time: inherit.time + inherit.delay + inherit.duration,\n delay: 0,\n duration: inherit.duration,\n ease: inherit.ease\n });\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/transition.js?"); + +/***/ }), + +/***/ "./node_modules/d3-transition/src/transition/tween.js": +/*!************************************************************!*\ + !*** ./node_modules/d3-transition/src/transition/tween.js ***! + \************************************************************/ +/*! exports provided: default, tweenValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tweenValue\", function() { return tweenValue; });\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = tween0 = tween;\n for (var i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1 = tween1.slice();\n tween1.splice(i, 1);\n break;\n }\n }\n }\n\n schedule.tween = tween1;\n };\n}\n\nfunction tweenFunction(id, name, value) {\n var tween0, tween1;\n if (typeof value !== \"function\") throw new Error;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = (tween0 = tween).slice();\n for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1[i] = t;\n break;\n }\n }\n if (i === n) tween1.push(t);\n }\n\n schedule.tween = tween1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var id = this._id;\n\n name += \"\";\n\n if (arguments.length < 2) {\n var tween = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).tween;\n for (var i = 0, n = tween.length, t; i < n; ++i) {\n if ((t = tween[i]).name === name) {\n return t.value;\n }\n }\n return null;\n }\n\n return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n});\n\nfunction tweenValue(transition, name, value) {\n var id = transition._id;\n\n transition.each(function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id);\n (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n });\n\n return function(node) {\n return Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(node, id).value[name];\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-transition/src/transition/tween.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/Beach.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-voronoi/src/Beach.js ***! + \**********************************************/ +/*! exports provided: removeBeach, addBeach */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeBeach\", function() { return removeBeach; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"addBeach\", function() { return addBeach; });\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/d3-voronoi/src/RedBlackTree.js\");\n/* harmony import */ var _Cell__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Cell */ \"./node_modules/d3-voronoi/src/Cell.js\");\n/* harmony import */ var _Circle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Circle */ \"./node_modules/d3-voronoi/src/Circle.js\");\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Edge */ \"./node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\n\n\n\nvar beachPool = [];\n\nfunction Beach() {\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(this);\n this.edge =\n this.site =\n this.circle = null;\n}\n\nfunction createBeach(site) {\n var beach = beachPool.pop() || new Beach;\n beach.site = site;\n return beach;\n}\n\nfunction detachBeach(beach) {\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(beach);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].remove(beach);\n beachPool.push(beach);\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(beach);\n}\n\nfunction removeBeach(beach) {\n var circle = beach.circle,\n x = circle.x,\n y = circle.cy,\n vertex = [x, y],\n previous = beach.P,\n next = beach.N,\n disappearing = [beach];\n\n detachBeach(beach);\n\n var lArc = previous;\n while (lArc.circle\n && Math.abs(x - lArc.circle.x) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]\n && Math.abs(y - lArc.circle.cy) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n previous = lArc.P;\n disappearing.unshift(lArc);\n detachBeach(lArc);\n lArc = previous;\n }\n\n disappearing.unshift(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n\n var rArc = next;\n while (rArc.circle\n && Math.abs(x - rArc.circle.x) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]\n && Math.abs(y - rArc.circle.cy) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n next = rArc.N;\n disappearing.push(rArc);\n detachBeach(rArc);\n rArc = next;\n }\n\n disappearing.push(rArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(rArc);\n\n var nArcs = disappearing.length,\n iArc;\n for (iArc = 1; iArc < nArcs; ++iArc) {\n rArc = disappearing[iArc];\n lArc = disappearing[iArc - 1];\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"setEdgeEnd\"])(rArc.edge, lArc.site, rArc.site, vertex);\n }\n\n lArc = disappearing[0];\n rArc = disappearing[nArcs - 1];\n rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, rArc.site, null, vertex);\n\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n}\n\nfunction addBeach(site) {\n var x = site[0],\n directrix = site[1],\n lArc,\n rArc,\n dxl,\n dxr,\n node = _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"]._;\n\n while (node) {\n dxl = leftBreakPoint(node, directrix) - x;\n if (dxl > _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) node = node.L; else {\n dxr = x - rightBreakPoint(node, directrix);\n if (dxr > _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n if (!node.R) {\n lArc = node;\n break;\n }\n node = node.R;\n } else {\n if (dxl > -_Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n lArc = node.P;\n rArc = node;\n } else if (dxr > -_Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n lArc = node;\n rArc = node.N;\n } else {\n lArc = rArc = node;\n }\n break;\n }\n }\n }\n\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"createCell\"])(site);\n var newArc = createBeach(site);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].insert(lArc, newArc);\n\n if (!lArc && !rArc) return;\n\n if (lArc === rArc) {\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n rArc = createBeach(lArc.site);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].insert(newArc, rArc);\n newArc.edge = rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, newArc.site);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n return;\n }\n\n if (!rArc) { // && lArc\n newArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, newArc.site);\n return;\n }\n\n // else lArc !== rArc\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(rArc);\n\n var lSite = lArc.site,\n ax = lSite[0],\n ay = lSite[1],\n bx = site[0] - ax,\n by = site[1] - ay,\n rSite = rArc.site,\n cx = rSite[0] - ax,\n cy = rSite[1] - ay,\n d = 2 * (bx * cy - by * cx),\n hb = bx * bx + by * by,\n hc = cx * cx + cy * cy,\n vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];\n\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"setEdgeEnd\"])(rArc.edge, lSite, rSite, vertex);\n newArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lSite, site, null, vertex);\n rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(site, rSite, null, vertex);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n}\n\nfunction leftBreakPoint(arc, directrix) {\n var site = arc.site,\n rfocx = site[0],\n rfocy = site[1],\n pby2 = rfocy - directrix;\n\n if (!pby2) return rfocx;\n\n var lArc = arc.P;\n if (!lArc) return -Infinity;\n\n site = lArc.site;\n var lfocx = site[0],\n lfocy = site[1],\n plby2 = lfocy - directrix;\n\n if (!plby2) return lfocx;\n\n var hl = lfocx - rfocx,\n aby2 = 1 / pby2 - 1 / plby2,\n b = hl / plby2;\n\n if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;\n\n return (rfocx + lfocx) / 2;\n}\n\nfunction rightBreakPoint(arc, directrix) {\n var rArc = arc.N;\n if (rArc) return leftBreakPoint(rArc, directrix);\n var site = arc.site;\n return site[1] === directrix ? site[0] : Infinity;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/Beach.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/Cell.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-voronoi/src/Cell.js ***! + \*********************************************/ +/*! exports provided: createCell, cellHalfedgeStart, cellHalfedgeEnd, sortCellHalfedges, clipCells */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createCell\", function() { return createCell; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cellHalfedgeStart\", function() { return cellHalfedgeStart; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cellHalfedgeEnd\", function() { return cellHalfedgeEnd; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sortCellHalfedges\", function() { return sortCellHalfedges; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clipCells\", function() { return clipCells; });\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Edge */ \"./node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\nfunction createCell(site) {\n return _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][site.index] = {\n site: site,\n halfedges: []\n };\n}\n\nfunction cellHalfedgeAngle(cell, edge) {\n var site = cell.site,\n va = edge.left,\n vb = edge.right;\n if (site === vb) vb = va, va = site;\n if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);\n if (site === va) va = edge[1], vb = edge[0];\n else va = edge[0], vb = edge[1];\n return Math.atan2(va[0] - vb[0], vb[1] - va[1]);\n}\n\nfunction cellHalfedgeStart(cell, edge) {\n return edge[+(edge.left !== cell.site)];\n}\n\nfunction cellHalfedgeEnd(cell, edge) {\n return edge[+(edge.left === cell.site)];\n}\n\nfunction sortCellHalfedges() {\n for (var i = 0, n = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"].length, cell, halfedges, j, m; i < n; ++i) {\n if ((cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][i]) && (m = (halfedges = cell.halfedges).length)) {\n var index = new Array(m),\n array = new Array(m);\n for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[j]]);\n index.sort(function(i, j) { return array[j] - array[i]; });\n for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];\n for (j = 0; j < m; ++j) halfedges[j] = array[j];\n }\n }\n}\n\nfunction clipCells(x0, y0, x1, y1) {\n var nCells = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"].length,\n iCell,\n cell,\n site,\n iHalfedge,\n halfedges,\n nHalfedges,\n start,\n startX,\n startY,\n end,\n endX,\n endY,\n cover = true;\n\n for (iCell = 0; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n site = cell.site;\n halfedges = cell.halfedges;\n iHalfedge = halfedges.length;\n\n // Remove any dangling clipped edges.\n while (iHalfedge--) {\n if (!_Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[iHalfedge]]) {\n halfedges.splice(iHalfedge, 1);\n }\n }\n\n // Insert any border edges as necessary.\n iHalfedge = 0, nHalfedges = halfedges.length;\n while (iHalfedge < nHalfedges) {\n end = cellHalfedgeEnd(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[iHalfedge]]), endX = end[0], endY = end[1];\n start = cellHalfedgeStart(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];\n if (Math.abs(endX - startX) > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] || Math.abs(endY - startY) > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) {\n halfedges.splice(iHalfedge, 0, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, end,\n Math.abs(endX - x0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && y1 - endY > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [x0, Math.abs(startX - x0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startY : y1]\n : Math.abs(endY - y1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && x1 - endX > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [Math.abs(startY - y1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startX : x1, y1]\n : Math.abs(endX - x1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && endY - y0 > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [x1, Math.abs(startX - x1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startY : y0]\n : Math.abs(endY - y0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && endX - x0 > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [Math.abs(startY - y0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startX : x0, y0]\n : null)) - 1);\n ++nHalfedges;\n }\n }\n\n if (nHalfedges) cover = false;\n }\n }\n\n // If there weren’t any edges, have the closest site cover the extent.\n // It doesn’t matter which corner of the extent we measure!\n if (cover) {\n var dx, dy, d2, dc = Infinity;\n\n for (iCell = 0, cover = null; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n site = cell.site;\n dx = site[0] - x0;\n dy = site[1] - y0;\n d2 = dx * dx + dy * dy;\n if (d2 < dc) dc = d2, cover = cell;\n }\n }\n\n if (cover) {\n var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];\n cover.halfedges.push(\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site = cover.site, v00, v01)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v01, v11)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v11, v10)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v10, v00)) - 1\n );\n }\n }\n\n // Lastly delete any cells with no edges; these were entirely clipped.\n for (iCell = 0; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n if (!cell.halfedges.length) {\n delete _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell];\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/Cell.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/Circle.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-voronoi/src/Circle.js ***! + \***********************************************/ +/*! exports provided: firstCircle, attachCircle, detachCircle */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"firstCircle\", function() { return firstCircle; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"attachCircle\", function() { return attachCircle; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"detachCircle\", function() { return detachCircle; });\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/d3-voronoi/src/RedBlackTree.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\nvar circlePool = [];\n\nvar firstCircle;\n\nfunction Circle() {\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(this);\n this.x =\n this.y =\n this.arc =\n this.site =\n this.cy = null;\n}\n\nfunction attachCircle(arc) {\n var lArc = arc.P,\n rArc = arc.N;\n\n if (!lArc || !rArc) return;\n\n var lSite = lArc.site,\n cSite = arc.site,\n rSite = rArc.site;\n\n if (lSite === rSite) return;\n\n var bx = cSite[0],\n by = cSite[1],\n ax = lSite[0] - bx,\n ay = lSite[1] - by,\n cx = rSite[0] - bx,\n cy = rSite[1] - by;\n\n var d = 2 * (ax * cy - ay * cx);\n if (d >= -_Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon2\"]) return;\n\n var ha = ax * ax + ay * ay,\n hc = cx * cx + cy * cy,\n x = (cy * ha - ay * hc) / d,\n y = (ax * hc - cx * ha) / d;\n\n var circle = circlePool.pop() || new Circle;\n circle.arc = arc;\n circle.site = cSite;\n circle.x = x + bx;\n circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom\n\n arc.circle = circle;\n\n var before = null,\n node = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"]._;\n\n while (node) {\n if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {\n if (node.L) node = node.L;\n else { before = node.P; break; }\n } else {\n if (node.R) node = node.R;\n else { before = node; break; }\n }\n }\n\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"].insert(before, circle);\n if (!before) firstCircle = circle;\n}\n\nfunction detachCircle(arc) {\n var circle = arc.circle;\n if (circle) {\n if (!circle.P) firstCircle = circle.N;\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"].remove(circle);\n circlePool.push(circle);\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(circle);\n arc.circle = null;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/Circle.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/Diagram.js": +/*!************************************************!*\ + !*** ./node_modules/d3-voronoi/src/Diagram.js ***! + \************************************************/ +/*! exports provided: epsilon, epsilon2, beaches, cells, circles, edges, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon2\", function() { return epsilon2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"beaches\", function() { return beaches; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cells\", function() { return cells; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circles\", function() { return circles; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"edges\", function() { return edges; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Diagram; });\n/* harmony import */ var _Beach__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Beach */ \"./node_modules/d3-voronoi/src/Beach.js\");\n/* harmony import */ var _Cell__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Cell */ \"./node_modules/d3-voronoi/src/Cell.js\");\n/* harmony import */ var _Circle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Circle */ \"./node_modules/d3-voronoi/src/Circle.js\");\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Edge */ \"./node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/d3-voronoi/src/RedBlackTree.js\");\n\n\n\n\n\n\nvar epsilon = 1e-6;\nvar epsilon2 = 1e-12;\nvar beaches;\nvar cells;\nvar circles;\nvar edges;\n\nfunction triangleArea(a, b, c) {\n return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);\n}\n\nfunction lexicographic(a, b) {\n return b[1] - a[1]\n || b[0] - a[0];\n}\n\nfunction Diagram(sites, extent) {\n var site = sites.sort(lexicographic).pop(),\n x,\n y,\n circle;\n\n edges = [];\n cells = new Array(sites.length);\n beaches = new _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n circles = new _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n\n while (true) {\n circle = _Circle__WEBPACK_IMPORTED_MODULE_2__[\"firstCircle\"];\n if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {\n if (site[0] !== x || site[1] !== y) {\n Object(_Beach__WEBPACK_IMPORTED_MODULE_0__[\"addBeach\"])(site);\n x = site[0], y = site[1];\n }\n site = sites.pop();\n } else if (circle) {\n Object(_Beach__WEBPACK_IMPORTED_MODULE_0__[\"removeBeach\"])(circle.arc);\n } else {\n break;\n }\n }\n\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"sortCellHalfedges\"])();\n\n if (extent) {\n var x0 = +extent[0][0],\n y0 = +extent[0][1],\n x1 = +extent[1][0],\n y1 = +extent[1][1];\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"clipEdges\"])(x0, y0, x1, y1);\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"clipCells\"])(x0, y0, x1, y1);\n }\n\n this.edges = edges;\n this.cells = cells;\n\n beaches =\n circles =\n edges =\n cells = null;\n}\n\nDiagram.prototype = {\n constructor: Diagram,\n\n polygons: function() {\n var edges = this.edges;\n\n return this.cells.map(function(cell) {\n var polygon = cell.halfedges.map(function(i) { return Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"cellHalfedgeStart\"])(cell, edges[i]); });\n polygon.data = cell.site.data;\n return polygon;\n });\n },\n\n triangles: function() {\n var triangles = [],\n edges = this.edges;\n\n this.cells.forEach(function(cell, i) {\n if (!(m = (halfedges = cell.halfedges).length)) return;\n var site = cell.site,\n halfedges,\n j = -1,\n m,\n s0,\n e1 = edges[halfedges[m - 1]],\n s1 = e1.left === site ? e1.right : e1.left;\n\n while (++j < m) {\n s0 = s1;\n e1 = edges[halfedges[j]];\n s1 = e1.left === site ? e1.right : e1.left;\n if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {\n triangles.push([site.data, s0.data, s1.data]);\n }\n }\n });\n\n return triangles;\n },\n\n links: function() {\n return this.edges.filter(function(edge) {\n return edge.right;\n }).map(function(edge) {\n return {\n source: edge.left.data,\n target: edge.right.data\n };\n });\n },\n\n find: function(x, y, radius) {\n var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell;\n\n // Use the previously-found cell, or start with an arbitrary one.\n while (!(cell = that.cells[i1])) if (++i1 >= n) return null;\n var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy;\n\n // Traverse the half-edges to find a closer cell, if any.\n do {\n cell = that.cells[i0 = i1], i1 = null;\n cell.halfedges.forEach(function(e) {\n var edge = that.edges[e], v = edge.left;\n if ((v === cell.site || !v) && !(v = edge.right)) return;\n var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy;\n if (v2 < d2) d2 = v2, i1 = v.index;\n });\n } while (i1 !== null);\n\n that._found = i0;\n\n return radius == null || d2 <= radius * radius ? cell.site : null;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/Diagram.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/Edge.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-voronoi/src/Edge.js ***! + \*********************************************/ +/*! exports provided: createEdge, createBorderEdge, setEdgeEnd, clipEdges */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createEdge\", function() { return createEdge; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createBorderEdge\", function() { return createBorderEdge; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"setEdgeEnd\", function() { return setEdgeEnd; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clipEdges\", function() { return clipEdges; });\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/d3-voronoi/src/Diagram.js\");\n\n\nfunction createEdge(left, right, v0, v1) {\n var edge = [null, null],\n index = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"].push(edge) - 1;\n edge.left = left;\n edge.right = right;\n if (v0) setEdgeEnd(edge, left, right, v0);\n if (v1) setEdgeEnd(edge, right, left, v1);\n _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"cells\"][left.index].halfedges.push(index);\n _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"cells\"][right.index].halfedges.push(index);\n return edge;\n}\n\nfunction createBorderEdge(left, v0, v1) {\n var edge = [v0, v1];\n edge.left = left;\n return edge;\n}\n\nfunction setEdgeEnd(edge, left, right, vertex) {\n if (!edge[0] && !edge[1]) {\n edge[0] = vertex;\n edge.left = left;\n edge.right = right;\n } else if (edge.left === right) {\n edge[1] = vertex;\n } else {\n edge[0] = vertex;\n }\n}\n\n// Liang–Barsky line clipping.\nfunction clipEdge(edge, x0, y0, x1, y1) {\n var a = edge[0],\n b = edge[1],\n ax = a[0],\n ay = a[1],\n bx = b[0],\n by = b[1],\n t0 = 0,\n t1 = 1,\n dx = bx - ax,\n dy = by - ay,\n r;\n\n r = x0 - ax;\n if (!dx && r > 0) return;\n r /= dx;\n if (dx < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dx > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = x1 - ax;\n if (!dx && r < 0) return;\n r /= dx;\n if (dx < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dx > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n r = y0 - ay;\n if (!dy && r > 0) return;\n r /= dy;\n if (dy < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dy > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = y1 - ay;\n if (!dy && r < 0) return;\n r /= dy;\n if (dy < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dy > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check?\n\n if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];\n if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];\n return true;\n}\n\nfunction connectEdge(edge, x0, y0, x1, y1) {\n var v1 = edge[1];\n if (v1) return true;\n\n var v0 = edge[0],\n left = edge.left,\n right = edge.right,\n lx = left[0],\n ly = left[1],\n rx = right[0],\n ry = right[1],\n fx = (lx + rx) / 2,\n fy = (ly + ry) / 2,\n fm,\n fb;\n\n if (ry === ly) {\n if (fx < x0 || fx >= x1) return;\n if (lx > rx) {\n if (!v0) v0 = [fx, y0];\n else if (v0[1] >= y1) return;\n v1 = [fx, y1];\n } else {\n if (!v0) v0 = [fx, y1];\n else if (v0[1] < y0) return;\n v1 = [fx, y0];\n }\n } else {\n fm = (lx - rx) / (ry - ly);\n fb = fy - fm * fx;\n if (fm < -1 || fm > 1) {\n if (lx > rx) {\n if (!v0) v0 = [(y0 - fb) / fm, y0];\n else if (v0[1] >= y1) return;\n v1 = [(y1 - fb) / fm, y1];\n } else {\n if (!v0) v0 = [(y1 - fb) / fm, y1];\n else if (v0[1] < y0) return;\n v1 = [(y0 - fb) / fm, y0];\n }\n } else {\n if (ly < ry) {\n if (!v0) v0 = [x0, fm * x0 + fb];\n else if (v0[0] >= x1) return;\n v1 = [x1, fm * x1 + fb];\n } else {\n if (!v0) v0 = [x1, fm * x1 + fb];\n else if (v0[0] < x0) return;\n v1 = [x0, fm * x0 + fb];\n }\n }\n }\n\n edge[0] = v0;\n edge[1] = v1;\n return true;\n}\n\nfunction clipEdges(x0, y0, x1, y1) {\n var i = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"].length,\n edge;\n\n while (i--) {\n if (!connectEdge(edge = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"][i], x0, y0, x1, y1)\n || !clipEdge(edge, x0, y0, x1, y1)\n || !(Math.abs(edge[0][0] - edge[1][0]) > _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]\n || Math.abs(edge[0][1] - edge[1][1]) > _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"])) {\n delete _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"][i];\n }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/Edge.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/RedBlackTree.js": +/*!*****************************************************!*\ + !*** ./node_modules/d3-voronoi/src/RedBlackTree.js ***! + \*****************************************************/ +/*! exports provided: RedBlackNode, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RedBlackNode\", function() { return RedBlackNode; });\nfunction RedBlackTree() {\n this._ = null; // root node\n}\n\nfunction RedBlackNode(node) {\n node.U = // parent node\n node.C = // color - true for red, false for black\n node.L = // left node\n node.R = // right node\n node.P = // previous node\n node.N = null; // next node\n}\n\nRedBlackTree.prototype = {\n constructor: RedBlackTree,\n\n insert: function(after, node) {\n var parent, grandpa, uncle;\n\n if (after) {\n node.P = after;\n node.N = after.N;\n if (after.N) after.N.P = node;\n after.N = node;\n if (after.R) {\n after = after.R;\n while (after.L) after = after.L;\n after.L = node;\n } else {\n after.R = node;\n }\n parent = after;\n } else if (this._) {\n after = RedBlackFirst(this._);\n node.P = null;\n node.N = after;\n after.P = after.L = node;\n parent = after;\n } else {\n node.P = node.N = null;\n this._ = node;\n parent = null;\n }\n node.L = node.R = null;\n node.U = parent;\n node.C = true;\n\n after = node;\n while (parent && parent.C) {\n grandpa = parent.U;\n if (parent === grandpa.L) {\n uncle = grandpa.R;\n if (uncle && uncle.C) {\n parent.C = uncle.C = false;\n grandpa.C = true;\n after = grandpa;\n } else {\n if (after === parent.R) {\n RedBlackRotateLeft(this, parent);\n after = parent;\n parent = after.U;\n }\n parent.C = false;\n grandpa.C = true;\n RedBlackRotateRight(this, grandpa);\n }\n } else {\n uncle = grandpa.L;\n if (uncle && uncle.C) {\n parent.C = uncle.C = false;\n grandpa.C = true;\n after = grandpa;\n } else {\n if (after === parent.L) {\n RedBlackRotateRight(this, parent);\n after = parent;\n parent = after.U;\n }\n parent.C = false;\n grandpa.C = true;\n RedBlackRotateLeft(this, grandpa);\n }\n }\n parent = after.U;\n }\n this._.C = false;\n },\n\n remove: function(node) {\n if (node.N) node.N.P = node.P;\n if (node.P) node.P.N = node.N;\n node.N = node.P = null;\n\n var parent = node.U,\n sibling,\n left = node.L,\n right = node.R,\n next,\n red;\n\n if (!left) next = right;\n else if (!right) next = left;\n else next = RedBlackFirst(right);\n\n if (parent) {\n if (parent.L === node) parent.L = next;\n else parent.R = next;\n } else {\n this._ = next;\n }\n\n if (left && right) {\n red = next.C;\n next.C = node.C;\n next.L = left;\n left.U = next;\n if (next !== right) {\n parent = next.U;\n next.U = node.U;\n node = next.R;\n parent.L = node;\n next.R = right;\n right.U = next;\n } else {\n next.U = parent;\n parent = next;\n node = next.R;\n }\n } else {\n red = node.C;\n node = next;\n }\n\n if (node) node.U = parent;\n if (red) return;\n if (node && node.C) { node.C = false; return; }\n\n do {\n if (node === this._) break;\n if (node === parent.L) {\n sibling = parent.R;\n if (sibling.C) {\n sibling.C = false;\n parent.C = true;\n RedBlackRotateLeft(this, parent);\n sibling = parent.R;\n }\n if ((sibling.L && sibling.L.C)\n || (sibling.R && sibling.R.C)) {\n if (!sibling.R || !sibling.R.C) {\n sibling.L.C = false;\n sibling.C = true;\n RedBlackRotateRight(this, sibling);\n sibling = parent.R;\n }\n sibling.C = parent.C;\n parent.C = sibling.R.C = false;\n RedBlackRotateLeft(this, parent);\n node = this._;\n break;\n }\n } else {\n sibling = parent.L;\n if (sibling.C) {\n sibling.C = false;\n parent.C = true;\n RedBlackRotateRight(this, parent);\n sibling = parent.L;\n }\n if ((sibling.L && sibling.L.C)\n || (sibling.R && sibling.R.C)) {\n if (!sibling.L || !sibling.L.C) {\n sibling.R.C = false;\n sibling.C = true;\n RedBlackRotateLeft(this, sibling);\n sibling = parent.L;\n }\n sibling.C = parent.C;\n parent.C = sibling.L.C = false;\n RedBlackRotateRight(this, parent);\n node = this._;\n break;\n }\n }\n sibling.C = true;\n node = parent;\n parent = parent.U;\n } while (!node.C);\n\n if (node) node.C = false;\n }\n};\n\nfunction RedBlackRotateLeft(tree, node) {\n var p = node,\n q = node.R,\n parent = p.U;\n\n if (parent) {\n if (parent.L === p) parent.L = q;\n else parent.R = q;\n } else {\n tree._ = q;\n }\n\n q.U = parent;\n p.U = q;\n p.R = q.L;\n if (p.R) p.R.U = p;\n q.L = p;\n}\n\nfunction RedBlackRotateRight(tree, node) {\n var p = node,\n q = node.L,\n parent = p.U;\n\n if (parent) {\n if (parent.L === p) parent.L = q;\n else parent.R = q;\n } else {\n tree._ = q;\n }\n\n q.U = parent;\n p.U = q;\n p.L = q.R;\n if (p.L) p.L.U = p;\n q.R = p;\n}\n\nfunction RedBlackFirst(node) {\n while (node.L) node = node.L;\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (RedBlackTree);\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/RedBlackTree.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/constant.js": +/*!*************************************************!*\ + !*** ./node_modules/d3-voronoi/src/constant.js ***! + \*************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/index.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-voronoi/src/index.js ***! + \**********************************************/ +/*! exports provided: voronoi */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _voronoi__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./voronoi */ \"./node_modules/d3-voronoi/src/voronoi.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"voronoi\", function() { return _voronoi__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/point.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-voronoi/src/point.js ***! + \**********************************************/ +/*! exports provided: x, y */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\nfunction x(d) {\n return d[0];\n}\n\nfunction y(d) {\n return d[1];\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/d3-voronoi/src/voronoi.js": +/*!************************************************!*\ + !*** ./node_modules/d3-voronoi/src/voronoi.js ***! + \************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-voronoi/src/constant.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/d3-voronoi/src/point.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x = _point__WEBPACK_IMPORTED_MODULE_1__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_1__[\"y\"],\n extent = null;\n\n function voronoi(data) {\n return new _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"default\"](data.map(function(d, i) {\n var s = [Math.round(x(d, i, data) / _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) * _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"], Math.round(y(d, i, data) / _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) * _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]];\n s.index = i;\n s.data = d;\n return s;\n }), extent);\n }\n\n voronoi.polygons = function(data) {\n return voronoi(data).polygons();\n };\n\n voronoi.links = function(data) {\n return voronoi(data).links();\n };\n\n voronoi.triangles = function(data) {\n return voronoi(data).triangles();\n };\n\n voronoi.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), voronoi) : x;\n };\n\n voronoi.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), voronoi) : y;\n };\n\n voronoi.extent = function(_) {\n return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];\n };\n\n voronoi.size = function(_) {\n return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];\n };\n\n return voronoi;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-voronoi/src/voronoi.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/constant.js": +/*!**********************************************!*\ + !*** ./node_modules/d3-zoom/src/constant.js ***! + \**********************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/event.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-zoom/src/event.js ***! + \*******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return ZoomEvent; });\nfunction ZoomEvent(target, type, transform) {\n this.target = target;\n this.type = type;\n this.transform = transform;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/index.js": +/*!*******************************************!*\ + !*** ./node_modules/d3-zoom/src/index.js ***! + \*******************************************/ +/*! exports provided: zoom, zoomTransform, zoomIdentity */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./zoom */ \"./node_modules/d3-zoom/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transform */ \"./node_modules/d3-zoom/src/transform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomTransform\", function() { return _transform__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomIdentity\", function() { return _transform__WEBPACK_IMPORTED_MODULE_1__[\"identity\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/noevent.js": +/*!*********************************************!*\ + !*** ./node_modules/d3-zoom/src/noevent.js ***! + \*********************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/transform.js": +/*!***********************************************!*\ + !*** ./node_modules/d3-zoom/src/transform.js ***! + \***********************************************/ +/*! exports provided: Transform, identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transform\", function() { return Transform; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transform; });\nfunction Transform(k, x, y) {\n this.k = k;\n this.x = x;\n this.y = y;\n}\n\nTransform.prototype = {\n constructor: Transform,\n scale: function(k) {\n return k === 1 ? this : new Transform(this.k * k, this.x, this.y);\n },\n translate: function(x, y) {\n return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);\n },\n apply: function(point) {\n return [point[0] * this.k + this.x, point[1] * this.k + this.y];\n },\n applyX: function(x) {\n return x * this.k + this.x;\n },\n applyY: function(y) {\n return y * this.k + this.y;\n },\n invert: function(location) {\n return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];\n },\n invertX: function(x) {\n return (x - this.x) / this.k;\n },\n invertY: function(y) {\n return (y - this.y) / this.k;\n },\n rescaleX: function(x) {\n return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));\n },\n rescaleY: function(y) {\n return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));\n },\n toString: function() {\n return \"translate(\" + this.x + \",\" + this.y + \") scale(\" + this.k + \")\";\n }\n};\n\nvar identity = new Transform(1, 0, 0);\n\ntransform.prototype = Transform.prototype;\n\nfunction transform(node) {\n return node.__zoom || identity;\n}\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/transform.js?"); + +/***/ }), + +/***/ "./node_modules/d3-zoom/src/zoom.js": +/*!******************************************!*\ + !*** ./node_modules/d3-zoom/src/zoom.js ***! + \******************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-drag */ \"./node_modules/d3-drag/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-transition */ \"./node_modules/d3-transition/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./constant */ \"./node_modules/d3-zoom/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./event */ \"./node_modules/d3-zoom/src/event.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./transform */ \"./node_modules/d3-zoom/src/transform.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./noevent */ \"./node_modules/d3-zoom/src/noevent.js\");\n\n\n\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].button;\n}\n\nfunction defaultExtent() {\n var e = this, w, h;\n if (e instanceof SVGElement) {\n e = e.ownerSVGElement || e;\n w = e.width.baseVal.value;\n h = e.height.baseVal.value;\n } else {\n w = e.clientWidth;\n h = e.clientHeight;\n }\n return [[0, 0], [w, h]];\n}\n\nfunction defaultTransform() {\n return this.__zoom || _transform__WEBPACK_IMPORTED_MODULE_7__[\"identity\"];\n}\n\nfunction defaultWheelDelta() {\n return -d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].deltaY * (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].deltaMode ? 120 : 1) / 500;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\nfunction defaultConstrain(transform, extent, translateExtent) {\n var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],\n dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],\n dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],\n dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];\n return transform.translate(\n dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),\n dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)\n );\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n extent = defaultExtent,\n constrain = defaultConstrain,\n wheelDelta = defaultWheelDelta,\n touchable = defaultTouchable,\n scaleExtent = [0, Infinity],\n translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],\n duration = 250,\n interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_2__[\"interpolateZoom\"],\n gestures = [],\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"zoom\", \"end\"),\n touchstarting,\n touchending,\n touchDelay = 500,\n wheelDelay = 150,\n clickDistance2 = 0;\n\n function zoom(selection) {\n selection\n .property(\"__zoom\", defaultTransform)\n .on(\"wheel.zoom\", wheeled)\n .on(\"mousedown.zoom\", mousedowned)\n .on(\"dblclick.zoom\", dblclicked)\n .filter(touchable)\n .on(\"touchstart.zoom\", touchstarted)\n .on(\"touchmove.zoom\", touchmoved)\n .on(\"touchend.zoom touchcancel.zoom\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n zoom.transform = function(collection, transform) {\n var selection = collection.selection ? collection.selection() : collection;\n selection.property(\"__zoom\", defaultTransform);\n if (collection !== selection) {\n schedule(collection, transform);\n } else {\n selection.interrupt().each(function() {\n gesture(this, arguments)\n .start()\n .zoom(null, typeof transform === \"function\" ? transform.apply(this, arguments) : transform)\n .end();\n });\n }\n };\n\n zoom.scaleBy = function(selection, k) {\n zoom.scaleTo(selection, function() {\n var k0 = this.__zoom.k,\n k1 = typeof k === \"function\" ? k.apply(this, arguments) : k;\n return k0 * k1;\n });\n };\n\n zoom.scaleTo = function(selection, k) {\n zoom.transform(selection, function() {\n var e = extent.apply(this, arguments),\n t0 = this.__zoom,\n p0 = centroid(e),\n p1 = t0.invert(p0),\n k1 = typeof k === \"function\" ? k.apply(this, arguments) : k;\n return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);\n });\n };\n\n zoom.translateBy = function(selection, x, y) {\n zoom.transform(selection, function() {\n return constrain(this.__zoom.translate(\n typeof x === \"function\" ? x.apply(this, arguments) : x,\n typeof y === \"function\" ? y.apply(this, arguments) : y\n ), extent.apply(this, arguments), translateExtent);\n });\n };\n\n zoom.translateTo = function(selection, x, y) {\n zoom.transform(selection, function() {\n var e = extent.apply(this, arguments),\n t = this.__zoom,\n p = centroid(e);\n return constrain(_transform__WEBPACK_IMPORTED_MODULE_7__[\"identity\"].translate(p[0], p[1]).scale(t.k).translate(\n typeof x === \"function\" ? -x.apply(this, arguments) : -x,\n typeof y === \"function\" ? -y.apply(this, arguments) : -y\n ), e, translateExtent);\n });\n };\n\n function scale(transform, k) {\n k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));\n return k === transform.k ? transform : new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](k, transform.x, transform.y);\n }\n\n function translate(transform, p0, p1) {\n var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;\n return x === transform.x && y === transform.y ? transform : new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](transform.k, x, y);\n }\n\n function centroid(extent) {\n return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];\n }\n\n function schedule(transition, transform, center) {\n transition\n .on(\"start.zoom\", function() { gesture(this, arguments).start(); })\n .on(\"interrupt.zoom end.zoom\", function() { gesture(this, arguments).end(); })\n .tween(\"zoom\", function() {\n var that = this,\n args = arguments,\n g = gesture(that, args),\n e = extent.apply(that, args),\n p = center || centroid(e),\n w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),\n a = that.__zoom,\n b = typeof transform === \"function\" ? transform.apply(that, args) : transform,\n i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));\n return function(t) {\n if (t === 1) t = b; // Avoid rounding error on end.\n else { var l = i(t), k = w / l[2]; t = new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](k, p[0] - l[0] * k, p[1] - l[1] * k); }\n g.zoom(null, t);\n };\n });\n }\n\n function gesture(that, args) {\n for (var i = 0, n = gestures.length, g; i < n; ++i) {\n if ((g = gestures[i]).that === that) {\n return g;\n }\n }\n return new Gesture(that, args);\n }\n\n function Gesture(that, args) {\n this.that = that;\n this.args = args;\n this.index = -1;\n this.active = 0;\n this.extent = extent.apply(that, args);\n }\n\n Gesture.prototype = {\n start: function() {\n if (++this.active === 1) {\n this.index = gestures.push(this) - 1;\n this.emit(\"start\");\n }\n return this;\n },\n zoom: function(key, transform) {\n if (this.mouse && key !== \"mouse\") this.mouse[1] = transform.invert(this.mouse[0]);\n if (this.touch0 && key !== \"touch\") this.touch0[1] = transform.invert(this.touch0[0]);\n if (this.touch1 && key !== \"touch\") this.touch1[1] = transform.invert(this.touch1[0]);\n this.that.__zoom = transform;\n this.emit(\"zoom\");\n return this;\n },\n end: function() {\n if (--this.active === 0) {\n gestures.splice(this.index, 1);\n this.index = -1;\n this.emit(\"end\");\n }\n return this;\n },\n emit: function(type) {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_6__[\"default\"](zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);\n }\n };\n\n function wheeled() {\n if (!filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n t = this.__zoom,\n k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this);\n\n // If the mouse is in the same location as before, reuse it.\n // If there were recent wheel events, reset the wheel idle timeout.\n if (g.wheel) {\n if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {\n g.mouse[1] = t.invert(g.mouse[0] = p);\n }\n clearTimeout(g.wheel);\n }\n\n // If this wheel event won’t trigger a transform change, ignore it.\n else if (t.k === k) return;\n\n // Otherwise, capture the mouse point and location at the start.\n else {\n g.mouse = [p, t.invert(p)];\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n }\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n g.wheel = setTimeout(wheelidled, wheelDelay);\n g.zoom(\"mouse\", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));\n\n function wheelidled() {\n g.wheel = null;\n g.end();\n }\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n v = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view).on(\"mousemove.zoom\", mousemoved, true).on(\"mouseup.zoom\", mouseupped, true),\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this),\n x0 = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientX,\n y0 = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientY;\n\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragDisable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n g.mouse = [p, this.__zoom.invert(p)];\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (!g.moved) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientX - x0, dy = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientY - y0;\n g.moved = dx * dx + dy * dy > clickDistance2;\n }\n g.zoom(\"mouse\", constrain(translate(g.that.__zoom, g.mouse[0] = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(g.that), g.mouse[1]), g.extent, translateExtent));\n }\n\n function mouseupped() {\n v.on(\"mousemove.zoom mouseup.zoom\", null);\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragEnable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view, g.moved);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n g.end();\n }\n }\n\n function dblclicked() {\n if (!filter.apply(this, arguments)) return;\n var t0 = this.__zoom,\n p0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this),\n p1 = t0.invert(p0),\n k1 = t0.k * (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].shiftKey ? 0.5 : 2),\n t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (duration > 0) Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).transition().duration(duration).call(schedule, t1, p0);\n else Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).call(zoom.transform, t1);\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n started,\n n = touches.length, i, t, p;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n for (i = 0; i < n; ++i) {\n t = touches[i], p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"touch\"])(this, touches, t.identifier);\n p = [p, this.__zoom.invert(p), t.identifier];\n if (!g.touch0) g.touch0 = p, started = true;\n else if (!g.touch1) g.touch1 = p;\n }\n\n // If this is a dbltap, reroute to the (optional) dblclick.zoom handler.\n if (touchstarting) {\n touchstarting = clearTimeout(touchstarting);\n if (!g.touch1) {\n g.end();\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).on(\"dblclick.zoom\");\n if (p) p.apply(this, arguments);\n return;\n }\n }\n\n if (started) {\n touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n }\n }\n\n function touchmoved() {\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n n = touches.length, i, t, p, l;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (touchstarting) touchstarting = clearTimeout(touchstarting);\n for (i = 0; i < n; ++i) {\n t = touches[i], p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"touch\"])(this, touches, t.identifier);\n if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;\n else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;\n }\n t = g.that.__zoom;\n if (g.touch1) {\n var p0 = g.touch0[0], l0 = g.touch0[1],\n p1 = g.touch1[0], l1 = g.touch1[1],\n dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,\n dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;\n t = scale(t, Math.sqrt(dp / dl));\n p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];\n l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];\n }\n else if (g.touch0) p = g.touch0[0], l = g.touch0[1];\n else return;\n g.zoom(\"touch\", constrain(translate(t, p, l), g.extent, translateExtent));\n }\n\n function touchended() {\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n n = touches.length, i, t;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, touchDelay);\n for (i = 0; i < n; ++i) {\n t = touches[i];\n if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;\n else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;\n }\n if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;\n if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);\n else g.end();\n }\n\n zoom.wheelDelta = function(_) {\n return arguments.length ? (wheelDelta = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(+_), zoom) : wheelDelta;\n };\n\n zoom.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), zoom) : filter;\n };\n\n zoom.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), zoom) : touchable;\n };\n\n zoom.extent = function(_) {\n return arguments.length ? (extent = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;\n };\n\n zoom.scaleExtent = function(_) {\n return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];\n };\n\n zoom.translateExtent = function(_) {\n return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];\n };\n\n zoom.constrain = function(_) {\n return arguments.length ? (constrain = _, zoom) : constrain;\n };\n\n zoom.duration = function(_) {\n return arguments.length ? (duration = +_, zoom) : duration;\n };\n\n zoom.interpolate = function(_) {\n return arguments.length ? (interpolate = _, zoom) : interpolate;\n };\n\n zoom.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? zoom : value;\n };\n\n zoom.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);\n };\n\n return zoom;\n});\n\n\n//# sourceURL=webpack:///./node_modules/d3-zoom/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/d3/dist/package.js": +/*!*****************************************!*\ + !*** ./node_modules/d3/dist/package.js ***! + \*****************************************/ +/*! exports provided: name, version, description, keywords, homepage, license, author, main, unpkg, jsdelivr, module, repository, scripts, devDependencies, dependencies */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"name\", function() { return name; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"version\", function() { return version; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"description\", function() { return description; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keywords\", function() { return keywords; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"homepage\", function() { return homepage; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"license\", function() { return license; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"author\", function() { return author; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"main\", function() { return main; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"unpkg\", function() { return unpkg; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"jsdelivr\", function() { return jsdelivr; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"module\", function() { return module; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"repository\", function() { return repository; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scripts\", function() { return scripts; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"devDependencies\", function() { return devDependencies; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"dependencies\", function() { return dependencies; });\nvar name = \"d3\";\nvar version = \"5.7.0\";\nvar description = \"Data-Driven Documents\";\nvar keywords = [\"dom\",\"visualization\",\"svg\",\"animation\",\"canvas\"];\nvar homepage = \"https://d3js.org\";\nvar license = \"BSD-3-Clause\";\nvar author = {\"name\":\"Mike Bostock\",\"url\":\"https://bost.ocks.org/mike\"};\nvar main = \"dist/d3.node.js\";\nvar unpkg = \"dist/d3.min.js\";\nvar jsdelivr = \"dist/d3.min.js\";\nvar module = \"index.js\";\nvar repository = {\"type\":\"git\",\"url\":\"https://github.com/d3/d3.git\"};\nvar scripts = {\"pretest\":\"rimraf dist && mkdir dist && json2module package.json > dist/package.js && node rollup.node\",\"test\":\"tape 'test/**/*-test.js'\",\"prepublishOnly\":\"yarn test && rollup -c\",\"postpublish\":\"git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3/dist/d3.js d3.v5.js && cp ../d3/dist/d3.min.js d3.v5.min.js && git add d3.v5.js d3.v5.min.js && git commit -m \\\"d3 ${npm_package_version}\\\" && git push && cd - && cd ../d3-bower && git pull && cp ../d3/LICENSE ../d3/README.md ../d3/dist/d3.js ../d3/dist/d3.min.js . && git add -- LICENSE README.md d3.js d3.min.js && git commit -m \\\"${npm_package_version}\\\" && git tag -am \\\"${npm_package_version}\\\" v${npm_package_version} && git push && git push --tags && cd - && zip -j dist/d3.zip -- LICENSE README.md API.md CHANGES.md dist/d3.js dist/d3.min.js\"};\nvar devDependencies = {\"json2module\":\"0.0\",\"rimraf\":\"2\",\"rollup\":\"0.64\",\"rollup-plugin-ascii\":\"0.0\",\"rollup-plugin-node-resolve\":\"3\",\"rollup-plugin-terser\":\"1\",\"tape\":\"4\"};\nvar dependencies = {\"d3-array\":\"1\",\"d3-axis\":\"1\",\"d3-brush\":\"1\",\"d3-chord\":\"1\",\"d3-collection\":\"1\",\"d3-color\":\"1\",\"d3-contour\":\"1\",\"d3-dispatch\":\"1\",\"d3-drag\":\"1\",\"d3-dsv\":\"1\",\"d3-ease\":\"1\",\"d3-fetch\":\"1\",\"d3-force\":\"1\",\"d3-format\":\"1\",\"d3-geo\":\"1\",\"d3-hierarchy\":\"1\",\"d3-interpolate\":\"1\",\"d3-path\":\"1\",\"d3-polygon\":\"1\",\"d3-quadtree\":\"1\",\"d3-random\":\"1\",\"d3-scale\":\"2\",\"d3-scale-chromatic\":\"1\",\"d3-selection\":\"1\",\"d3-shape\":\"1\",\"d3-time\":\"1\",\"d3-time-format\":\"2\",\"d3-timer\":\"1\",\"d3-transition\":\"1\",\"d3-voronoi\":\"1\",\"d3-zoom\":\"1\"};\n\n\n//# sourceURL=webpack:///./node_modules/d3/dist/package.js?"); + +/***/ }), + +/***/ "./node_modules/d3/index.js": +/*!**********************************!*\ + !*** ./node_modules/d3/index.js ***! + \**********************************/ +/*! exports provided: version, bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip, axisTop, axisRight, axisBottom, axisLeft, brush, brushX, brushY, brushSelection, chord, ribbon, nest, set, map, keys, values, entries, color, rgb, hsl, lab, hcl, lch, gray, cubehelix, contours, contourDensity, dispatch, drag, dragDisable, dragEnable, dsvFormat, csvParse, csvParseRows, csvFormat, csvFormatRows, tsvParse, tsvParseRows, tsvFormat, tsvFormatRows, easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut, blob, buffer, dsv, csv, tsv, image, json, text, xml, html, svg, forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceSimulation, forceX, forceY, formatDefaultLocale, format, formatPrefix, formatLocale, formatSpecifier, precisionFixed, precisionPrefix, precisionRound, geoArea, geoBounds, geoCentroid, geoCircle, geoClipAntimeridian, geoClipCircle, geoClipExtent, geoClipRectangle, geoContains, geoDistance, geoGraticule, geoGraticule10, geoInterpolate, geoLength, geoPath, geoAlbers, geoAlbersUsa, geoAzimuthalEqualArea, geoAzimuthalEqualAreaRaw, geoAzimuthalEquidistant, geoAzimuthalEquidistantRaw, geoConicConformal, geoConicConformalRaw, geoConicEqualArea, geoConicEqualAreaRaw, geoConicEquidistant, geoConicEquidistantRaw, geoEqualEarth, geoEqualEarthRaw, geoEquirectangular, geoEquirectangularRaw, geoGnomonic, geoGnomonicRaw, geoIdentity, geoProjection, geoProjectionMutator, geoMercator, geoMercatorRaw, geoNaturalEarth1, geoNaturalEarth1Raw, geoOrthographic, geoOrthographicRaw, geoStereographic, geoStereographicRaw, geoTransverseMercator, geoTransverseMercatorRaw, geoRotation, geoStream, geoTransform, cluster, hierarchy, pack, packSiblings, packEnclose, partition, stratify, tree, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, treemapResquarify, interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize, path, polygonArea, polygonCentroid, polygonHull, polygonContains, polygonLength, quadtree, randomUniform, randomNormal, randomLogNormal, randomBates, randomIrwinHall, randomExponential, scaleBand, scalePoint, scaleIdentity, scaleLinear, scaleLog, scaleOrdinal, scaleImplicit, scalePow, scaleSqrt, scaleQuantile, scaleQuantize, scaleThreshold, scaleTime, scaleUtc, scaleSequential, scaleDiverging, schemeCategory10, schemeAccent, schemeDark2, schemePaired, schemePastel1, schemePastel2, schemeSet1, schemeSet2, schemeSet3, interpolateBrBG, schemeBrBG, interpolatePRGn, schemePRGn, interpolatePiYG, schemePiYG, interpolatePuOr, schemePuOr, interpolateRdBu, schemeRdBu, interpolateRdGy, schemeRdGy, interpolateRdYlBu, schemeRdYlBu, interpolateRdYlGn, schemeRdYlGn, interpolateSpectral, schemeSpectral, interpolateBuGn, schemeBuGn, interpolateBuPu, schemeBuPu, interpolateGnBu, schemeGnBu, interpolateOrRd, schemeOrRd, interpolatePuBuGn, schemePuBuGn, interpolatePuBu, schemePuBu, interpolatePuRd, schemePuRd, interpolateRdPu, schemeRdPu, interpolateYlGnBu, schemeYlGnBu, interpolateYlGn, schemeYlGn, interpolateYlOrBr, schemeYlOrBr, interpolateYlOrRd, schemeYlOrRd, interpolateBlues, schemeBlues, interpolateGreens, schemeGreens, interpolateGreys, schemeGreys, interpolatePurples, schemePurples, interpolateReds, schemeReds, interpolateOranges, schemeOranges, interpolateCubehelixDefault, interpolateRainbow, interpolateWarm, interpolateCool, interpolateSinebow, interpolateViridis, interpolateMagma, interpolateInferno, interpolatePlasma, create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent, arc, area, line, pie, areaRadial, radialArea, lineRadial, radialLine, pointRadial, linkHorizontal, linkVertical, linkRadial, symbol, symbols, symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye, curveBasisClosed, curveBasisOpen, curveBasis, curveBundle, curveCardinalClosed, curveCardinalOpen, curveCardinal, curveCatmullRomClosed, curveCatmullRomOpen, curveCatmullRom, curveLinearClosed, curveLinear, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, stack, stackOffsetExpand, stackOffsetDiverging, stackOffsetNone, stackOffsetSilhouette, stackOffsetWiggle, stackOrderAscending, stackOrderDescending, stackOrderInsideOut, stackOrderNone, stackOrderReverse, timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears, timeFormatDefaultLocale, timeFormat, timeParse, utcFormat, utcParse, timeFormatLocale, isoFormat, isoParse, now, timer, timerFlush, timeout, interval, transition, active, interrupt, voronoi, zoom, zoomTransform, zoomIdentity */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dist_package__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dist/package */ \"./node_modules/d3/dist/package.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"version\", function() { return _dist_package__WEBPACK_IMPORTED_MODULE_0__[\"version\"]; });\n\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-array */ \"./node_modules/d3-array/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisect\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisectLeft\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"ascending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisector\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"cross\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"descending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"deviation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"extent\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"histogram\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdFreedmanDiaconis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdScott\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdSturges\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"max\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"mean\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"median\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"merge\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"min\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"pairs\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"permute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"quantile\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"range\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"scan\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"shuffle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"sum\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"ticks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"tickStep\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"transpose\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"variance\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"zip\"]; });\n\n/* harmony import */ var d3_axis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-axis */ \"./node_modules/d3-axis/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisTop\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisBottom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisLeft\"]; });\n\n/* harmony import */ var d3_brush__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-brush */ \"./node_modules/d3-brush/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brush\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brush\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushSelection\"]; });\n\n/* harmony import */ var d3_chord__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-chord */ \"./node_modules/d3-chord/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"chord\", function() { return d3_chord__WEBPACK_IMPORTED_MODULE_4__[\"chord\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ribbon\", function() { return d3_chord__WEBPACK_IMPORTED_MODULE_4__[\"ribbon\"]; });\n\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! d3-collection */ \"./node_modules/d3-collection/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"nest\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"set\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"map\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"keys\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"values\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"entries\"]; });\n\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! d3-color */ \"./node_modules/d3-color/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"color\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"hsl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"lab\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"gray\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"cubehelix\"]; });\n\n/* harmony import */ var d3_contour__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! d3-contour */ \"./node_modules/d3-contour/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"contours\", function() { return d3_contour__WEBPACK_IMPORTED_MODULE_7__[\"contours\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"contourDensity\", function() { return d3_contour__WEBPACK_IMPORTED_MODULE_7__[\"contourDensity\"]; });\n\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/d3-dispatch/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return d3_dispatch__WEBPACK_IMPORTED_MODULE_8__[\"dispatch\"]; });\n\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! d3-drag */ \"./node_modules/d3-drag/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_9__[\"drag\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_9__[\"dragDisable\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_9__[\"dragEnable\"]; });\n\n/* harmony import */ var d3_dsv__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! d3-dsv */ \"./node_modules/d3-dsv/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"dsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"csvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"csvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"csvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"csvFormatRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"tsvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"tsvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"tsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_10__[\"tsvFormatRows\"]; });\n\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! d3-ease */ \"./node_modules/d3-ease/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeQuad\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeQuadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeQuadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeQuadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCubic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easePoly\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easePolyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easePolyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easePolyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeSin\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeSinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeSinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeSinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeExp\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeExpIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeExpOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeExpInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCircleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCircleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeCircleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBounce\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBounceInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBackIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBackOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeBackInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeElastic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeElasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeElasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_11__[\"easeElasticInOut\"]; });\n\n/* harmony import */ var d3_fetch__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! d3-fetch */ \"./node_modules/d3-fetch/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"blob\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"blob\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"buffer\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"buffer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsv\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"dsv\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csv\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"csv\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsv\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"tsv\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"image\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"image\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"json\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"json\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"text\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"text\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"xml\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"xml\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"html\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"html\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"svg\", function() { return d3_fetch__WEBPACK_IMPORTED_MODULE_12__[\"svg\"]; });\n\n/* harmony import */ var d3_force__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! d3-force */ \"./node_modules/d3-force/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCenter\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceCenter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCollide\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceCollide\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceLink\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceLink\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceManyBody\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceManyBody\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceRadial\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceSimulation\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceSimulation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceX\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceY\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_13__[\"forceY\"]; });\n\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! d3-format */ \"./node_modules/d3-format/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatDefaultLocale\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"formatDefaultLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"format\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"formatPrefix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatLocale\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"formatLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatSpecifier\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"formatSpecifier\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionFixed\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"precisionFixed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionPrefix\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"precisionPrefix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionRound\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_14__[\"precisionRound\"]; });\n\n/* harmony import */ var d3_geo__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! d3-geo */ \"./node_modules/d3-geo/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoBounds\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoBounds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCentroid\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoCentroid\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCircle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipAntimeridian\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoClipAntimeridian\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipCircle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoClipCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipExtent\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoClipExtent\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipRectangle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoClipRectangle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoContains\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoContains\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoDistance\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoDistance\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoGraticule\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule10\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoGraticule10\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoInterpolate\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoInterpolate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoLength\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoLength\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoPath\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoPath\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbers\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAlbers\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbersUsa\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAlbersUsa\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAzimuthalEqualArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualAreaRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAzimuthalEqualAreaRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistant\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAzimuthalEquidistant\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistantRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoAzimuthalEquidistantRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformal\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicConformal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformalRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicConformalRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicEqualArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualAreaRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicEqualAreaRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistant\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicEquidistant\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistantRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoConicEquidistantRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEqualEarth\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoEqualEarth\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEqualEarthRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoEqualEarthRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangular\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoEquirectangular\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangularRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoEquirectangularRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoGnomonic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoGnomonicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoIdentity\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoIdentity\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjection\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoProjection\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjectionMutator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoProjectionMutator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoMercator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercatorRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoMercatorRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoNaturalEarth1\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1Raw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoNaturalEarth1Raw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoOrthographic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoOrthographicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoStereographic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoStereographicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoTransverseMercator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercatorRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoTransverseMercatorRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoRotation\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoRotation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStream\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoStream\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransform\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_15__[\"geoTransform\"]; });\n\n/* harmony import */ var d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! d3-hierarchy */ \"./node_modules/d3-hierarchy/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cluster\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"cluster\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hierarchy\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"hierarchy\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pack\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"pack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packSiblings\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"packSiblings\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"packEnclose\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"partition\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"partition\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stratify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"stratify\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tree\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"tree\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemap\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemap\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapBinary\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapBinary\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapDice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapDice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSlice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapSlice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSliceDice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapSliceDice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSquarify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapSquarify\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapResquarify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_16__[\"treemapResquarify\"]; });\n\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/d3-interpolate/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateArray\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateDate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateDiscrete\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateHue\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateNumber\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateObject\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateRound\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateString\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateTransformSvg\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateZoom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateRgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateRgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateRgbBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateHsl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateHslLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateLab\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateHcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateHclLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateCubehelix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"interpolateCubehelixLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"piecewise\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_17__[\"quantize\"]; });\n\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! d3-path */ \"./node_modules/d3-path/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return d3_path__WEBPACK_IMPORTED_MODULE_18__[\"path\"]; });\n\n/* harmony import */ var d3_polygon__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! d3-polygon */ \"./node_modules/d3-polygon/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonArea\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_19__[\"polygonArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonCentroid\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_19__[\"polygonCentroid\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonHull\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_19__[\"polygonHull\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonContains\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_19__[\"polygonContains\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonLength\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_19__[\"polygonLength\"]; });\n\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/d3-quadtree/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quadtree\", function() { return d3_quadtree__WEBPACK_IMPORTED_MODULE_20__[\"quadtree\"]; });\n\n/* harmony import */ var d3_random__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! d3-random */ \"./node_modules/d3-random/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomUniform\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomUniform\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomNormal\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomNormal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomLogNormal\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomLogNormal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomBates\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomBates\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomIrwinHall\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomIrwinHall\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomExponential\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_21__[\"randomExponential\"]; });\n\n/* harmony import */ var d3_scale__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! d3-scale */ \"./node_modules/d3-scale/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleBand\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleBand\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePoint\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scalePoint\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleIdentity\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleIdentity\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLinear\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLog\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleLog\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleOrdinal\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleOrdinal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleImplicit\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleImplicit\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePow\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scalePow\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSqrt\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleSqrt\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantile\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleQuantile\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantize\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleQuantize\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleThreshold\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleThreshold\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleTime\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleTime\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleUtc\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleUtc\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSequential\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleSequential\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleDiverging\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleDiverging\"]; });\n\n/* harmony import */ var d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! d3-scale-chromatic */ \"./node_modules/d3-scale-chromatic/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory10\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeCategory10\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeAccent\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeAccent\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeDark2\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeDark2\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePaired\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePaired\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePastel1\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePastel1\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePastel2\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePastel2\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet1\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeSet1\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet2\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeSet2\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSet3\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeSet3\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBrBG\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateBrBG\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBrBG\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeBrBG\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePRGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePRGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePRGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePRGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePiYG\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePiYG\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePiYG\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePiYG\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuOr\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePuOr\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuOr\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePuOr\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRdBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeRdBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdGy\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRdGy\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdGy\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeRdGy\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdYlBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRdYlBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdYlBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeRdYlBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdYlGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRdYlGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdYlGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeRdYlGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateSpectral\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateSpectral\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeSpectral\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeSpectral\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBuGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateBuGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBuGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeBuGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBuPu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateBuPu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBuPu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeBuPu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGnBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateGnBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGnBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeGnBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateOrRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateOrRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeOrRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeOrRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuBuGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePuBuGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuBuGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePuBuGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePuBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePuBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePuRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePuRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePuRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePuRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRdPu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRdPu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeRdPu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeRdPu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlGnBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateYlGnBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlGnBu\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeYlGnBu\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateYlGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlGn\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeYlGn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlOrBr\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateYlOrBr\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlOrBr\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeYlOrBr\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateYlOrRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateYlOrRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeYlOrRd\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeYlOrRd\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBlues\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateBlues\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeBlues\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeBlues\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGreens\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateGreens\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGreens\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeGreens\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateGreys\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateGreys\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeGreys\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeGreys\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePurples\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePurples\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemePurples\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemePurples\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateReds\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateReds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeReds\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeReds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateOranges\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateOranges\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeOranges\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"schemeOranges\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixDefault\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateCubehelixDefault\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRainbow\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateRainbow\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateWarm\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateWarm\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCool\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateCool\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateSinebow\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateSinebow\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateViridis\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateViridis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateMagma\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateMagma\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateInferno\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolateInferno\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePlasma\", function() { return d3_scale_chromatic__WEBPACK_IMPORTED_MODULE_23__[\"interpolatePlasma\"]; });\n\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! d3-selection */ \"./node_modules/d3-selection/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"create\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"creator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"local\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"matcher\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"mouse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"namespace\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"namespaces\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"clientPoint\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"select\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"selectAll\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"selection\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"selector\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"selectorAll\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"style\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"touch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"touches\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"window\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_24__[\"customEvent\"]; });\n\n/* harmony import */ var d3_shape__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! d3-shape */ \"./node_modules/d3-shape/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"arc\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"arc\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"area\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"area\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"line\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"line\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pie\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"pie\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"areaRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"areaRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialArea\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"radialArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"lineRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialLine\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"radialLine\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pointRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"pointRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"linkHorizontal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"linkVertical\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"linkRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbol\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbol\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbols\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCircle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCross\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolCross\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolDiamond\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolDiamond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolSquare\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolSquare\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolStar\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolStar\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolTriangle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolTriangle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolWye\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"symbolWye\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveBasisOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasis\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBundle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveBundle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCardinalClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCardinalOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinal\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCardinal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCatmullRomClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCatmullRomOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRom\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveCatmullRom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinearClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveLinearClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinear\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneX\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveMonotoneX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneY\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveMonotoneY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveNatural\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveNatural\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStep\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveStep\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepAfter\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveStepAfter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepBefore\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"curveStepBefore\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stack\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetExpand\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOffsetExpand\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetDiverging\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOffsetDiverging\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetNone\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOffsetNone\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetSilhouette\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOffsetSilhouette\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetWiggle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOffsetWiggle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderAscending\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOrderAscending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderDescending\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOrderDescending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderInsideOut\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOrderInsideOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderNone\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOrderNone\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderReverse\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_25__[\"stackOrderReverse\"]; });\n\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! d3-time */ \"./node_modules/d3-time/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeInterval\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMillisecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMilliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMillisecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMilliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSeconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSeconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMinute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMinutes\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeHour\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeHours\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeDay\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeDays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeWeek\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeWeeks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeSaturdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMonth\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeMonths\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeYear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"timeYears\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMinute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMinutes\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcHour\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcHours\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcDay\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcDays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcWeek\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcWeeks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcSaturdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMonth\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcMonths\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcYear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_26__[\"utcYears\"]; });\n\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/d3-time-format/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatDefaultLocale\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"timeFormatDefaultLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"timeFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"timeParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"utcFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"utcParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatLocale\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"timeFormatLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"isoFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_27__[\"isoParse\"]; });\n\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! d3-timer */ \"./node_modules/d3-timer/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_28__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_28__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_28__[\"timerFlush\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_28__[\"timeout\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_28__[\"interval\"]; });\n\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! d3-transition */ \"./node_modules/d3-transition/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_29__[\"transition\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_29__[\"active\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_29__[\"interrupt\"]; });\n\n/* harmony import */ var d3_voronoi__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! d3-voronoi */ \"./node_modules/d3-voronoi/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"voronoi\", function() { return d3_voronoi__WEBPACK_IMPORTED_MODULE_30__[\"voronoi\"]; });\n\n/* harmony import */ var d3_zoom__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! d3-zoom */ \"./node_modules/d3-zoom/src/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoom\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_31__[\"zoom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomTransform\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_31__[\"zoomTransform\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomIdentity\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_31__[\"zoomIdentity\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/d3/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/index.js": +/*!****************************************!*\ + !*** ./node_modules/dagre-d3/index.js ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/**\n * @license\n * Copyright (c) 2012-2013 Chris Pettitt\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\nmodule.exports = {\n graphlib: __webpack_require__(/*! ./lib/graphlib */ \"./node_modules/dagre-d3/lib/graphlib.js\"),\n dagre: __webpack_require__(/*! ./lib/dagre */ \"./node_modules/dagre-d3/lib/dagre.js\"),\n intersect: __webpack_require__(/*! ./lib/intersect */ \"./node_modules/dagre-d3/lib/intersect/index.js\"),\n render: __webpack_require__(/*! ./lib/render */ \"./node_modules/dagre-d3/lib/render.js\"),\n util: __webpack_require__(/*! ./lib/util */ \"./node_modules/dagre-d3/lib/util.js\"),\n version: __webpack_require__(/*! ./lib/version */ \"./node_modules/dagre-d3/lib/version.js\")\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/arrows.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre-d3/lib/arrows.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\");\n\nmodule.exports = {\n \"default\": normal,\n \"normal\": normal,\n \"vee\": vee,\n \"undirected\": undirected\n};\n\nfunction normal(parent, id, edge, type) {\n var marker = parent.append(\"marker\")\n .attr(\"id\", id)\n .attr(\"viewBox\", \"0 0 10 10\")\n .attr(\"refX\", 9)\n .attr(\"refY\", 5)\n .attr(\"markerUnits\", \"strokeWidth\")\n .attr(\"markerWidth\", 8)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\");\n\n var path = marker.append(\"path\")\n .attr(\"d\", \"M 0 0 L 10 5 L 0 10 z\")\n .style(\"stroke-width\", 1)\n .style(\"stroke-dasharray\", \"1,0\");\n util.applyStyle(path, edge[type + \"Style\"]);\n if (edge[type + \"Class\"]) {\n path.attr(\"class\", edge[type + \"Class\"]);\n }\n}\n\nfunction vee(parent, id, edge, type) {\n var marker = parent.append(\"marker\")\n .attr(\"id\", id)\n .attr(\"viewBox\", \"0 0 10 10\")\n .attr(\"refX\", 9)\n .attr(\"refY\", 5)\n .attr(\"markerUnits\", \"strokeWidth\")\n .attr(\"markerWidth\", 8)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\");\n\n var path = marker.append(\"path\")\n .attr(\"d\", \"M 0 0 L 10 5 L 0 10 L 4 5 z\")\n .style(\"stroke-width\", 1)\n .style(\"stroke-dasharray\", \"1,0\");\n util.applyStyle(path, edge[type + \"Style\"]);\n if (edge[type + \"Class\"]) {\n path.attr(\"class\", edge[type + \"Class\"]);\n }\n}\n\nfunction undirected(parent, id, edge, type) {\n var marker = parent.append(\"marker\")\n .attr(\"id\", id)\n .attr(\"viewBox\", \"0 0 10 10\")\n .attr(\"refX\", 9)\n .attr(\"refY\", 5)\n .attr(\"markerUnits\", \"strokeWidth\")\n .attr(\"markerWidth\", 8)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\");\n\n var path = marker.append(\"path\")\n .attr(\"d\", \"M 0 5 L 10 5\")\n .style(\"stroke-width\", 1)\n .style(\"stroke-dasharray\", \"1,0\");\n util.applyStyle(path, edge[type + \"Style\"]);\n if (edge[type + \"Class\"]) {\n path.attr(\"class\", edge[type + \"Class\"]);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/arrows.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/create-clusters.js": +/*!******************************************************!*\ + !*** ./node_modules/dagre-d3/lib/create-clusters.js ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\"),\n addLabel = __webpack_require__(/*! ./label/add-label */ \"./node_modules/dagre-d3/lib/label/add-label.js\");\n\nmodule.exports = createClusters;\n\nfunction createClusters(selection, g) {\n var clusters = g.nodes().filter(function(v) { return util.isSubgraph(g, v); }),\n svgClusters = selection.selectAll(\"g.cluster\")\n .data(clusters, function(v) { return v; });\n\n svgClusters.selectAll(\"*\").remove();\n svgClusters.enter()\n .append(\"g\")\n .attr(\"class\", \"cluster\")\n .attr(\"id\",function(v){\n var node = g.node(v);\n return node.id;\n })\n .style(\"opacity\", 0);\n \n svgClusters = selection.selectAll(\"g.cluster\");\n\n util.applyTransition(svgClusters, g)\n .style(\"opacity\", 1);\n\n svgClusters.each(function(v) {\n var node = g.node(v),\n thisGroup = d3.select(this);\n d3.select(this).append(\"rect\");\n var labelGroup = thisGroup.append(\"g\").attr(\"class\", \"label\");\n addLabel(labelGroup, node, node.clusterLabelPos);\n });\n\n svgClusters.selectAll(\"rect\").each(function(c) {\n var node = g.node(c);\n var domCluster = d3.select(this);\n util.applyStyle(domCluster, node.style);\n });\n\n util.applyTransition(svgClusters.exit(), g)\n .style(\"opacity\", 0)\n .remove();\n\n return svgClusters;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/create-clusters.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/create-edge-labels.js": +/*!*********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/create-edge-labels.js ***! + \*********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\"),\n addLabel = __webpack_require__(/*! ./label/add-label */ \"./node_modules/dagre-d3/lib/label/add-label.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\");\n\nmodule.exports = createEdgeLabels;\n\nfunction createEdgeLabels(selection, g) {\n var svgEdgeLabels = selection.selectAll(\"g.edgeLabel\")\n .data(g.edges(), function(e) { return util.edgeToId(e); })\n .classed(\"update\", true);\n\n svgEdgeLabels.selectAll(\"*\").remove();\n svgEdgeLabels.enter()\n .append(\"g\")\n .classed(\"edgeLabel\", true)\n .style(\"opacity\", 0);\n\n svgEdgeLabels = selection.selectAll(\"g.edgeLabel\");\n \n svgEdgeLabels.each(function(e) {\n var edge = g.edge(e),\n label = addLabel(d3.select(this), g.edge(e), 0, 0).classed(\"label\", true),\n bbox = label.node().getBBox();\n\n if (edge.labelId) { label.attr(\"id\", edge.labelId); }\n if (!_.has(edge, \"width\")) { edge.width = bbox.width; }\n if (!_.has(edge, \"height\")) { edge.height = bbox.height; }\n });\n\n util.applyTransition(svgEdgeLabels.exit(), g)\n .style(\"opacity\", 0)\n .remove();\n\n return svgEdgeLabels;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/create-edge-labels.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/create-edge-paths.js": +/*!********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/create-edge-paths.js ***! + \********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\"),\n intersectNode = __webpack_require__(/*! ./intersect/intersect-node */ \"./node_modules/dagre-d3/lib/intersect/intersect-node.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\");\nmodule.exports = createEdgePaths;\n\nfunction createEdgePaths(selection, g, arrows) {\n var svgPaths = selection.selectAll(\"g.edgePath\")\n .data(g.edges(), function(e) { return util.edgeToId(e); })\n .classed(\"update\", true);\n\n enter(svgPaths, g);\n exit(svgPaths, g);\n\n svgPaths = selection.selectAll(\"g.edgePath\");\n\n util.applyTransition(svgPaths, g)\n .style(\"opacity\", 1);\n\n // Save DOM element in the path group, and set ID and class\n svgPaths.each(function(e) {\n var domEdge = d3.select(this);\n var edge = g.edge(e);\n edge.elem = this;\n\n if (edge.id) {\n domEdge.attr(\"id\", edge.id);\n }\n\n util.applyClass(domEdge, edge[\"class\"],\n (domEdge.classed(\"update\") ? \"update \" : \"\") + \"edgePath\");\n });\n\n svgPaths.selectAll(\"path.path\")\n .each(function(e) {\n var edge = g.edge(e);\n edge.arrowheadId = _.uniqueId(\"arrowhead\");\n\n var domEdge = d3.select(this)\n .attr(\"marker-end\", function() {\n return \"url(\" + makeFragmentRef(location.href, edge.arrowheadId) + \")\";\n })\n .style(\"fill\", \"none\");\n\n util.applyTransition(domEdge, g)\n .attr(\"d\", function(e) { return calcPoints(g, e); });\n\n util.applyStyle(domEdge, edge.style);\n });\n\n svgPaths.selectAll(\"defs *\").remove();\n svgPaths.selectAll(\"defs\")\n .each(function(e) {\n var edge = g.edge(e),\n arrowhead = arrows[edge.arrowhead];\n arrowhead(d3.select(this), edge.arrowheadId, edge, \"arrowhead\");\n });\n\n return svgPaths;\n}\n\nfunction makeFragmentRef(url, fragmentId) {\n var baseUrl = url.split(\"#\")[0];\n return baseUrl + \"#\" + fragmentId;\n}\n\nfunction calcPoints(g, e) {\n var edge = g.edge(e),\n tail = g.node(e.v),\n head = g.node(e.w),\n points = edge.points.slice(1, edge.points.length - 1);\n points.unshift(intersectNode(tail, points[0]));\n points.push(intersectNode(head, points[points.length - 1]));\n\n return createLine(edge, points);\n}\n\nfunction createLine(edge, points) {\n var line = d3.line()\n .x(function(d) { return d.x; })\n .y(function(d) { return d.y; });\n \n line.curve(edge.curve);\n\n return line(points);\n}\n\nfunction getCoords(elem) {\n var bbox = elem.getBBox(),\n matrix = elem.ownerSVGElement.getScreenCTM()\n .inverse()\n .multiply(elem.getScreenCTM())\n .translate(bbox.width / 2, bbox.height / 2);\n return { x: matrix.e, y: matrix.f };\n}\n\nfunction enter(svgPaths, g) {\n var svgPathsEnter = svgPaths.enter()\n .append(\"g\")\n .attr(\"class\", \"edgePath\")\n .style(\"opacity\", 0);\n svgPathsEnter.append(\"path\")\n .attr(\"class\", \"path\")\n .attr(\"d\", function(e) {\n var edge = g.edge(e),\n sourceElem = g.node(e.v).elem,\n points = _.range(edge.points.length).map(function() { return getCoords(sourceElem); });\n return createLine(edge, points);\n });\n svgPathsEnter.append(\"defs\");\n}\n\nfunction exit(svgPaths, g) {\n var svgPathExit = svgPaths.exit();\n util.applyTransition(svgPathExit, g)\n .style(\"opacity\", 0)\n .remove();\n\n util.applyTransition(svgPathExit.select(\"path.path\"), g)\n .attr(\"d\", function(e) {\n var source = g.node(e.v);\n\n if (source) {\n var points = _.range(this.getTotalLength()).map(function() { return source; });\n return createLine({}, points);\n } else {\n return d3.select(this).attr(\"d\");\n }\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/create-edge-paths.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/create-nodes.js": +/*!***************************************************!*\ + !*** ./node_modules/dagre-d3/lib/create-nodes.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\"),\n addLabel = __webpack_require__(/*! ./label/add-label */ \"./node_modules/dagre-d3/lib/label/add-label.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\");\n\nmodule.exports = createNodes;\n\nfunction createNodes(selection, g, shapes) {\n var simpleNodes = g.nodes().filter(function(v) { return !util.isSubgraph(g, v); });\n var svgNodes = selection.selectAll(\"g.node\")\n .data(simpleNodes, function(v) { return v; })\n .classed(\"update\", true);\n\n svgNodes.selectAll(\"*\").remove();\n\n svgNodes.enter()\n .append(\"g\")\n .attr(\"class\", \"node\")\n .style(\"opacity\", 0);\n\n svgNodes = selection.selectAll(\"g.node\"); \n\n svgNodes.each(function(v) {\n var node = g.node(v),\n thisGroup = d3.select(this);\n util.applyClass(thisGroup, node[\"class\"],\n (thisGroup.classed(\"update\") ? \"update \" : \"\") + \"node\");\n var labelGroup = thisGroup.append(\"g\").attr(\"class\", \"label\"),\n labelDom = addLabel(labelGroup, node),\n shape = shapes[node.shape],\n bbox = _.pick(labelDom.node().getBBox(), \"width\", \"height\");\n\n node.elem = this;\n\n if (node.id) { thisGroup.attr(\"id\", node.id); }\n if (node.labelId) { labelGroup.attr(\"id\", node.labelId); }\n\n if (_.has(node, \"width\")) { bbox.width = node.width; }\n if (_.has(node, \"height\")) { bbox.height = node.height; }\n\n bbox.width += node.paddingLeft + node.paddingRight;\n bbox.height += node.paddingTop + node.paddingBottom;\n labelGroup.attr(\"transform\", \"translate(\" +\n ((node.paddingLeft - node.paddingRight) / 2) + \",\" +\n ((node.paddingTop - node.paddingBottom) / 2) + \")\");\n\n var shapeSvg = shape(d3.select(this), bbox, node);\n util.applyStyle(shapeSvg, node.style);\n\n var shapeBBox = shapeSvg.node().getBBox();\n node.width = shapeBBox.width;\n node.height = shapeBBox.height;\n });\n\n util.applyTransition(svgNodes.exit(), g)\n .style(\"opacity\", 0)\n .remove();\n\n return svgNodes;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/create-nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/d3.js": +/*!*****************************************!*\ + !*** ./node_modules/dagre-d3/lib/d3.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// Stub to get D3 either via NPM or from the global object\nvar d3;\n\nif (!d3) {\n if (true) {\n try {\n d3 = __webpack_require__(/*! d3 */ \"./node_modules/dagre-d3/node_modules/d3/index.js\");\n }\n catch (e) {\n }\n }\n}\n\nif (!d3) {\n d3 = window.d3;\n}\n\nmodule.exports = d3;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/d3.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/dagre.js": +/*!********************************************!*\ + !*** ./node_modules/dagre-d3/lib/dagre.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar dagre;\n\nif (true) {\n try {\n dagre = __webpack_require__(/*! dagre */ \"./node_modules/dagre/index.js\");\n } catch (e) {}\n}\n\nif (!dagre) {\n dagre = window.dagre;\n}\n\nmodule.exports = dagre;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/dagre.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/graphlib.js": +/*!***********************************************!*\ + !*** ./node_modules/dagre-d3/lib/graphlib.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar graphlib;\n\nif (true) {\n try {\n graphlib = __webpack_require__(/*! graphlib */ \"./node_modules/graphlib/index.js\");\n } catch (e) {}\n}\n\nif (!graphlib) {\n graphlib = window.graphlib;\n}\n\nmodule.exports = graphlib;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/graphlib.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/index.js": +/*!******************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/index.js ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("module.exports = {\n node: __webpack_require__(/*! ./intersect-node */ \"./node_modules/dagre-d3/lib/intersect/intersect-node.js\"),\n circle: __webpack_require__(/*! ./intersect-circle */ \"./node_modules/dagre-d3/lib/intersect/intersect-circle.js\"),\n ellipse: __webpack_require__(/*! ./intersect-ellipse */ \"./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js\"),\n polygon: __webpack_require__(/*! ./intersect-polygon */ \"./node_modules/dagre-d3/lib/intersect/intersect-polygon.js\"),\n rect: __webpack_require__(/*! ./intersect-rect */ \"./node_modules/dagre-d3/lib/intersect/intersect-rect.js\")\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-circle.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-circle.js ***! + \*****************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var intersectEllipse = __webpack_require__(/*! ./intersect-ellipse */ \"./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js\");\n\nmodule.exports = intersectCircle;\n\nfunction intersectCircle(node, rx, point) {\n return intersectEllipse(node, rx, rx, point);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js ***! + \******************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = intersectEllipse;\n\nfunction intersectEllipse(node, rx, ry, point) {\n // Formulae from: http://mathworld.wolfram.com/Ellipse-LineIntersection.html\n\n var cx = node.x;\n var cy = node.y;\n\n var px = cx - point.x;\n var py = cy - point.y;\n\n var det = Math.sqrt(rx * rx * py * py + ry * ry * px * px);\n\n var dx = Math.abs(rx * ry * px / det);\n if (point.x < cx) {\n dx = -dx;\n }\n var dy = Math.abs(rx * ry * py / det);\n if (point.y < cy) {\n dy = -dy;\n }\n\n return {x: cx + dx, y: cy + dy};\n}\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-line.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-line.js ***! + \***************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = intersectLine;\n\n/*\n * Returns the point at which two lines, p and q, intersect or returns\n * undefined if they do not intersect.\n */\nfunction intersectLine(p1, p2, q1, q2) {\n // Algorithm from J. Avro, (ed.) Graphics Gems, No 2, Morgan Kaufmann, 1994,\n // p7 and p473.\n\n var a1, a2, b1, b2, c1, c2;\n var r1, r2 , r3, r4;\n var denom, offset, num;\n var x, y;\n\n // Compute a1, b1, c1, where line joining points 1 and 2 is F(x,y) = a1 x +\n // b1 y + c1 = 0.\n a1 = p2.y - p1.y;\n b1 = p1.x - p2.x;\n c1 = (p2.x * p1.y) - (p1.x * p2.y);\n\n // Compute r3 and r4.\n r3 = ((a1 * q1.x) + (b1 * q1.y) + c1);\n r4 = ((a1 * q2.x) + (b1 * q2.y) + c1);\n\n // Check signs of r3 and r4. If both point 3 and point 4 lie on\n // same side of line 1, the line segments do not intersect.\n if ((r3 !== 0) && (r4 !== 0) && sameSign(r3, r4)) {\n return /*DONT_INTERSECT*/;\n }\n\n // Compute a2, b2, c2 where line joining points 3 and 4 is G(x,y) = a2 x + b2 y + c2 = 0\n a2 = q2.y - q1.y;\n b2 = q1.x - q2.x;\n c2 = (q2.x * q1.y) - (q1.x * q2.y);\n\n // Compute r1 and r2\n r1 = (a2 * p1.x) + (b2 * p1.y) + c2;\n r2 = (a2 * p2.x) + (b2 * p2.y) + c2;\n\n // Check signs of r1 and r2. If both point 1 and point 2 lie\n // on same side of second line segment, the line segments do\n // not intersect.\n if ((r1 !== 0) && (r2 !== 0) && (sameSign(r1, r2))) {\n return /*DONT_INTERSECT*/;\n }\n\n // Line segments intersect: compute intersection point.\n denom = (a1 * b2) - (a2 * b1);\n if (denom === 0) {\n return /*COLLINEAR*/;\n }\n\n offset = Math.abs(denom / 2);\n\n // The denom/2 is to get rounding instead of truncating. It\n // is added or subtracted to the numerator, depending upon the\n // sign of the numerator.\n num = (b1 * c2) - (b2 * c1);\n x = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);\n\n num = (a2 * c1) - (a1 * c2);\n y = (num < 0) ? ((num - offset) / denom) : ((num + offset) / denom);\n\n return { x: x, y: y };\n}\n\nfunction sameSign(r1, r2) {\n return r1 * r2 > 0;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-line.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-node.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-node.js ***! + \***************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = intersectNode;\n\nfunction intersectNode(node, point) {\n return node.intersect(point);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-polygon.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-polygon.js ***! + \******************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var intersectLine = __webpack_require__(/*! ./intersect-line */ \"./node_modules/dagre-d3/lib/intersect/intersect-line.js\");\n\nmodule.exports = intersectPolygon;\n\n/*\n * Returns the point ({x, y}) at which the point argument intersects with the\n * node argument assuming that it has the shape specified by polygon.\n */\nfunction intersectPolygon(node, polyPoints, point) {\n var x1 = node.x;\n var y1 = node.y;\n\n var intersections = [];\n\n var minX = Number.POSITIVE_INFINITY,\n minY = Number.POSITIVE_INFINITY;\n polyPoints.forEach(function(entry) {\n minX = Math.min(minX, entry.x);\n minY = Math.min(minY, entry.y);\n });\n\n var left = x1 - node.width / 2 - minX;\n var top = y1 - node.height / 2 - minY;\n\n for (var i = 0; i < polyPoints.length; i++) {\n var p1 = polyPoints[i];\n var p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0];\n var intersect = intersectLine(node, point,\n {x: left + p1.x, y: top + p1.y}, {x: left + p2.x, y: top + p2.y});\n if (intersect) {\n intersections.push(intersect);\n }\n }\n\n if (!intersections.length) {\n console.log(\"NO INTERSECTION FOUND, RETURN NODE CENTER\", node);\n return node;\n }\n\n if (intersections.length > 1) {\n // More intersections, find the one nearest to edge end point\n intersections.sort(function(p, q) {\n var pdx = p.x - point.x,\n pdy = p.y - point.y,\n distp = Math.sqrt(pdx * pdx + pdy * pdy),\n\n qdx = q.x - point.x,\n qdy = q.y - point.y,\n distq = Math.sqrt(qdx * qdx + qdy * qdy);\n\n return (distp < distq) ? -1 : (distp === distq ? 0 : 1);\n });\n }\n return intersections[0];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-polygon.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/intersect/intersect-rect.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/lib/intersect/intersect-rect.js ***! + \***************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = intersectRect;\n\nfunction intersectRect(node, point) {\n var x = node.x;\n var y = node.y;\n\n // Rectangle intersection algorithm from:\n // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes\n var dx = point.x - x;\n var dy = point.y - y;\n var w = node.width / 2;\n var h = node.height / 2;\n\n var sx, sy;\n if (Math.abs(dy) * w > Math.abs(dx) * h) {\n // Intersection is top or bottom of rect.\n if (dy < 0) {\n h = -h;\n }\n sx = dy === 0 ? 0 : h * dx / dy;\n sy = h;\n } else {\n // Intersection is left or right of rect.\n if (dx < 0) {\n w = -w;\n }\n sx = w;\n sy = dx === 0 ? 0 : w * dy / dx;\n }\n\n return {x: x + sx, y: y + sy};\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/intersect/intersect-rect.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/label/add-html-label.js": +/*!***********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/label/add-html-label.js ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var util = __webpack_require__(/*! ../util */ \"./node_modules/dagre-d3/lib/util.js\");\n\nmodule.exports = addHtmlLabel;\n\nfunction addHtmlLabel(root, node) {\n var fo = root\n .append(\"foreignObject\")\n .attr(\"width\", \"100000\");\n\n var div = fo\n .append(\"xhtml:div\");\n div.attr(\"xmlns\", \"http://www.w3.org/1999/xhtml\");\n\n var label = node.label;\n switch(typeof label) {\n case \"function\":\n div.insert(label);\n break;\n case \"object\":\n // Currently we assume this is a DOM object.\n div.insert(function() { return label; });\n break;\n default: div.html(label);\n }\n\n util.applyStyle(div, node.labelStyle);\n div.style(\"display\", \"inline-block\");\n // Fix for firefox\n div.style(\"white-space\", \"nowrap\");\n\n var client = div.node().getBoundingClientRect();\n fo\n .attr(\"width\", client.width)\n .attr(\"height\", client.height); \n\n return fo;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/label/add-html-label.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/label/add-label.js": +/*!******************************************************!*\ + !*** ./node_modules/dagre-d3/lib/label/add-label.js ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var addTextLabel = __webpack_require__(/*! ./add-text-label */ \"./node_modules/dagre-d3/lib/label/add-text-label.js\"),\n addHtmlLabel = __webpack_require__(/*! ./add-html-label */ \"./node_modules/dagre-d3/lib/label/add-html-label.js\"),\n addSVGLabel = __webpack_require__(/*! ./add-svg-label */ \"./node_modules/dagre-d3/lib/label/add-svg-label.js\");\n\nmodule.exports = addLabel;\n\nfunction addLabel(root, node, location) {\n var label = node.label;\n var labelSvg = root.append(\"g\");\n\n // Allow the label to be a string, a function that returns a DOM element, or\n // a DOM element itself.\n if (node.labelType === \"svg\") {\n addSVGLabel(labelSvg, node);\n } else if (typeof label !== \"string\" || node.labelType === \"html\") {\n addHtmlLabel(labelSvg, node);\n } else {\n addTextLabel(labelSvg, node);\n }\n\n var labelBBox = labelSvg.node().getBBox();\n var y;\n switch(location) {\n case \"top\":\n y = (-node.height / 2);\n break;\n case \"bottom\":\n y = (node.height / 2) - labelBBox.height;\n break;\n default:\n y = (-labelBBox.height / 2);\n }\n labelSvg.attr(\"transform\",\n \"translate(\" + (-labelBBox.width / 2) + \",\" + y + \")\");\n\n return labelSvg;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/label/add-label.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/label/add-svg-label.js": +/*!**********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/label/add-svg-label.js ***! + \**********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var util = __webpack_require__(/*! ../util */ \"./node_modules/dagre-d3/lib/util.js\");\n\nmodule.exports = addSVGLabel;\n\nfunction addSVGLabel(root, node) {\n var domNode = root;\n\n domNode.node().appendChild(node.label);\n\n util.applyStyle(domNode, node.labelStyle);\n\n return domNode;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/label/add-svg-label.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/label/add-text-label.js": +/*!***********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/label/add-text-label.js ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var util = __webpack_require__(/*! ../util */ \"./node_modules/dagre-d3/lib/util.js\");\n\nmodule.exports = addTextLabel;\n\n/*\n * Attaches a text label to the specified root. Handles escape sequences.\n */\nfunction addTextLabel(root, node) {\n var domNode = root.append(\"text\");\n\n var lines = processEscapeSequences(node.label).split(\"\\n\");\n for (var i = 0; i < lines.length; i++) {\n domNode\n .append(\"tspan\")\n .attr(\"xml:space\", \"preserve\")\n .attr(\"dy\", \"1em\")\n .attr(\"x\", \"1\")\n .text(lines[i]);\n }\n\n util.applyStyle(domNode, node.labelStyle);\n\n return domNode;\n}\n\nfunction processEscapeSequences(text) {\n var newText = \"\",\n escaped = false,\n ch;\n for (var i = 0; i < text.length; ++i) {\n ch = text[i];\n if (escaped) {\n switch(ch) {\n case \"n\": newText += \"\\n\"; break;\n default: newText += ch;\n }\n escaped = false;\n } else if (ch === \"\\\\\") {\n escaped = true;\n } else {\n newText += ch;\n }\n }\n return newText;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/label/add-text-label.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/lodash.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre-d3/lib/lodash.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar lodash;\n\nif (true) {\n try {\n lodash = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\n } catch (e) {}\n}\n\nif (!lodash) {\n lodash = window._;\n}\n\nmodule.exports = lodash;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/lodash.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/position-clusters.js": +/*!********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/position-clusters.js ***! + \********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\");\n\nmodule.exports = positionClusters;\n\nfunction positionClusters(selection, g) {\n var created = selection.filter(function() { return !d3.select(this).classed(\"update\"); });\n\n function translate(v) {\n var node = g.node(v);\n return \"translate(\" + node.x + \",\" + node.y + \")\";\n }\n\n created.attr(\"transform\", translate);\n\n util.applyTransition(selection, g)\n .style(\"opacity\", 1)\n .attr(\"transform\", translate);\n\n util.applyTransition(created.selectAll(\"rect\"), g)\n .attr(\"width\", function(v) { return g.node(v).width; })\n .attr(\"height\", function(v) { return g.node(v).height; })\n .attr(\"x\", function(v) {\n var node = g.node(v);\n return -node.width / 2;\n })\n .attr(\"y\", function(v) {\n var node = g.node(v);\n return -node.height / 2;\n });\n\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/position-clusters.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/position-edge-labels.js": +/*!***********************************************************!*\ + !*** ./node_modules/dagre-d3/lib/position-edge-labels.js ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\"),\n _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\");\n\nmodule.exports = positionEdgeLabels;\n\nfunction positionEdgeLabels(selection, g) {\n var created = selection.filter(function() { return !d3.select(this).classed(\"update\"); });\n\n function translate(e) {\n var edge = g.edge(e);\n return _.has(edge, \"x\") ? \"translate(\" + edge.x + \",\" + edge.y + \")\" : \"\";\n }\n\n created.attr(\"transform\", translate);\n\n util.applyTransition(selection, g)\n .style(\"opacity\", 1)\n .attr(\"transform\", translate);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/position-edge-labels.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/position-nodes.js": +/*!*****************************************************!*\ + !*** ./node_modules/dagre-d3/lib/position-nodes.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar util = __webpack_require__(/*! ./util */ \"./node_modules/dagre-d3/lib/util.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\");\n\nmodule.exports = positionNodes;\n\nfunction positionNodes(selection, g) {\n var created = selection.filter(function() { return !d3.select(this).classed(\"update\"); });\n\n function translate(v) {\n var node = g.node(v);\n return \"translate(\" + node.x + \",\" + node.y + \")\";\n }\n\n created.attr(\"transform\", translate);\n\n util.applyTransition(selection, g)\n .style(\"opacity\", 1)\n .attr(\"transform\", translate);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/position-nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/render.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre-d3/lib/render.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\"),\n d3 = __webpack_require__(/*! ./d3 */ \"./node_modules/dagre-d3/lib/d3.js\"),\n layout = __webpack_require__(/*! ./dagre */ \"./node_modules/dagre-d3/lib/dagre.js\").layout;\n\nmodule.exports = render;\n\n// This design is based on http://bost.ocks.org/mike/chart/.\nfunction render() {\n var createNodes = __webpack_require__(/*! ./create-nodes */ \"./node_modules/dagre-d3/lib/create-nodes.js\"),\n createClusters = __webpack_require__(/*! ./create-clusters */ \"./node_modules/dagre-d3/lib/create-clusters.js\"),\n createEdgeLabels = __webpack_require__(/*! ./create-edge-labels */ \"./node_modules/dagre-d3/lib/create-edge-labels.js\"),\n createEdgePaths = __webpack_require__(/*! ./create-edge-paths */ \"./node_modules/dagre-d3/lib/create-edge-paths.js\"),\n positionNodes = __webpack_require__(/*! ./position-nodes */ \"./node_modules/dagre-d3/lib/position-nodes.js\"),\n positionEdgeLabels = __webpack_require__(/*! ./position-edge-labels */ \"./node_modules/dagre-d3/lib/position-edge-labels.js\"),\n positionClusters = __webpack_require__(/*! ./position-clusters */ \"./node_modules/dagre-d3/lib/position-clusters.js\"),\n shapes = __webpack_require__(/*! ./shapes */ \"./node_modules/dagre-d3/lib/shapes.js\"),\n arrows = __webpack_require__(/*! ./arrows */ \"./node_modules/dagre-d3/lib/arrows.js\");\n\n var fn = function(svg, g) {\n preProcessGraph(g);\n\n svg.selectAll(\"*\").remove();\n\n var outputGroup = createOrSelectGroup(svg, \"output\"),\n clustersGroup = createOrSelectGroup(outputGroup, \"clusters\"),\n edgePathsGroup = createOrSelectGroup(outputGroup, \"edgePaths\"),\n edgeLabels = createEdgeLabels(createOrSelectGroup(outputGroup, \"edgeLabels\"), g),\n nodes = createNodes(createOrSelectGroup(outputGroup, \"nodes\"), g, shapes);\n\n layout(g);\n\n positionNodes(nodes, g);\n positionEdgeLabels(edgeLabels, g);\n createEdgePaths(edgePathsGroup, g, arrows);\n\n var clusters = createClusters(clustersGroup, g);\n positionClusters(clusters, g);\n\n postProcessGraph(g);\n };\n\n fn.createNodes = function(value) {\n if (!arguments.length) return createNodes;\n createNodes = value;\n return fn;\n };\n\n fn.createClusters = function(value) {\n if (!arguments.length) return createClusters;\n createClusters = value;\n return fn;\n };\n\n fn.createEdgeLabels = function(value) {\n if (!arguments.length) return createEdgeLabels;\n createEdgeLabels = value;\n return fn;\n };\n\n fn.createEdgePaths = function(value) {\n if (!arguments.length) return createEdgePaths;\n createEdgePaths = value;\n return fn;\n };\n\n fn.shapes = function(value) {\n if (!arguments.length) return shapes;\n shapes = value;\n return fn;\n };\n\n fn.arrows = function(value) {\n if (!arguments.length) return arrows;\n arrows = value;\n return fn;\n };\n\n return fn;\n}\n\nvar NODE_DEFAULT_ATTRS = {\n paddingLeft: 10,\n paddingRight: 10,\n paddingTop: 10,\n paddingBottom: 10,\n rx: 0,\n ry: 0,\n shape: \"rect\"\n};\n\nvar EDGE_DEFAULT_ATTRS = {\n arrowhead: \"normal\",\n curve: d3.curveLinear\n};\n\nfunction preProcessGraph(g) {\n g.nodes().forEach(function(v) {\n var node = g.node(v);\n if (!_.has(node, \"label\") && !g.children(v).length) { node.label = v; }\n\n if (_.has(node, \"paddingX\")) {\n _.defaults(node, {\n paddingLeft: node.paddingX,\n paddingRight: node.paddingX\n });\n }\n\n if (_.has(node, \"paddingY\")) {\n _.defaults(node, {\n paddingTop: node.paddingY,\n paddingBottom: node.paddingY\n });\n }\n\n if (_.has(node, \"padding\")) {\n _.defaults(node, {\n paddingLeft: node.padding,\n paddingRight: node.padding,\n paddingTop: node.padding,\n paddingBottom: node.padding\n });\n }\n\n _.defaults(node, NODE_DEFAULT_ATTRS);\n\n _.each([\"paddingLeft\", \"paddingRight\", \"paddingTop\", \"paddingBottom\"], function(k) {\n node[k] = Number(node[k]);\n });\n\n // Save dimensions for restore during post-processing\n if (_.has(node, \"width\")) { node._prevWidth = node.width; }\n if (_.has(node, \"height\")) { node._prevHeight = node.height; }\n });\n\n g.edges().forEach(function(e) {\n var edge = g.edge(e);\n if (!_.has(edge, \"label\")) { edge.label = \"\"; }\n _.defaults(edge, EDGE_DEFAULT_ATTRS);\n });\n}\n\nfunction postProcessGraph(g) {\n _.each(g.nodes(), function(v) {\n var node = g.node(v);\n\n // Restore original dimensions\n if (_.has(node, \"_prevWidth\")) {\n node.width = node._prevWidth;\n } else {\n delete node.width;\n }\n\n if (_.has(node, \"_prevHeight\")) {\n node.height = node._prevHeight;\n } else {\n delete node.height;\n }\n\n delete node._prevWidth;\n delete node._prevHeight;\n });\n}\n\nfunction createOrSelectGroup(root, name) {\n var selection = root.select(\"g.\" + name);\n if (selection.empty()) {\n selection = root.append(\"g\").attr(\"class\", name);\n }\n return selection;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/render.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/shapes.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre-d3/lib/shapes.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar intersectRect = __webpack_require__(/*! ./intersect/intersect-rect */ \"./node_modules/dagre-d3/lib/intersect/intersect-rect.js\"),\n intersectEllipse = __webpack_require__(/*! ./intersect/intersect-ellipse */ \"./node_modules/dagre-d3/lib/intersect/intersect-ellipse.js\"),\n intersectCircle = __webpack_require__(/*! ./intersect/intersect-circle */ \"./node_modules/dagre-d3/lib/intersect/intersect-circle.js\"),\n intersectPolygon = __webpack_require__(/*! ./intersect/intersect-polygon */ \"./node_modules/dagre-d3/lib/intersect/intersect-polygon.js\");\n\nmodule.exports = {\n rect: rect,\n ellipse: ellipse,\n circle: circle,\n diamond: diamond\n};\n\nfunction rect(parent, bbox, node) {\n var shapeSvg = parent.insert(\"rect\", \":first-child\")\n .attr(\"rx\", node.rx)\n .attr(\"ry\", node.ry)\n .attr(\"x\", -bbox.width / 2)\n .attr(\"y\", -bbox.height / 2)\n .attr(\"width\", bbox.width)\n .attr(\"height\", bbox.height);\n\n node.intersect = function(point) {\n return intersectRect(node, point);\n };\n\n return shapeSvg;\n}\n\nfunction ellipse(parent, bbox, node) {\n var rx = bbox.width / 2,\n ry = bbox.height / 2,\n shapeSvg = parent.insert(\"ellipse\", \":first-child\")\n .attr(\"x\", -bbox.width / 2)\n .attr(\"y\", -bbox.height / 2)\n .attr(\"rx\", rx)\n .attr(\"ry\", ry);\n\n node.intersect = function(point) {\n return intersectEllipse(node, rx, ry, point);\n };\n\n return shapeSvg;\n}\n\nfunction circle(parent, bbox, node) {\n var r = Math.max(bbox.width, bbox.height) / 2,\n shapeSvg = parent.insert(\"circle\", \":first-child\")\n .attr(\"x\", -bbox.width / 2)\n .attr(\"y\", -bbox.height / 2)\n .attr(\"r\", r);\n\n node.intersect = function(point) {\n return intersectCircle(node, r, point);\n };\n\n return shapeSvg;\n}\n\n// Circumscribe an ellipse for the bounding box with a diamond shape. I derived\n// the function to calculate the diamond shape from:\n// http://mathforum.org/kb/message.jspa?messageID=3750236\nfunction diamond(parent, bbox, node) {\n var w = (bbox.width * Math.SQRT2) / 2,\n h = (bbox.height * Math.SQRT2) / 2,\n points = [\n { x: 0, y: -h },\n { x: -w, y: 0 },\n { x: 0, y: h },\n { x: w, y: 0 }\n ],\n shapeSvg = parent.insert(\"polygon\", \":first-child\")\n .attr(\"points\", points.map(function(p) { return p.x + \",\" + p.y; }).join(\" \"));\n\n node.intersect = function(p) {\n return intersectPolygon(node, points, p);\n };\n\n return shapeSvg;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/shapes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/util.js": +/*!*******************************************!*\ + !*** ./node_modules/dagre-d3/lib/util.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre-d3/lib/lodash.js\");\n\n// Public utility functions\nmodule.exports = {\n isSubgraph: isSubgraph,\n edgeToId: edgeToId,\n applyStyle: applyStyle,\n applyClass: applyClass,\n applyTransition: applyTransition\n};\n\n/*\n * Returns true if the specified node in the graph is a subgraph node. A\n * subgraph node is one that contains other nodes.\n */\nfunction isSubgraph(g, v) {\n return !!g.children(v).length;\n}\n\nfunction edgeToId(e) {\n return escapeId(e.v) + \":\" + escapeId(e.w) + \":\" + escapeId(e.name);\n}\n\nvar ID_DELIM = /:/g;\nfunction escapeId(str) {\n return str ? String(str).replace(ID_DELIM, \"\\\\:\") : \"\";\n}\n\nfunction applyStyle(dom, styleFn) {\n if (styleFn) {\n dom.attr(\"style\", styleFn);\n }\n}\n\nfunction applyClass(dom, classFn, otherClasses) {\n if (classFn) {\n dom\n .attr(\"class\", classFn)\n .attr(\"class\", otherClasses + \" \" + dom.attr(\"class\"));\n }\n}\n\nfunction applyTransition(selection, g) {\n var graph = g.graph();\n\n if (_.isPlainObject(graph)) {\n var transition = graph.transition;\n if (_.isFunction(transition)) {\n return transition(selection);\n }\n }\n\n return selection;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/util.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/lib/version.js": +/*!**********************************************!*\ + !*** ./node_modules/dagre-d3/lib/version.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = \"0.6.1\";\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/lib/version.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/index.js ***! + \**************************************************************/ +/*! exports provided: bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_bisect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/bisect */ \"./node_modules/dagre-d3/node_modules/d3-array/src/bisect.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return _src_bisect__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return _src_bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return _src_bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectLeft\"]; });\n\n/* harmony import */ var _src_ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return _src_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_bisector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/bisector */ \"./node_modules/dagre-d3/node_modules/d3-array/src/bisector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return _src_bisector__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_cross__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/cross */ \"./node_modules/dagre-d3/node_modules/d3-array/src/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return _src_cross__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_descending__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/descending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return _src_descending__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_deviation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/deviation */ \"./node_modules/dagre-d3/node_modules/d3-array/src/deviation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return _src_deviation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/extent */ \"./node_modules/dagre-d3/node_modules/d3-array/src/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return _src_extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_histogram__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/histogram */ \"./node_modules/dagre-d3/node_modules/d3-array/src/histogram.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return _src_histogram__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/threshold/freedmanDiaconis */ \"./node_modules/dagre-d3/node_modules/d3-array/src/threshold/freedmanDiaconis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return _src_threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_threshold_scott__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/threshold/scott */ \"./node_modules/dagre-d3/node_modules/d3-array/src/threshold/scott.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return _src_threshold_scott__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_threshold_sturges__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-array/src/threshold/sturges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return _src_threshold_sturges__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_max__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/max */ \"./node_modules/dagre-d3/node_modules/d3-array/src/max.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return _src_max__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_mean__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/mean */ \"./node_modules/dagre-d3/node_modules/d3-array/src/mean.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return _src_mean__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_median__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/median */ \"./node_modules/dagre-d3/node_modules/d3-array/src/median.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return _src_median__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_merge__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/merge */ \"./node_modules/dagre-d3/node_modules/d3-array/src/merge.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return _src_merge__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _src_min__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/min */ \"./node_modules/dagre-d3/node_modules/d3-array/src/min.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return _src_min__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _src_pairs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/pairs */ \"./node_modules/dagre-d3/node_modules/d3-array/src/pairs.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return _src_pairs__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _src_permute__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./src/permute */ \"./node_modules/dagre-d3/node_modules/d3-array/src/permute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return _src_permute__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _src_quantile__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./src/quantile */ \"./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return _src_quantile__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _src_range__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./src/range */ \"./node_modules/dagre-d3/node_modules/d3-array/src/range.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return _src_range__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _src_scan__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./src/scan */ \"./node_modules/dagre-d3/node_modules/d3-array/src/scan.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return _src_scan__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _src_shuffle__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./src/shuffle */ \"./node_modules/dagre-d3/node_modules/d3-array/src/shuffle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return _src_shuffle__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _src_sum__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./src/sum */ \"./node_modules/dagre-d3/node_modules/d3-array/src/sum.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return _src_sum__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _src_ticks__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./src/ticks */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ticks.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return _src_ticks__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return _src_ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return _src_ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickStep\"]; });\n\n/* harmony import */ var _src_transpose__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./src/transpose */ \"./node_modules/dagre-d3/node_modules/d3-array/src/transpose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return _src_transpose__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _src_variance__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./src/variance */ \"./node_modules/dagre-d3/node_modules/d3-array/src/variance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return _src_variance__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _src_zip__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./src/zip */ \"./node_modules/dagre-d3/node_modules/d3-array/src/zip.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return _src_zip__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/array.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/array.js ***! + \******************************************************************/ +/*! exports provided: slice, map */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\nvar map = array.map;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/bisect.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/bisect.js ***! + \*******************************************************************/ +/*! exports provided: bisectRight, bisectLeft, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return bisectRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return bisectLeft; });\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-array/src/bisector.js\");\n\n\n\nvar ascendingBisect = Object(_bisector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\nvar bisectRight = ascendingBisect.right;\nvar bisectLeft = ascendingBisect.left;\n/* harmony default export */ __webpack_exports__[\"default\"] = (bisectRight);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/bisect.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/bisector.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/bisector.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n});\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(f(d), x);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/bisector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/cross.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/cross.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-array/src/pairs.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values0, values1, reduce) {\n var n0 = values0.length,\n n1 = values1.length,\n values = new Array(n0 * n1),\n i0,\n i1,\n i,\n value0;\n\n if (reduce == null) reduce = _pairs__WEBPACK_IMPORTED_MODULE_0__[\"pair\"];\n\n for (i0 = i = 0; i0 < n0; ++i0) {\n for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {\n values[i] = reduce(value0, values1[i1]);\n }\n }\n\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/descending.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/descending.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/deviation.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/deviation.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-array/src/variance.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n var v = Object(_variance__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(array, f);\n return v ? Math.sqrt(v) : v;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/deviation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/extent.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/extent.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n return [min, max];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/histogram.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/histogram.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-array/src/bisect.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-array/src/constant.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-array/src/extent.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-array/src/identity.js\");\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-array/src/range.js\");\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ticks.js\");\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-array/src/threshold/sturges.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n domain = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n threshold = _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\n\n function histogram(data) {\n var i,\n n = data.length,\n x,\n values = new Array(n);\n\n for (i = 0; i < n; ++i) {\n values[i] = value(data[i], i, data);\n }\n\n var xz = domain(values),\n x0 = xz[0],\n x1 = xz[1],\n tz = threshold(values, x0, x1);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n tz = Object(_ticks__WEBPACK_IMPORTED_MODULE_6__[\"tickStep\"])(x0, x1, tz);\n tz = Object(_range__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(Math.ceil(x0 / tz) * tz, Math.floor(x1 / tz) * tz, tz); // exclusive\n }\n\n // Remove any thresholds outside the domain.\n var m = tz.length;\n while (tz[0] <= x0) tz.shift(), --m;\n while (tz[m - 1] > x1) tz.pop(), --m;\n\n var bins = new Array(m + 1),\n bin;\n\n // Initialize bins.\n for (i = 0; i <= m; ++i) {\n bin = bins[i] = [];\n bin.x0 = i > 0 ? tz[i - 1] : x0;\n bin.x1 = i < m ? tz[i] : x1;\n }\n\n // Assign data to bins by value, ignoring any outside the domain.\n for (i = 0; i < n; ++i) {\n x = values[i];\n if (x0 <= x && x <= x1) {\n bins[Object(_bisect__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(tz, x, 0, m)].push(data[i]);\n }\n }\n\n return bins;\n }\n\n histogram.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : value;\n };\n\n histogram.domain = function(_) {\n return arguments.length ? (domain = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])([_[0], _[1]]), histogram) : domain;\n };\n\n histogram.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : threshold;\n };\n\n return histogram;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/histogram.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/identity.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/identity.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/max.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/max.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n return max;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/max.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/mean.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/mean.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = n,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) sum += value;\n else --m;\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) sum += value;\n else --m;\n }\n }\n\n if (m) return sum / m;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/mean.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/median.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/median.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n numbers = [];\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(values[i]))) {\n numbers.push(value);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(valueof(values[i], i, values)))) {\n numbers.push(value);\n }\n }\n }\n\n return Object(_quantile__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(numbers.sort(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), 0.5);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/median.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/merge.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/merge.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(arrays) {\n var n = arrays.length,\n m,\n i = -1,\n j = 0,\n merged,\n array;\n\n while (++i < n) j += arrays[i].length;\n merged = new Array(j);\n\n while (--n >= 0) {\n array = arrays[n];\n m = array.length;\n while (--m >= 0) {\n merged[--j] = array[m];\n }\n }\n\n return merged;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/min.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/min.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n return min;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/min.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/number.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/number.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x === null ? NaN : +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/pairs.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/pairs.js ***! + \******************************************************************/ +/*! exports provided: default, pair */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pair\", function() { return pair; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n if (f == null) f = pair;\n var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);\n while (i < n) pairs[i] = f(p, p = array[++i]);\n return pairs;\n});\n\nfunction pair(a, b) {\n return [a, b];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/pairs.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/permute.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/permute.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, indexes) {\n var i = indexes.length, permutes = new Array(i);\n while (i--) permutes[i] = array[indexes[i]];\n return permutes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/permute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, p, valueof) {\n if (valueof == null) valueof = _number__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n if (!(n = values.length)) return;\n if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n var n,\n i = (n - 1) * p,\n i0 = Math.floor(i),\n value0 = +valueof(values[i0], i0, values),\n value1 = +valueof(values[i0 + 1], i0 + 1, values);\n return value0 + (value1 - value0) * (i - i0);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/range.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/range.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/range.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/scan.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/scan.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, compare) {\n if (!(n = values.length)) return;\n var n,\n i = 0,\n j = 0,\n xi,\n xj = values[j];\n\n if (compare == null) compare = _ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n\n while (++i < n) {\n if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {\n xj = xi, j = i;\n }\n }\n\n if (compare(xj, xj) === 0) return j;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/scan.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/shuffle.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/shuffle.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, i0, i1) {\n var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m + i0];\n array[m + i0] = array[i + i0];\n array[i + i0] = t;\n }\n\n return array;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/shuffle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/sum.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/sum.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (value = +values[i]) sum += value; // Note: zero and null are equivalent.\n }\n }\n\n else {\n while (++i < n) {\n if (value = +valueof(values[i], i, values)) sum += value;\n }\n }\n\n return sum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/sum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/threshold/freedmanDiaconis.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/threshold/freedmanDiaconis.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ascending */ \"./node_modules/dagre-d3/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../quantile */ \"./node_modules/dagre-d3/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n values = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(values, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]).sort(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n return Math.ceil((max - min) / (2 * (Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.75) - Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.25)) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/threshold/freedmanDiaconis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/threshold/scott.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/threshold/scott.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deviation */ \"./node_modules/dagre-d3/node_modules/d3-array/src/deviation.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n return Math.ceil((max - min) / (3.5 * Object(_deviation__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/threshold/scott.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/threshold/sturges.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/threshold/sturges.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n return Math.ceil(Math.log(values.length) / Math.LN2) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/threshold/sturges.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/ticks.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/ticks.js ***! + \******************************************************************/ +/*! exports provided: default, tickIncrement, tickStep */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return tickIncrement; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return tickStep; });\nvar e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n});\n\nfunction tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nfunction tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/ticks.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/transpose.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/transpose.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-array/src/min.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(matrix) {\n if (!(n = matrix.length)) return [];\n for (var i = -1, m = Object(_min__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(matrix, length), transpose = new Array(m); ++i < m;) {\n for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n row[j] = matrix[j][i];\n }\n }\n return transpose;\n});\n\nfunction length(d) {\n return d.length;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/transpose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/variance.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/variance.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = 0,\n i = -1,\n mean = 0,\n value,\n delta,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n if (m > 1) return sum / (m - 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/variance.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-array/src/zip.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-array/src/zip.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-array/src/transpose.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_transpose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(arguments);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-array/src/zip.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-axis/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-axis/index.js ***! + \*************************************************************/ +/*! exports provided: axisTop, axisRight, axisBottom, axisLeft */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_axis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/axis */ \"./node_modules/dagre-d3/node_modules/d3-axis/src/axis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return _src_axis__WEBPACK_IMPORTED_MODULE_0__[\"axisTop\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return _src_axis__WEBPACK_IMPORTED_MODULE_0__[\"axisRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return _src_axis__WEBPACK_IMPORTED_MODULE_0__[\"axisBottom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return _src_axis__WEBPACK_IMPORTED_MODULE_0__[\"axisLeft\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-axis/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-axis/src/array.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-axis/src/array.js ***! + \*****************************************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-axis/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-axis/src/axis.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-axis/src/axis.js ***! + \****************************************************************/ +/*! exports provided: axisTop, axisRight, axisBottom, axisLeft */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return axisTop; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return axisRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return axisBottom; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return axisLeft; });\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-axis/src/array.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-axis/src/identity.js\");\n\n\n\nvar top = 1,\n right = 2,\n bottom = 3,\n left = 4,\n epsilon = 1e-6;\n\nfunction translateX(x) {\n return \"translate(\" + (x + 0.5) + \",0)\";\n}\n\nfunction translateY(y) {\n return \"translate(0,\" + (y + 0.5) + \")\";\n}\n\nfunction number(scale) {\n return function(d) {\n return +scale(d);\n };\n}\n\nfunction center(scale) {\n var offset = Math.max(0, scale.bandwidth() - 1) / 2; // Adjust for 0.5px offset.\n if (scale.round()) offset = Math.round(offset);\n return function(d) {\n return +scale(d) + offset;\n };\n}\n\nfunction entering() {\n return !this.__axis;\n}\n\nfunction axis(orient, scale) {\n var tickArguments = [],\n tickValues = null,\n tickFormat = null,\n tickSizeInner = 6,\n tickSizeOuter = 6,\n tickPadding = 3,\n k = orient === top || orient === left ? -1 : 1,\n x = orient === left || orient === right ? \"x\" : \"y\",\n transform = orient === top || orient === bottom ? translateX : translateY;\n\n function axis(context) {\n var values = tickValues == null ? (scale.ticks ? scale.ticks.apply(scale, tickArguments) : scale.domain()) : tickValues,\n format = tickFormat == null ? (scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments) : _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : tickFormat,\n spacing = Math.max(tickSizeInner, 0) + tickPadding,\n range = scale.range(),\n range0 = +range[0] + 0.5,\n range1 = +range[range.length - 1] + 0.5,\n position = (scale.bandwidth ? center : number)(scale.copy()),\n selection = context.selection ? context.selection() : context,\n path = selection.selectAll(\".domain\").data([null]),\n tick = selection.selectAll(\".tick\").data(values, scale).order(),\n tickExit = tick.exit(),\n tickEnter = tick.enter().append(\"g\").attr(\"class\", \"tick\"),\n line = tick.select(\"line\"),\n text = tick.select(\"text\");\n\n path = path.merge(path.enter().insert(\"path\", \".tick\")\n .attr(\"class\", \"domain\")\n .attr(\"stroke\", \"#000\"));\n\n tick = tick.merge(tickEnter);\n\n line = line.merge(tickEnter.append(\"line\")\n .attr(\"stroke\", \"#000\")\n .attr(x + \"2\", k * tickSizeInner));\n\n text = text.merge(tickEnter.append(\"text\")\n .attr(\"fill\", \"#000\")\n .attr(x, k * spacing)\n .attr(\"dy\", orient === top ? \"0em\" : orient === bottom ? \"0.71em\" : \"0.32em\"));\n\n if (context !== selection) {\n path = path.transition(context);\n tick = tick.transition(context);\n line = line.transition(context);\n text = text.transition(context);\n\n tickExit = tickExit.transition(context)\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { return isFinite(d = position(d)) ? transform(d) : this.getAttribute(\"transform\"); });\n\n tickEnter\n .attr(\"opacity\", epsilon)\n .attr(\"transform\", function(d) { var p = this.parentNode.__axis; return transform(p && isFinite(p = p(d)) ? p : position(d)); });\n }\n\n tickExit.remove();\n\n path\n .attr(\"d\", orient === left || orient == right\n ? \"M\" + k * tickSizeOuter + \",\" + range0 + \"H0.5V\" + range1 + \"H\" + k * tickSizeOuter\n : \"M\" + range0 + \",\" + k * tickSizeOuter + \"V0.5H\" + range1 + \"V\" + k * tickSizeOuter);\n\n tick\n .attr(\"opacity\", 1)\n .attr(\"transform\", function(d) { return transform(position(d)); });\n\n line\n .attr(x + \"2\", k * tickSizeInner);\n\n text\n .attr(x, k * spacing)\n .text(format);\n\n selection.filter(entering)\n .attr(\"fill\", \"none\")\n .attr(\"font-size\", 10)\n .attr(\"font-family\", \"sans-serif\")\n .attr(\"text-anchor\", orient === right ? \"start\" : orient === left ? \"end\" : \"middle\");\n\n selection\n .each(function() { this.__axis = position; });\n }\n\n axis.scale = function(_) {\n return arguments.length ? (scale = _, axis) : scale;\n };\n\n axis.ticks = function() {\n return tickArguments = _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(arguments), axis;\n };\n\n axis.tickArguments = function(_) {\n return arguments.length ? (tickArguments = _ == null ? [] : _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_), axis) : tickArguments.slice();\n };\n\n axis.tickValues = function(_) {\n return arguments.length ? (tickValues = _ == null ? null : _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_), axis) : tickValues && tickValues.slice();\n };\n\n axis.tickFormat = function(_) {\n return arguments.length ? (tickFormat = _, axis) : tickFormat;\n };\n\n axis.tickSize = function(_) {\n return arguments.length ? (tickSizeInner = tickSizeOuter = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeInner = function(_) {\n return arguments.length ? (tickSizeInner = +_, axis) : tickSizeInner;\n };\n\n axis.tickSizeOuter = function(_) {\n return arguments.length ? (tickSizeOuter = +_, axis) : tickSizeOuter;\n };\n\n axis.tickPadding = function(_) {\n return arguments.length ? (tickPadding = +_, axis) : tickPadding;\n };\n\n return axis;\n}\n\nfunction axisTop(scale) {\n return axis(top, scale);\n}\n\nfunction axisRight(scale) {\n return axis(right, scale);\n}\n\nfunction axisBottom(scale) {\n return axis(bottom, scale);\n}\n\nfunction axisLeft(scale) {\n return axis(left, scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-axis/src/axis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-axis/src/identity.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-axis/src/identity.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-axis/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/index.js ***! + \**************************************************************/ +/*! exports provided: brush, brushX, brushY, brushSelection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_brush__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/brush */ \"./node_modules/dagre-d3/node_modules/d3-brush/src/brush.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brush\", function() { return _src_brush__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return _src_brush__WEBPACK_IMPORTED_MODULE_0__[\"brushX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return _src_brush__WEBPACK_IMPORTED_MODULE_0__[\"brushY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return _src_brush__WEBPACK_IMPORTED_MODULE_0__[\"brushSelection\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js ***! + \****************************************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/cubehelix.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/cubehelix.js ***! + \********************************************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js ***! + \*****************************************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/lab.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/lab.js ***! + \**************************************************************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/math.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/math.js ***! + \***************************************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/dispatch.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/dispatch.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js ***! + \*******************************************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/constant.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/constant.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/drag.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/drag.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/nodrag.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/noevent.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./event */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/event.js\");\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].button;\n}\n\nfunction defaultContainer() {\n return this.parentNode;\n}\n\nfunction defaultSubject(d) {\n return d == null ? {x: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].x, y: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].y} : d;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n container = defaultContainer,\n subject = defaultSubject,\n touchable = defaultTouchable,\n gestures = {},\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"drag\", \"end\"),\n active = 0,\n mousedownx,\n mousedowny,\n mousemoving,\n touchending,\n clickDistance2 = 0;\n\n function drag(selection) {\n selection\n .on(\"mousedown.drag\", mousedowned)\n .filter(touchable)\n .on(\"touchstart.drag\", touchstarted)\n .on(\"touchmove.drag\", touchmoved)\n .on(\"touchend.drag touchcancel.drag\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var gesture = beforestart(\"mouse\", container.apply(this, arguments), d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"mouse\"], this, arguments);\n if (!gesture) return;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag\", mousemoved, true).on(\"mouseup.drag\", mouseupped, true);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n mousemoving = false;\n mousedownx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX;\n mousedowny = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY;\n gesture(\"start\");\n }\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n if (!mousemoving) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX - mousedownx, dy = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY - mousedowny;\n mousemoving = dx * dx + dy * dy > clickDistance2;\n }\n gestures.mouse(\"drag\");\n }\n\n function mouseupped() {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag mouseup.drag\", null);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"yesdrag\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view, mousemoving);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gestures.mouse(\"end\");\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n c = container.apply(this, arguments),\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = beforestart(touches[i].identifier, c, d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"touch\"], this, arguments)) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"start\");\n }\n }\n }\n\n function touchmoved() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gesture(\"drag\");\n }\n }\n }\n\n function touchended() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"end\");\n }\n }\n }\n\n function beforestart(id, container, point, that, args) {\n var p = point(container, id), s, dx, dy,\n sublisteners = listeners.copy();\n\n if (!Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, \"beforestart\", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {\n if ((d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].subject = s = subject.apply(that, args)) == null) return false;\n dx = s.x - p[0] || 0;\n dy = s.y - p[1] || 0;\n return true;\n })) return;\n\n return function gesture(type) {\n var p0 = p, n;\n switch (type) {\n case \"start\": gestures[id] = gesture, n = active++; break;\n case \"end\": delete gestures[id], --active; // nobreak\n case \"drag\": p = point(container, id), n = active; break;\n }\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);\n };\n }\n\n drag.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : filter;\n };\n\n drag.container = function(_) {\n return arguments.length ? (container = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : container;\n };\n\n drag.subject = function(_) {\n return arguments.length ? (subject = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : subject;\n };\n\n drag.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : touchable;\n };\n\n drag.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? drag : value;\n };\n\n drag.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);\n };\n\n return drag;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/drag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/event.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/event.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return DragEvent; });\nfunction DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {\n this.target = target;\n this.type = type;\n this.subject = subject;\n this.identifier = id;\n this.active = active;\n this.x = x;\n this.y = y;\n this.dx = dx;\n this.dy = dy;\n this._ = dispatch;\n}\n\nDragEvent.prototype.on = function() {\n var value = this._.on.apply(this._, arguments);\n return value === this._ ? this : value;\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: drag, dragDisable, dragEnable */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _drag__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./drag */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/drag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return _drag__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/nodrag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"yesdrag\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/nodrag.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/nodrag.js ***! + \****************************************************************************************/ +/*! exports provided: default, yesdrag */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"yesdrag\", function() { return yesdrag; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/noevent.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(view) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n } else {\n root.__noselect = root.style.MozUserSelect;\n root.style.MozUserSelect = \"none\";\n }\n});\n\nfunction yesdrag(view, noclick) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", null);\n if (noclick) {\n selection.on(\"click.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n setTimeout(function() { selection.on(\"click.drag\", null); }, 0);\n }\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", null);\n } else {\n root.style.MozUserSelect = root.__noselect;\n delete root.__noselect;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/nodrag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/noevent.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/noevent.js ***! + \*****************************************************************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/back.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/back.js ***! + \**************************************************************************************/ +/*! exports provided: backIn, backOut, backInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backIn\", function() { return backIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backOut\", function() { return backOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backInOut\", function() { return backInOut; });\nvar overshoot = 1.70158;\n\nvar backIn = (function custom(s) {\n s = +s;\n\n function backIn(t) {\n return t * t * ((s + 1) * t - s);\n }\n\n backIn.overshoot = custom;\n\n return backIn;\n})(overshoot);\n\nvar backOut = (function custom(s) {\n s = +s;\n\n function backOut(t) {\n return --t * t * ((s + 1) * t + s) + 1;\n }\n\n backOut.overshoot = custom;\n\n return backOut;\n})(overshoot);\n\nvar backInOut = (function custom(s) {\n s = +s;\n\n function backInOut(t) {\n return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n }\n\n backInOut.overshoot = custom;\n\n return backInOut;\n})(overshoot);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/back.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/bounce.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/bounce.js ***! + \****************************************************************************************/ +/*! exports provided: bounceIn, bounceOut, bounceInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceIn\", function() { return bounceIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceOut\", function() { return bounceOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceInOut\", function() { return bounceInOut; });\nvar b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\n\nfunction bounceIn(t) {\n return 1 - bounceOut(1 - t);\n}\n\nfunction bounceOut(t) {\n return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n}\n\nfunction bounceInOut(t) {\n return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/bounce.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/circle.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/circle.js ***! + \****************************************************************************************/ +/*! exports provided: circleIn, circleOut, circleInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleIn\", function() { return circleIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleOut\", function() { return circleOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleInOut\", function() { return circleInOut; });\nfunction circleIn(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n\nfunction circleOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n\nfunction circleInOut(t) {\n return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/cubic.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/cubic.js ***! + \***************************************************************************************/ +/*! exports provided: cubicIn, cubicOut, cubicInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicIn\", function() { return cubicIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicOut\", function() { return cubicOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicInOut\", function() { return cubicInOut; });\nfunction cubicIn(t) {\n return t * t * t;\n}\n\nfunction cubicOut(t) {\n return --t * t * t + 1;\n}\n\nfunction cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/cubic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/elastic.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/elastic.js ***! + \*****************************************************************************************/ +/*! exports provided: elasticIn, elasticOut, elasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticIn\", function() { return elasticIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticOut\", function() { return elasticOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticInOut\", function() { return elasticInOut; });\nvar tau = 2 * Math.PI,\n amplitude = 1,\n period = 0.3;\n\nvar elasticIn = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticIn(t) {\n return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n }\n\n elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n elasticIn.period = function(p) { return custom(a, p); };\n\n return elasticIn;\n})(amplitude, period);\n\nvar elasticOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticOut(t) {\n return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n }\n\n elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticOut.period = function(p) { return custom(a, p); };\n\n return elasticOut;\n})(amplitude, period);\n\nvar elasticInOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticInOut(t) {\n return ((t = t * 2 - 1) < 0\n ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n }\n\n elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticInOut.period = function(p) { return custom(a, p); };\n\n return elasticInOut;\n})(amplitude, period);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/elastic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/exp.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/exp.js ***! + \*************************************************************************************/ +/*! exports provided: expIn, expOut, expInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expIn\", function() { return expIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expOut\", function() { return expOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expInOut\", function() { return expInOut; });\nfunction expIn(t) {\n return Math.pow(2, 10 * t - 10);\n}\n\nfunction expOut(t) {\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction expInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/exp.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return _linear__WEBPACK_IMPORTED_MODULE_0__[\"linear\"]; });\n\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/quad.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony import */ var _cubic__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubic */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/cubic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony import */ var _poly__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./poly */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/poly.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony import */ var _sin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sin */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/sin.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony import */ var _exp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exp */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/exp.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./circle */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony import */ var _bounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./bounce */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/bounce.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceInOut\"]; });\n\n/* harmony import */ var _back__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./back */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/back.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony import */ var _elastic__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./elastic */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/elastic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticInOut\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/linear.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/linear.js ***! + \****************************************************************************************/ +/*! exports provided: linear */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linear\", function() { return linear; });\nfunction linear(t) {\n return +t;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/poly.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/poly.js ***! + \**************************************************************************************/ +/*! exports provided: polyIn, polyOut, polyInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyIn\", function() { return polyIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyOut\", function() { return polyOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyInOut\", function() { return polyInOut; });\nvar exponent = 3;\n\nvar polyIn = (function custom(e) {\n e = +e;\n\n function polyIn(t) {\n return Math.pow(t, e);\n }\n\n polyIn.exponent = custom;\n\n return polyIn;\n})(exponent);\n\nvar polyOut = (function custom(e) {\n e = +e;\n\n function polyOut(t) {\n return 1 - Math.pow(1 - t, e);\n }\n\n polyOut.exponent = custom;\n\n return polyOut;\n})(exponent);\n\nvar polyInOut = (function custom(e) {\n e = +e;\n\n function polyInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n }\n\n polyInOut.exponent = custom;\n\n return polyInOut;\n})(exponent);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/poly.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/quad.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/quad.js ***! + \**************************************************************************************/ +/*! exports provided: quadIn, quadOut, quadInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadIn\", function() { return quadIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadOut\", function() { return quadOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadInOut\", function() { return quadInOut; });\nfunction quadIn(t) {\n return t * t;\n}\n\nfunction quadOut(t) {\n return t * (2 - t);\n}\n\nfunction quadInOut(t) {\n return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/sin.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/sin.js ***! + \*************************************************************************************/ +/*! exports provided: sinIn, sinOut, sinInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinIn\", function() { return sinIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinOut\", function() { return sinOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinInOut\", function() { return sinInOut; });\nvar pi = Math.PI,\n halfPi = pi / 2;\n\nfunction sinIn(t) {\n return 1 - Math.cos(t * halfPi);\n}\n\nfunction sinOut(t) {\n return Math.sin(t * halfPi);\n}\n\nfunction sinInOut(t) {\n return (1 - Math.cos(pi * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/sin.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/array.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/array.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js ***! + \**********************************************************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basisClosed.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basisClosed.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js ***! + \**********************************************************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/constant.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/constant.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/cubehelix.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/cubehelix.js ***! + \**************************************************************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/date.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/date.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/discrete.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/discrete.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/discrete.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hcl.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hcl.js ***! + \********************************************************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hsl.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hsl.js ***! + \********************************************************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hue.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hue.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = Object(_color__WEBPACK_IMPORTED_MODULE_0__[\"hue\"])(+a, +b);\n return function(t) {\n var x = i(t);\n return x - 360 * Math.floor(x / 360);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hue.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js ***! + \**********************************************************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _discrete__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discrete */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/discrete.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return _discrete__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _hue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hue */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return _hue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _number__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _object__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./round */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _round__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _string__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _transform_index__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transform/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./zoom */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _hsl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./hsl */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"hslLong\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _hcl__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./hcl */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"hclLong\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _piecewise__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./piecewise */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/piecewise.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return _piecewise__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./quantize */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/lab.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/lab.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/object.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/object.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/piecewise.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/piecewise.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return piecewise; });\nfunction piecewise(interpolate, values) {\n var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n while (i < n) I[i] = interpolate(v, v = values[++i]);\n return function(t) {\n var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n return I[i](t - i);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/piecewise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/quantize.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/quantize.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/rgb.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/rgb.js ***! + \********************************************************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/round.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/round.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/string.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/string.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/decompose.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/decompose.js ***! + \************************************************************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/index.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/index.js ***! + \********************************************************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/parse.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/parse.js ***! + \********************************************************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/zoom.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/zoom.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/constant.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/constant.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/create.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/create.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js ***! + \********************************************************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./create */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./local */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./matcher */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mouse */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selector */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./selection/style */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./touch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./touches */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./window */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/local.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/local.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/matcher.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/matcher.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/mouse.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/mouse.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js ***! + \*************************************************************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/select.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/select.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectAll.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectAll.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/append.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/append.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/attr.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/attr.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/call.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/call.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/classed.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/classed.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/clone.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/clone.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/data.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/data.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/datum.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/datum.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/dispatch.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/dispatch.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/each.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/each.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/empty.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/empty.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/enter.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/enter.js ***! + \******************************************************************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/exit.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/exit.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/filter.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/filter.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/html.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/html.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js ***! + \******************************************************************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/insert.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/insert.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/lower.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/lower.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/merge.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/merge.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/node.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/node.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/nodes.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/nodes.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js ***! + \***************************************************************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/order.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/order.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/property.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/property.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/raise.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/raise.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/remove.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/remove.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/select.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/select.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/selectAll.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/selectAll.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/size.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/size.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sort.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sort.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sparse.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sparse.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/style.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/style.js ***! + \******************************************************************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/text.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/text.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectorAll.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectorAll.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touch.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touch.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touches.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touches.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./timeout */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/interval.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/interval.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timeout.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timeout.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js ***! + \****************************************************************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/active.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/active.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\nvar root = [null];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n i;\n\n if (schedules) {\n name = name == null ? null : name + \"\";\n for (i in schedules) {\n if ((schedule = schedules[i]).state > _transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"SCHEDULED\"] && schedule.name === name) {\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]([[node]], root, name, +i);\n }\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/active.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: transition, active, interrupt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/index.js\");\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return _transition_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _active__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./active */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/active.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return _active__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/interrupt.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return _interrupt__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/interrupt.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/interrupt.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n active,\n empty = true,\n i;\n\n if (!schedules) return;\n\n name = name == null ? null : name + \"\";\n\n for (i in schedules) {\n if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n active = schedule.state > _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"STARTING\"] && schedule.state < _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDING\"];\n schedule.state = _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDED\"];\n schedule.timer.stop();\n if (active) schedule.on.call(\"interrupt\", node, node.__data__, schedule.index, schedule.group);\n delete schedules[i];\n }\n\n if (empty) delete node.__transition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/index.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/index.js ***! + \*******************************************************************************************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/interrupt.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/transition.js\");\n\n\n\n\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.interrupt = _interrupt__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.transition = _transition__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/interrupt.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/interrupt.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../interrupt */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/interrupt.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return this.each(function() {\n Object(_interrupt__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(this, name);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/transition.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/transition.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../transition/index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-ease */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-ease/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/index.js\");\n\n\n\n\n\nvar defaultTiming = {\n time: null, // Set on use.\n delay: 0,\n duration: 250,\n ease: d3_ease__WEBPACK_IMPORTED_MODULE_2__[\"easeCubicInOut\"]\n};\n\nfunction inherit(node, id) {\n var timing;\n while (!(timing = node.__transition) || !(timing = timing[id])) {\n if (!(node = node.parentNode)) {\n return defaultTiming.time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), defaultTiming;\n }\n }\n return timing;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var id,\n timing;\n\n if (name instanceof _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]) {\n id = name._id, name = name._name;\n } else {\n id = Object(_transition_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])(), (timing = defaultTiming).time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), name = name == null ? null : name + \"\";\n }\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n Object(_transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id, i, group, timing || inherit(node, id));\n }\n }\n }\n\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/selection/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attr.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attr.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrConstantNS(fullname, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttribute(name);\n value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction attrFunctionNS(fullname, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"namespace\"])(name), i = fullname === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformSvg\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return this.attrTween(name, typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"attr.\" + name, value))\n : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attrTween.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attrTween.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n\n\nfunction attrTweenNS(fullname, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttributeNS(fullname.space, fullname.local, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\nfunction attrTween(name, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttribute(name, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var key = \"attr.\" + name;\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"namespace\"])(name);\n return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attrTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/delay.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/delay.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction delayFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = +value.apply(this, arguments);\n };\n}\n\nfunction delayConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? delayFunction\n : delayConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).delay;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/delay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/duration.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/duration.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction durationFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = +value.apply(this, arguments);\n };\n}\n\nfunction durationConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? durationFunction\n : durationConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).duration;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/ease.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/ease.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new Error;\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).ease = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each(easeConstant(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).ease;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/ease.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/filter.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/filter.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"matcher\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js ***! + \********************************************************************************************************/ +/*! exports provided: Transition, default, newId */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transition\", function() { return Transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"newId\", function() { return newId; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attr.js\");\n/* harmony import */ var _attrTween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./attrTween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/attrTween.js\");\n/* harmony import */ var _delay__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./delay */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/delay.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/duration.js\");\n/* harmony import */ var _ease__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ease */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/ease.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/filter.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/merge.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/on.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/remove.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selectAll.js\");\n/* harmony import */ var _selection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selection.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/style.js\");\n/* harmony import */ var _styleTween__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./styleTween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/styleTween.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/text.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/transition.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar id = 0;\n\nfunction Transition(groups, parents, name, id) {\n this._groups = groups;\n this._parents = parents;\n this._name = name;\n this._id = id;\n}\n\nfunction transition(name) {\n return Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"])().transition(name);\n}\n\nfunction newId() {\n return ++id;\n}\n\nvar selection_prototype = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype;\n\nTransition.prototype = transition.prototype = {\n constructor: Transition,\n select: _select__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n selection: _selection__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n transition: _transition__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n call: selection_prototype.call,\n nodes: selection_prototype.nodes,\n node: selection_prototype.node,\n size: selection_prototype.size,\n empty: selection_prototype.empty,\n each: selection_prototype.each,\n on: _on__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n attrTween: _attrTween__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n styleTween: _styleTween__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n tween: _tween__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n delay: _delay__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n duration: _duration__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n ease: _ease__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/interpolate.js": +/*!**************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/interpolate.js ***! + \**************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var c;\n return (typeof b === \"number\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"]\n : (c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"])\n : d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateString\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/merge.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/merge.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(transition) {\n if (transition._id !== this._id) throw new Error;\n\n for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](merges, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/on.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/on.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n var i = t.indexOf(\".\");\n if (i >= 0) t = t.slice(0, i);\n return !t || t === \"start\";\n });\n}\n\nfunction onFunction(id, name, listener) {\n var on0, on1, sit = start(name) ? _schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"] : _schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"];\n return function() {\n var schedule = sit(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n\n schedule.on = on1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, listener) {\n var id = this._id;\n\n return arguments.length < 2\n ? Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).on.on(name)\n : this.each(onFunction(id, name, listener));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/remove.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/remove.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transition) if (+i !== id) return;\n if (parent) parent.removeChild(this);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.on(\"end.remove\", removeFunction(this._id));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js ***! + \***********************************************************************************************************/ +/*! exports provided: CREATED, SCHEDULED, STARTING, STARTED, RUNNING, ENDING, ENDED, default, init, set, get */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CREATED\", function() { return CREATED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SCHEDULED\", function() { return SCHEDULED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTING\", function() { return STARTING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTED\", function() { return STARTED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RUNNING\", function() { return RUNNING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDING\", function() { return ENDING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDED\", function() { return ENDED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return set; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"get\", function() { return get; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-timer/src/index.js\");\n\n\n\nvar emptyOn = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"end\", \"interrupt\");\nvar emptyTween = [];\n\nvar CREATED = 0;\nvar SCHEDULED = 1;\nvar STARTING = 2;\nvar STARTED = 3;\nvar RUNNING = 4;\nvar ENDING = 5;\nvar ENDED = 6;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name, id, index, group, timing) {\n var schedules = node.__transition;\n if (!schedules) node.__transition = {};\n else if (id in schedules) return;\n create(node, id, {\n name: name,\n index: index, // For context during callback.\n group: group, // For context during callback.\n on: emptyOn,\n tween: emptyTween,\n time: timing.time,\n delay: timing.delay,\n duration: timing.duration,\n ease: timing.ease,\n timer: null,\n state: CREATED\n });\n});\n\nfunction init(node, id) {\n var schedule = get(node, id);\n if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n return schedule;\n}\n\nfunction set(node, id) {\n var schedule = get(node, id);\n if (schedule.state > STARTING) throw new Error(\"too late; already started\");\n return schedule;\n}\n\nfunction get(node, id) {\n var schedule = node.__transition;\n if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n return schedule;\n}\n\nfunction create(node, id, self) {\n var schedules = node.__transition,\n tween;\n\n // Initialize the self timer when the transition is created.\n // Note the actual delay is not known until the first callback!\n schedules[id] = self;\n self.timer = Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timer\"])(schedule, 0, self.time);\n\n function schedule(elapsed) {\n self.state = SCHEDULED;\n self.timer.restart(start, self.delay, self.time);\n\n // If the elapsed delay is less than our first sleep, start immediately.\n if (self.delay <= elapsed) start(elapsed - self.delay);\n }\n\n function start(elapsed) {\n var i, j, n, o;\n\n // If the state is not SCHEDULED, then we previously errored on start.\n if (self.state !== SCHEDULED) return stop();\n\n for (i in schedules) {\n o = schedules[i];\n if (o.name !== self.name) continue;\n\n // While this element already has a starting transition during this frame,\n // defer starting an interrupting transition until that transition has a\n // chance to tick (and possibly end); see d3/d3-transition#54!\n if (o.state === STARTED) return Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(start);\n\n // Interrupt the active transition, if any.\n // Dispatch the interrupt event.\n if (o.state === RUNNING) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n\n // Cancel any pre-empted transitions. No interrupt event is dispatched\n // because the cancelled transitions never started. Note that this also\n // removes this transition from the pending list!\n else if (+i < id) {\n o.state = ENDED;\n o.timer.stop();\n delete schedules[i];\n }\n }\n\n // Defer the first tick to end of the current frame; see d3/d3#1576.\n // Note the transition may be canceled after start and before the first tick!\n // Note this must be scheduled before the start event; see d3/d3-transition#16!\n // Assuming this is successful, subsequent callbacks go straight to tick.\n Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(function() {\n if (self.state === STARTED) {\n self.state = RUNNING;\n self.timer.restart(tick, self.delay, self.time);\n tick(elapsed);\n }\n });\n\n // Dispatch the start event.\n // Note this must be done before the tween are initialized.\n self.state = STARTING;\n self.on.call(\"start\", node, node.__data__, self.index, self.group);\n if (self.state !== STARTING) return; // interrupted\n self.state = STARTED;\n\n // Initialize the tween, deleting null tween.\n tween = new Array(n = self.tween.length);\n for (i = 0, j = -1; i < n; ++i) {\n if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n tween[++j] = o;\n }\n }\n tween.length = j + 1;\n }\n\n function tick(elapsed) {\n var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n i = -1,\n n = tween.length;\n\n while (++i < n) {\n tween[i].call(null, t);\n }\n\n // Dispatch the end event.\n if (self.state === ENDING) {\n self.on.call(\"end\", node, node.__data__, self.index, self.group);\n stop();\n }\n }\n\n function stop() {\n self.state = ENDED;\n self.timer.stop();\n delete schedules[id];\n for (var i in schedules) return; // eslint-disable-line no-unused-vars\n delete node.__transition;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/select.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/select.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selector\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(subgroup[i], name, id, i, subgroup, Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id));\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selectAll.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selectAll.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selectorAll\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n for (var children = select.call(node, node.__data__, i, group), child, inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id), k = 0, l = children.length; k < l; ++k) {\n if (child = children[k]) {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(child, name, id, k, children, inherit);\n }\n }\n subgroups.push(children);\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selection.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selection.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n\n\nvar Selection = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.constructor;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Selection(this._groups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/selection.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/style.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/style.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction styleRemove(name, interpolate) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction styleRemoveEnd(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction styleFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = value(this);\n if (value1 == null) value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var i = (name += \"\") === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformCss\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return value == null ? this\n .styleTween(name, styleRemove(name, i))\n .on(\"end.style.\" + name, styleRemoveEnd(name))\n : this.styleTween(name, typeof value === \"function\"\n ? styleFunction(name, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"style.\" + name, value))\n : styleConstant(name, i, value + \"\"), priority);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/styleTween.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/styleTween.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction styleTween(name, value, priority) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.style.setProperty(name, i(t), priority);\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var key = \"style.\" + (name += \"\");\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/styleTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/text.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/text.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js\");\n\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var value1 = value(this);\n this.textContent = value1 == null ? \"\" : value1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.tween(\"text\", typeof value === \"function\"\n ? textFunction(Object(_tween__WEBPACK_IMPORTED_MODULE_0__[\"tweenValue\"])(this, \"text\", value))\n : textConstant(value == null ? \"\" : value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/transition.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/transition.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var name = this._name,\n id0 = this._id,\n id1 = Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])();\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n var inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"get\"])(node, id0);\n Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id1, i, group, {\n time: inherit.time + inherit.delay + inherit.duration,\n delay: 0,\n duration: inherit.duration,\n ease: inherit.ease\n });\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js ***! + \********************************************************************************************************/ +/*! exports provided: default, tweenValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tweenValue\", function() { return tweenValue; });\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = tween0 = tween;\n for (var i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1 = tween1.slice();\n tween1.splice(i, 1);\n break;\n }\n }\n }\n\n schedule.tween = tween1;\n };\n}\n\nfunction tweenFunction(id, name, value) {\n var tween0, tween1;\n if (typeof value !== \"function\") throw new Error;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = (tween0 = tween).slice();\n for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1[i] = t;\n break;\n }\n }\n if (i === n) tween1.push(t);\n }\n\n schedule.tween = tween1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var id = this._id;\n\n name += \"\";\n\n if (arguments.length < 2) {\n var tween = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).tween;\n for (var i = 0, n = tween.length, t; i < n; ++i) {\n if ((t = tween[i]).name === name) {\n return t.value;\n }\n }\n return null;\n }\n\n return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n});\n\nfunction tweenValue(transition, name, value) {\n var id = transition._id;\n\n transition.each(function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id);\n (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n });\n\n return function(node) {\n return Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(node, id).value[name];\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/transition/tween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/src/brush.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/src/brush.js ***! + \******************************************************************/ +/*! exports provided: brushSelection, brushX, brushY, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return brushSelection; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return brushX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return brushY; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-drag */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-drag/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-transition */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-transition/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-brush/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./event */ \"./node_modules/dagre-d3/node_modules/d3-brush/src/event.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-brush/src/noevent.js\");\n\n\n\n\n\n\n\n\n\nvar MODE_DRAG = {name: \"drag\"},\n MODE_SPACE = {name: \"space\"},\n MODE_HANDLE = {name: \"handle\"},\n MODE_CENTER = {name: \"center\"};\n\nvar X = {\n name: \"x\",\n handles: [\"e\", \"w\"].map(type),\n input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; },\n output: function(xy) { return xy && [xy[0][0], xy[1][0]]; }\n};\n\nvar Y = {\n name: \"y\",\n handles: [\"n\", \"s\"].map(type),\n input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; },\n output: function(xy) { return xy && [xy[0][1], xy[1][1]]; }\n};\n\nvar XY = {\n name: \"xy\",\n handles: [\"n\", \"e\", \"s\", \"w\", \"nw\", \"ne\", \"se\", \"sw\"].map(type),\n input: function(xy) { return xy; },\n output: function(xy) { return xy; }\n};\n\nvar cursors = {\n overlay: \"crosshair\",\n selection: \"move\",\n n: \"ns-resize\",\n e: \"ew-resize\",\n s: \"ns-resize\",\n w: \"ew-resize\",\n nw: \"nwse-resize\",\n ne: \"nesw-resize\",\n se: \"nwse-resize\",\n sw: \"nesw-resize\"\n};\n\nvar flipX = {\n e: \"w\",\n w: \"e\",\n nw: \"ne\",\n ne: \"nw\",\n se: \"sw\",\n sw: \"se\"\n};\n\nvar flipY = {\n n: \"s\",\n s: \"n\",\n nw: \"sw\",\n ne: \"se\",\n se: \"ne\",\n sw: \"nw\"\n};\n\nvar signsX = {\n overlay: +1,\n selection: +1,\n n: null,\n e: +1,\n s: null,\n w: -1,\n nw: -1,\n ne: +1,\n se: +1,\n sw: -1\n};\n\nvar signsY = {\n overlay: +1,\n selection: +1,\n n: -1,\n e: null,\n s: +1,\n w: null,\n nw: -1,\n ne: -1,\n se: +1,\n sw: +1\n};\n\nfunction type(t) {\n return {type: t};\n}\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].button;\n}\n\nfunction defaultExtent() {\n var svg = this.ownerSVGElement || this;\n return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]];\n}\n\n// Like d3.local, but with the name “__brush” rather than auto-generated.\nfunction local(node) {\n while (!node.__brush) if (!(node = node.parentNode)) return;\n return node.__brush;\n}\n\nfunction empty(extent) {\n return extent[0][0] === extent[1][0]\n || extent[0][1] === extent[1][1];\n}\n\nfunction brushSelection(node) {\n var state = node.__brush;\n return state ? state.dim.output(state.selection) : null;\n}\n\nfunction brushX() {\n return brush(X);\n}\n\nfunction brushY() {\n return brush(Y);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return brush(XY);\n});\n\nfunction brush(dim) {\n var extent = defaultExtent,\n filter = defaultFilter,\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(brush, \"start\", \"brush\", \"end\"),\n handleSize = 6,\n touchending;\n\n function brush(group) {\n var overlay = group\n .property(\"__brush\", initialize)\n .selectAll(\".overlay\")\n .data([type(\"overlay\")]);\n\n overlay.enter().append(\"rect\")\n .attr(\"class\", \"overlay\")\n .attr(\"pointer-events\", \"all\")\n .attr(\"cursor\", cursors.overlay)\n .merge(overlay)\n .each(function() {\n var extent = local(this).extent;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this)\n .attr(\"x\", extent[0][0])\n .attr(\"y\", extent[0][1])\n .attr(\"width\", extent[1][0] - extent[0][0])\n .attr(\"height\", extent[1][1] - extent[0][1]);\n });\n\n group.selectAll(\".selection\")\n .data([type(\"selection\")])\n .enter().append(\"rect\")\n .attr(\"class\", \"selection\")\n .attr(\"cursor\", cursors.selection)\n .attr(\"fill\", \"#777\")\n .attr(\"fill-opacity\", 0.3)\n .attr(\"stroke\", \"#fff\")\n .attr(\"shape-rendering\", \"crispEdges\");\n\n var handle = group.selectAll(\".handle\")\n .data(dim.handles, function(d) { return d.type; });\n\n handle.exit().remove();\n\n handle.enter().append(\"rect\")\n .attr(\"class\", function(d) { return \"handle handle--\" + d.type; })\n .attr(\"cursor\", function(d) { return cursors[d.type]; });\n\n group\n .each(redraw)\n .attr(\"fill\", \"none\")\n .attr(\"pointer-events\", \"all\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\")\n .on(\"mousedown.brush touchstart.brush\", started);\n }\n\n brush.move = function(group, selection) {\n if (group.selection) {\n group\n .on(\"start.brush\", function() { emitter(this, arguments).beforestart().start(); })\n .on(\"interrupt.brush end.brush\", function() { emitter(this, arguments).end(); })\n .tween(\"brush\", function() {\n var that = this,\n state = that.__brush,\n emit = emitter(that, arguments),\n selection0 = state.selection,\n selection1 = dim.input(typeof selection === \"function\" ? selection.apply(this, arguments) : selection, state.extent),\n i = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_2__[\"interpolate\"])(selection0, selection1);\n\n function tween(t) {\n state.selection = t === 1 && empty(selection1) ? null : i(t);\n redraw.call(that);\n emit.brush();\n }\n\n return selection0 && selection1 ? tween : tween(1);\n });\n } else {\n group\n .each(function() {\n var that = this,\n args = arguments,\n state = that.__brush,\n selection1 = dim.input(typeof selection === \"function\" ? selection.apply(that, args) : selection, state.extent),\n emit = emitter(that, args).beforestart();\n\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(that);\n state.selection = selection1 == null || empty(selection1) ? null : selection1;\n redraw.call(that);\n emit.start().brush().end();\n });\n }\n };\n\n function redraw() {\n var group = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this),\n selection = local(this).selection;\n\n if (selection) {\n group.selectAll(\".selection\")\n .style(\"display\", null)\n .attr(\"x\", selection[0][0])\n .attr(\"y\", selection[0][1])\n .attr(\"width\", selection[1][0] - selection[0][0])\n .attr(\"height\", selection[1][1] - selection[0][1]);\n\n group.selectAll(\".handle\")\n .style(\"display\", null)\n .attr(\"x\", function(d) { return d.type[d.type.length - 1] === \"e\" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; })\n .attr(\"y\", function(d) { return d.type[0] === \"s\" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; })\n .attr(\"width\", function(d) { return d.type === \"n\" || d.type === \"s\" ? selection[1][0] - selection[0][0] + handleSize : handleSize; })\n .attr(\"height\", function(d) { return d.type === \"e\" || d.type === \"w\" ? selection[1][1] - selection[0][1] + handleSize : handleSize; });\n }\n\n else {\n group.selectAll(\".selection,.handle\")\n .style(\"display\", \"none\")\n .attr(\"x\", null)\n .attr(\"y\", null)\n .attr(\"width\", null)\n .attr(\"height\", null);\n }\n }\n\n function emitter(that, args) {\n return that.__brush.emitter || new Emitter(that, args);\n }\n\n function Emitter(that, args) {\n this.that = that;\n this.args = args;\n this.state = that.__brush;\n this.active = 0;\n }\n\n Emitter.prototype = {\n beforestart: function() {\n if (++this.active === 1) this.state.emitter = this, this.starting = true;\n return this;\n },\n start: function() {\n if (this.starting) this.starting = false, this.emit(\"start\");\n return this;\n },\n brush: function() {\n this.emit(\"brush\");\n return this;\n },\n end: function() {\n if (--this.active === 0) delete this.state.emitter, this.emit(\"end\");\n return this;\n },\n emit: function(type) {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_6__[\"default\"](brush, type, dim.output(this.state.selection)), listeners.apply, listeners, [type, this.that, this.args]);\n }\n };\n\n function started() {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) { if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches.length < d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches.length) return Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(); }\n else if (touchending) return;\n if (!filter.apply(this, arguments)) return;\n\n var that = this,\n type = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].target.__data__.type,\n mode = (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].metaKey ? type = \"overlay\" : type) === \"selection\" ? MODE_DRAG : (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].altKey ? MODE_CENTER : MODE_HANDLE),\n signX = dim === Y ? null : signsX[type],\n signY = dim === X ? null : signsY[type],\n state = local(that),\n extent = state.extent,\n selection = state.selection,\n W = extent[0][0], w0, w1,\n N = extent[0][1], n0, n1,\n E = extent[1][0], e0, e1,\n S = extent[1][1], s0, s1,\n dx,\n dy,\n moving,\n shifting = signX && signY && d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].shiftKey,\n lockX,\n lockY,\n point0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(that),\n point = point0,\n emit = emitter(that, arguments).beforestart();\n\n if (type === \"overlay\") {\n state.selection = selection = [\n [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],\n [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]\n ];\n } else {\n w0 = selection[0][0];\n n0 = selection[0][1];\n e0 = selection[1][0];\n s0 = selection[1][1];\n }\n\n w1 = w0;\n n1 = n0;\n e1 = e0;\n s1 = s0;\n\n var group = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(that)\n .attr(\"pointer-events\", \"none\");\n\n var overlay = group.selectAll(\".overlay\")\n .attr(\"cursor\", cursors[type]);\n\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) {\n group\n .on(\"touchmove.brush\", moved, true)\n .on(\"touchend.brush touchcancel.brush\", ended, true);\n } else {\n var view = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view)\n .on(\"keydown.brush\", keydowned, true)\n .on(\"keyup.brush\", keyupped, true)\n .on(\"mousemove.brush\", moved, true)\n .on(\"mouseup.brush\", ended, true);\n\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragDisable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view);\n }\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"nopropagation\"])();\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(that);\n redraw.call(that);\n emit.start();\n\n function moved() {\n var point1 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(that);\n if (shifting && !lockX && !lockY) {\n if (Math.abs(point1[0] - point[0]) > Math.abs(point1[1] - point[1])) lockY = true;\n else lockX = true;\n }\n point = point1;\n moving = true;\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n move();\n }\n\n function move() {\n var t;\n\n dx = point[0] - point0[0];\n dy = point[1] - point0[1];\n\n switch (mode) {\n case MODE_SPACE:\n case MODE_DRAG: {\n if (signX) dx = Math.max(W - w0, Math.min(E - e0, dx)), w1 = w0 + dx, e1 = e0 + dx;\n if (signY) dy = Math.max(N - n0, Math.min(S - s0, dy)), n1 = n0 + dy, s1 = s0 + dy;\n break;\n }\n case MODE_HANDLE: {\n if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0;\n else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx;\n if (signY < 0) dy = Math.max(N - n0, Math.min(S - n0, dy)), n1 = n0 + dy, s1 = s0;\n else if (signY > 0) dy = Math.max(N - s0, Math.min(S - s0, dy)), n1 = n0, s1 = s0 + dy;\n break;\n }\n case MODE_CENTER: {\n if (signX) w1 = Math.max(W, Math.min(E, w0 - dx * signX)), e1 = Math.max(W, Math.min(E, e0 + dx * signX));\n if (signY) n1 = Math.max(N, Math.min(S, n0 - dy * signY)), s1 = Math.max(N, Math.min(S, s0 + dy * signY));\n break;\n }\n }\n\n if (e1 < w1) {\n signX *= -1;\n t = w0, w0 = e0, e0 = t;\n t = w1, w1 = e1, e1 = t;\n if (type in flipX) overlay.attr(\"cursor\", cursors[type = flipX[type]]);\n }\n\n if (s1 < n1) {\n signY *= -1;\n t = n0, n0 = s0, s0 = t;\n t = n1, n1 = s1, s1 = t;\n if (type in flipY) overlay.attr(\"cursor\", cursors[type = flipY[type]]);\n }\n\n if (state.selection) selection = state.selection; // May be set by brush.move!\n if (lockX) w1 = selection[0][0], e1 = selection[1][0];\n if (lockY) n1 = selection[0][1], s1 = selection[1][1];\n\n if (selection[0][0] !== w1\n || selection[0][1] !== n1\n || selection[1][0] !== e1\n || selection[1][1] !== s1) {\n state.selection = [[w1, n1], [e1, s1]];\n redraw.call(that);\n emit.brush();\n }\n }\n\n function ended() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"nopropagation\"])();\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches) {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].touches.length) return;\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n group.on(\"touchmove.brush touchend.brush touchcancel.brush\", null);\n } else {\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragEnable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view, moving);\n view.on(\"keydown.brush keyup.brush mousemove.brush mouseup.brush\", null);\n }\n group.attr(\"pointer-events\", \"all\");\n overlay.attr(\"cursor\", cursors.overlay);\n if (state.selection) selection = state.selection; // May be set by brush.move (on start)!\n if (empty(selection)) state.selection = null, redraw.call(that);\n emit.end();\n }\n\n function keydowned() {\n switch (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].keyCode) {\n case 16: { // SHIFT\n shifting = signX && signY;\n break;\n }\n case 18: { // ALT\n if (mode === MODE_HANDLE) {\n if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;\n if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;\n mode = MODE_CENTER;\n move();\n }\n break;\n }\n case 32: { // SPACE; takes priority over ALT\n if (mode === MODE_HANDLE || mode === MODE_CENTER) {\n if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx;\n if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy;\n mode = MODE_SPACE;\n overlay.attr(\"cursor\", cursors.selection);\n move();\n }\n break;\n }\n default: return;\n }\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n }\n\n function keyupped() {\n switch (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].keyCode) {\n case 16: { // SHIFT\n if (shifting) {\n lockX = lockY = shifting = false;\n move();\n }\n break;\n }\n case 18: { // ALT\n if (mode === MODE_CENTER) {\n if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;\n if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;\n mode = MODE_HANDLE;\n move();\n }\n break;\n }\n case 32: { // SPACE\n if (mode === MODE_SPACE) {\n if (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].altKey) {\n if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX;\n if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY;\n mode = MODE_CENTER;\n } else {\n if (signX < 0) e0 = e1; else if (signX > 0) w0 = w1;\n if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1;\n mode = MODE_HANDLE;\n }\n overlay.attr(\"cursor\", cursors[type]);\n move();\n }\n break;\n }\n default: return;\n }\n Object(_noevent__WEBPACK_IMPORTED_MODULE_7__[\"default\"])();\n }\n }\n\n function initialize() {\n var state = this.__brush || {selection: null};\n state.extent = extent.apply(this, arguments);\n state.dim = dim;\n return state;\n }\n\n brush.extent = function(_) {\n return arguments.length ? (extent = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), brush) : extent;\n };\n\n brush.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), brush) : filter;\n };\n\n brush.handleSize = function(_) {\n return arguments.length ? (handleSize = +_, brush) : handleSize;\n };\n\n brush.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? brush : value;\n };\n\n return brush;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/src/brush.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/src/event.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/src/event.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(target, type, selection) {\n this.target = target;\n this.type = type;\n this.selection = selection;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-brush/src/noevent.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-brush/src/noevent.js ***! + \********************************************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-brush/node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-brush/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/index.js ***! + \**************************************************************/ +/*! exports provided: chord, ribbon */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_chord__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/chord */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/chord.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"chord\", function() { return _src_chord__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_ribbon__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/ribbon */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/ribbon.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ribbon\", function() { return _src_ribbon__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/array.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/array.js ***! + \****************************************************************************************/ +/*! exports provided: slice, map */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\nvar map = array.map;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisect.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisect.js ***! + \*****************************************************************************************/ +/*! exports provided: bisectRight, bisectLeft, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return bisectRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return bisectLeft; });\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisector.js\");\n\n\n\nvar ascendingBisect = Object(_bisector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\nvar bisectRight = ascendingBisect.right;\nvar bisectLeft = ascendingBisect.left;\n/* harmony default export */ __webpack_exports__[\"default\"] = (bisectRight);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisect.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisector.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisector.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n});\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(f(d), x);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/constant.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/constant.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/cross.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/cross.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/pairs.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values0, values1, reduce) {\n var n0 = values0.length,\n n1 = values1.length,\n values = new Array(n0 * n1),\n i0,\n i1,\n i,\n value0;\n\n if (reduce == null) reduce = _pairs__WEBPACK_IMPORTED_MODULE_0__[\"pair\"];\n\n for (i0 = i = 0; i0 < n0; ++i0) {\n for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {\n values[i] = reduce(value0, values1[i1]);\n }\n }\n\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/descending.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/descending.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/deviation.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/deviation.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/variance.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n var v = Object(_variance__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(array, f);\n return v ? Math.sqrt(v) : v;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/deviation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/extent.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/extent.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n return [min, max];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/histogram.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/histogram.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisect.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/constant.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/extent.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/identity.js\");\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/range.js\");\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ticks.js\");\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/sturges.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n domain = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n threshold = _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\n\n function histogram(data) {\n var i,\n n = data.length,\n x,\n values = new Array(n);\n\n for (i = 0; i < n; ++i) {\n values[i] = value(data[i], i, data);\n }\n\n var xz = domain(values),\n x0 = xz[0],\n x1 = xz[1],\n tz = threshold(values, x0, x1);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n tz = Object(_ticks__WEBPACK_IMPORTED_MODULE_6__[\"tickStep\"])(x0, x1, tz);\n tz = Object(_range__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(Math.ceil(x0 / tz) * tz, x1, tz); // exclusive\n }\n\n // Remove any thresholds outside the domain.\n var m = tz.length;\n while (tz[0] <= x0) tz.shift(), --m;\n while (tz[m - 1] > x1) tz.pop(), --m;\n\n var bins = new Array(m + 1),\n bin;\n\n // Initialize bins.\n for (i = 0; i <= m; ++i) {\n bin = bins[i] = [];\n bin.x0 = i > 0 ? tz[i - 1] : x0;\n bin.x1 = i < m ? tz[i] : x1;\n }\n\n // Assign data to bins by value, ignoring any outside the domain.\n for (i = 0; i < n; ++i) {\n x = values[i];\n if (x0 <= x && x <= x1) {\n bins[Object(_bisect__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(tz, x, 0, m)].push(data[i]);\n }\n }\n\n return bins;\n }\n\n histogram.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : value;\n };\n\n histogram.domain = function(_) {\n return arguments.length ? (domain = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])([_[0], _[1]]), histogram) : domain;\n };\n\n histogram.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : threshold;\n };\n\n return histogram;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/histogram.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/identity.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/identity.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisect.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectLeft\"]; });\n\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return _ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/bisector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return _bisector__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./cross */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return _cross__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./descending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return _descending__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./deviation */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/deviation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return _deviation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return _extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _histogram__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./histogram */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/histogram.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return _histogram__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./threshold/freedmanDiaconis */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/freedmanDiaconis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _threshold_scott__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./threshold/scott */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/scott.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return _threshold_scott__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/sturges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _max__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./max */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/max.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return _max__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _mean__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./mean */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/mean.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return _mean__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _median__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./median */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/median.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return _median__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/merge.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return _merge__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/min.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return _min__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/pairs.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return _pairs__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _permute__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./permute */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/permute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return _permute__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return _quantile__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/range.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return _range__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./scan */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/scan.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return _scan__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _shuffle__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./shuffle */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/shuffle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return _shuffle__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./sum */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/sum.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return _sum__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ticks.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickStep\"]; });\n\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/transpose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return _transpose__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/variance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return _variance__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _zip__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./zip */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/zip.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return _zip__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/max.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/max.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n return max;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/max.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/mean.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/mean.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = n,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) sum += value;\n else --m;\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) sum += value;\n else --m;\n }\n }\n\n if (m) return sum / m;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/mean.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/median.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/median.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n numbers = [];\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(values[i]))) {\n numbers.push(value);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(valueof(values[i], i, values)))) {\n numbers.push(value);\n }\n }\n }\n\n return Object(_quantile__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(numbers.sort(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), 0.5);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/median.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/merge.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/merge.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(arrays) {\n var n = arrays.length,\n m,\n i = -1,\n j = 0,\n merged,\n array;\n\n while (++i < n) j += arrays[i].length;\n merged = new Array(j);\n\n while (--n >= 0) {\n array = arrays[n];\n m = array.length;\n while (--m >= 0) {\n merged[--j] = array[m];\n }\n }\n\n return merged;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/min.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/min.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n return min;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/min.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x === null ? NaN : +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/pairs.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/pairs.js ***! + \****************************************************************************************/ +/*! exports provided: default, pair */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pair\", function() { return pair; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n if (f == null) f = pair;\n var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);\n while (i < n) pairs[i] = f(p, p = array[++i]);\n return pairs;\n});\n\nfunction pair(a, b) {\n return [a, b];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/pairs.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/permute.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/permute.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, indexes) {\n var i = indexes.length, permutes = new Array(i);\n while (i--) permutes[i] = array[indexes[i]];\n return permutes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/permute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, p, valueof) {\n if (valueof == null) valueof = _number__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n if (!(n = values.length)) return;\n if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n var n,\n i = (n - 1) * p,\n i0 = Math.floor(i),\n value0 = +valueof(values[i0], i0, values),\n value1 = +valueof(values[i0 + 1], i0 + 1, values);\n return value0 + (value1 - value0) * (i - i0);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/range.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/range.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/range.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/scan.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/scan.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, compare) {\n if (!(n = values.length)) return;\n var n,\n i = 0,\n j = 0,\n xi,\n xj = values[j];\n\n if (compare == null) compare = _ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n\n while (++i < n) {\n if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {\n xj = xi, j = i;\n }\n }\n\n if (compare(xj, xj) === 0) return j;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/scan.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/shuffle.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/shuffle.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, i0, i1) {\n var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m + i0];\n array[m + i0] = array[i + i0];\n array[i + i0] = t;\n }\n\n return array;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/shuffle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/sum.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/sum.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (value = +values[i]) sum += value; // Note: zero and null are equivalent.\n }\n }\n\n else {\n while (++i < n) {\n if (value = +valueof(values[i], i, values)) sum += value;\n }\n }\n\n return sum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/sum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/freedmanDiaconis.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/freedmanDiaconis.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ascending */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../quantile */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n values = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(values, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]).sort(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n return Math.ceil((max - min) / (2 * (Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.75) - Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.25)) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/freedmanDiaconis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/scott.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/scott.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deviation */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/deviation.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n return Math.ceil((max - min) / (3.5 * Object(_deviation__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/scott.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/sturges.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/sturges.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n return Math.ceil(Math.log(values.length) / Math.LN2) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/threshold/sturges.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ticks.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ticks.js ***! + \****************************************************************************************/ +/*! exports provided: default, tickIncrement, tickStep */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return tickIncrement; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return tickStep; });\nvar e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n});\n\nfunction tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nfunction tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/ticks.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/transpose.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/transpose.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/min.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(matrix) {\n if (!(n = matrix.length)) return [];\n for (var i = -1, m = Object(_min__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(matrix, length), transpose = new Array(m); ++i < m;) {\n for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n row[j] = matrix[j][i];\n }\n }\n return transpose;\n});\n\nfunction length(d) {\n return d.length;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/transpose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/variance.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/variance.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = 0,\n i = -1,\n mean = 0,\n value,\n delta,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n if (m > 1) return sum / (m - 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/variance.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/zip.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/zip.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/transpose.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_transpose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(arguments);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/zip.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: path */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./path */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/path.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return _path__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/path.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/path.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (path);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/path.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/src/array.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/src/array.js ***! + \******************************************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/src/chord.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/src/chord.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/math.js\");\n\n\n\nfunction compareValue(compare) {\n return function(a, b) {\n return compare(\n a.source.value + a.target.value,\n b.source.value + b.target.value\n );\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var padAngle = 0,\n sortGroups = null,\n sortSubgroups = null,\n sortChords = null;\n\n function chord(matrix) {\n var n = matrix.length,\n groupSums = [],\n groupIndex = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n),\n subgroupIndex = [],\n chords = [],\n groups = chords.groups = new Array(n),\n subgroups = new Array(n * n),\n k,\n x,\n x0,\n dx,\n i,\n j;\n\n // Compute the sum.\n k = 0, i = -1; while (++i < n) {\n x = 0, j = -1; while (++j < n) {\n x += matrix[i][j];\n }\n groupSums.push(x);\n subgroupIndex.push(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n));\n k += x;\n }\n\n // Sort groups…\n if (sortGroups) groupIndex.sort(function(a, b) {\n return sortGroups(groupSums[a], groupSums[b]);\n });\n\n // Sort subgroups…\n if (sortSubgroups) subgroupIndex.forEach(function(d, i) {\n d.sort(function(a, b) {\n return sortSubgroups(matrix[i][a], matrix[i][b]);\n });\n });\n\n // Convert the sum to scaling factor for [0, 2pi].\n // TODO Allow start and end angle to be specified?\n // TODO Allow padding to be specified as percentage?\n k = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"max\"])(0, _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] - padAngle * n) / k;\n dx = k ? padAngle : _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] / n;\n\n // Compute the start and end angle for each group and subgroup.\n // Note: Opera has a bug reordering object literal properties!\n x = 0, i = -1; while (++i < n) {\n x0 = x, j = -1; while (++j < n) {\n var di = groupIndex[i],\n dj = subgroupIndex[di][j],\n v = matrix[di][dj],\n a0 = x,\n a1 = x += v * k;\n subgroups[dj * n + di] = {\n index: di,\n subindex: dj,\n startAngle: a0,\n endAngle: a1,\n value: v\n };\n }\n groups[di] = {\n index: di,\n startAngle: x0,\n endAngle: x,\n value: groupSums[di]\n };\n x += dx;\n }\n\n // Generate chords for each (non-empty) subgroup-subgroup link.\n i = -1; while (++i < n) {\n j = i - 1; while (++j < n) {\n var source = subgroups[j * n + i],\n target = subgroups[i * n + j];\n if (source.value || target.value) {\n chords.push(source.value < target.value\n ? {source: target, target: source}\n : {source: source, target: target});\n }\n }\n }\n\n return sortChords ? chords.sort(sortChords) : chords;\n }\n\n chord.padAngle = function(_) {\n return arguments.length ? (padAngle = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"max\"])(0, _), chord) : padAngle;\n };\n\n chord.sortGroups = function(_) {\n return arguments.length ? (sortGroups = _, chord) : sortGroups;\n };\n\n chord.sortSubgroups = function(_) {\n return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;\n };\n\n chord.sortChords = function(_) {\n return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;\n };\n\n return chord;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/src/chord.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/src/math.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/src/math.js ***! + \*****************************************************************/ +/*! exports provided: cos, sin, pi, halfPi, tau, max */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return max; });\nvar cos = Math.cos;\nvar sin = Math.sin;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar tau = pi * 2;\nvar max = Math.max;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-chord/src/ribbon.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-chord/src/ribbon.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-chord/src/math.js\");\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-chord/node_modules/d3-path/src/index.js\");\n\n\n\n\n\nfunction defaultSource(d) {\n return d.source;\n}\n\nfunction defaultTarget(d) {\n return d.target;\n}\n\nfunction defaultRadius(d) {\n return d.radius;\n}\n\nfunction defaultStartAngle(d) {\n return d.startAngle;\n}\n\nfunction defaultEndAngle(d) {\n return d.endAngle;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var source = defaultSource,\n target = defaultTarget,\n radius = defaultRadius,\n startAngle = defaultStartAngle,\n endAngle = defaultEndAngle,\n context = null;\n\n function ribbon() {\n var buffer,\n argv = _array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(arguments),\n s = source.apply(this, argv),\n t = target.apply(this, argv),\n sr = +radius.apply(this, (argv[0] = s, argv)),\n sa0 = startAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n sa1 = endAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n sx0 = sr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(sa0),\n sy0 = sr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(sa0),\n tr = +radius.apply(this, (argv[0] = t, argv)),\n ta0 = startAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n ta1 = endAngle.apply(this, argv) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"];\n\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_3__[\"path\"])();\n\n context.moveTo(sx0, sy0);\n context.arc(0, 0, sr, sa0, sa1);\n if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?\n context.quadraticCurveTo(0, 0, tr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(ta0), tr * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ta0));\n context.arc(0, 0, tr, ta0, ta1);\n }\n context.quadraticCurveTo(0, 0, sx0, sy0);\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n ribbon.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : radius;\n };\n\n ribbon.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : startAngle;\n };\n\n ribbon.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), ribbon) : endAngle;\n };\n\n ribbon.source = function(_) {\n return arguments.length ? (source = _, ribbon) : source;\n };\n\n ribbon.target = function(_) {\n return arguments.length ? (target = _, ribbon) : target;\n };\n\n ribbon.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), ribbon) : context;\n };\n\n return ribbon;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-chord/src/ribbon.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/index.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/index.js ***! + \*******************************************************************/ +/*! exports provided: nest, set, map, keys, values, entries */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_nest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/nest */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/nest.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return _src_nest__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_set__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/set */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/set.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return _src_set__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/map */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/map.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return _src_map__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_keys__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/keys */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/keys.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return _src_keys__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_values__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/values */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/values.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return _src_values__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_entries__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/entries */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/entries.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return _src_entries__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/entries.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/entries.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var entries = [];\n for (var key in map) entries.push({key: key, value: map[key]});\n return entries;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/entries.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/keys.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/keys.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var keys = [];\n for (var key in map) keys.push(key);\n return keys;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/keys.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/map.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/map.js ***! + \*********************************************************************/ +/*! exports provided: prefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefix\", function() { return prefix; });\nvar prefix = \"$\";\n\nfunction Map() {}\n\nMap.prototype = map.prototype = {\n constructor: Map,\n has: function(key) {\n return (prefix + key) in this;\n },\n get: function(key) {\n return this[prefix + key];\n },\n set: function(key, value) {\n this[prefix + key] = value;\n return this;\n },\n remove: function(key) {\n var property = prefix + key;\n return property in this && delete this[property];\n },\n clear: function() {\n for (var property in this) if (property[0] === prefix) delete this[property];\n },\n keys: function() {\n var keys = [];\n for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));\n return keys;\n },\n values: function() {\n var values = [];\n for (var property in this) if (property[0] === prefix) values.push(this[property]);\n return values;\n },\n entries: function() {\n var entries = [];\n for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});\n return entries;\n },\n size: function() {\n var size = 0;\n for (var property in this) if (property[0] === prefix) ++size;\n return size;\n },\n empty: function() {\n for (var property in this) if (property[0] === prefix) return false;\n return true;\n },\n each: function(f) {\n for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);\n }\n};\n\nfunction map(object, f) {\n var map = new Map;\n\n // Copy constructor.\n if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });\n\n // Index array by numeric index or specified key function.\n else if (Array.isArray(object)) {\n var i = -1,\n n = object.length,\n o;\n\n if (f == null) while (++i < n) map.set(i, object[i]);\n else while (++i < n) map.set(f(o = object[i], i, object), o);\n }\n\n // Convert object to map.\n else if (object) for (var key in object) map.set(key, object[key]);\n\n return map;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (map);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/map.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/nest.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/nest.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/map.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = [],\n sortKeys = [],\n sortValues,\n rollup,\n nest;\n\n function apply(array, depth, createResult, setResult) {\n if (depth >= keys.length) {\n if (sortValues != null) array.sort(sortValues);\n return rollup != null ? rollup(array) : array;\n }\n\n var i = -1,\n n = array.length,\n key = keys[depth++],\n keyValue,\n value,\n valuesByKey = Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n values,\n result = createResult();\n\n while (++i < n) {\n if (values = valuesByKey.get(keyValue = key(value = array[i]) + \"\")) {\n values.push(value);\n } else {\n valuesByKey.set(keyValue, [value]);\n }\n }\n\n valuesByKey.each(function(values, key) {\n setResult(result, key, apply(values, depth, createResult, setResult));\n });\n\n return result;\n }\n\n function entries(map, depth) {\n if (++depth > keys.length) return map;\n var array, sortKey = sortKeys[depth - 1];\n if (rollup != null && depth >= keys.length) array = map.entries();\n else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });\n return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;\n }\n\n return nest = {\n object: function(array) { return apply(array, 0, createObject, setObject); },\n map: function(array) { return apply(array, 0, createMap, setMap); },\n entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },\n key: function(d) { keys.push(d); return nest; },\n sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },\n sortValues: function(order) { sortValues = order; return nest; },\n rollup: function(f) { rollup = f; return nest; }\n };\n});\n\nfunction createObject() {\n return {};\n}\n\nfunction setObject(object, key, value) {\n object[key] = value;\n}\n\nfunction createMap() {\n return Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n}\n\nfunction setMap(map, key, value) {\n map.set(key, value);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/nest.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/set.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/set.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-collection/src/map.js\");\n\n\nfunction Set() {}\n\nvar proto = _map__WEBPACK_IMPORTED_MODULE_0__[\"default\"].prototype;\n\nSet.prototype = set.prototype = {\n constructor: Set,\n has: proto.has,\n add: function(value) {\n value += \"\";\n this[_map__WEBPACK_IMPORTED_MODULE_0__[\"prefix\"] + value] = value;\n return this;\n },\n remove: proto.remove,\n clear: proto.clear,\n values: proto.keys,\n size: proto.size,\n empty: proto.empty,\n each: proto.each\n};\n\nfunction set(object, f) {\n var set = new Set;\n\n // Copy constructor.\n if (object instanceof Set) object.each(function(value) { set.add(value); });\n\n // Otherwise, assume it’s an array.\n else if (object) {\n var i = -1, n = object.length;\n if (f == null) while (++i < n) set.add(object[i]);\n else while (++i < n) set.add(f(object[i], i, object));\n }\n\n return set;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (set);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/set.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-collection/src/values.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-collection/src/values.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var values = [];\n for (var key in map) values.push(map[key]);\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-collection/src/values.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/index.js ***! + \**************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/color */ \"./node_modules/dagre-d3/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _src_color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _src_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _src_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _src_lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/lab */ \"./node_modules/dagre-d3/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _src_lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _src_lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony import */ var _src_cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _src_cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/src/color.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/src/color.js ***! + \******************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/src/cubehelix.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/src/cubehelix.js ***! + \**********************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/src/define.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/src/define.js ***! + \*******************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/src/lab.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/src/lab.js ***! + \****************************************************************/ +/*! exports provided: default, Lab, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar Kn = 18,\n Xn = 0.950470, // D65 standard referent\n Yn = 1,\n Zn = 1.088830,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var b = rgb2xyz(o.r),\n a = rgb2xyz(o.g),\n l = rgb2xyz(o.b),\n x = xyz2lab((0.4124564 * b + 0.3575761 * a + 0.1804375 * l) / Xn),\n y = xyz2lab((0.2126729 * b + 0.7151522 * a + 0.0721750 * l) / Yn),\n z = xyz2lab((0.0193339 * b + 0.1191920 * a + 0.9503041 * l) / Zn);\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - Kn * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n y = Yn * lab2xyz(y);\n x = Xn * lab2xyz(x);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n xyz2rgb( 3.2404542 * x - 1.5371385 * y - 0.4985314 * z), // D65 -> sRGB\n xyz2rgb(-0.9692660 * x + 1.8760108 * y + 0.0415560 * z),\n xyz2rgb( 0.0556434 * x - 0.2040259 * y + 1.0572252 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction xyz2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2xyz(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + Kn * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - Kn * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-color/src/math.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-color/src/math.js ***! + \*****************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dispatch/index.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dispatch/index.js ***! + \*****************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/dispatch */ \"./node_modules/dagre-d3/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _src_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dispatch/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dispatch/src/dispatch.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dispatch/src/dispatch.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/index.js ***! + \*************************************************************/ +/*! exports provided: drag, dragDisable, dragEnable */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_drag__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/drag */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/drag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return _src_drag__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_nodrag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/nodrag */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/nodrag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return _src_nodrag__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return _src_nodrag__WEBPACK_IMPORTED_MODULE_1__[\"yesdrag\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/dispatch.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/dispatch.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/index.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/index.js ***! + \******************************************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/constant.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/constant.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/create.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/create.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js ***! + \*******************************************************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./create */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./local */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./matcher */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mouse */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selector */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./selection/style */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./touch */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./touches */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./window */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/local.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/local.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/matcher.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/matcher.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/mouse.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/mouse.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js ***! + \************************************************************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/select.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/select.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectAll.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectAll.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/append.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/append.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/attr.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/attr.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/call.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/call.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/classed.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/classed.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/clone.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/clone.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/data.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/data.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/datum.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/datum.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/dispatch.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/dispatch.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/each.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/each.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/empty.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/empty.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/enter.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/enter.js ***! + \*****************************************************************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/exit.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/exit.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/filter.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/filter.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/html.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/html.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js ***! + \*****************************************************************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/insert.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/insert.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/lower.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/lower.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/merge.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/merge.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/node.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/node.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/nodes.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/nodes.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js ***! + \**************************************************************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/order.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/order.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/property.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/property.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/raise.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/raise.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/remove.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/remove.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/select.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/select.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/selectAll.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/selectAll.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/size.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/size.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sort.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sort.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sparse.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sparse.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/style.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/style.js ***! + \*****************************************************************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/text.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/text.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectorAll.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectorAll.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touch.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touch.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touches.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touches.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/src/constant.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/src/constant.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/src/drag.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/src/drag.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/nodrag.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/noevent.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./event */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/event.js\");\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].button;\n}\n\nfunction defaultContainer() {\n return this.parentNode;\n}\n\nfunction defaultSubject(d) {\n return d == null ? {x: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].x, y: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].y} : d;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n container = defaultContainer,\n subject = defaultSubject,\n touchable = defaultTouchable,\n gestures = {},\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"drag\", \"end\"),\n active = 0,\n mousedownx,\n mousedowny,\n mousemoving,\n touchending,\n clickDistance2 = 0;\n\n function drag(selection) {\n selection\n .on(\"mousedown.drag\", mousedowned)\n .filter(touchable)\n .on(\"touchstart.drag\", touchstarted)\n .on(\"touchmove.drag\", touchmoved)\n .on(\"touchend.drag touchcancel.drag\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var gesture = beforestart(\"mouse\", container.apply(this, arguments), d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"mouse\"], this, arguments);\n if (!gesture) return;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag\", mousemoved, true).on(\"mouseup.drag\", mouseupped, true);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n mousemoving = false;\n mousedownx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX;\n mousedowny = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY;\n gesture(\"start\");\n }\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n if (!mousemoving) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX - mousedownx, dy = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY - mousedowny;\n mousemoving = dx * dx + dy * dy > clickDistance2;\n }\n gestures.mouse(\"drag\");\n }\n\n function mouseupped() {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag mouseup.drag\", null);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"yesdrag\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view, mousemoving);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gestures.mouse(\"end\");\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n c = container.apply(this, arguments),\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = beforestart(touches[i].identifier, c, d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"touch\"], this, arguments)) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"start\");\n }\n }\n }\n\n function touchmoved() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gesture(\"drag\");\n }\n }\n }\n\n function touchended() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"end\");\n }\n }\n }\n\n function beforestart(id, container, point, that, args) {\n var p = point(container, id), s, dx, dy,\n sublisteners = listeners.copy();\n\n if (!Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, \"beforestart\", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {\n if ((d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].subject = s = subject.apply(that, args)) == null) return false;\n dx = s.x - p[0] || 0;\n dy = s.y - p[1] || 0;\n return true;\n })) return;\n\n return function gesture(type) {\n var p0 = p, n;\n switch (type) {\n case \"start\": gestures[id] = gesture, n = active++; break;\n case \"end\": delete gestures[id], --active; // nobreak\n case \"drag\": p = point(container, id), n = active; break;\n }\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);\n };\n }\n\n drag.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : filter;\n };\n\n drag.container = function(_) {\n return arguments.length ? (container = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : container;\n };\n\n drag.subject = function(_) {\n return arguments.length ? (subject = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : subject;\n };\n\n drag.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : touchable;\n };\n\n drag.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? drag : value;\n };\n\n drag.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);\n };\n\n return drag;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/src/drag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/src/event.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/src/event.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return DragEvent; });\nfunction DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {\n this.target = target;\n this.type = type;\n this.subject = subject;\n this.identifier = id;\n this.active = active;\n this.x = x;\n this.y = y;\n this.dx = dx;\n this.dy = dy;\n this._ = dispatch;\n}\n\nDragEvent.prototype.on = function() {\n var value = this._.on.apply(this._, arguments);\n return value === this._ ? this : value;\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/src/nodrag.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/src/nodrag.js ***! + \******************************************************************/ +/*! exports provided: default, yesdrag */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"yesdrag\", function() { return yesdrag; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-drag/src/noevent.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(view) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n } else {\n root.__noselect = root.style.MozUserSelect;\n root.style.MozUserSelect = \"none\";\n }\n});\n\nfunction yesdrag(view, noclick) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", null);\n if (noclick) {\n selection.on(\"click.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n setTimeout(function() { selection.on(\"click.drag\", null); }, 0);\n }\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", null);\n } else {\n root.style.MozUserSelect = root.__noselect;\n delete root.__noselect;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/src/nodrag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-drag/src/noevent.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-drag/src/noevent.js ***! + \*******************************************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-drag/node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-drag/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dsv/index.js": +/*!************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dsv/index.js ***! + \************************************************************/ +/*! exports provided: dsvFormat, csvParse, csvParseRows, csvFormat, csvFormatRows, tsvParse, tsvParseRows, tsvFormat, tsvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/dsv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsvFormat\", function() { return _src_dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_csv__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/csv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/src/csv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return _src_csv__WEBPACK_IMPORTED_MODULE_1__[\"csvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return _src_csv__WEBPACK_IMPORTED_MODULE_1__[\"csvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return _src_csv__WEBPACK_IMPORTED_MODULE_1__[\"csvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return _src_csv__WEBPACK_IMPORTED_MODULE_1__[\"csvFormatRows\"]; });\n\n/* harmony import */ var _src_tsv__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/tsv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/src/tsv.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return _src_tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return _src_tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return _src_tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return _src_tsv__WEBPACK_IMPORTED_MODULE_2__[\"tsvFormatRows\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dsv/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dsv/src/csv.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dsv/src/csv.js ***! + \**************************************************************/ +/*! exports provided: csvParse, csvParseRows, csvFormat, csvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return csvParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return csvParseRows; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return csvFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return csvFormatRows; });\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dsv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js\");\n\n\nvar csv = Object(_dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\",\");\n\nvar csvParse = csv.parse;\nvar csvParseRows = csv.parseRows;\nvar csvFormat = csv.format;\nvar csvFormatRows = csv.formatRows;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dsv/src/csv.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar EOL = {},\n EOF = {},\n QUOTE = 34,\n NEWLINE = 10,\n RETURN = 13;\n\nfunction objectConverter(columns) {\n return new Function(\"d\", \"return {\" + columns.map(function(name, i) {\n return JSON.stringify(name) + \": d[\" + i + \"]\";\n }).join(\",\") + \"}\");\n}\n\nfunction customConverter(columns, f) {\n var object = objectConverter(columns);\n return function(row, i) {\n return f(object(row), i, columns);\n };\n}\n\n// Compute unique columns in order of discovery.\nfunction inferColumns(rows) {\n var columnSet = Object.create(null),\n columns = [];\n\n rows.forEach(function(row) {\n for (var column in row) {\n if (!(column in columnSet)) {\n columns.push(columnSet[column] = column);\n }\n }\n });\n\n return columns;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(delimiter) {\n var reFormat = new RegExp(\"[\\\"\" + delimiter + \"\\n\\r]\"),\n DELIMITER = delimiter.charCodeAt(0);\n\n function parse(text, f) {\n var convert, columns, rows = parseRows(text, function(row, i) {\n if (convert) return convert(row, i - 1);\n columns = row, convert = f ? customConverter(row, f) : objectConverter(row);\n });\n rows.columns = columns || [];\n return rows;\n }\n\n function parseRows(text, f) {\n var rows = [], // output rows\n N = text.length,\n I = 0, // current character index\n n = 0, // current line number\n t, // current token\n eof = N <= 0, // current token followed by EOF?\n eol = false; // current token followed by EOL?\n\n // Strip the trailing newline.\n if (text.charCodeAt(N - 1) === NEWLINE) --N;\n if (text.charCodeAt(N - 1) === RETURN) --N;\n\n function token() {\n if (eof) return EOF;\n if (eol) return eol = false, EOL;\n\n // Unescape quotes.\n var i, j = I, c;\n if (text.charCodeAt(j) === QUOTE) {\n while (I++ < N && text.charCodeAt(I) !== QUOTE || text.charCodeAt(++I) === QUOTE);\n if ((i = I) >= N) eof = true;\n else if ((c = text.charCodeAt(I++)) === NEWLINE) eol = true;\n else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }\n return text.slice(j + 1, i - 1).replace(/\"\"/g, \"\\\"\");\n }\n\n // Find next delimiter or newline.\n while (I < N) {\n if ((c = text.charCodeAt(i = I++)) === NEWLINE) eol = true;\n else if (c === RETURN) { eol = true; if (text.charCodeAt(I) === NEWLINE) ++I; }\n else if (c !== DELIMITER) continue;\n return text.slice(j, i);\n }\n\n // Return last token before EOF.\n return eof = true, text.slice(j, N);\n }\n\n while ((t = token()) !== EOF) {\n var row = [];\n while (t !== EOL && t !== EOF) row.push(t), t = token();\n if (f && (row = f(row, n++)) == null) continue;\n rows.push(row);\n }\n\n return rows;\n }\n\n function format(rows, columns) {\n if (columns == null) columns = inferColumns(rows);\n return [columns.map(formatValue).join(delimiter)].concat(rows.map(function(row) {\n return columns.map(function(column) {\n return formatValue(row[column]);\n }).join(delimiter);\n })).join(\"\\n\");\n }\n\n function formatRows(rows) {\n return rows.map(formatRow).join(\"\\n\");\n }\n\n function formatRow(row) {\n return row.map(formatValue).join(delimiter);\n }\n\n function formatValue(text) {\n return text == null ? \"\"\n : reFormat.test(text += \"\") ? \"\\\"\" + text.replace(/\"/g, \"\\\"\\\"\") + \"\\\"\"\n : text;\n }\n\n return {\n parse: parse,\n parseRows: parseRows,\n format: format,\n formatRows: formatRows\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-dsv/src/tsv.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-dsv/src/tsv.js ***! + \**************************************************************/ +/*! exports provided: tsvParse, tsvParseRows, tsvFormat, tsvFormatRows */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return tsvParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return tsvParseRows; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return tsvFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return tsvFormatRows; });\n/* harmony import */ var _dsv__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dsv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/src/dsv.js\");\n\n\nvar tsv = Object(_dsv__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"\\t\");\n\nvar tsvParse = tsv.parse;\nvar tsvParseRows = tsv.parseRows;\nvar tsvFormat = tsv.format;\nvar tsvFormatRows = tsv.formatRows;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-dsv/src/tsv.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/index.js ***! + \*************************************************************/ +/*! exports provided: easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/linear */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return _src_linear__WEBPACK_IMPORTED_MODULE_0__[\"linear\"]; });\n\n/* harmony import */ var _src_quad__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/quad */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/quad.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return _src_quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return _src_quad__WEBPACK_IMPORTED_MODULE_1__[\"quadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return _src_quad__WEBPACK_IMPORTED_MODULE_1__[\"quadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return _src_quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony import */ var _src_cubic__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/cubic */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/cubic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return _src_cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return _src_cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return _src_cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return _src_cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony import */ var _src_poly__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/poly */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/poly.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return _src_poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return _src_poly__WEBPACK_IMPORTED_MODULE_3__[\"polyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return _src_poly__WEBPACK_IMPORTED_MODULE_3__[\"polyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return _src_poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony import */ var _src_sin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/sin */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/sin.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return _src_sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return _src_sin__WEBPACK_IMPORTED_MODULE_4__[\"sinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return _src_sin__WEBPACK_IMPORTED_MODULE_4__[\"sinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return _src_sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony import */ var _src_exp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/exp */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/exp.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return _src_exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return _src_exp__WEBPACK_IMPORTED_MODULE_5__[\"expIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return _src_exp__WEBPACK_IMPORTED_MODULE_5__[\"expOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return _src_exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony import */ var _src_circle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/circle */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return _src_circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return _src_circle__WEBPACK_IMPORTED_MODULE_6__[\"circleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return _src_circle__WEBPACK_IMPORTED_MODULE_6__[\"circleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return _src_circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony import */ var _src_bounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/bounce */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/bounce.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return _src_bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return _src_bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return _src_bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return _src_bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceInOut\"]; });\n\n/* harmony import */ var _src_back__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/back */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/back.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return _src_back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return _src_back__WEBPACK_IMPORTED_MODULE_8__[\"backIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return _src_back__WEBPACK_IMPORTED_MODULE_8__[\"backOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return _src_back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony import */ var _src_elastic__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/elastic */ \"./node_modules/dagre-d3/node_modules/d3-ease/src/elastic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return _src_elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return _src_elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return _src_elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return _src_elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticInOut\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/back.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/back.js ***! + \****************************************************************/ +/*! exports provided: backIn, backOut, backInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backIn\", function() { return backIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backOut\", function() { return backOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backInOut\", function() { return backInOut; });\nvar overshoot = 1.70158;\n\nvar backIn = (function custom(s) {\n s = +s;\n\n function backIn(t) {\n return t * t * ((s + 1) * t - s);\n }\n\n backIn.overshoot = custom;\n\n return backIn;\n})(overshoot);\n\nvar backOut = (function custom(s) {\n s = +s;\n\n function backOut(t) {\n return --t * t * ((s + 1) * t + s) + 1;\n }\n\n backOut.overshoot = custom;\n\n return backOut;\n})(overshoot);\n\nvar backInOut = (function custom(s) {\n s = +s;\n\n function backInOut(t) {\n return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n }\n\n backInOut.overshoot = custom;\n\n return backInOut;\n})(overshoot);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/back.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/bounce.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/bounce.js ***! + \******************************************************************/ +/*! exports provided: bounceIn, bounceOut, bounceInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceIn\", function() { return bounceIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceOut\", function() { return bounceOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceInOut\", function() { return bounceInOut; });\nvar b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\n\nfunction bounceIn(t) {\n return 1 - bounceOut(1 - t);\n}\n\nfunction bounceOut(t) {\n return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n}\n\nfunction bounceInOut(t) {\n return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/bounce.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/circle.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/circle.js ***! + \******************************************************************/ +/*! exports provided: circleIn, circleOut, circleInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleIn\", function() { return circleIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleOut\", function() { return circleOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleInOut\", function() { return circleInOut; });\nfunction circleIn(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n\nfunction circleOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n\nfunction circleInOut(t) {\n return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/cubic.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/cubic.js ***! + \*****************************************************************/ +/*! exports provided: cubicIn, cubicOut, cubicInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicIn\", function() { return cubicIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicOut\", function() { return cubicOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicInOut\", function() { return cubicInOut; });\nfunction cubicIn(t) {\n return t * t * t;\n}\n\nfunction cubicOut(t) {\n return --t * t * t + 1;\n}\n\nfunction cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/cubic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/elastic.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/elastic.js ***! + \*******************************************************************/ +/*! exports provided: elasticIn, elasticOut, elasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticIn\", function() { return elasticIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticOut\", function() { return elasticOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticInOut\", function() { return elasticInOut; });\nvar tau = 2 * Math.PI,\n amplitude = 1,\n period = 0.3;\n\nvar elasticIn = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticIn(t) {\n return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n }\n\n elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n elasticIn.period = function(p) { return custom(a, p); };\n\n return elasticIn;\n})(amplitude, period);\n\nvar elasticOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticOut(t) {\n return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n }\n\n elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticOut.period = function(p) { return custom(a, p); };\n\n return elasticOut;\n})(amplitude, period);\n\nvar elasticInOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticInOut(t) {\n return ((t = t * 2 - 1) < 0\n ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n }\n\n elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticInOut.period = function(p) { return custom(a, p); };\n\n return elasticInOut;\n})(amplitude, period);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/elastic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/exp.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/exp.js ***! + \***************************************************************/ +/*! exports provided: expIn, expOut, expInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expIn\", function() { return expIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expOut\", function() { return expOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expInOut\", function() { return expInOut; });\nfunction expIn(t) {\n return Math.pow(2, 10 * t - 10);\n}\n\nfunction expOut(t) {\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction expInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/exp.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/linear.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/linear.js ***! + \******************************************************************/ +/*! exports provided: linear */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linear\", function() { return linear; });\nfunction linear(t) {\n return +t;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/poly.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/poly.js ***! + \****************************************************************/ +/*! exports provided: polyIn, polyOut, polyInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyIn\", function() { return polyIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyOut\", function() { return polyOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyInOut\", function() { return polyInOut; });\nvar exponent = 3;\n\nvar polyIn = (function custom(e) {\n e = +e;\n\n function polyIn(t) {\n return Math.pow(t, e);\n }\n\n polyIn.exponent = custom;\n\n return polyIn;\n})(exponent);\n\nvar polyOut = (function custom(e) {\n e = +e;\n\n function polyOut(t) {\n return 1 - Math.pow(1 - t, e);\n }\n\n polyOut.exponent = custom;\n\n return polyOut;\n})(exponent);\n\nvar polyInOut = (function custom(e) {\n e = +e;\n\n function polyInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n }\n\n polyInOut.exponent = custom;\n\n return polyInOut;\n})(exponent);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/poly.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/quad.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/quad.js ***! + \****************************************************************/ +/*! exports provided: quadIn, quadOut, quadInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadIn\", function() { return quadIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadOut\", function() { return quadOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadInOut\", function() { return quadInOut; });\nfunction quadIn(t) {\n return t * t;\n}\n\nfunction quadOut(t) {\n return t * (2 - t);\n}\n\nfunction quadInOut(t) {\n return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-ease/src/sin.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-ease/src/sin.js ***! + \***************************************************************/ +/*! exports provided: sinIn, sinOut, sinInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinIn\", function() { return sinIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinOut\", function() { return sinOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinInOut\", function() { return sinInOut; });\nvar pi = Math.PI,\n halfPi = pi / 2;\n\nfunction sinIn(t) {\n return 1 - Math.cos(t * halfPi);\n}\n\nfunction sinOut(t) {\n return Math.sin(t * halfPi);\n}\n\nfunction sinInOut(t) {\n return (1 - Math.cos(pi * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-ease/src/sin.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/index.js ***! + \**************************************************************/ +/*! exports provided: forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceSimulation, forceX, forceY */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_center__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/center */ \"./node_modules/dagre-d3/node_modules/d3-force/src/center.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCenter\", function() { return _src_center__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_collide__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/collide */ \"./node_modules/dagre-d3/node_modules/d3-force/src/collide.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCollide\", function() { return _src_collide__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_link__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/link */ \"./node_modules/dagre-d3/node_modules/d3-force/src/link.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceLink\", function() { return _src_link__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_manyBody__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/manyBody */ \"./node_modules/dagre-d3/node_modules/d3-force/src/manyBody.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceManyBody\", function() { return _src_manyBody__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_radial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/radial */ \"./node_modules/dagre-d3/node_modules/d3-force/src/radial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceRadial\", function() { return _src_radial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_simulation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/simulation */ \"./node_modules/dagre-d3/node_modules/d3-force/src/simulation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceSimulation\", function() { return _src_simulation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_x__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/x */ \"./node_modules/dagre-d3/node_modules/d3-force/src/x.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceX\", function() { return _src_x__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_y__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/y */ \"./node_modules/dagre-d3/node_modules/d3-force/src/y.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceY\", function() { return _src_y__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/entries.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/entries.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var entries = [];\n for (var key in map) entries.push({key: key, value: map[key]});\n return entries;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/entries.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: nest, set, map, keys, values, entries */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./nest */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/nest.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return _nest__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _set__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./set */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/set.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return _set__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return _map__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _keys__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./keys */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/keys.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return _keys__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _values__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./values */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/values.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return _values__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _entries__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./entries */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/entries.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return _entries__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/keys.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/keys.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var keys = [];\n for (var key in map) keys.push(key);\n return keys;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/keys.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js ***! + \*******************************************************************************************/ +/*! exports provided: prefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefix\", function() { return prefix; });\nvar prefix = \"$\";\n\nfunction Map() {}\n\nMap.prototype = map.prototype = {\n constructor: Map,\n has: function(key) {\n return (prefix + key) in this;\n },\n get: function(key) {\n return this[prefix + key];\n },\n set: function(key, value) {\n this[prefix + key] = value;\n return this;\n },\n remove: function(key) {\n var property = prefix + key;\n return property in this && delete this[property];\n },\n clear: function() {\n for (var property in this) if (property[0] === prefix) delete this[property];\n },\n keys: function() {\n var keys = [];\n for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));\n return keys;\n },\n values: function() {\n var values = [];\n for (var property in this) if (property[0] === prefix) values.push(this[property]);\n return values;\n },\n entries: function() {\n var entries = [];\n for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});\n return entries;\n },\n size: function() {\n var size = 0;\n for (var property in this) if (property[0] === prefix) ++size;\n return size;\n },\n empty: function() {\n for (var property in this) if (property[0] === prefix) return false;\n return true;\n },\n each: function(f) {\n for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);\n }\n};\n\nfunction map(object, f) {\n var map = new Map;\n\n // Copy constructor.\n if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });\n\n // Index array by numeric index or specified key function.\n else if (Array.isArray(object)) {\n var i = -1,\n n = object.length,\n o;\n\n if (f == null) while (++i < n) map.set(i, object[i]);\n else while (++i < n) map.set(f(o = object[i], i, object), o);\n }\n\n // Convert object to map.\n else if (object) for (var key in object) map.set(key, object[key]);\n\n return map;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (map);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/nest.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/nest.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = [],\n sortKeys = [],\n sortValues,\n rollup,\n nest;\n\n function apply(array, depth, createResult, setResult) {\n if (depth >= keys.length) {\n if (sortValues != null) array.sort(sortValues);\n return rollup != null ? rollup(array) : array;\n }\n\n var i = -1,\n n = array.length,\n key = keys[depth++],\n keyValue,\n value,\n valuesByKey = Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n values,\n result = createResult();\n\n while (++i < n) {\n if (values = valuesByKey.get(keyValue = key(value = array[i]) + \"\")) {\n values.push(value);\n } else {\n valuesByKey.set(keyValue, [value]);\n }\n }\n\n valuesByKey.each(function(values, key) {\n setResult(result, key, apply(values, depth, createResult, setResult));\n });\n\n return result;\n }\n\n function entries(map, depth) {\n if (++depth > keys.length) return map;\n var array, sortKey = sortKeys[depth - 1];\n if (rollup != null && depth >= keys.length) array = map.entries();\n else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });\n return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;\n }\n\n return nest = {\n object: function(array) { return apply(array, 0, createObject, setObject); },\n map: function(array) { return apply(array, 0, createMap, setMap); },\n entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },\n key: function(d) { keys.push(d); return nest; },\n sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },\n sortValues: function(order) { sortValues = order; return nest; },\n rollup: function(f) { rollup = f; return nest; }\n };\n});\n\nfunction createObject() {\n return {};\n}\n\nfunction setObject(object, key, value) {\n object[key] = value;\n}\n\nfunction createMap() {\n return Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n}\n\nfunction setMap(map, key, value) {\n map.set(key, value);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/nest.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/set.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/set.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/map.js\");\n\n\nfunction Set() {}\n\nvar proto = _map__WEBPACK_IMPORTED_MODULE_0__[\"default\"].prototype;\n\nSet.prototype = set.prototype = {\n constructor: Set,\n has: proto.has,\n add: function(value) {\n value += \"\";\n this[_map__WEBPACK_IMPORTED_MODULE_0__[\"prefix\"] + value] = value;\n return this;\n },\n remove: proto.remove,\n clear: proto.clear,\n values: proto.keys,\n size: proto.size,\n empty: proto.empty,\n each: proto.each\n};\n\nfunction set(object, f) {\n var set = new Set;\n\n // Copy constructor.\n if (object instanceof Set) object.each(function(value) { set.add(value); });\n\n // Otherwise, assume it’s an array.\n else if (object) {\n var i = -1, n = object.length;\n if (f == null) while (++i < n) set.add(object[i]);\n else while (++i < n) set.add(f(object[i], i, object));\n }\n\n return set;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (set);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/set.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/values.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/values.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var values = [];\n for (var key in map) values.push(map[key]);\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/values.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/dispatch.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/dispatch.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/index.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/index.js ***! + \*******************************************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/add.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/add.js ***! + \*****************************************************************************************/ +/*! exports provided: default, addAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"addAll\", function() { return addAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n var x = +this._x.call(null, d),\n y = +this._y.call(null, d);\n return add(this.cover(x, y), x, y, d);\n});\n\nfunction add(tree, x, y, d) {\n if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points\n\n var parent,\n node = tree._root,\n leaf = {data: d},\n x0 = tree._x0,\n y0 = tree._y0,\n x1 = tree._x1,\n y1 = tree._y1,\n xm,\n ym,\n xp,\n yp,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return tree._root = leaf, tree;\n\n // Find the existing leaf for the new point, or add it.\n while (node.length) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;\n }\n\n // Is the new point is exactly coincident with the existing point?\n xp = +tree._x.call(null, node.data);\n yp = +tree._y.call(null, node.data);\n if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;\n\n // Otherwise, split the leaf node until the old and new point are separated.\n do {\n parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));\n return parent[j] = node, parent[i] = leaf, tree;\n}\n\nfunction addAll(data) {\n var d, i, n = data.length,\n x,\n y,\n xz = new Array(n),\n yz = new Array(n),\n x0 = Infinity,\n y0 = Infinity,\n x1 = -Infinity,\n y1 = -Infinity;\n\n // Compute the points and their extent.\n for (i = 0; i < n; ++i) {\n if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;\n xz[i] = x;\n yz[i] = y;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n\n // If there were no (valid) points, inherit the existing extent.\n if (x1 < x0) x0 = this._x0, x1 = this._x1;\n if (y1 < y0) y0 = this._y0, y1 = this._y1;\n\n // Expand the tree to cover the new points.\n this.cover(x0, y0).cover(x1, y1);\n\n // Add the new points.\n for (i = 0; i < n; ++i) {\n add(this, xz[i], yz[i], data[i]);\n }\n\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/add.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/cover.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/cover.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points\n\n var x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1;\n\n // If the quadtree has no extent, initialize them.\n // Integer extent are necessary so that if we later double the extent,\n // the existing quadrant boundaries don’t change due to floating point error!\n if (isNaN(x0)) {\n x1 = (x0 = Math.floor(x)) + 1;\n y1 = (y0 = Math.floor(y)) + 1;\n }\n\n // Otherwise, double repeatedly to cover.\n else if (x0 > x || x > x1 || y0 > y || y > y1) {\n var z = x1 - x0,\n node = this._root,\n parent,\n i;\n\n switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {\n case 0: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);\n break;\n }\n case 1: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);\n break;\n }\n case 2: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);\n break;\n }\n case 3: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);\n break;\n }\n }\n\n if (this._root && this._root.length) this._root = node;\n }\n\n // If the quadtree covers the point already, just return.\n else return this;\n\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/cover.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/data.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/data.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var data = [];\n this.visit(function(node) {\n if (!node.length) do data.push(node.data); while (node = node.next)\n });\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/extent.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/extent.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length\n ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])\n : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/find.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/find.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y, radius) {\n var data,\n x0 = this._x0,\n y0 = this._y0,\n x1,\n y1,\n x2,\n y2,\n x3 = this._x1,\n y3 = this._y1,\n quads = [],\n node = this._root,\n q,\n i;\n\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, x0, y0, x3, y3));\n if (radius == null) radius = Infinity;\n else {\n x0 = x - radius, y0 = y - radius;\n x3 = x + radius, y3 = y + radius;\n radius *= radius;\n }\n\n while (q = quads.pop()) {\n\n // Stop searching if this quadrant can’t contain a closer node.\n if (!(node = q.node)\n || (x1 = q.x0) > x3\n || (y1 = q.y0) > y3\n || (x2 = q.x1) < x0\n || (y2 = q.y1) < y0) continue;\n\n // Bisect the current quadrant.\n if (node.length) {\n var xm = (x1 + x2) / 2,\n ym = (y1 + y2) / 2;\n\n quads.push(\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[3], xm, ym, x2, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[2], x1, ym, xm, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[1], xm, y1, x2, ym),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[0], x1, y1, xm, ym)\n );\n\n // Visit the closest quadrant first.\n if (i = (y >= ym) << 1 | (x >= xm)) {\n q = quads[quads.length - 1];\n quads[quads.length - 1] = quads[quads.length - 1 - i];\n quads[quads.length - 1 - i] = q;\n }\n }\n\n // Visit this point. (Visiting coincident points isn’t necessary!)\n else {\n var dx = x - +this._x.call(null, node.data),\n dy = y - +this._y.call(null, node.data),\n d2 = dx * dx + dy * dy;\n if (d2 < radius) {\n var d = Math.sqrt(radius = d2);\n x0 = x - d, y0 = y - d;\n x3 = x + d, y3 = y + d;\n data = node.data;\n }\n }\n }\n\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/find.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/index.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/index.js ***! + \*******************************************************************************************/ +/*! exports provided: quadtree */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quadtree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quadtree */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quadtree.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quadtree\", function() { return _quadtree__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, x0, y0, x1, y1) {\n this.node = node;\n this.x0 = x0;\n this.y0 = y0;\n this.x1 = x1;\n this.y1 = y1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quadtree.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quadtree.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quadtree; });\n/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/add.js\");\n/* harmony import */ var _cover__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cover */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/cover.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/data.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/extent.js\");\n/* harmony import */ var _find__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./find */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/find.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/remove.js\");\n/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./root */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/root.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/size.js\");\n/* harmony import */ var _visit__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./visit */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visit.js\");\n/* harmony import */ var _visitAfter__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./visitAfter */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visitAfter.js\");\n/* harmony import */ var _x__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./x */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/x.js\");\n/* harmony import */ var _y__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./y */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/y.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\nfunction quadtree(nodes, x, y) {\n var tree = new Quadtree(x == null ? _x__WEBPACK_IMPORTED_MODULE_10__[\"defaultX\"] : x, y == null ? _y__WEBPACK_IMPORTED_MODULE_11__[\"defaultY\"] : y, NaN, NaN, NaN, NaN);\n return nodes == null ? tree : tree.addAll(nodes);\n}\n\nfunction Quadtree(x, y, x0, y0, x1, y1) {\n this._x = x;\n this._y = y;\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n this._root = undefined;\n}\n\nfunction leaf_copy(leaf) {\n var copy = {data: leaf.data}, next = copy;\n while (leaf = leaf.next) next = next.next = {data: leaf.data};\n return copy;\n}\n\nvar treeProto = quadtree.prototype = Quadtree.prototype;\n\ntreeProto.copy = function() {\n var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),\n node = this._root,\n nodes,\n child;\n\n if (!node) return copy;\n\n if (!node.length) return copy._root = leaf_copy(node), copy;\n\n nodes = [{source: node, target: copy._root = new Array(4)}];\n while (node = nodes.pop()) {\n for (var i = 0; i < 4; ++i) {\n if (child = node.source[i]) {\n if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});\n else node.target[i] = leaf_copy(child);\n }\n }\n }\n\n return copy;\n};\n\ntreeProto.add = _add__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\ntreeProto.addAll = _add__WEBPACK_IMPORTED_MODULE_0__[\"addAll\"];\ntreeProto.cover = _cover__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\ntreeProto.data = _data__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\ntreeProto.extent = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\ntreeProto.find = _find__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\ntreeProto.remove = _remove__WEBPACK_IMPORTED_MODULE_5__[\"default\"];\ntreeProto.removeAll = _remove__WEBPACK_IMPORTED_MODULE_5__[\"removeAll\"];\ntreeProto.root = _root__WEBPACK_IMPORTED_MODULE_6__[\"default\"];\ntreeProto.size = _size__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\ntreeProto.visit = _visit__WEBPACK_IMPORTED_MODULE_8__[\"default\"];\ntreeProto.visitAfter = _visitAfter__WEBPACK_IMPORTED_MODULE_9__[\"default\"];\ntreeProto.x = _x__WEBPACK_IMPORTED_MODULE_10__[\"default\"];\ntreeProto.y = _y__WEBPACK_IMPORTED_MODULE_11__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quadtree.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/remove.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/remove.js ***! + \********************************************************************************************/ +/*! exports provided: default, removeAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeAll\", function() { return removeAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points\n\n var parent,\n node = this._root,\n retainer,\n previous,\n next,\n x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1,\n x,\n y,\n xm,\n ym,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return this;\n\n // Find the leaf node for the point.\n // While descending, also retain the deepest parent with a non-removed sibling.\n if (node.length) while (true) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (!(parent = node, node = node[i = bottom << 1 | right])) return this;\n if (!node.length) break;\n if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;\n }\n\n // Find the point to remove.\n while (node.data !== d) if (!(previous = node, node = node.next)) return this;\n if (next = node.next) delete node.next;\n\n // If there are multiple coincident points, remove just the point.\n if (previous) return (next ? previous.next = next : delete previous.next), this;\n\n // If this is the root point, remove it.\n if (!parent) return this._root = next, this;\n\n // Remove this leaf.\n next ? parent[i] = next : delete parent[i];\n\n // If the parent now contains exactly one leaf, collapse superfluous parents.\n if ((node = parent[0] || parent[1] || parent[2] || parent[3])\n && node === (parent[3] || parent[2] || parent[1] || parent[0])\n && !node.length) {\n if (retainer) retainer[j] = node;\n else this._root = node;\n }\n\n return this;\n});\n\nfunction removeAll(data) {\n for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/root.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/root.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this._root;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/root.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/size.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/size.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.visit(function(node) {\n if (!node.length) do ++size; while (node = node.next)\n });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visit.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visit.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], q, node = this._root, child, x0, y0, x1, y1;\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {\n var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n }\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visitAfter.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visitAfter.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], next = [], q;\n if (this._root) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this._root, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n var node = q.node;\n if (node.length) {\n var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n }\n next.push(q);\n }\n while (q = next.pop()) {\n callback(q.node, q.x0, q.y0, q.x1, q.y1);\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/visitAfter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/x.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/x.js ***! + \***************************************************************************************/ +/*! exports provided: defaultX, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultX\", function() { return defaultX; });\nfunction defaultX(d) {\n return d[0];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._x = _, this) : this._x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/x.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/y.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/y.js ***! + \***************************************************************************************/ +/*! exports provided: defaultY, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultY\", function() { return defaultY; });\nfunction defaultY(d) {\n return d[1];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._y = _, this) : this._y;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/y.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./timeout */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/interval.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/interval.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timeout.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timeout.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js ***! + \****************************************************************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/center.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/center.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n var nodes;\n\n if (x == null) x = 0;\n if (y == null) y = 0;\n\n function force() {\n var i,\n n = nodes.length,\n node,\n sx = 0,\n sy = 0;\n\n for (i = 0; i < n; ++i) {\n node = nodes[i], sx += node.x, sy += node.y;\n }\n\n for (sx = sx / n - x, sy = sy / n - y, i = 0; i < n; ++i) {\n node = nodes[i], node.x -= sx, node.y -= sy;\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = +_, force) : x;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = +_, force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/center.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/collide.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/collide.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/index.js\");\n\n\n\n\nfunction x(d) {\n return d.x + d.vx;\n}\n\nfunction y(d) {\n return d.y + d.vy;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius) {\n var nodes,\n radii,\n strength = 1,\n iterations = 1;\n\n if (typeof radius !== \"function\") radius = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(radius == null ? 1 : +radius);\n\n function force() {\n var i, n = nodes.length,\n tree,\n node,\n xi,\n yi,\n ri,\n ri2;\n\n for (var k = 0; k < iterations; ++k) {\n tree = Object(d3_quadtree__WEBPACK_IMPORTED_MODULE_2__[\"quadtree\"])(nodes, x, y).visitAfter(prepare);\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n ri = radii[node.index], ri2 = ri * ri;\n xi = node.x + node.vx;\n yi = node.y + node.vy;\n tree.visit(apply);\n }\n }\n\n function apply(quad, x0, y0, x1, y1) {\n var data = quad.data, rj = quad.r, r = ri + rj;\n if (data) {\n if (data.index > node.index) {\n var x = xi - data.x - data.vx,\n y = yi - data.y - data.vy,\n l = x * x + y * y;\n if (l < r * r) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n l = (r - (l = Math.sqrt(l))) / l * strength;\n node.vx += (x *= l) * (r = (rj *= rj) / (ri2 + rj));\n node.vy += (y *= l) * r;\n data.vx -= x * (r = 1 - r);\n data.vy -= y * r;\n }\n }\n return;\n }\n return x0 > xi + r || x1 < xi - r || y0 > yi + r || y1 < yi - r;\n }\n }\n\n function prepare(quad) {\n if (quad.data) return quad.r = radii[quad.data.index];\n for (var i = quad.r = 0; i < 4; ++i) {\n if (quad[i] && quad[i].r > quad.r) {\n quad.r = quad[i].r;\n }\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length, node;\n radii = new Array(n);\n for (i = 0; i < n; ++i) node = nodes[i], radii[node.index] = +radius(node, i, nodes);\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.iterations = function(_) {\n return arguments.length ? (iterations = +_, force) : iterations;\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = +_, force) : strength;\n };\n\n force.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : radius;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/collide.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return (Math.random() - 0.5) * 1e-6;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/link.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/link.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-collection */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/index.js\");\n\n\n\n\nfunction index(d) {\n return d.index;\n}\n\nfunction find(nodeById, nodeId) {\n var node = nodeById.get(nodeId);\n if (!node) throw new Error(\"missing: \" + nodeId);\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(links) {\n var id = index,\n strength = defaultStrength,\n strengths,\n distance = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(30),\n distances,\n nodes,\n count,\n bias,\n iterations = 1;\n\n if (links == null) links = [];\n\n function defaultStrength(link) {\n return 1 / Math.min(count[link.source.index], count[link.target.index]);\n }\n\n function force(alpha) {\n for (var k = 0, n = links.length; k < iterations; ++k) {\n for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) {\n link = links[i], source = link.source, target = link.target;\n x = target.x + target.vx - source.x - source.vx || Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n y = target.y + target.vy - source.y - source.vy || Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])();\n l = Math.sqrt(x * x + y * y);\n l = (l - distances[i]) / l * alpha * strengths[i];\n x *= l, y *= l;\n target.vx -= x * (b = bias[i]);\n target.vy -= y * b;\n source.vx += x * (b = 1 - b);\n source.vy += y * b;\n }\n }\n }\n\n function initialize() {\n if (!nodes) return;\n\n var i,\n n = nodes.length,\n m = links.length,\n nodeById = Object(d3_collection__WEBPACK_IMPORTED_MODULE_2__[\"map\"])(nodes, id),\n link;\n\n for (i = 0, count = new Array(n); i < m; ++i) {\n link = links[i], link.index = i;\n if (typeof link.source !== \"object\") link.source = find(nodeById, link.source);\n if (typeof link.target !== \"object\") link.target = find(nodeById, link.target);\n count[link.source.index] = (count[link.source.index] || 0) + 1;\n count[link.target.index] = (count[link.target.index] || 0) + 1;\n }\n\n for (i = 0, bias = new Array(m); i < m; ++i) {\n link = links[i], bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);\n }\n\n strengths = new Array(m), initializeStrength();\n distances = new Array(m), initializeDistance();\n }\n\n function initializeStrength() {\n if (!nodes) return;\n\n for (var i = 0, n = links.length; i < n; ++i) {\n strengths[i] = +strength(links[i], i, links);\n }\n }\n\n function initializeDistance() {\n if (!nodes) return;\n\n for (var i = 0, n = links.length; i < n; ++i) {\n distances[i] = +distance(links[i], i, links);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.links = function(_) {\n return arguments.length ? (links = _, initialize(), force) : links;\n };\n\n force.id = function(_) {\n return arguments.length ? (id = _, force) : id;\n };\n\n force.iterations = function(_) {\n return arguments.length ? (iterations = +_, force) : iterations;\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initializeStrength(), force) : strength;\n };\n\n force.distance = function(_) {\n return arguments.length ? (distance = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initializeDistance(), force) : distance;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/link.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/manyBody.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/manyBody.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n/* harmony import */ var _jiggle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./jiggle */ \"./node_modules/dagre-d3/node_modules/d3-force/src/jiggle.js\");\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-quadtree/src/index.js\");\n/* harmony import */ var _simulation__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./simulation */ \"./node_modules/dagre-d3/node_modules/d3-force/src/simulation.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes,\n node,\n alpha,\n strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(-30),\n strengths,\n distanceMin2 = 1,\n distanceMax2 = Infinity,\n theta2 = 0.81;\n\n function force(_) {\n var i, n = nodes.length, tree = Object(d3_quadtree__WEBPACK_IMPORTED_MODULE_2__[\"quadtree\"])(nodes, _simulation__WEBPACK_IMPORTED_MODULE_3__[\"x\"], _simulation__WEBPACK_IMPORTED_MODULE_3__[\"y\"]).visitAfter(accumulate);\n for (alpha = _, i = 0; i < n; ++i) node = nodes[i], tree.visit(apply);\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length, node;\n strengths = new Array(n);\n for (i = 0; i < n; ++i) node = nodes[i], strengths[node.index] = +strength(node, i, nodes);\n }\n\n function accumulate(quad) {\n var strength = 0, q, c, weight = 0, x, y, i;\n\n // For internal nodes, accumulate forces from child quadrants.\n if (quad.length) {\n for (x = y = i = 0; i < 4; ++i) {\n if ((q = quad[i]) && (c = Math.abs(q.value))) {\n strength += q.value, weight += c, x += c * q.x, y += c * q.y;\n }\n }\n quad.x = x / weight;\n quad.y = y / weight;\n }\n\n // For leaf nodes, accumulate forces from coincident quadrants.\n else {\n q = quad;\n q.x = q.data.x;\n q.y = q.data.y;\n do strength += strengths[q.data.index];\n while (q = q.next);\n }\n\n quad.value = strength;\n }\n\n function apply(quad, x1, _, x2) {\n if (!quad.value) return true;\n\n var x = quad.x - node.x,\n y = quad.y - node.y,\n w = x2 - x1,\n l = x * x + y * y;\n\n // Apply the Barnes-Hut approximation if possible.\n // Limit forces for very close nodes; randomize direction if coincident.\n if (w * w / theta2 < l) {\n if (l < distanceMax2) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n node.vx += x * quad.value * alpha / l;\n node.vy += y * quad.value * alpha / l;\n }\n return true;\n }\n\n // Otherwise, process points directly.\n else if (quad.length || l >= distanceMax2) return;\n\n // Limit forces for very close nodes; randomize direction if coincident.\n if (quad.data !== node || quad.next) {\n if (x === 0) x = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += x * x;\n if (y === 0) y = Object(_jiggle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), l += y * y;\n if (l < distanceMin2) l = Math.sqrt(distanceMin2 * l);\n }\n\n do if (quad.data !== node) {\n w = strengths[quad.data.index] * alpha / l;\n node.vx += x * w;\n node.vy += y * w;\n } while (quad = quad.next);\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.distanceMin = function(_) {\n return arguments.length ? (distanceMin2 = _ * _, force) : Math.sqrt(distanceMin2);\n };\n\n force.distanceMax = function(_) {\n return arguments.length ? (distanceMax2 = _ * _, force) : Math.sqrt(distanceMax2);\n };\n\n force.theta = function(_) {\n return arguments.length ? (theta2 = _ * _, force) : Math.sqrt(theta2);\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/manyBody.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/radial.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/radial.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius, x, y) {\n var nodes,\n strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n strengths,\n radiuses;\n\n if (typeof radius !== \"function\") radius = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+radius);\n if (x == null) x = 0;\n if (y == null) y = 0;\n\n function force(alpha) {\n for (var i = 0, n = nodes.length; i < n; ++i) {\n var node = nodes[i],\n dx = node.x - x || 1e-6,\n dy = node.y - y || 1e-6,\n r = Math.sqrt(dx * dx + dy * dy),\n k = (radiuses[i] - r) * strengths[i] * alpha / r;\n node.vx += dx * k;\n node.vy += dy * k;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n radiuses = new Array(n);\n for (i = 0; i < n; ++i) {\n radiuses[i] = +radius(nodes[i], i, nodes);\n strengths[i] = isNaN(radiuses[i]) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _, initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : radius;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = +_, force) : x;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = +_, force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/radial.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/simulation.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/simulation.js ***! + \***********************************************************************/ +/*! exports provided: x, y, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-collection */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-collection/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-force/node_modules/d3-timer/src/index.js\");\n\n\n\n\nfunction x(d) {\n return d.x;\n}\n\nfunction y(d) {\n return d.y;\n}\n\nvar initialRadius = 10,\n initialAngle = Math.PI * (3 - Math.sqrt(5));\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(nodes) {\n var simulation,\n alpha = 1,\n alphaMin = 0.001,\n alphaDecay = 1 - Math.pow(alphaMin, 1 / 300),\n alphaTarget = 0,\n velocityDecay = 0.6,\n forces = Object(d3_collection__WEBPACK_IMPORTED_MODULE_1__[\"map\"])(),\n stepper = Object(d3_timer__WEBPACK_IMPORTED_MODULE_2__[\"timer\"])(step),\n event = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"tick\", \"end\");\n\n if (nodes == null) nodes = [];\n\n function step() {\n tick();\n event.call(\"tick\", simulation);\n if (alpha < alphaMin) {\n stepper.stop();\n event.call(\"end\", simulation);\n }\n }\n\n function tick() {\n var i, n = nodes.length, node;\n\n alpha += (alphaTarget - alpha) * alphaDecay;\n\n forces.each(function(force) {\n force(alpha);\n });\n\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n if (node.fx == null) node.x += node.vx *= velocityDecay;\n else node.x = node.fx, node.vx = 0;\n if (node.fy == null) node.y += node.vy *= velocityDecay;\n else node.y = node.fy, node.vy = 0;\n }\n }\n\n function initializeNodes() {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.index = i;\n if (isNaN(node.x) || isNaN(node.y)) {\n var radius = initialRadius * Math.sqrt(i), angle = i * initialAngle;\n node.x = radius * Math.cos(angle);\n node.y = radius * Math.sin(angle);\n }\n if (isNaN(node.vx) || isNaN(node.vy)) {\n node.vx = node.vy = 0;\n }\n }\n }\n\n function initializeForce(force) {\n if (force.initialize) force.initialize(nodes);\n return force;\n }\n\n initializeNodes();\n\n return simulation = {\n tick: tick,\n\n restart: function() {\n return stepper.restart(step), simulation;\n },\n\n stop: function() {\n return stepper.stop(), simulation;\n },\n\n nodes: function(_) {\n return arguments.length ? (nodes = _, initializeNodes(), forces.each(initializeForce), simulation) : nodes;\n },\n\n alpha: function(_) {\n return arguments.length ? (alpha = +_, simulation) : alpha;\n },\n\n alphaMin: function(_) {\n return arguments.length ? (alphaMin = +_, simulation) : alphaMin;\n },\n\n alphaDecay: function(_) {\n return arguments.length ? (alphaDecay = +_, simulation) : +alphaDecay;\n },\n\n alphaTarget: function(_) {\n return arguments.length ? (alphaTarget = +_, simulation) : alphaTarget;\n },\n\n velocityDecay: function(_) {\n return arguments.length ? (velocityDecay = 1 - _, simulation) : 1 - velocityDecay;\n },\n\n force: function(name, _) {\n return arguments.length > 1 ? ((_ == null ? forces.remove(name) : forces.set(name, initializeForce(_))), simulation) : forces.get(name);\n },\n\n find: function(x, y, radius) {\n var i = 0,\n n = nodes.length,\n dx,\n dy,\n d2,\n node,\n closest;\n\n if (radius == null) radius = Infinity;\n else radius *= radius;\n\n for (i = 0; i < n; ++i) {\n node = nodes[i];\n dx = x - node.x;\n dy = y - node.y;\n d2 = dx * dx + dy * dy;\n if (d2 < radius) closest = node, radius = d2;\n }\n\n return closest;\n },\n\n on: function(name, _) {\n return arguments.length > 1 ? (event.on(name, _), simulation) : event.on(name);\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/simulation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/x.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/x.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n var strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n nodes,\n strengths,\n xz;\n\n if (typeof x !== \"function\") x = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x == null ? 0 : +x);\n\n function force(alpha) {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.vx += (xz[i] - node.x) * strengths[i] * alpha;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n xz = new Array(n);\n for (i = 0; i < n; ++i) {\n strengths[i] = isNaN(xz[i] = +x(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : x;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/x.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-force/src/y.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-force/src/y.js ***! + \**************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-force/src/constant.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(y) {\n var strength = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0.1),\n nodes,\n strengths,\n yz;\n\n if (typeof y !== \"function\") y = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(y == null ? 0 : +y);\n\n function force(alpha) {\n for (var i = 0, n = nodes.length, node; i < n; ++i) {\n node = nodes[i], node.vy += (yz[i] - node.y) * strengths[i] * alpha;\n }\n }\n\n function initialize() {\n if (!nodes) return;\n var i, n = nodes.length;\n strengths = new Array(n);\n yz = new Array(n);\n for (i = 0; i < n; ++i) {\n strengths[i] = isNaN(yz[i] = +y(nodes[i], i, nodes)) ? 0 : +strength(nodes[i], i, nodes);\n }\n }\n\n force.initialize = function(_) {\n nodes = _;\n initialize();\n };\n\n force.strength = function(_) {\n return arguments.length ? (strength = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : strength;\n };\n\n force.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), initialize(), force) : y;\n };\n\n return force;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-force/src/y.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/index.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/index.js ***! + \***************************************************************/ +/*! exports provided: formatDefaultLocale, format, formatPrefix, formatLocale, formatSpecifier, precisionFixed, precisionPrefix, precisionRound */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatDefaultLocale\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"format\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"formatPrefix\"]; });\n\n/* harmony import */ var _src_locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/locale */ \"./node_modules/dagre-d3/node_modules/d3-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatLocale\", function() { return _src_locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_formatSpecifier__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/formatSpecifier */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatSpecifier\", function() { return _src_formatSpecifier__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_precisionFixed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/precisionFixed */ \"./node_modules/dagre-d3/node_modules/d3-format/src/precisionFixed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionFixed\", function() { return _src_precisionFixed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_precisionPrefix__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/precisionPrefix */ \"./node_modules/dagre-d3/node_modules/d3-format/src/precisionPrefix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionPrefix\", function() { return _src_precisionPrefix__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_precisionRound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/precisionRound */ \"./node_modules/dagre-d3/node_modules/d3-format/src/precisionRound.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionRound\", function() { return _src_precisionRound__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/defaultLocale.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/defaultLocale.js ***! + \***************************************************************************/ +/*! exports provided: format, formatPrefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return format; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return formatPrefix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-format/src/locale.js\");\n\n\nvar locale;\nvar format;\nvar formatPrefix;\n\ndefaultLocale({\n decimal: \".\",\n thousands: \",\",\n grouping: [3],\n currency: [\"$\", \"\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n format = locale.format;\n formatPrefix = locale.formatPrefix;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(x)), x ? x[1] : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Computes the decimal coefficient and exponent of the specified number x with\n// significant digits p, where x is positive and p is in [1, 21] or undefined.\n// For example, formatDecimal(1.23) returns [\"123\", 0].\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf(\"e\")) < 0) return null; // NaN, ±Infinity\n var i, coefficient = x.slice(0, i);\n\n // The string returned by toExponential either has the form \\d\\.\\d+e[-+]\\d+\n // (e.g., 1.2e+3) or the form \\de[-+]\\d+ (e.g., 1e+3).\n return [\n coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,\n +x.slice(i + 1)\n ];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatDefault.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatDefault.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n x = x.toPrecision(p);\n\n out: for (var n = x.length, i = 1, i0 = -1, i1; i < n; ++i) {\n switch (x[i]) {\n case \".\": i0 = i1 = i; break;\n case \"0\": if (i0 === 0) i0 = i; i1 = i; break;\n case \"e\": break out;\n default: if (i0 > 0) i0 = 0; break;\n }\n }\n\n return i0 > 0 ? x.slice(0, i0) + x.slice(i1 + 1) : x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatDefault.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatGroup.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatGroup.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(grouping, thousands) {\n return function(value, width) {\n var i = value.length,\n t = [],\n j = 0,\n g = grouping[0],\n length = 0;\n\n while (i > 0 && g > 0) {\n if (length + g + 1 > width) g = Math.max(1, width - length);\n t.push(value.substring(i -= g, i + g));\n if ((length += g + 1) > width) break;\n g = grouping[j = (j + 1) % grouping.length];\n }\n\n return t.reverse().join(thousands);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatGroup.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatNumerals.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatNumerals.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(numerals) {\n return function(value) {\n return value.replace(/[0-9]/g, function(i) {\n return numerals[+i];\n });\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatNumerals.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatPrefixAuto.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatPrefixAuto.js ***! + \******************************************************************************/ +/*! exports provided: prefixExponent, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefixExponent\", function() { return prefixExponent; });\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js\");\n\n\nvar prefixExponent;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1],\n i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,\n n = coefficient.length;\n return i === n ? coefficient\n : i > n ? coefficient + new Array(i - n + 1).join(\"0\")\n : i > 0 ? coefficient.slice(0, i) + \".\" + coefficient.slice(i)\n : \"0.\" + new Array(1 - i).join(\"0\") + Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, Math.max(0, p + i - 1))[0]; // less than 1y!\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatPrefixAuto.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatRounded.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatRounded.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1];\n return exponent < 0 ? \"0.\" + new Array(-exponent).join(\"0\") + coefficient\n : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + \".\" + coefficient.slice(exponent + 1)\n : coefficient + new Array(exponent - coefficient.length + 2).join(\"0\");\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatRounded.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatSpecifier.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatSpecifier.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatSpecifier; });\n/* harmony import */ var _formatTypes__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatTypes */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatTypes.js\");\n\n\n// [[fill]align][sign][symbol][0][width][,][.precision][type]\nvar re = /^(?:(.)?([<>=^]))?([+\\-\\( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?([a-z%])?$/i;\n\nfunction formatSpecifier(specifier) {\n return new FormatSpecifier(specifier);\n}\n\nformatSpecifier.prototype = FormatSpecifier.prototype; // instanceof\n\nfunction FormatSpecifier(specifier) {\n if (!(match = re.exec(specifier))) throw new Error(\"invalid format: \" + specifier);\n\n var match,\n fill = match[1] || \" \",\n align = match[2] || \">\",\n sign = match[3] || \"-\",\n symbol = match[4] || \"\",\n zero = !!match[5],\n width = match[6] && +match[6],\n comma = !!match[7],\n precision = match[8] && +match[8].slice(1),\n type = match[9] || \"\";\n\n // The \"n\" type is an alias for \",g\".\n if (type === \"n\") comma = true, type = \"g\";\n\n // Map invalid types to the default format.\n else if (!_formatTypes__WEBPACK_IMPORTED_MODULE_0__[\"default\"][type]) type = \"\";\n\n // If zero fill is specified, padding goes after sign and before digits.\n if (zero || (fill === \"0\" && align === \"=\")) zero = true, fill = \"0\", align = \"=\";\n\n this.fill = fill;\n this.align = align;\n this.sign = sign;\n this.symbol = symbol;\n this.zero = zero;\n this.width = width;\n this.comma = comma;\n this.precision = precision;\n this.type = type;\n}\n\nFormatSpecifier.prototype.toString = function() {\n return this.fill\n + this.align\n + this.sign\n + this.symbol\n + (this.zero ? \"0\" : \"\")\n + (this.width == null ? \"\" : Math.max(1, this.width | 0))\n + (this.comma ? \",\" : \"\")\n + (this.precision == null ? \"\" : \".\" + Math.max(0, this.precision | 0))\n + this.type;\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatSpecifier.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/formatTypes.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/formatTypes.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDefault__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDefault */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatDefault.js\");\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _formatRounded__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatRounded */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatRounded.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n \"\": _formatDefault__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n \"%\": function(x, p) { return (x * 100).toFixed(p); },\n \"b\": function(x) { return Math.round(x).toString(2); },\n \"c\": function(x) { return x + \"\"; },\n \"d\": function(x) { return Math.round(x).toString(10); },\n \"e\": function(x, p) { return x.toExponential(p); },\n \"f\": function(x, p) { return x.toFixed(p); },\n \"g\": function(x, p) { return x.toPrecision(p); },\n \"o\": function(x) { return Math.round(x).toString(8); },\n \"p\": function(x, p) { return Object(_formatRounded__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(x * 100, p); },\n \"r\": _formatRounded__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n \"s\": _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n \"X\": function(x) { return Math.round(x).toString(16).toUpperCase(); },\n \"x\": function(x) { return Math.round(x).toString(16); }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/formatTypes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/identity.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/identity.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/locale.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/locale.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js\");\n/* harmony import */ var _formatGroup__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatGroup */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatGroup.js\");\n/* harmony import */ var _formatNumerals__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatNumerals */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatNumerals.js\");\n/* harmony import */ var _formatSpecifier__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./formatSpecifier */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony import */ var _formatTypes__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./formatTypes */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatTypes.js\");\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/dagre-d3/node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-format/src/identity.js\");\n\n\n\n\n\n\n\n\nvar prefixes = [\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"µ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(locale) {\n var group = locale.grouping && locale.thousands ? Object(_formatGroup__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(locale.grouping, locale.thousands) : _identity__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n currency = locale.currency,\n decimal = locale.decimal,\n numerals = locale.numerals ? Object(_formatNumerals__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(locale.numerals) : _identity__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n percent = locale.percent || \"%\";\n\n function newFormat(specifier) {\n specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier);\n\n var fill = specifier.fill,\n align = specifier.align,\n sign = specifier.sign,\n symbol = specifier.symbol,\n zero = specifier.zero,\n width = specifier.width,\n comma = specifier.comma,\n precision = specifier.precision,\n type = specifier.type;\n\n // Compute the prefix and suffix.\n // For SI-prefix, the suffix is lazily computed.\n var prefix = symbol === \"$\" ? currency[0] : symbol === \"#\" && /[boxX]/.test(type) ? \"0\" + type.toLowerCase() : \"\",\n suffix = symbol === \"$\" ? currency[1] : /[%p]/.test(type) ? percent : \"\";\n\n // What format function should we use?\n // Is this an integer type?\n // Can this type generate exponential notation?\n var formatType = _formatTypes__WEBPACK_IMPORTED_MODULE_4__[\"default\"][type],\n maybeSuffix = !type || /[defgprs%]/.test(type);\n\n // Set the default precision if not specified,\n // or clamp the specified precision to the supported range.\n // For significant precision, it must be in [1, 21].\n // For fixed precision, it must be in [0, 20].\n precision = precision == null ? (type ? 6 : 12)\n : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))\n : Math.max(0, Math.min(20, precision));\n\n function format(value) {\n var valuePrefix = prefix,\n valueSuffix = suffix,\n i, n, c;\n\n if (type === \"c\") {\n valueSuffix = formatType(value) + valueSuffix;\n value = \"\";\n } else {\n value = +value;\n\n // Perform the initial formatting.\n var valueNegative = value < 0;\n value = formatType(Math.abs(value), precision);\n\n // If a negative value rounds to zero during formatting, treat as positive.\n if (valueNegative && +value === 0) valueNegative = false;\n\n // Compute the prefix and suffix.\n valuePrefix = (valueNegative ? (sign === \"(\" ? sign : \"-\") : sign === \"-\" || sign === \"(\" ? \"\" : sign) + valuePrefix;\n valueSuffix = (type === \"s\" ? prefixes[8 + _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_5__[\"prefixExponent\"] / 3] : \"\") + valueSuffix + (valueNegative && sign === \"(\" ? \")\" : \"\");\n\n // Break the formatted value into the integer “value” part that can be\n // grouped, and fractional or exponential “suffix” part that is not.\n if (maybeSuffix) {\n i = -1, n = value.length;\n while (++i < n) {\n if (c = value.charCodeAt(i), 48 > c || c > 57) {\n valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;\n value = value.slice(0, i);\n break;\n }\n }\n }\n }\n\n // If the fill character is not \"0\", grouping is applied before padding.\n if (comma && !zero) value = group(value, Infinity);\n\n // Compute the padding.\n var length = valuePrefix.length + value.length + valueSuffix.length,\n padding = length < width ? new Array(width - length + 1).join(fill) : \"\";\n\n // If the fill character is \"0\", grouping is applied after padding.\n if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = \"\";\n\n // Reconstruct the final output based on the desired alignment.\n switch (align) {\n case \"<\": value = valuePrefix + value + valueSuffix + padding; break;\n case \"=\": value = valuePrefix + padding + value + valueSuffix; break;\n case \"^\": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;\n default: value = padding + valuePrefix + value + valueSuffix; break;\n }\n\n return numerals(value);\n }\n\n format.toString = function() {\n return specifier + \"\";\n };\n\n return format;\n }\n\n function formatPrefix(specifier, value) {\n var f = newFormat((specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier), specifier.type = \"f\", specifier)),\n e = Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3,\n k = Math.pow(10, -e),\n prefix = prefixes[8 + e / 3];\n return function(value) {\n return f(k * value) + prefix;\n };\n }\n\n return {\n format: newFormat,\n formatPrefix: formatPrefix\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/precisionFixed.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/precisionFixed.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step) {\n return Math.max(0, -Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/precisionFixed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/precisionPrefix.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/precisionPrefix.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, value) {\n return Math.max(0, Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3 - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/precisionPrefix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-format/src/precisionRound.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-format/src/precisionRound.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, max) {\n step = Math.abs(step), max = Math.abs(max) - step;\n return Math.max(0, Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(max) - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(step)) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-format/src/precisionRound.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/index.js": +/*!************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/index.js ***! + \************************************************************/ +/*! exports provided: geoArea, geoBounds, geoCentroid, geoCircle, geoClipAntimeridian, geoClipCircle, geoClipExtent, geoClipRectangle, geoContains, geoDistance, geoGraticule, geoGraticule10, geoInterpolate, geoLength, geoPath, geoAlbers, geoAlbersUsa, geoAzimuthalEqualArea, geoAzimuthalEqualAreaRaw, geoAzimuthalEquidistant, geoAzimuthalEquidistantRaw, geoConicConformal, geoConicConformalRaw, geoConicEqualArea, geoConicEqualAreaRaw, geoConicEquidistant, geoConicEquidistantRaw, geoEquirectangular, geoEquirectangularRaw, geoGnomonic, geoGnomonicRaw, geoIdentity, geoProjection, geoProjectionMutator, geoMercator, geoMercatorRaw, geoNaturalEarth1, geoNaturalEarth1Raw, geoOrthographic, geoOrthographicRaw, geoStereographic, geoStereographicRaw, geoTransverseMercator, geoTransverseMercatorRaw, geoRotation, geoStream, geoTransform */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_area__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/area */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoArea\", function() { return _src_area__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_bounds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/bounds */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/bounds.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoBounds\", function() { return _src_bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_centroid__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/centroid */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/centroid.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCentroid\", function() { return _src_centroid__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_circle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/circle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCircle\", function() { return _src_circle__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_clip_antimeridian__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/clip/antimeridian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/antimeridian.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipAntimeridian\", function() { return _src_clip_antimeridian__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_clip_circle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/clip/circle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipCircle\", function() { return _src_clip_circle__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_clip_extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/clip/extent */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipExtent\", function() { return _src_clip_extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_clip_rectangle__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/clip/rectangle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipRectangle\", function() { return _src_clip_rectangle__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_contains__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/contains */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/contains.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoContains\", function() { return _src_contains__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_distance__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/distance */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/distance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoDistance\", function() { return _src_distance__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_graticule__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/graticule */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/graticule.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule\", function() { return _src_graticule__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule10\", function() { return _src_graticule__WEBPACK_IMPORTED_MODULE_10__[\"graticule10\"]; });\n\n/* harmony import */ var _src_interpolate__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/interpolate */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/interpolate.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoInterpolate\", function() { return _src_interpolate__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_length__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/length */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/length.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoLength\", function() { return _src_length__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_path_index__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/path/index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoPath\", function() { return _src_path_index__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_projection_albers__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/projection/albers */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albers.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbers\", function() { return _src_projection_albers__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _src_projection_albersUsa__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/projection/albersUsa */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albersUsa.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbersUsa\", function() { return _src_projection_albersUsa__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _src_projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/projection/azimuthalEqualArea */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEqualArea.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualArea\", function() { return _src_projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualAreaRaw\", function() { return _src_projection_azimuthalEqualArea__WEBPACK_IMPORTED_MODULE_16__[\"azimuthalEqualAreaRaw\"]; });\n\n/* harmony import */ var _src_projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./src/projection/azimuthalEquidistant */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEquidistant.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistant\", function() { return _src_projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistantRaw\", function() { return _src_projection_azimuthalEquidistant__WEBPACK_IMPORTED_MODULE_17__[\"azimuthalEquidistantRaw\"]; });\n\n/* harmony import */ var _src_projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./src/projection/conicConformal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicConformal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformal\", function() { return _src_projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformalRaw\", function() { return _src_projection_conicConformal__WEBPACK_IMPORTED_MODULE_18__[\"conicConformalRaw\"]; });\n\n/* harmony import */ var _src_projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./src/projection/conicEqualArea */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualArea\", function() { return _src_projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualAreaRaw\", function() { return _src_projection_conicEqualArea__WEBPACK_IMPORTED_MODULE_19__[\"conicEqualAreaRaw\"]; });\n\n/* harmony import */ var _src_projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./src/projection/conicEquidistant */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEquidistant.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistant\", function() { return _src_projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistantRaw\", function() { return _src_projection_conicEquidistant__WEBPACK_IMPORTED_MODULE_20__[\"conicEquidistantRaw\"]; });\n\n/* harmony import */ var _src_projection_equirectangular__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./src/projection/equirectangular */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/equirectangular.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangular\", function() { return _src_projection_equirectangular__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangularRaw\", function() { return _src_projection_equirectangular__WEBPACK_IMPORTED_MODULE_21__[\"equirectangularRaw\"]; });\n\n/* harmony import */ var _src_projection_gnomonic__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./src/projection/gnomonic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/gnomonic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonic\", function() { return _src_projection_gnomonic__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonicRaw\", function() { return _src_projection_gnomonic__WEBPACK_IMPORTED_MODULE_22__[\"gnomonicRaw\"]; });\n\n/* harmony import */ var _src_projection_identity__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./src/projection/identity */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/identity.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoIdentity\", function() { return _src_projection_identity__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony import */ var _src_projection_index__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./src/projection/index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjection\", function() { return _src_projection_index__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjectionMutator\", function() { return _src_projection_index__WEBPACK_IMPORTED_MODULE_24__[\"projectionMutator\"]; });\n\n/* harmony import */ var _src_projection_mercator__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./src/projection/mercator */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercator\", function() { return _src_projection_mercator__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercatorRaw\", function() { return _src_projection_mercator__WEBPACK_IMPORTED_MODULE_25__[\"mercatorRaw\"]; });\n\n/* harmony import */ var _src_projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./src/projection/naturalEarth1 */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/naturalEarth1.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1\", function() { return _src_projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1Raw\", function() { return _src_projection_naturalEarth1__WEBPACK_IMPORTED_MODULE_26__[\"naturalEarth1Raw\"]; });\n\n/* harmony import */ var _src_projection_orthographic__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./src/projection/orthographic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/orthographic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographic\", function() { return _src_projection_orthographic__WEBPACK_IMPORTED_MODULE_27__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographicRaw\", function() { return _src_projection_orthographic__WEBPACK_IMPORTED_MODULE_27__[\"orthographicRaw\"]; });\n\n/* harmony import */ var _src_projection_stereographic__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./src/projection/stereographic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/stereographic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographic\", function() { return _src_projection_stereographic__WEBPACK_IMPORTED_MODULE_28__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographicRaw\", function() { return _src_projection_stereographic__WEBPACK_IMPORTED_MODULE_28__[\"stereographicRaw\"]; });\n\n/* harmony import */ var _src_projection_transverseMercator__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./src/projection/transverseMercator */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/transverseMercator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercator\", function() { return _src_projection_transverseMercator__WEBPACK_IMPORTED_MODULE_29__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercatorRaw\", function() { return _src_projection_transverseMercator__WEBPACK_IMPORTED_MODULE_29__[\"transverseMercatorRaw\"]; });\n\n/* harmony import */ var _src_rotation__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./src/rotation */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoRotation\", function() { return _src_rotation__WEBPACK_IMPORTED_MODULE_30__[\"default\"]; });\n\n/* harmony import */ var _src_stream__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./src/stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStream\", function() { return _src_stream__WEBPACK_IMPORTED_MODULE_31__[\"default\"]; });\n\n/* harmony import */ var _src_transform__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./src/transform */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransform\", function() { return _src_transform__WEBPACK_IMPORTED_MODULE_32__[\"default\"]; });\n\n\n\n\n\n\n\n // DEPRECATED! Use d3.geoIdentity().clipExtent(…).\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/array.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/array.js ***! + \**************************************************************************************/ +/*! exports provided: slice, map */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\nvar map = array.map;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisect.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisect.js ***! + \***************************************************************************************/ +/*! exports provided: bisectRight, bisectLeft, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return bisectRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return bisectLeft; });\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisector.js\");\n\n\n\nvar ascendingBisect = Object(_bisector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\nvar bisectRight = ascendingBisect.right;\nvar bisectLeft = ascendingBisect.left;\n/* harmony default export */ __webpack_exports__[\"default\"] = (bisectRight);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisect.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisector.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisector.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n});\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(f(d), x);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/constant.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/constant.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/cross.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/cross.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/pairs.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values0, values1, reduce) {\n var n0 = values0.length,\n n1 = values1.length,\n values = new Array(n0 * n1),\n i0,\n i1,\n i,\n value0;\n\n if (reduce == null) reduce = _pairs__WEBPACK_IMPORTED_MODULE_0__[\"pair\"];\n\n for (i0 = i = 0; i0 < n0; ++i0) {\n for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {\n values[i] = reduce(value0, values1[i1]);\n }\n }\n\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/descending.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/descending.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/deviation.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/deviation.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/variance.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n var v = Object(_variance__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(array, f);\n return v ? Math.sqrt(v) : v;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/deviation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/extent.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/extent.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n return [min, max];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/histogram.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/histogram.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisect.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/constant.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/extent.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/identity.js\");\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/range.js\");\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ticks.js\");\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/sturges.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n domain = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n threshold = _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\n\n function histogram(data) {\n var i,\n n = data.length,\n x,\n values = new Array(n);\n\n for (i = 0; i < n; ++i) {\n values[i] = value(data[i], i, data);\n }\n\n var xz = domain(values),\n x0 = xz[0],\n x1 = xz[1],\n tz = threshold(values, x0, x1);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n tz = Object(_ticks__WEBPACK_IMPORTED_MODULE_6__[\"tickStep\"])(x0, x1, tz);\n tz = Object(_range__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(Math.ceil(x0 / tz) * tz, x1, tz); // exclusive\n }\n\n // Remove any thresholds outside the domain.\n var m = tz.length;\n while (tz[0] <= x0) tz.shift(), --m;\n while (tz[m - 1] > x1) tz.pop(), --m;\n\n var bins = new Array(m + 1),\n bin;\n\n // Initialize bins.\n for (i = 0; i <= m; ++i) {\n bin = bins[i] = [];\n bin.x0 = i > 0 ? tz[i - 1] : x0;\n bin.x1 = i < m ? tz[i] : x1;\n }\n\n // Assign data to bins by value, ignoring any outside the domain.\n for (i = 0; i < n; ++i) {\n x = values[i];\n if (x0 <= x && x <= x1) {\n bins[Object(_bisect__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(tz, x, 0, m)].push(data[i]);\n }\n }\n\n return bins;\n }\n\n histogram.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : value;\n };\n\n histogram.domain = function(_) {\n return arguments.length ? (domain = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])([_[0], _[1]]), histogram) : domain;\n };\n\n histogram.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : threshold;\n };\n\n return histogram;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/histogram.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/identity.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/identity.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js ***! + \**************************************************************************************/ +/*! exports provided: bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisect.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectLeft\"]; });\n\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return _ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/bisector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return _bisector__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./cross */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return _cross__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./descending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return _descending__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./deviation */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/deviation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return _deviation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return _extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _histogram__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./histogram */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/histogram.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return _histogram__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./threshold/freedmanDiaconis */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/freedmanDiaconis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _threshold_scott__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./threshold/scott */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/scott.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return _threshold_scott__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/sturges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _max__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./max */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/max.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return _max__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _mean__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./mean */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/mean.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return _mean__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _median__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./median */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/median.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return _median__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/merge.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return _merge__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/min.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return _min__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/pairs.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return _pairs__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _permute__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./permute */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/permute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return _permute__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return _quantile__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/range.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return _range__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./scan */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/scan.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return _scan__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _shuffle__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./shuffle */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/shuffle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return _shuffle__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./sum */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/sum.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return _sum__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ticks.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickStep\"]; });\n\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/transpose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return _transpose__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/variance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return _variance__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _zip__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./zip */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/zip.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return _zip__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/max.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/max.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n return max;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/max.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/mean.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/mean.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = n,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) sum += value;\n else --m;\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) sum += value;\n else --m;\n }\n }\n\n if (m) return sum / m;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/mean.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/median.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/median.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n numbers = [];\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(values[i]))) {\n numbers.push(value);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(valueof(values[i], i, values)))) {\n numbers.push(value);\n }\n }\n }\n\n return Object(_quantile__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(numbers.sort(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), 0.5);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/median.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/merge.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/merge.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(arrays) {\n var n = arrays.length,\n m,\n i = -1,\n j = 0,\n merged,\n array;\n\n while (++i < n) j += arrays[i].length;\n merged = new Array(j);\n\n while (--n >= 0) {\n array = arrays[n];\n m = array.length;\n while (--m >= 0) {\n merged[--j] = array[m];\n }\n }\n\n return merged;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/min.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/min.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n return min;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/min.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x === null ? NaN : +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/pairs.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/pairs.js ***! + \**************************************************************************************/ +/*! exports provided: default, pair */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pair\", function() { return pair; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n if (f == null) f = pair;\n var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);\n while (i < n) pairs[i] = f(p, p = array[++i]);\n return pairs;\n});\n\nfunction pair(a, b) {\n return [a, b];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/pairs.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/permute.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/permute.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, indexes) {\n var i = indexes.length, permutes = new Array(i);\n while (i--) permutes[i] = array[indexes[i]];\n return permutes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/permute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, p, valueof) {\n if (valueof == null) valueof = _number__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n if (!(n = values.length)) return;\n if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n var n,\n i = (n - 1) * p,\n i0 = Math.floor(i),\n value0 = +valueof(values[i0], i0, values),\n value1 = +valueof(values[i0 + 1], i0 + 1, values);\n return value0 + (value1 - value0) * (i - i0);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/range.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/range.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/range.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/scan.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/scan.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, compare) {\n if (!(n = values.length)) return;\n var n,\n i = 0,\n j = 0,\n xi,\n xj = values[j];\n\n if (compare == null) compare = _ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n\n while (++i < n) {\n if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {\n xj = xi, j = i;\n }\n }\n\n if (compare(xj, xj) === 0) return j;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/scan.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/shuffle.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/shuffle.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, i0, i1) {\n var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m + i0];\n array[m + i0] = array[i + i0];\n array[i + i0] = t;\n }\n\n return array;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/shuffle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/sum.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/sum.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (value = +values[i]) sum += value; // Note: zero and null are equivalent.\n }\n }\n\n else {\n while (++i < n) {\n if (value = +valueof(values[i], i, values)) sum += value;\n }\n }\n\n return sum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/sum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/freedmanDiaconis.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/freedmanDiaconis.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ascending */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../quantile */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n values = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(values, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]).sort(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n return Math.ceil((max - min) / (2 * (Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.75) - Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.25)) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/freedmanDiaconis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/scott.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/scott.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deviation */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/deviation.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n return Math.ceil((max - min) / (3.5 * Object(_deviation__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/scott.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/sturges.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/sturges.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n return Math.ceil(Math.log(values.length) / Math.LN2) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/threshold/sturges.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ticks.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ticks.js ***! + \**************************************************************************************/ +/*! exports provided: default, tickIncrement, tickStep */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return tickIncrement; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return tickStep; });\nvar e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n});\n\nfunction tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nfunction tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/ticks.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/transpose.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/transpose.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/min.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(matrix) {\n if (!(n = matrix.length)) return [];\n for (var i = -1, m = Object(_min__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(matrix, length), transpose = new Array(m); ++i < m;) {\n for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n row[j] = matrix[j][i];\n }\n }\n return transpose;\n});\n\nfunction length(d) {\n return d.length;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/transpose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/variance.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/variance.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = 0,\n i = -1,\n mean = 0,\n value,\n delta,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n if (m > 1) return sum / (m - 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/variance.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/zip.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/zip.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/transpose.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_transpose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(arguments);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/zip.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Adds floating point numbers with twice the normal precision.\n// Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and\n// Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)\n// 305–363 (1997).\n// Code adapted from GeographicLib by Charles F. F. Karney,\n// http://geographiclib.sourceforge.net/\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Adder;\n});\n\nfunction Adder() {\n this.reset();\n}\n\nAdder.prototype = {\n constructor: Adder,\n reset: function() {\n this.s = // rounded value\n this.t = 0; // exact error\n },\n add: function(y) {\n add(temp, y, this.t);\n add(this, temp.s, this.s);\n if (this.s) this.t += temp.t;\n else this.s = temp.t;\n },\n valueOf: function() {\n return this.s;\n }\n};\n\nvar temp = new Adder;\n\nfunction add(adder, a, b) {\n var x = adder.s = a + b,\n bv = x - a,\n av = x - bv;\n adder.t = (a - av) + (b - bv);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/area.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/area.js ***! + \***************************************************************/ +/*! exports provided: areaRingSum, areaStream, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"areaRingSum\", function() { return areaRingSum; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"areaStream\", function() { return areaStream; });\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\nvar areaRingSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n\nvar areaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lambda00,\n phi00,\n lambda0,\n cosPhi0,\n sinPhi0;\n\nvar areaStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: function() {\n areaRingSum.reset();\n areaStream.lineStart = areaRingStart;\n areaStream.lineEnd = areaRingEnd;\n },\n polygonEnd: function() {\n var areaRing = +areaRingSum;\n areaSum.add(areaRing < 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] + areaRing : areaRing);\n this.lineStart = this.lineEnd = this.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n },\n sphere: function() {\n areaSum.add(_math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"]);\n }\n};\n\nfunction areaRingStart() {\n areaStream.point = areaPointFirst;\n}\n\nfunction areaRingEnd() {\n areaPoint(lambda00, phi00);\n}\n\nfunction areaPointFirst(lambda, phi) {\n areaStream.point = areaPoint;\n lambda00 = lambda, phi00 = phi;\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n lambda0 = lambda, cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi = phi / 2 + _math__WEBPACK_IMPORTED_MODULE_1__[\"quarterPi\"]), sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi);\n}\n\nfunction areaPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n phi = phi / 2 + _math__WEBPACK_IMPORTED_MODULE_1__[\"quarterPi\"]; // half the angular distance from south pole\n\n // Spherical excess E for a spherical triangle with vertices: south pole,\n // previous point, current point. Uses a formula derived from Cagnoli’s\n // theorem. See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).\n var dLambda = lambda - lambda0,\n sdLambda = dLambda >= 0 ? 1 : -1,\n adLambda = sdLambda * dLambda,\n cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n sinPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = sinPhi0 * sinPhi,\n u = cosPhi0 * cosPhi + k * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(adLambda),\n v = k * sdLambda * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(adLambda);\n areaRingSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(v, u));\n\n // Advance the previous points.\n lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n areaSum.reset();\n Object(_stream__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(object, areaStream);\n return areaSum * 2;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/bounds.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/bounds.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./area */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/area.js\");\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\n\nvar lambda0, phi0, lambda1, phi1, // bounds\n lambda2, // previous lambda-coordinate\n lambda00, phi00, // first point\n p0, // previous 3D point\n deltaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n ranges,\n range;\n\nvar boundsStream = {\n point: boundsPoint,\n lineStart: boundsLineStart,\n lineEnd: boundsLineEnd,\n polygonStart: function() {\n boundsStream.point = boundsRingPoint;\n boundsStream.lineStart = boundsRingStart;\n boundsStream.lineEnd = boundsRingEnd;\n deltaSum.reset();\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].polygonStart();\n },\n polygonEnd: function() {\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].polygonEnd();\n boundsStream.point = boundsPoint;\n boundsStream.lineStart = boundsLineStart;\n boundsStream.lineEnd = boundsLineEnd;\n if (_area__WEBPACK_IMPORTED_MODULE_1__[\"areaRingSum\"] < 0) lambda0 = -(lambda1 = 180), phi0 = -(phi1 = 90);\n else if (deltaSum > _math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) phi1 = 90;\n else if (deltaSum < -_math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) phi0 = -90;\n range[0] = lambda0, range[1] = lambda1;\n }\n};\n\nfunction boundsPoint(lambda, phi) {\n ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n}\n\nfunction linePoint(lambda, phi) {\n var p = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesian\"])([lambda * _math__WEBPACK_IMPORTED_MODULE_3__[\"radians\"], phi * _math__WEBPACK_IMPORTED_MODULE_3__[\"radians\"]]);\n if (p0) {\n var normal = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianCross\"])(p0, p),\n equatorial = [normal[1], -normal[0], 0],\n inflection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianCross\"])(equatorial, normal);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"cartesianNormalizeInPlace\"])(inflection);\n inflection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_2__[\"spherical\"])(inflection);\n var delta = lambda - lambda2,\n sign = delta > 0 ? 1 : -1,\n lambdai = inflection[0] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"] * sign,\n phii,\n antimeridian = Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(delta) > 180;\n if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {\n phii = inflection[1] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"];\n if (phii > phi1) phi1 = phii;\n } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {\n phii = -inflection[1] * _math__WEBPACK_IMPORTED_MODULE_3__[\"degrees\"];\n if (phii < phi0) phi0 = phii;\n } else {\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n }\n if (antimeridian) {\n if (lambda < lambda2) {\n if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;\n } else {\n if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;\n }\n } else {\n if (lambda1 >= lambda0) {\n if (lambda < lambda0) lambda0 = lambda;\n if (lambda > lambda1) lambda1 = lambda;\n } else {\n if (lambda > lambda2) {\n if (angle(lambda0, lambda) > angle(lambda0, lambda1)) lambda1 = lambda;\n } else {\n if (angle(lambda, lambda1) > angle(lambda0, lambda1)) lambda0 = lambda;\n }\n }\n }\n } else {\n ranges.push(range = [lambda0 = lambda, lambda1 = lambda]);\n }\n if (phi < phi0) phi0 = phi;\n if (phi > phi1) phi1 = phi;\n p0 = p, lambda2 = lambda;\n}\n\nfunction boundsLineStart() {\n boundsStream.point = linePoint;\n}\n\nfunction boundsLineEnd() {\n range[0] = lambda0, range[1] = lambda1;\n boundsStream.point = boundsPoint;\n p0 = null;\n}\n\nfunction boundsRingPoint(lambda, phi) {\n if (p0) {\n var delta = lambda - lambda2;\n deltaSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);\n } else {\n lambda00 = lambda, phi00 = phi;\n }\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].point(lambda, phi);\n linePoint(lambda, phi);\n}\n\nfunction boundsRingStart() {\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].lineStart();\n}\n\nfunction boundsRingEnd() {\n boundsRingPoint(lambda00, phi00);\n _area__WEBPACK_IMPORTED_MODULE_1__[\"areaStream\"].lineEnd();\n if (Object(_math__WEBPACK_IMPORTED_MODULE_3__[\"abs\"])(deltaSum) > _math__WEBPACK_IMPORTED_MODULE_3__[\"epsilon\"]) lambda0 = -(lambda1 = 180);\n range[0] = lambda0, range[1] = lambda1;\n p0 = null;\n}\n\n// Finds the left-right distance between two longitudes.\n// This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want\n// the distance between ±180° to be 360°.\nfunction angle(lambda0, lambda1) {\n return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;\n}\n\nfunction rangeCompare(a, b) {\n return a[0] - b[0];\n}\n\nfunction rangeContains(range, x) {\n return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(feature) {\n var i, n, a, b, merged, deltaMax, delta;\n\n phi1 = lambda1 = -(lambda0 = phi0 = Infinity);\n ranges = [];\n Object(_stream__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(feature, boundsStream);\n\n // First, sort ranges by their minimum longitudes.\n if (n = ranges.length) {\n ranges.sort(rangeCompare);\n\n // Then, merge any ranges that overlap.\n for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {\n b = ranges[i];\n if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {\n if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];\n if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];\n } else {\n merged.push(a = b);\n }\n }\n\n // Finally, find the largest gap between the merged ranges.\n // The final bounding box will be the inverse of this gap.\n for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {\n b = merged[i];\n if ((delta = angle(a[1], b[0])) > deltaMax) deltaMax = delta, lambda0 = b[0], lambda1 = a[1];\n }\n }\n\n ranges = range = null;\n\n return lambda0 === Infinity || phi0 === Infinity\n ? [[NaN, NaN], [NaN, NaN]]\n : [[lambda0, phi0], [lambda1, phi1]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/bounds.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js ***! + \********************************************************************/ +/*! exports provided: spherical, cartesian, cartesianDot, cartesianCross, cartesianAddInPlace, cartesianScale, cartesianNormalizeInPlace */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"spherical\", function() { return spherical; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesian\", function() { return cartesian; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianDot\", function() { return cartesianDot; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianCross\", function() { return cartesianCross; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianAddInPlace\", function() { return cartesianAddInPlace; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianScale\", function() { return cartesianScale; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cartesianNormalizeInPlace\", function() { return cartesianNormalizeInPlace; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\nfunction spherical(cartesian) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(cartesian[1], cartesian[0]), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(cartesian[2])];\n}\n\nfunction cartesian(spherical) {\n var lambda = spherical[0], phi = spherical[1], cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n return [cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda), cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi)];\n}\n\nfunction cartesianDot(a, b) {\n return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n}\n\nfunction cartesianCross(a, b) {\n return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];\n}\n\n// TODO return a\nfunction cartesianAddInPlace(a, b) {\n a[0] += b[0], a[1] += b[1], a[2] += b[2];\n}\n\nfunction cartesianScale(vector, k) {\n return [vector[0] * k, vector[1] * k, vector[2] * k];\n}\n\n// TODO return d\nfunction cartesianNormalizeInPlace(d) {\n var l = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);\n d[0] /= l, d[1] /= l, d[2] /= l;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/centroid.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/centroid.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n\n\n\n\nvar W0, W1,\n X0, Y0, Z0,\n X1, Y1, Z1,\n X2, Y2, Z2,\n lambda00, phi00, // first point\n x0, y0, z0; // previous point\n\nvar centroidStream = {\n sphere: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n point: centroidPoint,\n lineStart: centroidLineStart,\n lineEnd: centroidLineEnd,\n polygonStart: function() {\n centroidStream.lineStart = centroidRingStart;\n centroidStream.lineEnd = centroidRingEnd;\n },\n polygonEnd: function() {\n centroidStream.lineStart = centroidLineStart;\n centroidStream.lineEnd = centroidLineEnd;\n }\n};\n\n// Arithmetic mean of Cartesian vectors.\nfunction centroidPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n centroidPointCartesian(cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda), cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi));\n}\n\nfunction centroidPointCartesian(x, y, z) {\n ++W0;\n X0 += (x - X0) / W0;\n Y0 += (y - Y0) / W0;\n Z0 += (z - Z0) / W0;\n}\n\nfunction centroidLineStart() {\n centroidStream.point = centroidLinePointFirst;\n}\n\nfunction centroidLinePointFirst(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n x0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda);\n y0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda);\n z0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi);\n centroidStream.point = centroidLinePoint;\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidLinePoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi),\n x = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda),\n y = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda),\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi),\n w = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);\n W1 += w;\n X1 += w * (x0 + (x0 = x));\n Y1 += w * (y0 + (y0 = y));\n Z1 += w * (z0 + (z0 = z));\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidLineEnd() {\n centroidStream.point = centroidPoint;\n}\n\n// See J. E. Brock, The Inertia Tensor for a Spherical Triangle,\n// J. Applied Mechanics 42, 239 (1975).\nfunction centroidRingStart() {\n centroidStream.point = centroidRingPointFirst;\n}\n\nfunction centroidRingEnd() {\n centroidRingPoint(lambda00, phi00);\n centroidStream.point = centroidPoint;\n}\n\nfunction centroidRingPointFirst(lambda, phi) {\n lambda00 = lambda, phi00 = phi;\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n centroidStream.point = centroidRingPoint;\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi);\n x0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda);\n y0 = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda);\n z0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi);\n centroidPointCartesian(x0, y0, z0);\n}\n\nfunction centroidRingPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"];\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi),\n x = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(lambda),\n y = cosPhi * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(lambda),\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi),\n cx = y0 * z - z0 * y,\n cy = z0 * x - x0 * z,\n cz = x0 * y - y0 * x,\n m = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(cx * cx + cy * cy + cz * cz),\n w = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(m), // line weight = angle\n v = m && -w / m; // area weight multiplier\n X2 += v * cx;\n Y2 += v * cy;\n Z2 += v * cz;\n W1 += w;\n X1 += w * (x0 + (x0 = x));\n Y1 += w * (y0 + (y0 = y));\n Z1 += w * (z0 + (z0 = z));\n centroidPointCartesian(x0, y0, z0);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n W0 = W1 =\n X0 = Y0 = Z0 =\n X1 = Y1 = Z1 =\n X2 = Y2 = Z2 = 0;\n Object(_stream__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(object, centroidStream);\n\n var x = X2,\n y = Y2,\n z = Z2,\n m = x * x + y * y + z * z;\n\n // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.\n if (m < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon2\"]) {\n x = X1, y = Y1, z = Z1;\n // If the feature has zero length, fall back to arithmetic mean of point vectors.\n if (W1 < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) x = X0, y = Y0, z = Z0;\n m = x * x + y * y + z * z;\n // If the feature still has an undefined ccentroid, then return.\n if (m < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon2\"]) return [NaN, NaN];\n }\n\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(y, x) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(m)) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/circle.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/circle.js ***! + \*****************************************************************/ +/*! exports provided: circleStream, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleStream\", function() { return circleStream; });\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./rotation */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js\");\n\n\n\n\n\n// Generates a circle centered at [0°, 0°], with a given radius and precision.\nfunction circleStream(stream, radius, delta, direction, t0, t1) {\n if (!delta) return;\n var cosRadius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(radius),\n sinRadius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(radius),\n step = direction * delta;\n if (t0 == null) {\n t0 = radius + direction * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n t1 = radius - step / 2;\n } else {\n t0 = circleRadius(cosRadius, t0);\n t1 = circleRadius(cosRadius, t1);\n if (direction > 0 ? t0 < t1 : t0 > t1) t0 += direction * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n }\n for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {\n point = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])([cosRadius, -sinRadius * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(t), -sinRadius * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(t)]);\n stream.point(point[0], point[1]);\n }\n}\n\n// Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].\nfunction circleRadius(cosRadius, point) {\n point = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(point), point[0] -= cosRadius;\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianNormalizeInPlace\"])(point);\n var radius = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"acos\"])(-point[1]);\n return ((-point[2] < 0 ? -radius : radius) + _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) % _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var center = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([0, 0]),\n radius = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(90),\n precision = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(6),\n ring,\n rotate,\n stream = {point: point};\n\n function point(x, y) {\n ring.push(x = rotate(x, y));\n x[0] *= _math__WEBPACK_IMPORTED_MODULE_2__[\"degrees\"], x[1] *= _math__WEBPACK_IMPORTED_MODULE_2__[\"degrees\"];\n }\n\n function circle() {\n var c = center.apply(this, arguments),\n r = radius.apply(this, arguments) * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"],\n p = precision.apply(this, arguments) * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"];\n ring = [];\n rotate = Object(_rotation__WEBPACK_IMPORTED_MODULE_3__[\"rotateRadians\"])(-c[0] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], -c[1] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], 0).invert;\n circleStream(stream, r, p, 1);\n c = {type: \"Polygon\", coordinates: [ring]};\n ring = rotate = null;\n return c;\n }\n\n circle.center = function(_) {\n return arguments.length ? (center = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([+_[0], +_[1]]), circle) : center;\n };\n\n circle.radius = function(_) {\n return arguments.length ? (radius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), circle) : radius;\n };\n\n circle.precision = function(_) {\n return arguments.length ? (precision = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), circle) : precision;\n };\n\n return circle;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/antimeridian.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/antimeridian.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\n function() { return true; },\n clipAntimeridianLine,\n clipAntimeridianInterpolate,\n [-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -_math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"]]\n));\n\n// Takes a line and cuts into visible segments. Return values: 0 - there were\n// intersections or the line was empty; 1 - no intersections; 2 - there were\n// intersections, and the first and last segments should be rejoined.\nfunction clipAntimeridianLine(stream) {\n var lambda0 = NaN,\n phi0 = NaN,\n sign0 = NaN,\n clean; // no intersections\n\n return {\n lineStart: function() {\n stream.lineStart();\n clean = 1;\n },\n point: function(lambda1, phi1) {\n var sign1 = lambda1 > 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"],\n delta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda1 - lambda0);\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(delta - _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"]) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) { // line crosses a pole\n stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? _math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"]);\n stream.point(sign0, phi0);\n stream.lineEnd();\n stream.lineStart();\n stream.point(sign1, phi0);\n stream.point(lambda1, phi0);\n clean = 0;\n } else if (sign0 !== sign1 && delta >= _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"]) { // line crosses antimeridian\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda0 - sign0) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) lambda0 -= sign0 * _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; // handle degeneracies\n if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda1 - sign1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) lambda1 -= sign1 * _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"];\n phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);\n stream.point(sign0, phi0);\n stream.lineEnd();\n stream.lineStart();\n stream.point(sign1, phi0);\n clean = 0;\n }\n stream.point(lambda0 = lambda1, phi0 = phi1);\n sign0 = sign1;\n },\n lineEnd: function() {\n stream.lineEnd();\n lambda0 = phi0 = NaN;\n },\n clean: function() {\n return 2 - clean; // if intersections, rejoin first and last segments\n }\n };\n}\n\nfunction clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {\n var cosPhi0,\n cosPhi1,\n sinLambda0Lambda1 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda0 - lambda1);\n return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(sinLambda0Lambda1) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]\n ? Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan\"])((Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi0) * (cosPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi1)) * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda1)\n - Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi1) * (cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi0)) * Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda0))\n / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))\n : (phi0 + phi1) / 2;\n}\n\nfunction clipAntimeridianInterpolate(from, to, direction, stream) {\n var phi;\n if (from == null) {\n phi = direction * _math__WEBPACK_IMPORTED_MODULE_1__[\"halfPi\"];\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n stream.point(0, phi);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], 0);\n stream.point(_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -phi);\n stream.point(0, -phi);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], -phi);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], 0);\n stream.point(-_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"], phi);\n } else if (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(from[0] - to[0]) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) {\n var lambda = from[0] < to[0] ? _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"];\n phi = direction * lambda / 2;\n stream.point(-lambda, phi);\n stream.point(0, phi);\n stream.point(lambda, phi);\n } else {\n stream.point(to[0], to[1]);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/antimeridian.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/buffer.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/buffer.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var lines = [],\n line;\n return {\n point: function(x, y) {\n line.push([x, y]);\n },\n lineStart: function() {\n lines.push(line = []);\n },\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n rejoin: function() {\n if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));\n },\n result: function() {\n var result = lines;\n lines = [];\n line = null;\n return result;\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/buffer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/circle.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/circle.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../cartesian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../circle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/circle.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _pointEqual__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../pointEqual */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/pointEqual.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/index.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(radius) {\n var cr = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(radius),\n delta = 6 * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"],\n smallRadius = cr > 0,\n notHemisphere = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(cr) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]; // TODO optimise for this common case\n\n function interpolate(from, to, direction, stream) {\n Object(_circle__WEBPACK_IMPORTED_MODULE_1__[\"circleStream\"])(stream, radius, delta, direction, from, to);\n }\n\n function visible(lambda, phi) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(lambda) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi) > cr;\n }\n\n // Takes a line and cuts into visible segments. Return values used for polygon\n // clipping: 0 - there were intersections or the line was empty; 1 - no\n // intersections 2 - there were intersections, and the first and last segments\n // should be rejoined.\n function clipLine(stream) {\n var point0, // previous point\n c0, // code for previous point\n v0, // visibility of previous point\n v00, // visibility of first point\n clean; // no intersections\n return {\n lineStart: function() {\n v00 = v0 = false;\n clean = 1;\n },\n point: function(lambda, phi) {\n var point1 = [lambda, phi],\n point2,\n v = visible(lambda, phi),\n c = smallRadius\n ? v ? 0 : code(lambda, phi)\n : v ? code(lambda + (lambda < 0 ? _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] : -_math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]), phi) : 0;\n if (!point0 && (v00 = v0 = v)) stream.lineStart();\n // Handle degeneracies.\n // TODO ignore if not clipping polygons.\n if (v !== v0) {\n point2 = intersect(point0, point1);\n if (!point2 || Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point0, point2) || Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point1, point2)) {\n point1[0] += _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n point1[1] += _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n v = visible(point1[0], point1[1]);\n }\n }\n if (v !== v0) {\n clean = 0;\n if (v) {\n // outside going in\n stream.lineStart();\n point2 = intersect(point1, point0);\n stream.point(point2[0], point2[1]);\n } else {\n // inside going out\n point2 = intersect(point0, point1);\n stream.point(point2[0], point2[1]);\n stream.lineEnd();\n }\n point0 = point2;\n } else if (notHemisphere && point0 && smallRadius ^ v) {\n var t;\n // If the codes for two points are different, or are both zero,\n // and there this segment intersects with the small circle.\n if (!(c & c0) && (t = intersect(point1, point0, true))) {\n clean = 0;\n if (smallRadius) {\n stream.lineStart();\n stream.point(t[0][0], t[0][1]);\n stream.point(t[1][0], t[1][1]);\n stream.lineEnd();\n } else {\n stream.point(t[1][0], t[1][1]);\n stream.lineEnd();\n stream.lineStart();\n stream.point(t[0][0], t[0][1]);\n }\n }\n }\n if (v && (!point0 || !Object(_pointEqual__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(point0, point1))) {\n stream.point(point1[0], point1[1]);\n }\n point0 = point1, v0 = v, c0 = c;\n },\n lineEnd: function() {\n if (v0) stream.lineEnd();\n point0 = null;\n },\n // Rejoin first and last segments if there were intersections and the first\n // and last points were visible.\n clean: function() {\n return clean | ((v00 && v0) << 1);\n }\n };\n }\n\n // Intersects the great circle between a and b with the clip circle.\n function intersect(a, b, two) {\n var pa = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(a),\n pb = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])(b);\n\n // We have two planes, n1.p = d1 and n2.p = d2.\n // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).\n var n1 = [1, 0, 0], // normal\n n2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianCross\"])(pa, pb),\n n2n2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(n2, n2),\n n1n2 = n2[0], // cartesianDot(n1, n2),\n determinant = n2n2 - n1n2 * n1n2;\n\n // Two polar points.\n if (!determinant) return !two && a;\n\n var c1 = cr * n2n2 / determinant,\n c2 = -cr * n1n2 / determinant,\n n1xn2 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianCross\"])(n1, n2),\n A = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(n1, c1),\n B = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(n2, c2);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(A, B);\n\n // Solve |p(t)|^2 = 1.\n var u = n1xn2,\n w = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(A, u),\n uu = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(u, u),\n t2 = w * w - uu * (Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianDot\"])(A, A) - 1);\n\n if (t2 < 0) return;\n\n var t = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(t2),\n q = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(u, (-w - t) / uu);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(q, A);\n q = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])(q);\n\n if (!two) return q;\n\n // Two intersection points.\n var lambda0 = a[0],\n lambda1 = b[0],\n phi0 = a[1],\n phi1 = b[1],\n z;\n\n if (lambda1 < lambda0) z = lambda0, lambda0 = lambda1, lambda1 = z;\n\n var delta = lambda1 - lambda0,\n polar = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(delta - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]) < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"],\n meridian = polar || delta < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n\n if (!polar && phi1 < phi0) z = phi0, phi0 = phi1, phi1 = z;\n\n // Check that the first point is between a and b.\n if (meridian\n ? polar\n ? phi0 + phi1 > 0 ^ q[1] < (Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(q[0] - lambda0) < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] ? phi0 : phi1)\n : phi0 <= q[1] && q[1] <= phi1\n : delta > _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] ^ (lambda0 <= q[0] && q[0] <= lambda1)) {\n var q1 = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianScale\"])(u, (-w + t) / uu);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesianAddInPlace\"])(q1, A);\n return [q, Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"spherical\"])(q1)];\n }\n }\n\n // Generates a 4-bit vector representing the location of a point relative to\n // the small circle's bounding box.\n function code(lambda, phi) {\n var r = smallRadius ? radius : _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] - radius,\n code = 0;\n if (lambda < -r) code |= 1; // left\n else if (lambda > r) code |= 2; // right\n if (phi < -r) code |= 4; // below\n else if (phi > r) code |= 8; // above\n return code;\n }\n\n return Object(_index__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-_math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"], radius - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/extent.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/extent.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rectangle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x0 = 0,\n y0 = 0,\n x1 = 960,\n y1 = 500,\n cache,\n cacheStream,\n clip;\n\n return clip = {\n stream: function(stream) {\n return cache && cacheStream === stream ? cache : cache = Object(_rectangle__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x0, y0, x1, y1)(cacheStream = stream);\n },\n extent: function(_) {\n return arguments.length ? (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1], cache = cacheStream = null, clip) : [[x0, y0], [x1, y1]];\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/index.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/index.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./buffer */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/buffer.js\");\n/* harmony import */ var _rejoin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rejoin */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rejoin.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _polygonContains__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../polygonContains */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/polygonContains.js\");\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(pointVisible, clipLine, interpolate, start) {\n return function(sink) {\n var line = clipLine(sink),\n ringBuffer = Object(_buffer__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n ringSink = clipLine(ringBuffer),\n polygonStarted = false,\n polygon,\n segments,\n ring;\n\n var clip = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: function() {\n clip.point = pointRing;\n clip.lineStart = ringStart;\n clip.lineEnd = ringEnd;\n segments = [];\n polygon = [];\n },\n polygonEnd: function() {\n clip.point = point;\n clip.lineStart = lineStart;\n clip.lineEnd = lineEnd;\n segments = Object(d3_array__WEBPACK_IMPORTED_MODULE_4__[\"merge\"])(segments);\n var startInside = Object(_polygonContains__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(polygon, start);\n if (segments.length) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n Object(_rejoin__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(segments, compareIntersection, startInside, interpolate, sink);\n } else if (startInside) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n sink.lineStart();\n interpolate(null, null, 1, sink);\n sink.lineEnd();\n }\n if (polygonStarted) sink.polygonEnd(), polygonStarted = false;\n segments = polygon = null;\n },\n sphere: function() {\n sink.polygonStart();\n sink.lineStart();\n interpolate(null, null, 1, sink);\n sink.lineEnd();\n sink.polygonEnd();\n }\n };\n\n function point(lambda, phi) {\n if (pointVisible(lambda, phi)) sink.point(lambda, phi);\n }\n\n function pointLine(lambda, phi) {\n line.point(lambda, phi);\n }\n\n function lineStart() {\n clip.point = pointLine;\n line.lineStart();\n }\n\n function lineEnd() {\n clip.point = point;\n line.lineEnd();\n }\n\n function pointRing(lambda, phi) {\n ring.push([lambda, phi]);\n ringSink.point(lambda, phi);\n }\n\n function ringStart() {\n ringSink.lineStart();\n ring = [];\n }\n\n function ringEnd() {\n pointRing(ring[0][0], ring[0][1]);\n ringSink.lineEnd();\n\n var clean = ringSink.clean(),\n ringSegments = ringBuffer.result(),\n i, n = ringSegments.length, m,\n segment,\n point;\n\n ring.pop();\n polygon.push(ring);\n ring = null;\n\n if (!n) return;\n\n // No intersections.\n if (clean & 1) {\n segment = ringSegments[0];\n if ((m = segment.length - 1) > 0) {\n if (!polygonStarted) sink.polygonStart(), polygonStarted = true;\n sink.lineStart();\n for (i = 0; i < m; ++i) sink.point((point = segment[i])[0], point[1]);\n sink.lineEnd();\n }\n return;\n }\n\n // Rejoin connected segments.\n // TODO reuse ringBuffer.rejoin()?\n if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));\n\n segments.push(ringSegments.filter(validSegment));\n }\n\n return clip;\n };\n});\n\nfunction validSegment(segment) {\n return segment.length > 1;\n}\n\n// Intersections are sorted along the clip edge. For both antimeridian cutting\n// and circle clipping, the same comparison is used.\nfunction compareIntersection(a, b) {\n return ((a = a.x)[0] < 0 ? a[1] - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] : _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - a[1])\n - ((b = b.x)[0] < 0 ? b[1] - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] : _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"] - b[1]);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/line.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/line.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, x0, y0, x1, y1) {\n var ax = a[0],\n ay = a[1],\n bx = b[0],\n by = b[1],\n t0 = 0,\n t1 = 1,\n dx = bx - ax,\n dy = by - ay,\n r;\n\n r = x0 - ax;\n if (!dx && r > 0) return;\n r /= dx;\n if (dx < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dx > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = x1 - ax;\n if (!dx && r < 0) return;\n r /= dx;\n if (dx < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dx > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n r = y0 - ay;\n if (!dy && r > 0) return;\n r /= dy;\n if (dy < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dy > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = y1 - ay;\n if (!dy && r < 0) return;\n r /= dy;\n if (dy < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dy > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n if (t0 > 0) a[0] = ax + t0 * dx, a[1] = ay + t0 * dy;\n if (t1 < 1) b[0] = ax + t1 * dx, b[1] = ay + t1 * dy;\n return true;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/line.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return clipRectangle; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _buffer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./buffer */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/buffer.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./line */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/line.js\");\n/* harmony import */ var _rejoin__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./rejoin */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rejoin.js\");\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js\");\n\n\n\n\n\n\nvar clipMax = 1e9, clipMin = -clipMax;\n\n// TODO Use d3-polygon’s polygonContains here for the ring check?\n// TODO Eliminate duplicate buffering in clipBuffer and polygon.push?\n\nfunction clipRectangle(x0, y0, x1, y1) {\n\n function visible(x, y) {\n return x0 <= x && x <= x1 && y0 <= y && y <= y1;\n }\n\n function interpolate(from, to, direction, stream) {\n var a = 0, a1 = 0;\n if (from == null\n || (a = corner(from, direction)) !== (a1 = corner(to, direction))\n || comparePoint(from, to) < 0 ^ direction > 0) {\n do stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);\n while ((a = (a + direction + 4) % 4) !== a1);\n } else {\n stream.point(to[0], to[1]);\n }\n }\n\n function corner(p, direction) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[0] - x0) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 0 : 3\n : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[0] - x1) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 2 : 1\n : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(p[1] - y0) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] ? direction > 0 ? 1 : 0\n : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon\n }\n\n function compareIntersection(a, b) {\n return comparePoint(a.x, b.x);\n }\n\n function comparePoint(a, b) {\n var ca = corner(a, 1),\n cb = corner(b, 1);\n return ca !== cb ? ca - cb\n : ca === 0 ? b[1] - a[1]\n : ca === 1 ? a[0] - b[0]\n : ca === 2 ? a[1] - b[1]\n : b[0] - a[0];\n }\n\n return function(stream) {\n var activeStream = stream,\n bufferStream = Object(_buffer__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(),\n segments,\n polygon,\n ring,\n x__, y__, v__, // first point\n x_, y_, v_, // previous point\n first,\n clean;\n\n var clipStream = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: polygonStart,\n polygonEnd: polygonEnd\n };\n\n function point(x, y) {\n if (visible(x, y)) activeStream.point(x, y);\n }\n\n function polygonInside() {\n var winding = 0;\n\n for (var i = 0, n = polygon.length; i < n; ++i) {\n for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {\n a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];\n if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) ++winding; }\n else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) --winding; }\n }\n }\n\n return winding;\n }\n\n // Buffer geometry within a polygon and then clip it en masse.\n function polygonStart() {\n activeStream = bufferStream, segments = [], polygon = [], clean = true;\n }\n\n function polygonEnd() {\n var startInside = polygonInside(),\n cleanInside = clean && startInside,\n visible = (segments = Object(d3_array__WEBPACK_IMPORTED_MODULE_4__[\"merge\"])(segments)).length;\n if (cleanInside || visible) {\n stream.polygonStart();\n if (cleanInside) {\n stream.lineStart();\n interpolate(null, null, 1, stream);\n stream.lineEnd();\n }\n if (visible) {\n Object(_rejoin__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(segments, compareIntersection, startInside, interpolate, stream);\n }\n stream.polygonEnd();\n }\n activeStream = stream, segments = polygon = ring = null;\n }\n\n function lineStart() {\n clipStream.point = linePoint;\n if (polygon) polygon.push(ring = []);\n first = true;\n v_ = false;\n x_ = y_ = NaN;\n }\n\n // TODO rather than special-case polygons, simply handle them separately.\n // Ideally, coincident intersection points should be jittered to avoid\n // clipping issues.\n function lineEnd() {\n if (segments) {\n linePoint(x__, y__);\n if (v__ && v_) bufferStream.rejoin();\n segments.push(bufferStream.result());\n }\n clipStream.point = point;\n if (v_) activeStream.lineEnd();\n }\n\n function linePoint(x, y) {\n var v = visible(x, y);\n if (polygon) ring.push([x, y]);\n if (first) {\n x__ = x, y__ = y, v__ = v;\n first = false;\n if (v) {\n activeStream.lineStart();\n activeStream.point(x, y);\n }\n } else {\n if (v && v_) activeStream.point(x, y);\n else {\n var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],\n b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];\n if (Object(_line__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(a, b, x0, y0, x1, y1)) {\n if (!v_) {\n activeStream.lineStart();\n activeStream.point(a[0], a[1]);\n }\n activeStream.point(b[0], b[1]);\n if (!v) activeStream.lineEnd();\n clean = false;\n } else if (v) {\n activeStream.lineStart();\n activeStream.point(x, y);\n clean = false;\n }\n }\n }\n x_ = x, y_ = y, v_ = v;\n }\n\n return clipStream;\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rejoin.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rejoin.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pointEqual__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../pointEqual */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/pointEqual.js\");\n\n\nfunction Intersection(point, points, other, entry) {\n this.x = point;\n this.z = points;\n this.o = other; // another intersection\n this.e = entry; // is an entry?\n this.v = false; // visited\n this.n = this.p = null; // next & previous\n}\n\n// A generalized polygon clipping algorithm: given a polygon that has been cut\n// into its visible line segments, and rejoins the segments by interpolating\n// along the clip edge.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(segments, compareIntersection, startInside, interpolate, stream) {\n var subject = [],\n clip = [],\n i,\n n;\n\n segments.forEach(function(segment) {\n if ((n = segment.length - 1) <= 0) return;\n var n, p0 = segment[0], p1 = segment[n], x;\n\n // If the first and last points of a segment are coincident, then treat as a\n // closed ring. TODO if all rings are closed, then the winding order of the\n // exterior ring should be checked.\n if (Object(_pointEqual__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(p0, p1)) {\n stream.lineStart();\n for (i = 0; i < n; ++i) stream.point((p0 = segment[i])[0], p0[1]);\n stream.lineEnd();\n return;\n }\n\n subject.push(x = new Intersection(p0, segment, null, true));\n clip.push(x.o = new Intersection(p0, null, x, false));\n subject.push(x = new Intersection(p1, segment, null, false));\n clip.push(x.o = new Intersection(p1, null, x, true));\n });\n\n if (!subject.length) return;\n\n clip.sort(compareIntersection);\n link(subject);\n link(clip);\n\n for (i = 0, n = clip.length; i < n; ++i) {\n clip[i].e = startInside = !startInside;\n }\n\n var start = subject[0],\n points,\n point;\n\n while (1) {\n // Find first unvisited intersection.\n var current = start,\n isSubject = true;\n while (current.v) if ((current = current.n) === start) return;\n points = current.z;\n stream.lineStart();\n do {\n current.v = current.o.v = true;\n if (current.e) {\n if (isSubject) {\n for (i = 0, n = points.length; i < n; ++i) stream.point((point = points[i])[0], point[1]);\n } else {\n interpolate(current.x, current.n.x, 1, stream);\n }\n current = current.n;\n } else {\n if (isSubject) {\n points = current.p.z;\n for (i = points.length - 1; i >= 0; --i) stream.point((point = points[i])[0], point[1]);\n } else {\n interpolate(current.x, current.p.x, -1, stream);\n }\n current = current.p;\n }\n current = current.o;\n points = current.z;\n isSubject = !isSubject;\n } while (!current.v);\n stream.lineEnd();\n }\n});\n\nfunction link(array) {\n if (!(n = array.length)) return;\n var n,\n i = 0,\n a = array[0],\n b;\n while (++i < n) {\n a.n = b = array[i];\n b.p = a;\n a = b;\n }\n a.n = b = array[0];\n b.p = a;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rejoin.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/compose.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/compose.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n\n function compose(x, y) {\n return x = a(x, y), b(x[0], x[1]);\n }\n\n if (a.invert && b.invert) compose.invert = function(x, y) {\n return x = b.invert(x, y), x && a.invert(x[0], x[1]);\n };\n\n return compose;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/compose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/constant.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/constant.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/contains.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/contains.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _polygonContains__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./polygonContains */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/polygonContains.js\");\n/* harmony import */ var _distance__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./distance */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/distance.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\n\nvar containsObjectType = {\n Feature: function(object, point) {\n return containsGeometry(object.geometry, point);\n },\n FeatureCollection: function(object, point) {\n var features = object.features, i = -1, n = features.length;\n while (++i < n) if (containsGeometry(features[i].geometry, point)) return true;\n return false;\n }\n};\n\nvar containsGeometryType = {\n Sphere: function() {\n return true;\n },\n Point: function(object, point) {\n return containsPoint(object.coordinates, point);\n },\n MultiPoint: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsPoint(coordinates[i], point)) return true;\n return false;\n },\n LineString: function(object, point) {\n return containsLine(object.coordinates, point);\n },\n MultiLineString: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsLine(coordinates[i], point)) return true;\n return false;\n },\n Polygon: function(object, point) {\n return containsPolygon(object.coordinates, point);\n },\n MultiPolygon: function(object, point) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) if (containsPolygon(coordinates[i], point)) return true;\n return false;\n },\n GeometryCollection: function(object, point) {\n var geometries = object.geometries, i = -1, n = geometries.length;\n while (++i < n) if (containsGeometry(geometries[i], point)) return true;\n return false;\n }\n};\n\nfunction containsGeometry(geometry, point) {\n return geometry && containsGeometryType.hasOwnProperty(geometry.type)\n ? containsGeometryType[geometry.type](geometry, point)\n : false;\n}\n\nfunction containsPoint(coordinates, point) {\n return Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates, point) === 0;\n}\n\nfunction containsLine(coordinates, point) {\n var ab = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates[0], coordinates[1]),\n ao = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(coordinates[0], point),\n ob = Object(_distance__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(point, coordinates[1]);\n return ao + ob <= ab + _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"];\n}\n\nfunction containsPolygon(coordinates, point) {\n return !!Object(_polygonContains__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(coordinates.map(ringRadians), pointRadians(point));\n}\n\nfunction ringRadians(ring) {\n return ring = ring.map(pointRadians), ring.pop(), ring;\n}\n\nfunction pointRadians(point) {\n return [point[0] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_2__[\"radians\"]];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object, point) {\n return (object && containsObjectType.hasOwnProperty(object.type)\n ? containsObjectType[object.type]\n : containsGeometry)(object, point);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/contains.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/distance.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/distance.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _length__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./length */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/length.js\");\n\n\nvar coordinates = [null, null],\n object = {type: \"LineString\", coordinates: coordinates};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n coordinates[0] = a;\n coordinates[1] = b;\n return Object(_length__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(object);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/distance.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/graticule.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/graticule.js ***! + \********************************************************************/ +/*! exports provided: default, graticule10 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return graticule; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"graticule10\", function() { return graticule10; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-geo/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\nfunction graticuleX(y0, y1, dy) {\n var y = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(y0, y1 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"], dy).concat(y1);\n return function(x) { return y.map(function(y) { return [x, y]; }); };\n}\n\nfunction graticuleY(x0, x1, dx) {\n var x = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(x0, x1 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"], dx).concat(x1);\n return function(y) { return x.map(function(x) { return [x, y]; }); };\n}\n\nfunction graticule() {\n var x1, x0, X1, X0,\n y1, y0, Y1, Y0,\n dx = 10, dy = dx, DX = 90, DY = 360,\n x, y, X, Y,\n precision = 2.5;\n\n function graticule() {\n return {type: \"MultiLineString\", coordinates: lines()};\n }\n\n function lines() {\n return Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(X0 / DX) * DX, X1, DX).map(X)\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(Y0 / DY) * DY, Y1, DY).map(Y))\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(x0 / dx) * dx, x1, dx).filter(function(x) { return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(x % DX) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; }).map(x))\n .concat(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"ceil\"])(y0 / dy) * dy, y1, dy).filter(function(y) { return Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(y % DY) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]; }).map(y));\n }\n\n graticule.lines = function() {\n return lines().map(function(coordinates) { return {type: \"LineString\", coordinates: coordinates}; });\n };\n\n graticule.outline = function() {\n return {\n type: \"Polygon\",\n coordinates: [\n X(X0).concat(\n Y(Y1).slice(1),\n X(X1).reverse().slice(1),\n Y(Y0).reverse().slice(1))\n ]\n };\n };\n\n graticule.extent = function(_) {\n if (!arguments.length) return graticule.extentMinor();\n return graticule.extentMajor(_).extentMinor(_);\n };\n\n graticule.extentMajor = function(_) {\n if (!arguments.length) return [[X0, Y0], [X1, Y1]];\n X0 = +_[0][0], X1 = +_[1][0];\n Y0 = +_[0][1], Y1 = +_[1][1];\n if (X0 > X1) _ = X0, X0 = X1, X1 = _;\n if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;\n return graticule.precision(precision);\n };\n\n graticule.extentMinor = function(_) {\n if (!arguments.length) return [[x0, y0], [x1, y1]];\n x0 = +_[0][0], x1 = +_[1][0];\n y0 = +_[0][1], y1 = +_[1][1];\n if (x0 > x1) _ = x0, x0 = x1, x1 = _;\n if (y0 > y1) _ = y0, y0 = y1, y1 = _;\n return graticule.precision(precision);\n };\n\n graticule.step = function(_) {\n if (!arguments.length) return graticule.stepMinor();\n return graticule.stepMajor(_).stepMinor(_);\n };\n\n graticule.stepMajor = function(_) {\n if (!arguments.length) return [DX, DY];\n DX = +_[0], DY = +_[1];\n return graticule;\n };\n\n graticule.stepMinor = function(_) {\n if (!arguments.length) return [dx, dy];\n dx = +_[0], dy = +_[1];\n return graticule;\n };\n\n graticule.precision = function(_) {\n if (!arguments.length) return precision;\n precision = +_;\n x = graticuleX(y0, y1, 90);\n y = graticuleY(x0, x1, precision);\n X = graticuleX(Y0, Y1, 90);\n Y = graticuleY(X0, X1, precision);\n return graticule;\n };\n\n return graticule\n .extentMajor([[-180, -90 + _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]], [180, 90 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]]])\n .extentMinor([[-180, -80 - _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]], [180, 80 + _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]]]);\n}\n\nfunction graticule10() {\n return graticule()();\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/graticule.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/interpolate.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/interpolate.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var x0 = a[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n y0 = a[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n x1 = b[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n y1 = b[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"],\n cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n sy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0),\n cy1 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1),\n sy1 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y1),\n kx0 = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x0),\n ky0 = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x0),\n kx1 = cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x1),\n ky1 = cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x1),\n d = 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"haversin\"])(y1 - y0) + cy0 * cy1 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"haversin\"])(x1 - x0))),\n k = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(d);\n\n var interpolate = d ? function(t) {\n var B = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(t *= d) / k,\n A = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(d - t) / k,\n x = A * kx0 + B * kx1,\n y = A * ky0 + B * ky1,\n z = A * sy0 + B * sy1;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(y, x) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"],\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(z, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + y * y)) * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]\n ];\n } : function() {\n return [x0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], y0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n };\n\n interpolate.distance = d;\n\n return interpolate;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/length.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/length.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n\n\n\n\n\nvar lengthSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lambda0,\n sinPhi0,\n cosPhi0;\n\nvar lengthStream = {\n sphere: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: lengthLineStart,\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n};\n\nfunction lengthLineStart() {\n lengthStream.point = lengthPointFirst;\n lengthStream.lineEnd = lengthLineEnd;\n}\n\nfunction lengthLineEnd() {\n lengthStream.point = lengthStream.lineEnd = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n}\n\nfunction lengthPointFirst(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n lambda0 = lambda, sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi), cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi);\n lengthStream.point = lengthPoint;\n}\n\nfunction lengthPoint(lambda, phi) {\n lambda *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], phi *= _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"];\n var sinPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n delta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda - lambda0),\n cosDelta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(delta),\n sinDelta = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(delta),\n x = cosPhi * sinDelta,\n y = cosPhi0 * sinPhi - sinPhi0 * cosPhi * cosDelta,\n z = sinPhi0 * sinPhi + cosPhi0 * cosPhi * cosDelta;\n lengthSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(x * x + y * y), z));\n lambda0 = lambda, sinPhi0 = sinPhi, cosPhi0 = cosPhi;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object) {\n lengthSum.reset();\n Object(_stream__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(object, lengthStream);\n return +lengthSum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/length.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/math.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/math.js ***! + \***************************************************************/ +/*! exports provided: epsilon, epsilon2, pi, halfPi, quarterPi, tau, degrees, radians, abs, atan, atan2, cos, ceil, exp, floor, log, pow, sin, sign, sqrt, tan, acos, asin, haversin */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon2\", function() { return epsilon2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quarterPi\", function() { return quarterPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"degrees\", function() { return degrees; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"radians\", function() { return radians; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"abs\", function() { return abs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan\", function() { return atan; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan2\", function() { return atan2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ceil\", function() { return ceil; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"exp\", function() { return exp; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"floor\", function() { return floor; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"log\", function() { return log; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pow\", function() { return pow; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sign\", function() { return sign; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tan\", function() { return tan; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"acos\", function() { return acos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"asin\", function() { return asin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"haversin\", function() { return haversin; });\nvar epsilon = 1e-6;\nvar epsilon2 = 1e-12;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar quarterPi = pi / 4;\nvar tau = pi * 2;\n\nvar degrees = 180 / pi;\nvar radians = pi / 180;\n\nvar abs = Math.abs;\nvar atan = Math.atan;\nvar atan2 = Math.atan2;\nvar cos = Math.cos;\nvar ceil = Math.ceil;\nvar exp = Math.exp;\nvar floor = Math.floor;\nvar log = Math.log;\nvar pow = Math.pow;\nvar sin = Math.sin;\nvar sign = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };\nvar sqrt = Math.sqrt;\nvar tan = Math.tan;\n\nfunction acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nfunction asin(x) {\n return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);\n}\n\nfunction haversin(x) {\n return (x = sin(x / 2)) * x;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js ***! + \***************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return noop; });\nfunction noop() {}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/area.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/area.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n\n\n\n\nvar areaSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n areaRingSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n x00,\n y00,\n x0,\n y0;\n\nvar areaStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n polygonStart: function() {\n areaStream.lineStart = areaRingStart;\n areaStream.lineEnd = areaRingEnd;\n },\n polygonEnd: function() {\n areaStream.lineStart = areaStream.lineEnd = areaStream.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n areaSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(areaRingSum));\n areaRingSum.reset();\n },\n result: function() {\n var area = areaSum / 2;\n areaSum.reset();\n return area;\n }\n};\n\nfunction areaRingStart() {\n areaStream.point = areaPointFirst;\n}\n\nfunction areaPointFirst(x, y) {\n areaStream.point = areaPoint;\n x00 = x0 = x, y00 = y0 = y;\n}\n\nfunction areaPoint(x, y) {\n areaRingSum.add(y0 * x - x0 * y);\n x0 = x, y0 = y;\n}\n\nfunction areaRingEnd() {\n areaPoint(x00, y00);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (areaStream);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/area.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/bounds.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/bounds.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n\n\nvar x0 = Infinity,\n y0 = x0,\n x1 = -x0,\n y1 = x1;\n\nvar boundsStream = {\n point: boundsPoint,\n lineStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n polygonStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n polygonEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n result: function() {\n var bounds = [[x0, y0], [x1, y1]];\n x1 = y1 = -(y0 = x0 = Infinity);\n return bounds;\n }\n};\n\nfunction boundsPoint(x, y) {\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (boundsStream);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/bounds.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/centroid.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/centroid.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n// TODO Enforce positive area for exterior, negative area for interior?\n\nvar X0 = 0,\n Y0 = 0,\n Z0 = 0,\n X1 = 0,\n Y1 = 0,\n Z1 = 0,\n X2 = 0,\n Y2 = 0,\n Z2 = 0,\n x00,\n y00,\n x0,\n y0;\n\nvar centroidStream = {\n point: centroidPoint,\n lineStart: centroidLineStart,\n lineEnd: centroidLineEnd,\n polygonStart: function() {\n centroidStream.lineStart = centroidRingStart;\n centroidStream.lineEnd = centroidRingEnd;\n },\n polygonEnd: function() {\n centroidStream.point = centroidPoint;\n centroidStream.lineStart = centroidLineStart;\n centroidStream.lineEnd = centroidLineEnd;\n },\n result: function() {\n var centroid = Z2 ? [X2 / Z2, Y2 / Z2]\n : Z1 ? [X1 / Z1, Y1 / Z1]\n : Z0 ? [X0 / Z0, Y0 / Z0]\n : [NaN, NaN];\n X0 = Y0 = Z0 =\n X1 = Y1 = Z1 =\n X2 = Y2 = Z2 = 0;\n return centroid;\n }\n};\n\nfunction centroidPoint(x, y) {\n X0 += x;\n Y0 += y;\n ++Z0;\n}\n\nfunction centroidLineStart() {\n centroidStream.point = centroidPointFirstLine;\n}\n\nfunction centroidPointFirstLine(x, y) {\n centroidStream.point = centroidPointLine;\n centroidPoint(x0 = x, y0 = y);\n}\n\nfunction centroidPointLine(x, y) {\n var dx = x - x0, dy = y - y0, z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(dx * dx + dy * dy);\n X1 += z * (x0 + x) / 2;\n Y1 += z * (y0 + y) / 2;\n Z1 += z;\n centroidPoint(x0 = x, y0 = y);\n}\n\nfunction centroidLineEnd() {\n centroidStream.point = centroidPoint;\n}\n\nfunction centroidRingStart() {\n centroidStream.point = centroidPointFirstRing;\n}\n\nfunction centroidRingEnd() {\n centroidPointRing(x00, y00);\n}\n\nfunction centroidPointFirstRing(x, y) {\n centroidStream.point = centroidPointRing;\n centroidPoint(x00 = x0 = x, y00 = y0 = y);\n}\n\nfunction centroidPointRing(x, y) {\n var dx = x - x0,\n dy = y - y0,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(dx * dx + dy * dy);\n\n X1 += z * (x0 + x) / 2;\n Y1 += z * (y0 + y) / 2;\n Z1 += z;\n\n z = y0 * x - x0 * y;\n X2 += z * (x0 + x);\n Y2 += z * (y0 + y);\n Z2 += z * 3;\n centroidPoint(x0 = x, y0 = y);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (centroidStream);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/context.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/context.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return PathContext; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n\n\n\nfunction PathContext(context) {\n this._context = context;\n}\n\nPathContext.prototype = {\n _radius: 4.5,\n pointRadius: function(_) {\n return this._radius = _, this;\n },\n polygonStart: function() {\n this._line = 0;\n },\n polygonEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line === 0) this._context.closePath();\n this._point = NaN;\n },\n point: function(x, y) {\n switch (this._point) {\n case 0: {\n this._context.moveTo(x, y);\n this._point = 1;\n break;\n }\n case 1: {\n this._context.lineTo(x, y);\n break;\n }\n default: {\n this._context.moveTo(x + this._radius, y);\n this._context.arc(x, y, this._radius, 0, _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n break;\n }\n }\n },\n result: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/context.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/index.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/index.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../identity */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./area */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/area.js\");\n/* harmony import */ var _bounds__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./bounds */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/bounds.js\");\n/* harmony import */ var _centroid__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./centroid */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/centroid.js\");\n/* harmony import */ var _context__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./context */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/context.js\");\n/* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./measure */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/measure.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/string.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(projection, context) {\n var pointRadius = 4.5,\n projectionStream,\n contextStream;\n\n function path(object) {\n if (object) {\n if (typeof pointRadius === \"function\") contextStream.pointRadius(+pointRadius.apply(this, arguments));\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(contextStream));\n }\n return contextStream.result();\n }\n\n path.area = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_area__WEBPACK_IMPORTED_MODULE_2__[\"default\"]));\n return _area__WEBPACK_IMPORTED_MODULE_2__[\"default\"].result();\n };\n\n path.measure = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_measure__WEBPACK_IMPORTED_MODULE_6__[\"default\"]));\n return _measure__WEBPACK_IMPORTED_MODULE_6__[\"default\"].result();\n };\n\n path.bounds = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_bounds__WEBPACK_IMPORTED_MODULE_3__[\"default\"]));\n return _bounds__WEBPACK_IMPORTED_MODULE_3__[\"default\"].result();\n };\n\n path.centroid = function(object) {\n Object(_stream__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(object, projectionStream(_centroid__WEBPACK_IMPORTED_MODULE_4__[\"default\"]));\n return _centroid__WEBPACK_IMPORTED_MODULE_4__[\"default\"].result();\n };\n\n path.projection = function(_) {\n return arguments.length ? (projectionStream = _ == null ? (projection = null, _identity__WEBPACK_IMPORTED_MODULE_0__[\"default\"]) : (projection = _).stream, path) : projection;\n };\n\n path.context = function(_) {\n if (!arguments.length) return context;\n contextStream = _ == null ? (context = null, new _string__WEBPACK_IMPORTED_MODULE_7__[\"default\"]) : new _context__WEBPACK_IMPORTED_MODULE_5__[\"default\"](context = _);\n if (typeof pointRadius !== \"function\") contextStream.pointRadius(pointRadius);\n return path;\n };\n\n path.pointRadius = function(_) {\n if (!arguments.length) return pointRadius;\n pointRadius = typeof _ === \"function\" ? _ : (contextStream.pointRadius(+_), +_);\n return path;\n };\n\n return path.projection(projection).context(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/measure.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/measure.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/noop.js\");\n\n\n\n\nvar lengthSum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n lengthRing,\n x00,\n y00,\n x0,\n y0;\n\nvar lengthStream = {\n point: _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n lineStart: function() {\n lengthStream.point = lengthPointFirst;\n },\n lineEnd: function() {\n if (lengthRing) lengthPoint(x00, y00);\n lengthStream.point = _noop__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n },\n polygonStart: function() {\n lengthRing = true;\n },\n polygonEnd: function() {\n lengthRing = null;\n },\n result: function() {\n var length = +lengthSum;\n lengthSum.reset();\n return length;\n }\n};\n\nfunction lengthPointFirst(x, y) {\n lengthStream.point = lengthPoint;\n x00 = x0 = x, y00 = y0 = y;\n}\n\nfunction lengthPoint(x, y) {\n x0 -= x, y0 -= y;\n lengthSum.add(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(x0 * x0 + y0 * y0));\n x0 = x, y0 = y;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (lengthStream);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/measure.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/path/string.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/path/string.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return PathString; });\nfunction PathString() {\n this._string = [];\n}\n\nPathString.prototype = {\n _radius: 4.5,\n _circle: circle(4.5),\n pointRadius: function(_) {\n if ((_ = +_) !== this._radius) this._radius = _, this._circle = null;\n return this;\n },\n polygonStart: function() {\n this._line = 0;\n },\n polygonEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line === 0) this._string.push(\"Z\");\n this._point = NaN;\n },\n point: function(x, y) {\n switch (this._point) {\n case 0: {\n this._string.push(\"M\", x, \",\", y);\n this._point = 1;\n break;\n }\n case 1: {\n this._string.push(\"L\", x, \",\", y);\n break;\n }\n default: {\n if (this._circle == null) this._circle = circle(this._radius);\n this._string.push(\"M\", x, \",\", y, this._circle);\n break;\n }\n }\n },\n result: function() {\n if (this._string.length) {\n var result = this._string.join(\"\");\n this._string = [];\n return result;\n } else {\n return null;\n }\n }\n};\n\nfunction circle(radius) {\n return \"m0,\" + radius\n + \"a\" + radius + \",\" + radius + \" 0 1,1 0,\" + -2 * radius\n + \"a\" + radius + \",\" + radius + \" 0 1,1 0,\" + 2 * radius\n + \"z\";\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/path/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/pointEqual.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/pointEqual.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(a[0] - b[0]) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"] && Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(a[1] - b[1]) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/pointEqual.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/polygonContains.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/polygonContains.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _adder__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adder */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/adder.js\");\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cartesian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\n\nvar sum = Object(_adder__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon, point) {\n var lambda = point[0],\n phi = point[1],\n normal = [Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(lambda), -Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(lambda), 0],\n angle = 0,\n winding = 0;\n\n sum.reset();\n\n for (var i = 0, n = polygon.length; i < n; ++i) {\n if (!(m = (ring = polygon[i]).length)) continue;\n var ring,\n m,\n point0 = ring[m - 1],\n lambda0 = point0[0],\n phi0 = point0[1] / 2 + _math__WEBPACK_IMPORTED_MODULE_2__[\"quarterPi\"],\n sinPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(phi0),\n cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi0);\n\n for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {\n var point1 = ring[j],\n lambda1 = point1[0],\n phi1 = point1[1] / 2 + _math__WEBPACK_IMPORTED_MODULE_2__[\"quarterPi\"],\n sinPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(phi1),\n cosPhi1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(phi1),\n delta = lambda1 - lambda0,\n sign = delta >= 0 ? 1 : -1,\n absDelta = sign * delta,\n antimeridian = absDelta > _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"],\n k = sinPhi0 * sinPhi1;\n\n sum.add(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(k * sign * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(absDelta), cosPhi0 * cosPhi1 + k * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(absDelta)));\n angle += antimeridian ? delta + sign * _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] : delta;\n\n // Are the longitudes either side of the point’s meridian (lambda),\n // and are the latitudes smaller than the parallel (phi)?\n if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {\n var arc = Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianCross\"])(Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesian\"])(point0), Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesian\"])(point1));\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianNormalizeInPlace\"])(arc);\n var intersection = Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianCross\"])(normal, arc);\n Object(_cartesian__WEBPACK_IMPORTED_MODULE_1__[\"cartesianNormalizeInPlace\"])(intersection);\n var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(intersection[2]);\n if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {\n winding += antimeridian ^ delta >= 0 ? 1 : -1;\n }\n }\n }\n }\n\n // First, determine whether the South pole is inside or outside:\n //\n // It is inside if:\n // * the polygon winds around it in a clockwise direction.\n // * the polygon does not (cumulatively) wind around it, but has a negative\n // (counter-clockwise) area.\n //\n // Second, count the (signed) number of times a segment crosses a lambda\n // from the point to the South pole. If it is zero, then the point is the\n // same side as the South pole.\n\n return (angle < -_math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] || angle < _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] && sum < -_math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) ^ (winding & 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/polygonContains.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albers.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albers.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _conicEqualArea__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./conicEqualArea */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n .parallels([29.5, 45.5])\n .scale(1070)\n .translate([480, 250])\n .rotate([96, 0])\n .center([-0.6, 38.7]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albers.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albersUsa.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albersUsa.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _albers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./albers */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albers.js\");\n/* harmony import */ var _conicEqualArea__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./conicEqualArea */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./fit */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js\");\n\n\n\n\n\n// The projections must have mutually exclusive clip regions on the sphere,\n// as this will avoid emitting interleaving lines and polygons.\nfunction multiplex(streams) {\n var n = streams.length;\n return {\n point: function(x, y) { var i = -1; while (++i < n) streams[i].point(x, y); },\n sphere: function() { var i = -1; while (++i < n) streams[i].sphere(); },\n lineStart: function() { var i = -1; while (++i < n) streams[i].lineStart(); },\n lineEnd: function() { var i = -1; while (++i < n) streams[i].lineEnd(); },\n polygonStart: function() { var i = -1; while (++i < n) streams[i].polygonStart(); },\n polygonEnd: function() { var i = -1; while (++i < n) streams[i].polygonEnd(); }\n };\n}\n\n// A composite projection for the United States, configured by default for\n// 960×500. The projection also works quite well at 960×600 if you change the\n// scale to 1285 and adjust the translate accordingly. The set of standard\n// parallels for each region comes from USGS, which is published here:\n// http://egsc.usgs.gov/isb/pubs/MapProjections/projections.html#albers\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var cache,\n cacheStream,\n lower48 = Object(_albers__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(), lower48Point,\n alaska = Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"default\"])().rotate([154, 0]).center([-2, 58.5]).parallels([55, 65]), alaskaPoint, // EPSG:3338\n hawaii = Object(_conicEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"default\"])().rotate([157, 0]).center([-3, 19.9]).parallels([8, 18]), hawaiiPoint, // ESRI:102007\n point, pointStream = {point: function(x, y) { point = [x, y]; }};\n\n function albersUsa(coordinates) {\n var x = coordinates[0], y = coordinates[1];\n return point = null,\n (lower48Point.point(x, y), point)\n || (alaskaPoint.point(x, y), point)\n || (hawaiiPoint.point(x, y), point);\n }\n\n albersUsa.invert = function(coordinates) {\n var k = lower48.scale(),\n t = lower48.translate(),\n x = (coordinates[0] - t[0]) / k,\n y = (coordinates[1] - t[1]) / k;\n return (y >= 0.120 && y < 0.234 && x >= -0.425 && x < -0.214 ? alaska\n : y >= 0.166 && y < 0.234 && x >= -0.214 && x < -0.115 ? hawaii\n : lower48).invert(coordinates);\n };\n\n albersUsa.stream = function(stream) {\n return cache && cacheStream === stream ? cache : cache = multiplex([lower48.stream(cacheStream = stream), alaska.stream(stream), hawaii.stream(stream)]);\n };\n\n albersUsa.precision = function(_) {\n if (!arguments.length) return lower48.precision();\n lower48.precision(_), alaska.precision(_), hawaii.precision(_);\n return reset();\n };\n\n albersUsa.scale = function(_) {\n if (!arguments.length) return lower48.scale();\n lower48.scale(_), alaska.scale(_ * 0.35), hawaii.scale(_);\n return albersUsa.translate(lower48.translate());\n };\n\n albersUsa.translate = function(_) {\n if (!arguments.length) return lower48.translate();\n var k = lower48.scale(), x = +_[0], y = +_[1];\n\n lower48Point = lower48\n .translate(_)\n .clipExtent([[x - 0.455 * k, y - 0.238 * k], [x + 0.455 * k, y + 0.238 * k]])\n .stream(pointStream);\n\n alaskaPoint = alaska\n .translate([x - 0.307 * k, y + 0.201 * k])\n .clipExtent([[x - 0.425 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.120 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]], [x - 0.214 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.234 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]]])\n .stream(pointStream);\n\n hawaiiPoint = hawaii\n .translate([x - 0.205 * k, y + 0.212 * k])\n .clipExtent([[x - 0.214 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.166 * k + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]], [x - 0.115 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"], y + 0.234 * k - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]]])\n .stream(pointStream);\n\n return reset();\n };\n\n albersUsa.fitExtent = function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitExtent\"])(albersUsa, extent, object);\n };\n\n albersUsa.fitSize = function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitSize\"])(albersUsa, size, object);\n };\n\n albersUsa.fitWidth = function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitWidth\"])(albersUsa, width, object);\n };\n\n albersUsa.fitHeight = function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitHeight\"])(albersUsa, height, object);\n };\n\n function reset() {\n cache = cacheStream = null;\n return albersUsa;\n }\n\n return albersUsa.scale(1070);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/albersUsa.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js ***! + \*******************************************************************************/ +/*! exports provided: azimuthalRaw, azimuthalInvert */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalRaw\", function() { return azimuthalRaw; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalInvert\", function() { return azimuthalInvert; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\nfunction azimuthalRaw(scale) {\n return function(x, y) {\n var cx = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x),\n cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y),\n k = scale(cx * cy);\n return [\n k * cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x),\n k * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)\n ];\n }\n}\n\nfunction azimuthalInvert(angle) {\n return function(x, y) {\n var z = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + y * y),\n c = angle(z),\n sc = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(c),\n cc = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(c);\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x * sc, z * cc),\n Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z && y * sc / z)\n ];\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEqualArea.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEqualArea.js ***! + \****************************************************************************************/ +/*! exports provided: azimuthalEqualAreaRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalEqualAreaRaw\", function() { return azimuthalEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nvar azimuthalEqualAreaRaw = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalRaw\"])(function(cxcy) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(2 / (1 + cxcy));\n});\n\nazimuthalEqualAreaRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(z / 2);\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(azimuthalEqualAreaRaw)\n .scale(124.75)\n .clipAngle(180 - 1e-3);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEquidistant.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEquidistant.js ***! + \******************************************************************************************/ +/*! exports provided: azimuthalEquidistantRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"azimuthalEquidistantRaw\", function() { return azimuthalEquidistantRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nvar azimuthalEquidistantRaw = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalRaw\"])(function(c) {\n return (c = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"acos\"])(c)) && c / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(c);\n});\n\nazimuthalEquidistantRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return z;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(azimuthalEquidistantRaw)\n .scale(79.4188)\n .clipAngle(180 - 1e-3);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthalEquidistant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js ***! + \***************************************************************************/ +/*! exports provided: conicProjection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicProjection\", function() { return conicProjection; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\nfunction conicProjection(projectAt) {\n var phi0 = 0,\n phi1 = _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 3,\n m = Object(_index__WEBPACK_IMPORTED_MODULE_1__[\"projectionMutator\"])(projectAt),\n p = m(phi0, phi1);\n\n p.parallels = function(_) {\n return arguments.length ? m(phi0 = _[0] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"], phi1 = _[1] * _math__WEBPACK_IMPORTED_MODULE_0__[\"radians\"]) : [phi0 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"], phi1 * _math__WEBPACK_IMPORTED_MODULE_0__[\"degrees\"]];\n };\n\n return p;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicConformal.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicConformal.js ***! + \************************************************************************************/ +/*! exports provided: conicConformalRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicConformalRaw\", function() { return conicConformalRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _mercator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./mercator */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js\");\n\n\n\n\nfunction tany(y) {\n return Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + y) / 2);\n}\n\nfunction conicConformalRaw(y0, y1) {\n var cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n n = y0 === y1 ? Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0) : Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(cy0 / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1)) / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(tany(y1) / tany(y0)),\n f = cy0 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(tany(y0), n) / n;\n\n if (!n) return _mercator__WEBPACK_IMPORTED_MODULE_2__[\"mercatorRaw\"];\n\n function project(x, y) {\n if (f > 0) { if (y < -_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) y = -_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]; }\n else { if (y > _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) y = _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] - _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]; }\n var r = f / Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(tany(y), n);\n return [r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(n * x), f - r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(n * x)];\n }\n\n project.invert = function(x, y) {\n var fy = f - y, r = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(n) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + fy * fy);\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(fy)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(fy), 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"pow\"])(f / r, 1 / n)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicConformalRaw)\n .scale(109.5)\n .parallels([30, 30]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicConformal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js ***! + \************************************************************************************/ +/*! exports provided: conicEqualAreaRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicEqualAreaRaw\", function() { return conicEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _cylindricalEqualArea__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cylindricalEqualArea */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/cylindricalEqualArea.js\");\n\n\n\n\nfunction conicEqualAreaRaw(y0, y1) {\n var sy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0), n = (sy0 + Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y1)) / 2;\n\n // Are the parallels symmetrical around the Equator?\n if (Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(n) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) return Object(_cylindricalEqualArea__WEBPACK_IMPORTED_MODULE_2__[\"cylindricalEqualAreaRaw\"])(y0);\n\n var c = 1 + sy0 * (2 * n - sy0), r0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(c) / n;\n\n function project(x, y) {\n var r = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(c - 2 * n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)) / n;\n return [r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x *= n), r0 - r * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x)];\n }\n\n project.invert = function(x, y) {\n var r0y = r0 - y;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(r0y)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(r0y), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])((c - (x * x + r0y * r0y) * n * n) / (2 * n))];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicEqualAreaRaw)\n .scale(155.424)\n .center([0, 33.6442]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEquidistant.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEquidistant.js ***! + \**************************************************************************************/ +/*! exports provided: conicEquidistantRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"conicEquidistantRaw\", function() { return conicEquidistantRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _conic__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./conic */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conic.js\");\n/* harmony import */ var _equirectangular__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./equirectangular */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/equirectangular.js\");\n\n\n\n\nfunction conicEquidistantRaw(y0, y1) {\n var cy0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y0),\n n = y0 === y1 ? Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y0) : (cy0 - Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y1)) / (y1 - y0),\n g = cy0 / n + y0;\n\n if (Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(n) < _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) return _equirectangular__WEBPACK_IMPORTED_MODULE_2__[\"equirectangularRaw\"];\n\n function project(x, y) {\n var gy = g - y, nx = n * x;\n return [gy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(nx), g - gy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(nx)];\n }\n\n project.invert = function(x, y) {\n var gy = g - y;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan2\"])(x, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"abs\"])(gy)) / n * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(gy), g - Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sign\"])(n) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sqrt\"])(x * x + gy * gy)];\n };\n\n return project;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_conic__WEBPACK_IMPORTED_MODULE_1__[\"conicProjection\"])(conicEquidistantRaw)\n .scale(131.154)\n .center([0, 13.9389]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/conicEquidistant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/cylindricalEqualArea.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/cylindricalEqualArea.js ***! + \******************************************************************************************/ +/*! exports provided: cylindricalEqualAreaRaw */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cylindricalEqualAreaRaw\", function() { return cylindricalEqualAreaRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\nfunction cylindricalEqualAreaRaw(phi0) {\n var cosPhi0 = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(phi0);\n\n function forward(lambda, phi) {\n return [lambda * cosPhi0, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(phi) / cosPhi0];\n }\n\n forward.invert = function(x, y) {\n return [x / cosPhi0, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"])(y * cosPhi0)];\n };\n\n return forward;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/cylindricalEqualArea.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/equirectangular.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/equirectangular.js ***! + \*************************************************************************************/ +/*! exports provided: equirectangularRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"equirectangularRaw\", function() { return equirectangularRaw; });\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\nfunction equirectangularRaw(lambda, phi) {\n return [lambda, phi];\n}\n\nequirectangularRaw.invert = equirectangularRaw;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(equirectangularRaw)\n .scale(152.63);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/equirectangular.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js ***! + \*************************************************************************/ +/*! exports provided: fitExtent, fitSize, fitWidth, fitHeight */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitExtent\", function() { return fitExtent; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitSize\", function() { return fitSize; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitWidth\", function() { return fitWidth; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fitHeight\", function() { return fitHeight; });\n/* harmony import */ var _stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../stream */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js\");\n/* harmony import */ var _path_bounds__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../path/bounds */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/path/bounds.js\");\n\n\n\nfunction fit(projection, fitBounds, object) {\n var clip = projection.clipExtent && projection.clipExtent();\n projection.scale(150).translate([0, 0]);\n if (clip != null) projection.clipExtent(null);\n Object(_stream__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(object, projection.stream(_path_bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"]));\n fitBounds(_path_bounds__WEBPACK_IMPORTED_MODULE_1__[\"default\"].result());\n if (clip != null) projection.clipExtent(clip);\n return projection;\n}\n\nfunction fitExtent(projection, extent, object) {\n return fit(projection, function(b) {\n var w = extent[1][0] - extent[0][0],\n h = extent[1][1] - extent[0][1],\n k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),\n x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,\n y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\nfunction fitSize(projection, size, object) {\n return fitExtent(projection, [[0, 0], size], object);\n}\n\nfunction fitWidth(projection, width, object) {\n return fit(projection, function(b) {\n var w = +width,\n k = w / (b[1][0] - b[0][0]),\n x = (w - k * (b[1][0] + b[0][0])) / 2,\n y = -k * b[0][1];\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\nfunction fitHeight(projection, height, object) {\n return fit(projection, function(b) {\n var h = +height,\n k = h / (b[1][1] - b[0][1]),\n x = -k * b[0][0],\n y = (h - k * (b[1][1] + b[0][1])) / 2;\n projection.scale(150 * k).translate([x, y]);\n }, object);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/gnomonic.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/gnomonic.js ***! + \******************************************************************************/ +/*! exports provided: gnomonicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gnomonicRaw\", function() { return gnomonicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction gnomonicRaw(x, y) {\n var cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y), k = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x) * cy;\n return [cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x) / k, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y) / k];\n}\n\ngnomonicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(gnomonicRaw)\n .scale(144.049)\n .clipAngle(60);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/gnomonic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/identity.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/identity.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _clip_rectangle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../clip/rectangle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../identity */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../transform */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./fit */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js\");\n\n\n\n\n\nfunction scaleTranslate(kx, ky, tx, ty) {\n return kx === 1 && ky === 1 && tx === 0 && ty === 0 ? _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"] : Object(_transform__WEBPACK_IMPORTED_MODULE_2__[\"transformer\"])({\n point: function(x, y) {\n this.stream.point(x * kx + tx, y * ky + ty);\n }\n });\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, transform = _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"], // scale, translate and reflect\n x0 = null, y0, x1, y1, // clip extent\n postclip = _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n cache,\n cacheStream,\n projection;\n\n function reset() {\n cache = cacheStream = null;\n return projection;\n }\n\n return projection = {\n stream: function(stream) {\n return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));\n },\n postclip: function(_) {\n return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;\n },\n clipExtent: function(_) {\n return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, _identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : Object(_clip_rectangle__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n },\n scale: function(_) {\n return arguments.length ? (transform = scaleTranslate((k = +_) * sx, k * sy, tx, ty), reset()) : k;\n },\n translate: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * sx, k * sy, tx = +_[0], ty = +_[1]), reset()) : [tx, ty];\n },\n reflectX: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * (sx = _ ? -1 : 1), k * sy, tx, ty), reset()) : sx < 0;\n },\n reflectY: function(_) {\n return arguments.length ? (transform = scaleTranslate(k * sx, k * (sy = _ ? -1 : 1), tx, ty), reset()) : sy < 0;\n },\n fitExtent: function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitExtent\"])(projection, extent, object);\n },\n fitSize: function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitSize\"])(projection, size, object);\n },\n fitWidth: function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitWidth\"])(projection, width, object);\n },\n fitHeight: function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_3__[\"fitHeight\"])(projection, height, object);\n }\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js ***! + \***************************************************************************/ +/*! exports provided: default, projectionMutator */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return projection; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"projectionMutator\", function() { return projectionMutator; });\n/* harmony import */ var _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../clip/antimeridian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/antimeridian.js\");\n/* harmony import */ var _clip_circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../clip/circle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/circle.js\");\n/* harmony import */ var _clip_rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../clip/rectangle */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/clip/rectangle.js\");\n/* harmony import */ var _compose__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../compose */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/compose.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../identity */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/identity.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../rotation */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ../transform */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js\");\n/* harmony import */ var _fit__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./fit */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/fit.js\");\n/* harmony import */ var _resample__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./resample */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/resample.js\");\n\n\n\n\n\n\n\n\n\n\n\nvar transformRadians = Object(_transform__WEBPACK_IMPORTED_MODULE_7__[\"transformer\"])({\n point: function(x, y) {\n this.stream.point(x * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], y * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]);\n }\n});\n\nfunction transformRotate(rotate) {\n return Object(_transform__WEBPACK_IMPORTED_MODULE_7__[\"transformer\"])({\n point: function(x, y) {\n var r = rotate(x, y);\n return this.stream.point(r[0], r[1]);\n }\n });\n}\n\nfunction projection(project) {\n return projectionMutator(function() { return project; })();\n}\n\nfunction projectionMutator(projectAt) {\n var project,\n k = 150, // scale\n x = 480, y = 250, // translate\n dx, dy, lambda = 0, phi = 0, // center\n deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, projectRotate, // rotate\n theta = null, preclip = _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__[\"default\"], // clip angle\n x0 = null, y0, x1, y1, postclip = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"], // clip extent\n delta2 = 0.5, projectResample = Object(_resample__WEBPACK_IMPORTED_MODULE_9__[\"default\"])(projectTransform, delta2), // precision\n cache,\n cacheStream;\n\n function projection(point) {\n point = projectRotate(point[0] * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]);\n return [point[0] * k + dx, dy - point[1] * k];\n }\n\n function invert(point) {\n point = projectRotate.invert((point[0] - dx) / k, (dy - point[1]) / k);\n return point && [point[0] * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], point[1] * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n }\n\n function projectTransform(x, y) {\n return x = project(x, y), [x[0] * k + dx, dy - x[1] * k];\n }\n\n projection.stream = function(stream) {\n return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));\n };\n\n projection.preclip = function(_) {\n return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;\n };\n\n projection.postclip = function(_) {\n return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;\n };\n\n projection.clipAngle = function(_) {\n return arguments.length ? (preclip = +_ ? Object(_clip_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(theta = _ * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"]) : (theta = null, _clip_antimeridian__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), reset()) : theta * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"];\n };\n\n projection.clipExtent = function(_) {\n return arguments.length ? (postclip = _ == null ? (x0 = y0 = x1 = y1 = null, _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"]) : Object(_clip_rectangle__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]), reset()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n };\n\n projection.scale = function(_) {\n return arguments.length ? (k = +_, recenter()) : k;\n };\n\n projection.translate = function(_) {\n return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];\n };\n\n projection.center = function(_) {\n return arguments.length ? (lambda = _[0] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], phi = _[1] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], recenter()) : [lambda * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], phi * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n };\n\n projection.rotate = function(_) {\n return arguments.length ? (deltaLambda = _[0] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], deltaPhi = _[1] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"], deltaGamma = _.length > 2 ? _[2] % 360 * _math__WEBPACK_IMPORTED_MODULE_5__[\"radians\"] : 0, recenter()) : [deltaLambda * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], deltaPhi * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"], deltaGamma * _math__WEBPACK_IMPORTED_MODULE_5__[\"degrees\"]];\n };\n\n projection.precision = function(_) {\n return arguments.length ? (projectResample = Object(_resample__WEBPACK_IMPORTED_MODULE_9__[\"default\"])(projectTransform, delta2 = _ * _), reset()) : Object(_math__WEBPACK_IMPORTED_MODULE_5__[\"sqrt\"])(delta2);\n };\n\n projection.fitExtent = function(extent, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitExtent\"])(projection, extent, object);\n };\n\n projection.fitSize = function(size, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitSize\"])(projection, size, object);\n };\n\n projection.fitWidth = function(width, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitWidth\"])(projection, width, object);\n };\n\n projection.fitHeight = function(height, object) {\n return Object(_fit__WEBPACK_IMPORTED_MODULE_8__[\"fitHeight\"])(projection, height, object);\n };\n\n function recenter() {\n projectRotate = Object(_compose__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(rotate = Object(_rotation__WEBPACK_IMPORTED_MODULE_6__[\"rotateRadians\"])(deltaLambda, deltaPhi, deltaGamma), project);\n var center = project(lambda, phi);\n dx = x - center[0] * k;\n dy = y + center[1] * k;\n return reset();\n }\n\n function reset() {\n cache = cacheStream = null;\n return projection;\n }\n\n return function() {\n project = projectAt.apply(this, arguments);\n projection.invert = project.invert && invert;\n return recenter();\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js ***! + \******************************************************************************/ +/*! exports provided: mercatorRaw, default, mercatorProjection */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mercatorRaw\", function() { return mercatorRaw; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mercatorProjection\", function() { return mercatorProjection; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _rotation__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../rotation */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction mercatorRaw(lambda, phi) {\n return [lambda, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + phi) / 2))];\n}\n\nmercatorRaw.invert = function(x, y) {\n return [x, 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"exp\"])(y)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return mercatorProjection(mercatorRaw)\n .scale(961 / _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n});\n\nfunction mercatorProjection(project) {\n var m = Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(project),\n center = m.center,\n scale = m.scale,\n translate = m.translate,\n clipExtent = m.clipExtent,\n x0 = null, y0, x1, y1; // clip extent\n\n m.scale = function(_) {\n return arguments.length ? (scale(_), reclip()) : scale();\n };\n\n m.translate = function(_) {\n return arguments.length ? (translate(_), reclip()) : translate();\n };\n\n m.center = function(_) {\n return arguments.length ? (center(_), reclip()) : center();\n };\n\n m.clipExtent = function(_) {\n return arguments.length ? ((_ == null ? x0 = y0 = x1 = y1 = null : (x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1])), reclip()) : x0 == null ? null : [[x0, y0], [x1, y1]];\n };\n\n function reclip() {\n var k = _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] * scale(),\n t = m(Object(_rotation__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(m.rotate()).invert([0, 0]));\n return clipExtent(x0 == null\n ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw\n ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]\n : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);\n }\n\n return reclip();\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/naturalEarth1.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/naturalEarth1.js ***! + \***********************************************************************************/ +/*! exports provided: naturalEarth1Raw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"naturalEarth1Raw\", function() { return naturalEarth1Raw; });\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\nfunction naturalEarth1Raw(lambda, phi) {\n var phi2 = phi * phi, phi4 = phi2 * phi2;\n return [\n lambda * (0.8707 - 0.131979 * phi2 + phi4 * (-0.013791 + phi4 * (0.003971 * phi2 - 0.001529 * phi4))),\n phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4)))\n ];\n}\n\nnaturalEarth1Raw.invert = function(x, y) {\n var phi = y, i = 25, delta;\n do {\n var phi2 = phi * phi, phi4 = phi2 * phi2;\n phi -= delta = (phi * (1.007226 + phi2 * (0.015085 + phi4 * (-0.044475 + 0.028874 * phi2 - 0.005916 * phi4))) - y) /\n (1.007226 + phi2 * (0.015085 * 3 + phi4 * (-0.044475 * 7 + 0.028874 * 9 * phi2 - 0.005916 * 11 * phi4)));\n } while (Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(delta) > _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && --i > 0);\n return [\n x / (0.8707 + (phi2 = phi * phi) * (-0.131979 + phi2 * (-0.013791 + phi2 * phi2 * phi2 * (0.003971 - 0.001529 * phi2)))),\n phi\n ];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(naturalEarth1Raw)\n .scale(175.295);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/naturalEarth1.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/orthographic.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/orthographic.js ***! + \**********************************************************************************/ +/*! exports provided: orthographicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"orthographicRaw\", function() { return orthographicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction orthographicRaw(x, y) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y) * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x), Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y)];\n}\n\northographicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(_math__WEBPACK_IMPORTED_MODULE_0__[\"asin\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(orthographicRaw)\n .scale(249.5)\n .clipAngle(90 + _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/orthographic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/resample.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/resample.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cartesian__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../cartesian */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/cartesian.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../transform */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js\");\n\n\n\n\nvar maxDepth = 16, // maximum depth of subdivision\n cosMinDistance = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(30 * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]); // cos(minimum angular distance)\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(project, delta2) {\n return +delta2 ? resample(project, delta2) : resampleNone(project);\n});\n\nfunction resampleNone(project) {\n return Object(_transform__WEBPACK_IMPORTED_MODULE_2__[\"transformer\"])({\n point: function(x, y) {\n x = project(x, y);\n this.stream.point(x[0], x[1]);\n }\n });\n}\n\nfunction resample(project, delta2) {\n\n function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {\n var dx = x1 - x0,\n dy = y1 - y0,\n d2 = dx * dx + dy * dy;\n if (d2 > 4 * delta2 && depth--) {\n var a = a0 + a1,\n b = b0 + b1,\n c = c0 + c1,\n m = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sqrt\"])(a * a + b * b + c * c),\n phi2 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(c /= m),\n lambda2 = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(c) - 1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] || Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])(lambda0 - lambda1) < _math__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? (lambda0 + lambda1) / 2 : Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(b, a),\n p = project(lambda2, phi2),\n x2 = p[0],\n y2 = p[1],\n dx2 = x2 - x0,\n dy2 = y2 - y0,\n dz = dy * dx2 - dx * dy2;\n if (dz * dz / d2 > delta2 // perpendicular projected distance\n || Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"abs\"])((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end\n || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);\n stream.point(x2, y2);\n resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);\n }\n }\n }\n return function(stream) {\n var lambda00, x00, y00, a00, b00, c00, // first point\n lambda0, x0, y0, a0, b0, c0; // previous point\n\n var resampleStream = {\n point: point,\n lineStart: lineStart,\n lineEnd: lineEnd,\n polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },\n polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }\n };\n\n function point(x, y) {\n x = project(x, y);\n stream.point(x[0], x[1]);\n }\n\n function lineStart() {\n x0 = NaN;\n resampleStream.point = linePoint;\n stream.lineStart();\n }\n\n function linePoint(lambda, phi) {\n var c = Object(_cartesian__WEBPACK_IMPORTED_MODULE_0__[\"cartesian\"])([lambda, phi]), p = project(lambda, phi);\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x0 = p[0], y0 = p[1], lambda0 = lambda, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);\n stream.point(x0, y0);\n }\n\n function lineEnd() {\n resampleStream.point = point;\n stream.lineEnd();\n }\n\n function ringStart() {\n lineStart();\n resampleStream.point = ringPoint;\n resampleStream.lineEnd = ringEnd;\n }\n\n function ringPoint(lambda, phi) {\n linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;\n resampleStream.point = linePoint;\n }\n\n function ringEnd() {\n resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);\n resampleStream.lineEnd = lineEnd;\n lineEnd();\n }\n\n return resampleStream;\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/resample.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/stereographic.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/stereographic.js ***! + \***********************************************************************************/ +/*! exports provided: stereographicRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stereographicRaw\", function() { return stereographicRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _azimuthal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./azimuthal */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/azimuthal.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/index.js\");\n\n\n\n\nfunction stereographicRaw(x, y) {\n var cy = Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(y), k = 1 + Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"cos\"])(x) * cy;\n return [cy * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(x) / k, Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"sin\"])(y) / k];\n}\n\nstereographicRaw.invert = Object(_azimuthal__WEBPACK_IMPORTED_MODULE_1__[\"azimuthalInvert\"])(function(z) {\n return 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(z);\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(stereographicRaw)\n .scale(250)\n .clipAngle(142);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/stereographic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/projection/transverseMercator.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/projection/transverseMercator.js ***! + \****************************************************************************************/ +/*! exports provided: transverseMercatorRaw, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"transverseMercatorRaw\", function() { return transverseMercatorRaw; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n/* harmony import */ var _mercator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./mercator */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/projection/mercator.js\");\n\n\n\nfunction transverseMercatorRaw(lambda, phi) {\n return [Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"log\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"tan\"])((_math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"] + phi) / 2)), -lambda];\n}\n\ntransverseMercatorRaw.invert = function(x, y) {\n return [-y, 2 * Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"atan\"])(Object(_math__WEBPACK_IMPORTED_MODULE_0__[\"exp\"])(x)) - _math__WEBPACK_IMPORTED_MODULE_0__[\"halfPi\"]];\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var m = Object(_mercator__WEBPACK_IMPORTED_MODULE_1__[\"mercatorProjection\"])(transverseMercatorRaw),\n center = m.center,\n rotate = m.rotate;\n\n m.center = function(_) {\n return arguments.length ? center([-_[1], _[0]]) : (_ = center(), [_[1], -_[0]]);\n };\n\n m.rotate = function(_) {\n return arguments.length ? rotate([_[0], _[1], _.length > 2 ? _[2] + 90 : 90]) : (_ = rotate(), [_[0], _[1], _[2] - 90]);\n };\n\n return rotate([0, 0, 90])\n .scale(159.155);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/projection/transverseMercator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js ***! + \*******************************************************************/ +/*! exports provided: rotateRadians, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rotateRadians\", function() { return rotateRadians; });\n/* harmony import */ var _compose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./compose */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/compose.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-geo/src/math.js\");\n\n\n\nfunction rotationIdentity(lambda, phi) {\n return [lambda > _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda - _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda < -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda + _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda, phi];\n}\n\nrotationIdentity.invert = rotationIdentity;\n\nfunction rotateRadians(deltaLambda, deltaPhi, deltaGamma) {\n return (deltaLambda %= _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"]) ? (deltaPhi || deltaGamma ? Object(_compose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))\n : rotationLambda(deltaLambda))\n : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)\n : rotationIdentity);\n}\n\nfunction forwardRotationLambda(deltaLambda) {\n return function(lambda, phi) {\n return lambda += deltaLambda, [lambda > _math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda - _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda < -_math__WEBPACK_IMPORTED_MODULE_1__[\"pi\"] ? lambda + _math__WEBPACK_IMPORTED_MODULE_1__[\"tau\"] : lambda, phi];\n };\n}\n\nfunction rotationLambda(deltaLambda) {\n var rotation = forwardRotationLambda(deltaLambda);\n rotation.invert = forwardRotationLambda(-deltaLambda);\n return rotation;\n}\n\nfunction rotationPhiGamma(deltaPhi, deltaGamma) {\n var cosDeltaPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(deltaPhi),\n sinDeltaPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(deltaPhi),\n cosDeltaGamma = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(deltaGamma),\n sinDeltaGamma = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(deltaGamma);\n\n function rotation(lambda, phi) {\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n x = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(lambda) * cosPhi,\n y = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda) * cosPhi,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = z * cosDeltaPhi + x * sinDeltaPhi;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(k * cosDeltaGamma + y * sinDeltaGamma)\n ];\n }\n\n rotation.invert = function(lambda, phi) {\n var cosPhi = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(phi),\n x = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"cos\"])(lambda) * cosPhi,\n y = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(lambda) * cosPhi,\n z = Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"sin\"])(phi),\n k = z * cosDeltaGamma - y * sinDeltaGamma;\n return [\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"atan2\"])(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),\n Object(_math__WEBPACK_IMPORTED_MODULE_1__[\"asin\"])(k * cosDeltaPhi - x * sinDeltaPhi)\n ];\n };\n\n return rotation;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(rotate) {\n rotate = rotateRadians(rotate[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], rotate[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], rotate.length > 2 ? rotate[2] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"] : 0);\n\n function forward(coordinates) {\n coordinates = rotate(coordinates[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], coordinates[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]);\n return coordinates[0] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates[1] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates;\n }\n\n forward.invert = function(coordinates) {\n coordinates = rotate.invert(coordinates[0] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"], coordinates[1] * _math__WEBPACK_IMPORTED_MODULE_1__[\"radians\"]);\n return coordinates[0] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates[1] *= _math__WEBPACK_IMPORTED_MODULE_1__[\"degrees\"], coordinates;\n };\n\n return forward;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/rotation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction streamGeometry(geometry, stream) {\n if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {\n streamGeometryType[geometry.type](geometry, stream);\n }\n}\n\nvar streamObjectType = {\n Feature: function(object, stream) {\n streamGeometry(object.geometry, stream);\n },\n FeatureCollection: function(object, stream) {\n var features = object.features, i = -1, n = features.length;\n while (++i < n) streamGeometry(features[i].geometry, stream);\n }\n};\n\nvar streamGeometryType = {\n Sphere: function(object, stream) {\n stream.sphere();\n },\n Point: function(object, stream) {\n object = object.coordinates;\n stream.point(object[0], object[1], object[2]);\n },\n MultiPoint: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) object = coordinates[i], stream.point(object[0], object[1], object[2]);\n },\n LineString: function(object, stream) {\n streamLine(object.coordinates, stream, 0);\n },\n MultiLineString: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) streamLine(coordinates[i], stream, 0);\n },\n Polygon: function(object, stream) {\n streamPolygon(object.coordinates, stream);\n },\n MultiPolygon: function(object, stream) {\n var coordinates = object.coordinates, i = -1, n = coordinates.length;\n while (++i < n) streamPolygon(coordinates[i], stream);\n },\n GeometryCollection: function(object, stream) {\n var geometries = object.geometries, i = -1, n = geometries.length;\n while (++i < n) streamGeometry(geometries[i], stream);\n }\n};\n\nfunction streamLine(coordinates, stream, closed) {\n var i = -1, n = coordinates.length - closed, coordinate;\n stream.lineStart();\n while (++i < n) coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]);\n stream.lineEnd();\n}\n\nfunction streamPolygon(coordinates, stream) {\n var i = -1, n = coordinates.length;\n stream.polygonStart();\n while (++i < n) streamLine(coordinates[i], stream, 1);\n stream.polygonEnd();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(object, stream) {\n if (object && streamObjectType.hasOwnProperty(object.type)) {\n streamObjectType[object.type](object, stream);\n } else {\n streamGeometry(object, stream);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/stream.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js ***! + \********************************************************************/ +/*! exports provided: default, transformer */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"transformer\", function() { return transformer; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(methods) {\n return {\n stream: transformer(methods)\n };\n});\n\nfunction transformer(methods) {\n return function(stream) {\n var s = new TransformStream;\n for (var key in methods) s[key] = methods[key];\n s.stream = stream;\n return s;\n };\n}\n\nfunction TransformStream() {}\n\nTransformStream.prototype = {\n constructor: TransformStream,\n point: function(x, y) { this.stream.point(x, y); },\n sphere: function() { this.stream.sphere(); },\n lineStart: function() { this.stream.lineStart(); },\n lineEnd: function() { this.stream.lineEnd(); },\n polygonStart: function() { this.stream.polygonStart(); },\n polygonEnd: function() { this.stream.polygonEnd(); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-geo/src/transform.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/index.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/index.js ***! + \******************************************************************/ +/*! exports provided: cluster, hierarchy, pack, packSiblings, packEnclose, partition, stratify, tree, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, treemapResquarify */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_cluster__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/cluster */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/cluster.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cluster\", function() { return _src_cluster__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_hierarchy_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/hierarchy/index */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hierarchy\", function() { return _src_hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_pack_index__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/pack/index */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pack\", function() { return _src_pack_index__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_pack_siblings__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/pack/siblings */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/siblings.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packSiblings\", function() { return _src_pack_siblings__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_pack_enclose__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/pack/enclose */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/enclose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return _src_pack_enclose__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_partition__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/partition */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/partition.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"partition\", function() { return _src_partition__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_stratify__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/stratify */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/stratify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stratify\", function() { return _src_stratify__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/tree */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/tree.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tree\", function() { return _src_tree__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_index__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/treemap/index */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemap\", function() { return _src_treemap_index__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_binary__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/treemap/binary */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/binary.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapBinary\", function() { return _src_treemap_binary__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_dice__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/treemap/dice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapDice\", function() { return _src_treemap_dice__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_slice__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/treemap/slice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSlice\", function() { return _src_treemap_slice__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_sliceDice__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/treemap/sliceDice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/sliceDice.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSliceDice\", function() { return _src_treemap_sliceDice__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_squarify__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/treemap/squarify */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSquarify\", function() { return _src_treemap_squarify__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_treemap_resquarify__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/treemap/resquarify */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/resquarify.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapResquarify\", function() { return _src_treemap_resquarify__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js ***! + \**************************************************************************/ +/*! exports provided: optional, required */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"optional\", function() { return optional; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"required\", function() { return required; });\nfunction optional(f) {\n return f == null ? null : required(f);\n}\n\nfunction required(f) {\n if (typeof f !== \"function\") throw new Error;\n return f;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/array.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/array.js ***! + \**********************************************************************/ +/*! exports provided: slice, shuffle */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return shuffle; });\nvar slice = Array.prototype.slice;\n\nfunction shuffle(array) {\n var m = array.length,\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m];\n array[m] = array[i];\n array[i] = t;\n }\n\n return array;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/cluster.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/cluster.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction defaultSeparation(a, b) {\n return a.parent === b.parent ? 1 : 2;\n}\n\nfunction meanX(children) {\n return children.reduce(meanXReduce, 0) / children.length;\n}\n\nfunction meanXReduce(x, c) {\n return x + c.x;\n}\n\nfunction maxY(children) {\n return 1 + children.reduce(maxYReduce, 0);\n}\n\nfunction maxYReduce(y, c) {\n return Math.max(y, c.y);\n}\n\nfunction leafLeft(node) {\n var children;\n while (children = node.children) node = children[0];\n return node;\n}\n\nfunction leafRight(node) {\n var children;\n while (children = node.children) node = children[children.length - 1];\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var separation = defaultSeparation,\n dx = 1,\n dy = 1,\n nodeSize = false;\n\n function cluster(root) {\n var previousNode,\n x = 0;\n\n // First walk, computing the initial x & y values.\n root.eachAfter(function(node) {\n var children = node.children;\n if (children) {\n node.x = meanX(children);\n node.y = maxY(children);\n } else {\n node.x = previousNode ? x += separation(node, previousNode) : 0;\n node.y = 0;\n previousNode = node;\n }\n });\n\n var left = leafLeft(root),\n right = leafRight(root),\n x0 = left.x - separation(left, right) / 2,\n x1 = right.x + separation(right, left) / 2;\n\n // Second walk, normalizing x & y to the desired size.\n return root.eachAfter(nodeSize ? function(node) {\n node.x = (node.x - root.x) * dx;\n node.y = (root.y - node.y) * dy;\n } : function(node) {\n node.x = (node.x - x0) / (x1 - x0) * dx;\n node.y = (1 - (root.y ? node.y / root.y : 1)) * dy;\n });\n }\n\n cluster.separation = function(x) {\n return arguments.length ? (separation = x, cluster) : separation;\n };\n\n cluster.size = function(x) {\n return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? null : [dx, dy]);\n };\n\n cluster.nodeSize = function(x) {\n return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], cluster) : (nodeSize ? [dx, dy] : null);\n };\n\n return cluster;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/cluster.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/constant.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/constant.js ***! + \*************************************************************************/ +/*! exports provided: constantZero, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"constantZero\", function() { return constantZero; });\nfunction constantZero() {\n return 0;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/ancestors.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/ancestors.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var node = this, nodes = [node];\n while (node = node.parent) {\n nodes.push(node);\n }\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/ancestors.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/count.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/count.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction count(node) {\n var sum = 0,\n children = node.children,\n i = children && children.length;\n if (!i) sum = 1;\n else while (--i >= 0) sum += children[i].value;\n node.value = sum;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.eachAfter(count);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/count.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/descendants.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/descendants.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = [];\n this.each(function(node) {\n nodes.push(node);\n });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/descendants.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/each.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/each.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, current, next = [node], children, i, n;\n do {\n current = next.reverse(), next = [];\n while (node = current.pop()) {\n callback(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n next.push(children[i]);\n }\n }\n } while (next.length);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachAfter.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachAfter.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, nodes = [node], next = [], children, i, n;\n while (node = nodes.pop()) {\n next.push(node), children = node.children;\n if (children) for (i = 0, n = children.length; i < n; ++i) {\n nodes.push(children[i]);\n }\n }\n while (node = next.pop()) {\n callback(node);\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachAfter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachBefore.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachBefore.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var node = this, nodes = [node], children, i;\n while (node = nodes.pop()) {\n callback(node), children = node.children;\n if (children) for (i = children.length - 1; i >= 0; --i) {\n nodes.push(children[i]);\n }\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachBefore.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js ***! + \********************************************************************************/ +/*! exports provided: default, computeHeight, Node */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return hierarchy; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"computeHeight\", function() { return computeHeight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Node\", function() { return Node; });\n/* harmony import */ var _count__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./count */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/count.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/each.js\");\n/* harmony import */ var _eachBefore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./eachBefore */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachBefore.js\");\n/* harmony import */ var _eachAfter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./eachAfter */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/eachAfter.js\");\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sum */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sum.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sort.js\");\n/* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./path */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/path.js\");\n/* harmony import */ var _ancestors__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./ancestors */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/ancestors.js\");\n/* harmony import */ var _descendants__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./descendants */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/descendants.js\");\n/* harmony import */ var _leaves__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./leaves */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/leaves.js\");\n/* harmony import */ var _links__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./links */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/links.js\");\n\n\n\n\n\n\n\n\n\n\n\n\nfunction hierarchy(data, children) {\n var root = new Node(data),\n valued = +data.value && (root.value = data.value),\n node,\n nodes = [root],\n child,\n childs,\n i,\n n;\n\n if (children == null) children = defaultChildren;\n\n while (node = nodes.pop()) {\n if (valued) node.value = +node.data.value;\n if ((childs = children(node.data)) && (n = childs.length)) {\n node.children = new Array(n);\n for (i = n - 1; i >= 0; --i) {\n nodes.push(child = node.children[i] = new Node(childs[i]));\n child.parent = node;\n child.depth = node.depth + 1;\n }\n }\n }\n\n return root.eachBefore(computeHeight);\n}\n\nfunction node_copy() {\n return hierarchy(this).eachBefore(copyData);\n}\n\nfunction defaultChildren(d) {\n return d.children;\n}\n\nfunction copyData(node) {\n node.data = node.data.data;\n}\n\nfunction computeHeight(node) {\n var height = 0;\n do node.height = height;\n while ((node = node.parent) && (node.height < ++height));\n}\n\nfunction Node(data) {\n this.data = data;\n this.depth =\n this.height = 0;\n this.parent = null;\n}\n\nNode.prototype = hierarchy.prototype = {\n constructor: Node,\n count: _count__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n eachAfter: _eachAfter__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n eachBefore: _eachBefore__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n sum: _sum__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n path: _path__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n ancestors: _ancestors__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n descendants: _descendants__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n leaves: _leaves__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n links: _links__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n copy: node_copy\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/leaves.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/leaves.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var leaves = [];\n this.eachBefore(function(node) {\n if (!node.children) {\n leaves.push(node);\n }\n });\n return leaves;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/leaves.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/links.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/links.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var root = this, links = [];\n root.each(function(node) {\n if (node !== root) { // Don’t include the root’s parent, if any.\n links.push({source: node.parent, target: node});\n }\n });\n return links;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/links.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/path.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/path.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(end) {\n var start = this,\n ancestor = leastCommonAncestor(start, end),\n nodes = [start];\n while (start !== ancestor) {\n start = start.parent;\n nodes.push(start);\n }\n var k = nodes.length;\n while (end !== ancestor) {\n nodes.splice(k, 0, end);\n end = end.parent;\n }\n return nodes;\n});\n\nfunction leastCommonAncestor(a, b) {\n if (a === b) return a;\n var aNodes = a.ancestors(),\n bNodes = b.ancestors(),\n c = null;\n a = aNodes.pop();\n b = bNodes.pop();\n while (a === b) {\n c = a;\n a = aNodes.pop();\n b = bNodes.pop();\n }\n return c;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/path.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sort.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sort.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n return this.eachBefore(function(node) {\n if (node.children) {\n node.children.sort(compare);\n }\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sum.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sum.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.eachAfter(function(node) {\n var sum = +value(node.data) || 0,\n children = node.children,\n i = children && children.length;\n while (--i >= 0) sum += children[i].value;\n node.value = sum;\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/sum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/enclose.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/enclose.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/array.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(circles) {\n var i = 0, n = (circles = Object(_array__WEBPACK_IMPORTED_MODULE_0__[\"shuffle\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(circles))).length, B = [], p, e;\n\n while (i < n) {\n p = circles[i];\n if (e && enclosesWeak(e, p)) ++i;\n else e = encloseBasis(B = extendBasis(B, p)), i = 0;\n }\n\n return e;\n});\n\nfunction extendBasis(B, p) {\n var i, j;\n\n if (enclosesWeakAll(p, B)) return [p];\n\n // If we get here then B must have at least one element.\n for (i = 0; i < B.length; ++i) {\n if (enclosesNot(p, B[i])\n && enclosesWeakAll(encloseBasis2(B[i], p), B)) {\n return [B[i], p];\n }\n }\n\n // If we get here then B must have at least two elements.\n for (i = 0; i < B.length - 1; ++i) {\n for (j = i + 1; j < B.length; ++j) {\n if (enclosesNot(encloseBasis2(B[i], B[j]), p)\n && enclosesNot(encloseBasis2(B[i], p), B[j])\n && enclosesNot(encloseBasis2(B[j], p), B[i])\n && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {\n return [B[i], B[j], p];\n }\n }\n }\n\n // If we get here then something is very wrong.\n throw new Error;\n}\n\nfunction enclosesNot(a, b) {\n var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y;\n return dr < 0 || dr * dr < dx * dx + dy * dy;\n}\n\nfunction enclosesWeak(a, b) {\n var dr = a.r - b.r + 1e-6, dx = b.x - a.x, dy = b.y - a.y;\n return dr > 0 && dr * dr > dx * dx + dy * dy;\n}\n\nfunction enclosesWeakAll(a, B) {\n for (var i = 0; i < B.length; ++i) {\n if (!enclosesWeak(a, B[i])) {\n return false;\n }\n }\n return true;\n}\n\nfunction encloseBasis(B) {\n switch (B.length) {\n case 1: return encloseBasis1(B[0]);\n case 2: return encloseBasis2(B[0], B[1]);\n case 3: return encloseBasis3(B[0], B[1], B[2]);\n }\n}\n\nfunction encloseBasis1(a) {\n return {\n x: a.x,\n y: a.y,\n r: a.r\n };\n}\n\nfunction encloseBasis2(a, b) {\n var x1 = a.x, y1 = a.y, r1 = a.r,\n x2 = b.x, y2 = b.y, r2 = b.r,\n x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,\n l = Math.sqrt(x21 * x21 + y21 * y21);\n return {\n x: (x1 + x2 + x21 / l * r21) / 2,\n y: (y1 + y2 + y21 / l * r21) / 2,\n r: (l + r1 + r2) / 2\n };\n}\n\nfunction encloseBasis3(a, b, c) {\n var x1 = a.x, y1 = a.y, r1 = a.r,\n x2 = b.x, y2 = b.y, r2 = b.r,\n x3 = c.x, y3 = c.y, r3 = c.r,\n a2 = x1 - x2,\n a3 = x1 - x3,\n b2 = y1 - y2,\n b3 = y1 - y3,\n c2 = r2 - r1,\n c3 = r3 - r1,\n d1 = x1 * x1 + y1 * y1 - r1 * r1,\n d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,\n d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,\n ab = a3 * b2 - a2 * b3,\n xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,\n xb = (b3 * c2 - b2 * c3) / ab,\n ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,\n yb = (a2 * c3 - a3 * c2) / ab,\n A = xb * xb + yb * yb - 1,\n B = 2 * (r1 + xa * xb + ya * yb),\n C = xa * xa + ya * ya - r1 * r1,\n r = -(A ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);\n return {\n x: x1 + xa + xb * r,\n y: y1 + ya + yb * r,\n r: r\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/enclose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/index.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/index.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _siblings__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./siblings */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/siblings.js\");\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../accessors */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/constant.js\");\n\n\n\n\nfunction defaultRadius(d) {\n return Math.sqrt(d.value);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var radius = null,\n dx = 1,\n dy = 1,\n padding = _constant__WEBPACK_IMPORTED_MODULE_2__[\"constantZero\"];\n\n function pack(root) {\n root.x = dx / 2, root.y = dy / 2;\n if (radius) {\n root.eachBefore(radiusLeaf(radius))\n .eachAfter(packChildren(padding, 0.5))\n .eachBefore(translateChild(1));\n } else {\n root.eachBefore(radiusLeaf(defaultRadius))\n .eachAfter(packChildren(_constant__WEBPACK_IMPORTED_MODULE_2__[\"constantZero\"], 1))\n .eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))\n .eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));\n }\n return root;\n }\n\n pack.radius = function(x) {\n return arguments.length ? (radius = Object(_accessors__WEBPACK_IMPORTED_MODULE_1__[\"optional\"])(x), pack) : radius;\n };\n\n pack.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], pack) : [dx, dy];\n };\n\n pack.padding = function(x) {\n return arguments.length ? (padding = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+x), pack) : padding;\n };\n\n return pack;\n});\n\nfunction radiusLeaf(radius) {\n return function(node) {\n if (!node.children) {\n node.r = Math.max(0, +radius(node) || 0);\n }\n };\n}\n\nfunction packChildren(padding, k) {\n return function(node) {\n if (children = node.children) {\n var children,\n i,\n n = children.length,\n r = padding(node) * k || 0,\n e;\n\n if (r) for (i = 0; i < n; ++i) children[i].r += r;\n e = Object(_siblings__WEBPACK_IMPORTED_MODULE_0__[\"packEnclose\"])(children);\n if (r) for (i = 0; i < n; ++i) children[i].r -= r;\n node.r = e + r;\n }\n };\n}\n\nfunction translateChild(k) {\n return function(node) {\n var parent = node.parent;\n node.r *= k;\n if (parent) {\n node.x = parent.x + k * node.x;\n node.y = parent.y + k * node.y;\n }\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/siblings.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/siblings.js ***! + \******************************************************************************/ +/*! exports provided: packEnclose, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return packEnclose; });\n/* harmony import */ var _enclose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./enclose */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/enclose.js\");\n\n\nfunction place(a, b, c) {\n var ax = a.x,\n ay = a.y,\n da = b.r + c.r,\n db = a.r + c.r,\n dx = b.x - ax,\n dy = b.y - ay,\n dc = dx * dx + dy * dy;\n if (dc) {\n var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc),\n y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);\n c.x = ax + x * dx + y * dy;\n c.y = ay + x * dy - y * dx;\n } else {\n c.x = ax + db;\n c.y = ay;\n }\n}\n\nfunction intersects(a, b) {\n var dx = b.x - a.x,\n dy = b.y - a.y,\n dr = a.r + b.r;\n return dr * dr - 1e-6 > dx * dx + dy * dy;\n}\n\nfunction score(node) {\n var a = node._,\n b = node.next._,\n ab = a.r + b.r,\n dx = (a.x * b.r + b.x * a.r) / ab,\n dy = (a.y * b.r + b.y * a.r) / ab;\n return dx * dx + dy * dy;\n}\n\nfunction Node(circle) {\n this._ = circle;\n this.next = null;\n this.previous = null;\n}\n\nfunction packEnclose(circles) {\n if (!(n = circles.length)) return 0;\n\n var a, b, c, n, aa, ca, i, j, k, sj, sk;\n\n // Place the first circle.\n a = circles[0], a.x = 0, a.y = 0;\n if (!(n > 1)) return a.r;\n\n // Place the second circle.\n b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;\n if (!(n > 2)) return a.r + b.r;\n\n // Place the third circle.\n place(b, a, c = circles[2]);\n\n // Initialize the front-chain using the first three circles a, b and c.\n a = new Node(a), b = new Node(b), c = new Node(c);\n a.next = c.previous = b;\n b.next = a.previous = c;\n c.next = b.previous = a;\n\n // Attempt to place each remaining circle…\n pack: for (i = 3; i < n; ++i) {\n place(a._, b._, c = circles[i]), c = new Node(c);\n\n // Find the closest intersecting circle on the front-chain, if any.\n // “Closeness” is determined by linear distance along the front-chain.\n // “Ahead” or “behind” is likewise determined by linear distance.\n j = b.next, k = a.previous, sj = b._.r, sk = a._.r;\n do {\n if (sj <= sk) {\n if (intersects(j._, c._)) {\n b = j, a.next = b, b.previous = a, --i;\n continue pack;\n }\n sj += j._.r, j = j.next;\n } else {\n if (intersects(k._, c._)) {\n a = k, a.next = b, b.previous = a, --i;\n continue pack;\n }\n sk += k._.r, k = k.previous;\n }\n } while (j !== k.next);\n\n // Success! Insert the new circle c between a and b.\n c.previous = a, c.next = b, a.next = b.previous = b = c;\n\n // Compute the new closest circle pair to the centroid.\n aa = score(a);\n while ((c = c.next) !== b) {\n if ((ca = score(c)) < aa) {\n a = c, aa = ca;\n }\n }\n b = a.next;\n }\n\n // Compute the enclosing circle of the front chain.\n a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = Object(_enclose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a);\n\n // Translate the circles to put the enclosing circle around the origin.\n for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;\n\n return c.r;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(circles) {\n packEnclose(circles);\n return circles;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/pack/siblings.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/partition.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/partition.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _treemap_round__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./treemap/round */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/round.js\");\n/* harmony import */ var _treemap_dice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./treemap/dice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var dx = 1,\n dy = 1,\n padding = 0,\n round = false;\n\n function partition(root) {\n var n = root.height + 1;\n root.x0 =\n root.y0 = padding;\n root.x1 = dx;\n root.y1 = dy / n;\n root.eachBefore(positionNode(dy, n));\n if (round) root.eachBefore(_treemap_round__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n return root;\n }\n\n function positionNode(dy, n) {\n return function(node) {\n if (node.children) {\n Object(_treemap_dice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, node.x0, dy * (node.depth + 1) / n, node.x1, dy * (node.depth + 2) / n);\n }\n var x0 = node.x0,\n y0 = node.y0,\n x1 = node.x1 - padding,\n y1 = node.y1 - padding;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n };\n }\n\n partition.round = function(x) {\n return arguments.length ? (round = !!x, partition) : round;\n };\n\n partition.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], partition) : [dx, dy];\n };\n\n partition.padding = function(x) {\n return arguments.length ? (padding = +x, partition) : padding;\n };\n\n return partition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/partition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/stratify.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/stratify.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./accessors */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./hierarchy/index */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js\");\n\n\n\nvar keyPrefix = \"$\", // Protect against keys like “__proto__”.\n preroot = {depth: -1},\n ambiguous = {};\n\nfunction defaultId(d) {\n return d.id;\n}\n\nfunction defaultParentId(d) {\n return d.parentId;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var id = defaultId,\n parentId = defaultParentId;\n\n function stratify(data) {\n var d,\n i,\n n = data.length,\n root,\n parent,\n node,\n nodes = new Array(n),\n nodeId,\n nodeKey,\n nodeByKey = {};\n\n for (i = 0; i < n; ++i) {\n d = data[i], node = nodes[i] = new _hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"Node\"](d);\n if ((nodeId = id(d, i, data)) != null && (nodeId += \"\")) {\n nodeKey = keyPrefix + (node.id = nodeId);\n nodeByKey[nodeKey] = nodeKey in nodeByKey ? ambiguous : node;\n }\n }\n\n for (i = 0; i < n; ++i) {\n node = nodes[i], nodeId = parentId(data[i], i, data);\n if (nodeId == null || !(nodeId += \"\")) {\n if (root) throw new Error(\"multiple roots\");\n root = node;\n } else {\n parent = nodeByKey[keyPrefix + nodeId];\n if (!parent) throw new Error(\"missing: \" + nodeId);\n if (parent === ambiguous) throw new Error(\"ambiguous: \" + nodeId);\n if (parent.children) parent.children.push(node);\n else parent.children = [node];\n node.parent = parent;\n }\n }\n\n if (!root) throw new Error(\"no root\");\n root.parent = preroot;\n root.eachBefore(function(node) { node.depth = node.parent.depth + 1; --n; }).eachBefore(_hierarchy_index__WEBPACK_IMPORTED_MODULE_1__[\"computeHeight\"]);\n root.parent = null;\n if (n > 0) throw new Error(\"cycle\");\n\n return root;\n }\n\n stratify.id = function(x) {\n return arguments.length ? (id = Object(_accessors__WEBPACK_IMPORTED_MODULE_0__[\"required\"])(x), stratify) : id;\n };\n\n stratify.parentId = function(x) {\n return arguments.length ? (parentId = Object(_accessors__WEBPACK_IMPORTED_MODULE_0__[\"required\"])(x), stratify) : parentId;\n };\n\n return stratify;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/stratify.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/tree.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/tree.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _hierarchy_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./hierarchy/index */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/hierarchy/index.js\");\n\n\nfunction defaultSeparation(a, b) {\n return a.parent === b.parent ? 1 : 2;\n}\n\n// function radialSeparation(a, b) {\n// return (a.parent === b.parent ? 1 : 2) / a.depth;\n// }\n\n// This function is used to traverse the left contour of a subtree (or\n// subforest). It returns the successor of v on this contour. This successor is\n// either given by the leftmost child of v or by the thread of v. The function\n// returns null if and only if v is on the highest level of its subtree.\nfunction nextLeft(v) {\n var children = v.children;\n return children ? children[0] : v.t;\n}\n\n// This function works analogously to nextLeft.\nfunction nextRight(v) {\n var children = v.children;\n return children ? children[children.length - 1] : v.t;\n}\n\n// Shifts the current subtree rooted at w+. This is done by increasing\n// prelim(w+) and mod(w+) by shift.\nfunction moveSubtree(wm, wp, shift) {\n var change = shift / (wp.i - wm.i);\n wp.c -= change;\n wp.s += shift;\n wm.c += change;\n wp.z += shift;\n wp.m += shift;\n}\n\n// All other shifts, applied to the smaller subtrees between w- and w+, are\n// performed by this function. To prepare the shifts, we have to adjust\n// change(w+), shift(w+), and change(w-).\nfunction executeShifts(v) {\n var shift = 0,\n change = 0,\n children = v.children,\n i = children.length,\n w;\n while (--i >= 0) {\n w = children[i];\n w.z += shift;\n w.m += shift;\n shift += w.s + (change += w.c);\n }\n}\n\n// If vi-’s ancestor is a sibling of v, returns vi-’s ancestor. Otherwise,\n// returns the specified (default) ancestor.\nfunction nextAncestor(vim, v, ancestor) {\n return vim.a.parent === v.parent ? vim.a : ancestor;\n}\n\nfunction TreeNode(node, i) {\n this._ = node;\n this.parent = null;\n this.children = null;\n this.A = null; // default ancestor\n this.a = this; // ancestor\n this.z = 0; // prelim\n this.m = 0; // mod\n this.c = 0; // change\n this.s = 0; // shift\n this.t = null; // thread\n this.i = i; // number\n}\n\nTreeNode.prototype = Object.create(_hierarchy_index__WEBPACK_IMPORTED_MODULE_0__[\"Node\"].prototype);\n\nfunction treeRoot(root) {\n var tree = new TreeNode(root, 0),\n node,\n nodes = [tree],\n child,\n children,\n i,\n n;\n\n while (node = nodes.pop()) {\n if (children = node._.children) {\n node.children = new Array(n = children.length);\n for (i = n - 1; i >= 0; --i) {\n nodes.push(child = node.children[i] = new TreeNode(children[i], i));\n child.parent = node;\n }\n }\n }\n\n (tree.parent = new TreeNode(null, 0)).children = [tree];\n return tree;\n}\n\n// Node-link tree diagram using the Reingold-Tilford \"tidy\" algorithm\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var separation = defaultSeparation,\n dx = 1,\n dy = 1,\n nodeSize = null;\n\n function tree(root) {\n var t = treeRoot(root);\n\n // Compute the layout using Buchheim et al.’s algorithm.\n t.eachAfter(firstWalk), t.parent.m = -t.z;\n t.eachBefore(secondWalk);\n\n // If a fixed node size is specified, scale x and y.\n if (nodeSize) root.eachBefore(sizeNode);\n\n // If a fixed tree size is specified, scale x and y based on the extent.\n // Compute the left-most, right-most, and depth-most nodes for extents.\n else {\n var left = root,\n right = root,\n bottom = root;\n root.eachBefore(function(node) {\n if (node.x < left.x) left = node;\n if (node.x > right.x) right = node;\n if (node.depth > bottom.depth) bottom = node;\n });\n var s = left === right ? 1 : separation(left, right) / 2,\n tx = s - left.x,\n kx = dx / (right.x + s + tx),\n ky = dy / (bottom.depth || 1);\n root.eachBefore(function(node) {\n node.x = (node.x + tx) * kx;\n node.y = node.depth * ky;\n });\n }\n\n return root;\n }\n\n // Computes a preliminary x-coordinate for v. Before that, FIRST WALK is\n // applied recursively to the children of v, as well as the function\n // APPORTION. After spacing out the children by calling EXECUTE SHIFTS, the\n // node v is placed to the midpoint of its outermost children.\n function firstWalk(v) {\n var children = v.children,\n siblings = v.parent.children,\n w = v.i ? siblings[v.i - 1] : null;\n if (children) {\n executeShifts(v);\n var midpoint = (children[0].z + children[children.length - 1].z) / 2;\n if (w) {\n v.z = w.z + separation(v._, w._);\n v.m = v.z - midpoint;\n } else {\n v.z = midpoint;\n }\n } else if (w) {\n v.z = w.z + separation(v._, w._);\n }\n v.parent.A = apportion(v, w, v.parent.A || siblings[0]);\n }\n\n // Computes all real x-coordinates by summing up the modifiers recursively.\n function secondWalk(v) {\n v._.x = v.z + v.parent.m;\n v.m += v.parent.m;\n }\n\n // The core of the algorithm. Here, a new subtree is combined with the\n // previous subtrees. Threads are used to traverse the inside and outside\n // contours of the left and right subtree up to the highest common level. The\n // vertices used for the traversals are vi+, vi-, vo-, and vo+, where the\n // superscript o means outside and i means inside, the subscript - means left\n // subtree and + means right subtree. For summing up the modifiers along the\n // contour, we use respective variables si+, si-, so-, and so+. Whenever two\n // nodes of the inside contours conflict, we compute the left one of the\n // greatest uncommon ancestors using the function ANCESTOR and call MOVE\n // SUBTREE to shift the subtree and prepare the shifts of smaller subtrees.\n // Finally, we add a new thread (if necessary).\n function apportion(v, w, ancestor) {\n if (w) {\n var vip = v,\n vop = v,\n vim = w,\n vom = vip.parent.children[0],\n sip = vip.m,\n sop = vop.m,\n sim = vim.m,\n som = vom.m,\n shift;\n while (vim = nextRight(vim), vip = nextLeft(vip), vim && vip) {\n vom = nextLeft(vom);\n vop = nextRight(vop);\n vop.a = v;\n shift = vim.z + sim - vip.z - sip + separation(vim._, vip._);\n if (shift > 0) {\n moveSubtree(nextAncestor(vim, v, ancestor), v, shift);\n sip += shift;\n sop += shift;\n }\n sim += vim.m;\n sip += vip.m;\n som += vom.m;\n sop += vop.m;\n }\n if (vim && !nextRight(vop)) {\n vop.t = vim;\n vop.m += sim - sop;\n }\n if (vip && !nextLeft(vom)) {\n vom.t = vip;\n vom.m += sip - som;\n ancestor = v;\n }\n }\n return ancestor;\n }\n\n function sizeNode(node) {\n node.x *= dx;\n node.y = node.depth * dy;\n }\n\n tree.separation = function(x) {\n return arguments.length ? (separation = x, tree) : separation;\n };\n\n tree.size = function(x) {\n return arguments.length ? (nodeSize = false, dx = +x[0], dy = +x[1], tree) : (nodeSize ? null : [dx, dy]);\n };\n\n tree.nodeSize = function(x) {\n return arguments.length ? (nodeSize = true, dx = +x[0], dy = +x[1], tree) : (nodeSize ? [dx, dy] : null);\n };\n\n return tree;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/tree.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/binary.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/binary.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n i, n = nodes.length,\n sum, sums = new Array(n + 1);\n\n for (sums[0] = sum = i = 0; i < n; ++i) {\n sums[i + 1] = sum += nodes[i].value;\n }\n\n partition(0, n, parent.value, x0, y0, x1, y1);\n\n function partition(i, j, value, x0, y0, x1, y1) {\n if (i >= j - 1) {\n var node = nodes[i];\n node.x0 = x0, node.y0 = y0;\n node.x1 = x1, node.y1 = y1;\n return;\n }\n\n var valueOffset = sums[i],\n valueTarget = (value / 2) + valueOffset,\n k = i + 1,\n hi = j - 1;\n\n while (k < hi) {\n var mid = k + hi >>> 1;\n if (sums[mid] < valueTarget) k = mid + 1;\n else hi = mid;\n }\n\n if ((valueTarget - sums[k - 1]) < (sums[k] - valueTarget) && i + 1 < k) --k;\n\n var valueLeft = sums[k] - valueOffset,\n valueRight = value - valueLeft;\n\n if ((x1 - x0) > (y1 - y0)) {\n var xk = (x0 * valueRight + x1 * valueLeft) / value;\n partition(i, k, valueLeft, x0, y0, xk, y1);\n partition(k, j, valueRight, xk, y0, x1, y1);\n } else {\n var yk = (y0 * valueRight + y1 * valueLeft) / value;\n partition(i, k, valueLeft, x0, y0, x1, yk);\n partition(k, j, valueRight, x0, yk, x1, y1);\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/binary.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (x1 - x0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.y0 = y0, node.y1 = y1;\n node.x0 = x0, node.x1 = x0 += node.value * k;\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/index.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/index.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./round */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/round.js\");\n/* harmony import */ var _squarify__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./squarify */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js\");\n/* harmony import */ var _accessors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../accessors */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/accessors.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/constant.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var tile = _squarify__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n round = false,\n dx = 1,\n dy = 1,\n paddingStack = [0],\n paddingInner = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingTop = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingRight = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingBottom = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"],\n paddingLeft = _constant__WEBPACK_IMPORTED_MODULE_3__[\"constantZero\"];\n\n function treemap(root) {\n root.x0 =\n root.y0 = 0;\n root.x1 = dx;\n root.y1 = dy;\n root.eachBefore(positionNode);\n paddingStack = [0];\n if (round) root.eachBefore(_round__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n return root;\n }\n\n function positionNode(node) {\n var p = paddingStack[node.depth],\n x0 = node.x0 + p,\n y0 = node.y0 + p,\n x1 = node.x1 - p,\n y1 = node.y1 - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n node.x0 = x0;\n node.y0 = y0;\n node.x1 = x1;\n node.y1 = y1;\n if (node.children) {\n p = paddingStack[node.depth + 1] = paddingInner(node) / 2;\n x0 += paddingLeft(node) - p;\n y0 += paddingTop(node) - p;\n x1 -= paddingRight(node) - p;\n y1 -= paddingBottom(node) - p;\n if (x1 < x0) x0 = x1 = (x0 + x1) / 2;\n if (y1 < y0) y0 = y1 = (y0 + y1) / 2;\n tile(node, x0, y0, x1, y1);\n }\n }\n\n treemap.round = function(x) {\n return arguments.length ? (round = !!x, treemap) : round;\n };\n\n treemap.size = function(x) {\n return arguments.length ? (dx = +x[0], dy = +x[1], treemap) : [dx, dy];\n };\n\n treemap.tile = function(x) {\n return arguments.length ? (tile = Object(_accessors__WEBPACK_IMPORTED_MODULE_2__[\"required\"])(x), treemap) : tile;\n };\n\n treemap.padding = function(x) {\n return arguments.length ? treemap.paddingInner(x).paddingOuter(x) : treemap.paddingInner();\n };\n\n treemap.paddingInner = function(x) {\n return arguments.length ? (paddingInner = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingInner;\n };\n\n treemap.paddingOuter = function(x) {\n return arguments.length ? treemap.paddingTop(x).paddingRight(x).paddingBottom(x).paddingLeft(x) : treemap.paddingTop();\n };\n\n treemap.paddingTop = function(x) {\n return arguments.length ? (paddingTop = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingTop;\n };\n\n treemap.paddingRight = function(x) {\n return arguments.length ? (paddingRight = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingRight;\n };\n\n treemap.paddingBottom = function(x) {\n return arguments.length ? (paddingBottom = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingBottom;\n };\n\n treemap.paddingLeft = function(x) {\n return arguments.length ? (paddingLeft = typeof x === \"function\" ? x : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(+x), treemap) : paddingLeft;\n };\n\n return treemap;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/resquarify.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/resquarify.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js\");\n/* harmony import */ var _squarify__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./squarify */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(ratio) {\n\n function resquarify(parent, x0, y0, x1, y1) {\n if ((rows = parent._squarify) && (rows.ratio === ratio)) {\n var rows,\n row,\n nodes,\n i,\n j = -1,\n n,\n m = rows.length,\n value = parent.value;\n\n while (++j < m) {\n row = rows[j], nodes = row.children;\n for (i = row.value = 0, n = nodes.length; i < n; ++i) row.value += nodes[i].value;\n if (row.dice) Object(_dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(row, x0, y0, x1, y0 += (y1 - y0) * row.value / value);\n else Object(_slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(row, x0, y0, x0 += (x1 - x0) * row.value / value, y1);\n value -= row.value;\n }\n } else {\n parent._squarify = rows = Object(_squarify__WEBPACK_IMPORTED_MODULE_2__[\"squarifyRatio\"])(ratio, parent, x0, y0, x1, y1);\n rows.ratio = ratio;\n }\n }\n\n resquarify.ratio = function(x) {\n return custom((x = +x) > 1 ? x : 1);\n };\n\n return resquarify;\n})(_squarify__WEBPACK_IMPORTED_MODULE_2__[\"phi\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/resquarify.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/round.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/round.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n node.x0 = Math.round(node.x0);\n node.y0 = Math.round(node.y0);\n node.x1 = Math.round(node.x1);\n node.y1 = Math.round(node.y1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n var nodes = parent.children,\n node,\n i = -1,\n n = nodes.length,\n k = parent.value && (y1 - y0) / parent.value;\n\n while (++i < n) {\n node = nodes[i], node.x0 = x0, node.x1 = x1;\n node.y0 = y0, node.y1 = y0 += node.value * k;\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/sliceDice.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/sliceDice.js ***! + \**********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(parent, x0, y0, x1, y1) {\n (parent.depth & 1 ? _slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"] : _dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(parent, x0, y0, x1, y1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/sliceDice.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js ***! + \*********************************************************************************/ +/*! exports provided: phi, squarifyRatio, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"phi\", function() { return phi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"squarifyRatio\", function() { return squarifyRatio; });\n/* harmony import */ var _dice__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/dice.js\");\n/* harmony import */ var _slice__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./slice */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/slice.js\");\n\n\n\nvar phi = (1 + Math.sqrt(5)) / 2;\n\nfunction squarifyRatio(ratio, parent, x0, y0, x1, y1) {\n var rows = [],\n nodes = parent.children,\n row,\n nodeValue,\n i0 = 0,\n i1 = 0,\n n = nodes.length,\n dx, dy,\n value = parent.value,\n sumValue,\n minValue,\n maxValue,\n newRatio,\n minRatio,\n alpha,\n beta;\n\n while (i0 < n) {\n dx = x1 - x0, dy = y1 - y0;\n\n // Find the next non-empty node.\n do sumValue = nodes[i1++].value; while (!sumValue && i1 < n);\n minValue = maxValue = sumValue;\n alpha = Math.max(dy / dx, dx / dy) / (value * ratio);\n beta = sumValue * sumValue * alpha;\n minRatio = Math.max(maxValue / beta, beta / minValue);\n\n // Keep adding nodes while the aspect ratio maintains or improves.\n for (; i1 < n; ++i1) {\n sumValue += nodeValue = nodes[i1].value;\n if (nodeValue < minValue) minValue = nodeValue;\n if (nodeValue > maxValue) maxValue = nodeValue;\n beta = sumValue * sumValue * alpha;\n newRatio = Math.max(maxValue / beta, beta / minValue);\n if (newRatio > minRatio) { sumValue -= nodeValue; break; }\n minRatio = newRatio;\n }\n\n // Position and record the row orientation.\n rows.push(row = {value: sumValue, dice: dx < dy, children: nodes.slice(i0, i1)});\n if (row.dice) Object(_dice__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(row, x0, y0, x1, value ? y0 += dy * sumValue / value : y1);\n else Object(_slice__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(row, x0, y0, value ? x0 += dx * sumValue / value : x1, y1);\n value -= sumValue, i0 = i1;\n }\n\n return rows;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(ratio) {\n\n function squarify(parent, x0, y0, x1, y1) {\n squarifyRatio(ratio, parent, x0, y0, x1, y1);\n }\n\n squarify.ratio = function(x) {\n return custom((x = +x) > 1 ? x : 1);\n };\n\n return squarify;\n})(phi));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-hierarchy/src/treemap/squarify.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/index.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/index.js ***! + \********************************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/value */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _src_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/array */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _src_array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/basis */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _src_basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _src_basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/date */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _src_date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_number__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/number */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _src_number__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_object__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/object */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _src_object__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_round__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/round */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _src_round__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_string__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/string */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _src_string__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_transform_index__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/transform/index */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _src_transform_index__WEBPACK_IMPORTED_MODULE_9__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _src_transform_index__WEBPACK_IMPORTED_MODULE_9__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _src_zoom__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/zoom */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _src_zoom__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_rgb__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/rgb */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _src_rgb__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _src_rgb__WEBPACK_IMPORTED_MODULE_11__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _src_rgb__WEBPACK_IMPORTED_MODULE_11__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _src_hsl__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/hsl */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _src_hsl__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _src_hsl__WEBPACK_IMPORTED_MODULE_12__[\"hslLong\"]; });\n\n/* harmony import */ var _src_lab__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/lab */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _src_lab__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_hcl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/hcl */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _src_hcl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _src_hcl__WEBPACK_IMPORTED_MODULE_14__[\"hclLong\"]; });\n\n/* harmony import */ var _src_cubehelix__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _src_cubehelix__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _src_cubehelix__WEBPACK_IMPORTED_MODULE_15__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _src_quantize__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/quantize */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _src_quantize__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js ***! + \**********************************************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/cubehelix.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/cubehelix.js ***! + \**************************************************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js ***! + \***********************************************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js ***! + \**********************************************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/lab.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/lab.js ***! + \********************************************************************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/math.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/math.js ***! + \*********************************************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/array.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/array.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js ***! + \************************************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/basisClosed.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/basisClosed.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js ***! + \************************************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/constant.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/constant.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/cubehelix.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/cubehelix.js ***! + \****************************************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/date.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/date.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/hcl.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/hcl.js ***! + \**********************************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/hsl.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/hsl.js ***! + \**********************************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/lab.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/lab.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/object.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/object.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/quantize.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/quantize.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/rgb.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/rgb.js ***! + \**********************************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/round.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/round.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/string.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/string.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/decompose.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/decompose.js ***! + \**************************************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/index.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/index.js ***! + \**********************************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/parse.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/parse.js ***! + \**********************************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-interpolate/src/zoom.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-interpolate/src/zoom.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-path/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-path/index.js ***! + \*************************************************************/ +/*! exports provided: path */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/path */ \"./node_modules/dagre-d3/node_modules/d3-path/src/path.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return _src_path__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-path/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-path/src/path.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-path/src/path.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon)) {}\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (path);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-path/src/path.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/index.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/index.js ***! + \****************************************************************/ +/*! exports provided: polygonArea, polygonCentroid, polygonHull, polygonContains, polygonLength */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_area__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/area */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonArea\", function() { return _src_area__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_centroid__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/centroid */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/centroid.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonCentroid\", function() { return _src_centroid__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_hull__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/hull */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/hull.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonHull\", function() { return _src_hull__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_contains__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/contains */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/contains.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonContains\", function() { return _src_contains__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_length__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/length */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/length.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonLength\", function() { return _src_length__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/area.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/area.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n a,\n b = polygon[n - 1],\n area = 0;\n\n while (++i < n) {\n a = b;\n b = polygon[i];\n area += a[1] * b[0] - a[0] * b[1];\n }\n\n return area / 2;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/centroid.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/centroid.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n x = 0,\n y = 0,\n a,\n b = polygon[n - 1],\n c,\n k = 0;\n\n while (++i < n) {\n a = b;\n b = polygon[i];\n k += c = a[0] * b[1] - b[0] * a[1];\n x += (a[0] + b[0]) * c;\n y += (a[1] + b[1]) * c;\n }\n\n return k *= 3, [x / k, y / k];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/centroid.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/contains.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/contains.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon, point) {\n var n = polygon.length,\n p = polygon[n - 1],\n x = point[0], y = point[1],\n x0 = p[0], y0 = p[1],\n x1, y1,\n inside = false;\n\n for (var i = 0; i < n; ++i) {\n p = polygon[i], x1 = p[0], y1 = p[1];\n if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) inside = !inside;\n x0 = x1, y0 = y1;\n }\n\n return inside;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/contains.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/cross.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/cross.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Returns the 2D cross product of AB and AC vectors, i.e., the z-component of\n// the 3D cross product in a quadrant I Cartesian coordinate system (+x is\n// right, +y is up). Returns a positive value if ABC is counter-clockwise,\n// negative if clockwise, and zero if the points are collinear.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c) {\n return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/hull.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/hull.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cross */ \"./node_modules/dagre-d3/node_modules/d3-polygon/src/cross.js\");\n\n\nfunction lexicographicOrder(a, b) {\n return a[0] - b[0] || a[1] - b[1];\n}\n\n// Computes the upper convex hull per the monotone chain algorithm.\n// Assumes points.length >= 3, is sorted by x, unique in y.\n// Returns an array of indices into points in left-to-right order.\nfunction computeUpperHullIndexes(points) {\n var n = points.length,\n indexes = [0, 1],\n size = 2;\n\n for (var i = 2; i < n; ++i) {\n while (size > 1 && Object(_cross__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) --size;\n indexes[size++] = i;\n }\n\n return indexes.slice(0, size); // remove popped points\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(points) {\n if ((n = points.length) < 3) return null;\n\n var i,\n n,\n sortedPoints = new Array(n),\n flippedPoints = new Array(n);\n\n for (i = 0; i < n; ++i) sortedPoints[i] = [+points[i][0], +points[i][1], i];\n sortedPoints.sort(lexicographicOrder);\n for (i = 0; i < n; ++i) flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]];\n\n var upperIndexes = computeUpperHullIndexes(sortedPoints),\n lowerIndexes = computeUpperHullIndexes(flippedPoints);\n\n // Construct the hull polygon, removing possible duplicate endpoints.\n var skipLeft = lowerIndexes[0] === upperIndexes[0],\n skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],\n hull = [];\n\n // Add upper hull in right-to-l order.\n // Then add lower hull in left-to-right order.\n for (i = upperIndexes.length - 1; i >= 0; --i) hull.push(points[sortedPoints[upperIndexes[i]][2]]);\n for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) hull.push(points[sortedPoints[lowerIndexes[i]][2]]);\n\n return hull;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/hull.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-polygon/src/length.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-polygon/src/length.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(polygon) {\n var i = -1,\n n = polygon.length,\n b = polygon[n - 1],\n xa,\n ya,\n xb = b[0],\n yb = b[1],\n perimeter = 0;\n\n while (++i < n) {\n xa = xb;\n ya = yb;\n b = polygon[i];\n xb = b[0];\n yb = b[1];\n xa -= xb;\n ya -= yb;\n perimeter += Math.sqrt(xa * xa + ya * ya);\n }\n\n return perimeter;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-polygon/src/length.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/index.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/index.js ***! + \*****************************************************************/ +/*! exports provided: quadtree */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_quadtree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/quadtree */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/quadtree.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quadtree\", function() { return _src_quadtree__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/add.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/add.js ***! + \*******************************************************************/ +/*! exports provided: default, addAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"addAll\", function() { return addAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n var x = +this._x.call(null, d),\n y = +this._y.call(null, d);\n return add(this.cover(x, y), x, y, d);\n});\n\nfunction add(tree, x, y, d) {\n if (isNaN(x) || isNaN(y)) return tree; // ignore invalid points\n\n var parent,\n node = tree._root,\n leaf = {data: d},\n x0 = tree._x0,\n y0 = tree._y0,\n x1 = tree._x1,\n y1 = tree._y1,\n xm,\n ym,\n xp,\n yp,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return tree._root = leaf, tree;\n\n // Find the existing leaf for the new point, or add it.\n while (node.length) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (parent = node, !(node = node[i = bottom << 1 | right])) return parent[i] = leaf, tree;\n }\n\n // Is the new point is exactly coincident with the existing point?\n xp = +tree._x.call(null, node.data);\n yp = +tree._y.call(null, node.data);\n if (x === xp && y === yp) return leaf.next = node, parent ? parent[i] = leaf : tree._root = leaf, tree;\n\n // Otherwise, split the leaf node until the old and new point are separated.\n do {\n parent = parent ? parent[i] = new Array(4) : tree._root = new Array(4);\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n } while ((i = bottom << 1 | right) === (j = (yp >= ym) << 1 | (xp >= xm)));\n return parent[j] = node, parent[i] = leaf, tree;\n}\n\nfunction addAll(data) {\n var d, i, n = data.length,\n x,\n y,\n xz = new Array(n),\n yz = new Array(n),\n x0 = Infinity,\n y0 = Infinity,\n x1 = -Infinity,\n y1 = -Infinity;\n\n // Compute the points and their extent.\n for (i = 0; i < n; ++i) {\n if (isNaN(x = +this._x.call(null, d = data[i])) || isNaN(y = +this._y.call(null, d))) continue;\n xz[i] = x;\n yz[i] = y;\n if (x < x0) x0 = x;\n if (x > x1) x1 = x;\n if (y < y0) y0 = y;\n if (y > y1) y1 = y;\n }\n\n // If there were no (valid) points, inherit the existing extent.\n if (x1 < x0) x0 = this._x0, x1 = this._x1;\n if (y1 < y0) y0 = this._y0, y1 = this._y1;\n\n // Expand the tree to cover the new points.\n this.cover(x0, y0).cover(x1, y1);\n\n // Add the new points.\n for (i = 0; i < n; ++i) {\n add(this, xz[i], yz[i], data[i]);\n }\n\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/add.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/cover.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/cover.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n if (isNaN(x = +x) || isNaN(y = +y)) return this; // ignore invalid points\n\n var x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1;\n\n // If the quadtree has no extent, initialize them.\n // Integer extent are necessary so that if we later double the extent,\n // the existing quadrant boundaries don’t change due to floating point error!\n if (isNaN(x0)) {\n x1 = (x0 = Math.floor(x)) + 1;\n y1 = (y0 = Math.floor(y)) + 1;\n }\n\n // Otherwise, double repeatedly to cover.\n else if (x0 > x || x > x1 || y0 > y || y > y1) {\n var z = x1 - x0,\n node = this._root,\n parent,\n i;\n\n switch (i = (y < (y0 + y1) / 2) << 1 | (x < (x0 + x1) / 2)) {\n case 0: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y1 = y0 + z, x > x1 || y > y1);\n break;\n }\n case 1: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y1 = y0 + z, x0 > x || y > y1);\n break;\n }\n case 2: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x1 = x0 + z, y0 = y1 - z, x > x1 || y0 > y);\n break;\n }\n case 3: {\n do parent = new Array(4), parent[i] = node, node = parent;\n while (z *= 2, x0 = x1 - z, y0 = y1 - z, x0 > x || y0 > y);\n break;\n }\n }\n\n if (this._root && this._root.length) this._root = node;\n }\n\n // If the quadtree covers the point already, just return.\n else return this;\n\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/cover.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/data.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/data.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var data = [];\n this.visit(function(node) {\n if (!node.length) do data.push(node.data); while (node = node.next)\n });\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/extent.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/extent.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length\n ? this.cover(+_[0][0], +_[0][1]).cover(+_[1][0], +_[1][1])\n : isNaN(this._x0) ? undefined : [[this._x0, this._y0], [this._x1, this._y1]];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/find.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/find.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y, radius) {\n var data,\n x0 = this._x0,\n y0 = this._y0,\n x1,\n y1,\n x2,\n y2,\n x3 = this._x1,\n y3 = this._y1,\n quads = [],\n node = this._root,\n q,\n i;\n\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, x0, y0, x3, y3));\n if (radius == null) radius = Infinity;\n else {\n x0 = x - radius, y0 = y - radius;\n x3 = x + radius, y3 = y + radius;\n radius *= radius;\n }\n\n while (q = quads.pop()) {\n\n // Stop searching if this quadrant can’t contain a closer node.\n if (!(node = q.node)\n || (x1 = q.x0) > x3\n || (y1 = q.y0) > y3\n || (x2 = q.x1) < x0\n || (y2 = q.y1) < y0) continue;\n\n // Bisect the current quadrant.\n if (node.length) {\n var xm = (x1 + x2) / 2,\n ym = (y1 + y2) / 2;\n\n quads.push(\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[3], xm, ym, x2, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[2], x1, ym, xm, y2),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[1], xm, y1, x2, ym),\n new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node[0], x1, y1, xm, ym)\n );\n\n // Visit the closest quadrant first.\n if (i = (y >= ym) << 1 | (x >= xm)) {\n q = quads[quads.length - 1];\n quads[quads.length - 1] = quads[quads.length - 1 - i];\n quads[quads.length - 1 - i] = q;\n }\n }\n\n // Visit this point. (Visiting coincident points isn’t necessary!)\n else {\n var dx = x - +this._x.call(null, node.data),\n dy = y - +this._y.call(null, node.data),\n d2 = dx * dx + dy * dy;\n if (d2 < radius) {\n var d = Math.sqrt(radius = d2);\n x0 = x - d, y0 = y - d;\n x3 = x + d, y3 = y + d;\n data = node.data;\n }\n }\n }\n\n return data;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/find.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, x0, y0, x1, y1) {\n this.node = node;\n this.x0 = x0;\n this.y0 = y0;\n this.x1 = x1;\n this.y1 = y1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/quadtree.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/quadtree.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quadtree; });\n/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/add.js\");\n/* harmony import */ var _cover__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cover */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/cover.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/data.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/extent.js\");\n/* harmony import */ var _find__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./find */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/find.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/remove.js\");\n/* harmony import */ var _root__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./root */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/root.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/size.js\");\n/* harmony import */ var _visit__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./visit */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/visit.js\");\n/* harmony import */ var _visitAfter__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./visitAfter */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/visitAfter.js\");\n/* harmony import */ var _x__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./x */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/x.js\");\n/* harmony import */ var _y__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./y */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/y.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\nfunction quadtree(nodes, x, y) {\n var tree = new Quadtree(x == null ? _x__WEBPACK_IMPORTED_MODULE_10__[\"defaultX\"] : x, y == null ? _y__WEBPACK_IMPORTED_MODULE_11__[\"defaultY\"] : y, NaN, NaN, NaN, NaN);\n return nodes == null ? tree : tree.addAll(nodes);\n}\n\nfunction Quadtree(x, y, x0, y0, x1, y1) {\n this._x = x;\n this._y = y;\n this._x0 = x0;\n this._y0 = y0;\n this._x1 = x1;\n this._y1 = y1;\n this._root = undefined;\n}\n\nfunction leaf_copy(leaf) {\n var copy = {data: leaf.data}, next = copy;\n while (leaf = leaf.next) next = next.next = {data: leaf.data};\n return copy;\n}\n\nvar treeProto = quadtree.prototype = Quadtree.prototype;\n\ntreeProto.copy = function() {\n var copy = new Quadtree(this._x, this._y, this._x0, this._y0, this._x1, this._y1),\n node = this._root,\n nodes,\n child;\n\n if (!node) return copy;\n\n if (!node.length) return copy._root = leaf_copy(node), copy;\n\n nodes = [{source: node, target: copy._root = new Array(4)}];\n while (node = nodes.pop()) {\n for (var i = 0; i < 4; ++i) {\n if (child = node.source[i]) {\n if (child.length) nodes.push({source: child, target: node.target[i] = new Array(4)});\n else node.target[i] = leaf_copy(child);\n }\n }\n }\n\n return copy;\n};\n\ntreeProto.add = _add__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\ntreeProto.addAll = _add__WEBPACK_IMPORTED_MODULE_0__[\"addAll\"];\ntreeProto.cover = _cover__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\ntreeProto.data = _data__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\ntreeProto.extent = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\ntreeProto.find = _find__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\ntreeProto.remove = _remove__WEBPACK_IMPORTED_MODULE_5__[\"default\"];\ntreeProto.removeAll = _remove__WEBPACK_IMPORTED_MODULE_5__[\"removeAll\"];\ntreeProto.root = _root__WEBPACK_IMPORTED_MODULE_6__[\"default\"];\ntreeProto.size = _size__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\ntreeProto.visit = _visit__WEBPACK_IMPORTED_MODULE_8__[\"default\"];\ntreeProto.visitAfter = _visitAfter__WEBPACK_IMPORTED_MODULE_9__[\"default\"];\ntreeProto.x = _x__WEBPACK_IMPORTED_MODULE_10__[\"default\"];\ntreeProto.y = _y__WEBPACK_IMPORTED_MODULE_11__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/quadtree.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/remove.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/remove.js ***! + \**********************************************************************/ +/*! exports provided: default, removeAll */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeAll\", function() { return removeAll; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n if (isNaN(x = +this._x.call(null, d)) || isNaN(y = +this._y.call(null, d))) return this; // ignore invalid points\n\n var parent,\n node = this._root,\n retainer,\n previous,\n next,\n x0 = this._x0,\n y0 = this._y0,\n x1 = this._x1,\n y1 = this._y1,\n x,\n y,\n xm,\n ym,\n right,\n bottom,\n i,\n j;\n\n // If the tree is empty, initialize the root as a leaf.\n if (!node) return this;\n\n // Find the leaf node for the point.\n // While descending, also retain the deepest parent with a non-removed sibling.\n if (node.length) while (true) {\n if (right = x >= (xm = (x0 + x1) / 2)) x0 = xm; else x1 = xm;\n if (bottom = y >= (ym = (y0 + y1) / 2)) y0 = ym; else y1 = ym;\n if (!(parent = node, node = node[i = bottom << 1 | right])) return this;\n if (!node.length) break;\n if (parent[(i + 1) & 3] || parent[(i + 2) & 3] || parent[(i + 3) & 3]) retainer = parent, j = i;\n }\n\n // Find the point to remove.\n while (node.data !== d) if (!(previous = node, node = node.next)) return this;\n if (next = node.next) delete node.next;\n\n // If there are multiple coincident points, remove just the point.\n if (previous) return (next ? previous.next = next : delete previous.next), this;\n\n // If this is the root point, remove it.\n if (!parent) return this._root = next, this;\n\n // Remove this leaf.\n next ? parent[i] = next : delete parent[i];\n\n // If the parent now contains exactly one leaf, collapse superfluous parents.\n if ((node = parent[0] || parent[1] || parent[2] || parent[3])\n && node === (parent[3] || parent[2] || parent[1] || parent[0])\n && !node.length) {\n if (retainer) retainer[j] = node;\n else this._root = node;\n }\n\n return this;\n});\n\nfunction removeAll(data) {\n for (var i = 0, n = data.length; i < n; ++i) this.remove(data[i]);\n return this;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/root.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/root.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this._root;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/root.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/size.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/size.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.visit(function(node) {\n if (!node.length) do ++size; while (node = node.next)\n });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/visit.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/visit.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], q, node = this._root, child, x0, y0, x1, y1;\n if (node) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](node, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n if (!callback(node = q.node, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1) && node.length) {\n var xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n }\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/visit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/visitAfter.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/visitAfter.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/src/quad.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n var quads = [], next = [], q;\n if (this._root) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this._root, this._x0, this._y0, this._x1, this._y1));\n while (q = quads.pop()) {\n var node = q.node;\n if (node.length) {\n var child, x0 = q.x0, y0 = q.y0, x1 = q.x1, y1 = q.y1, xm = (x0 + x1) / 2, ym = (y0 + y1) / 2;\n if (child = node[0]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, y0, xm, ym));\n if (child = node[1]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, y0, x1, ym));\n if (child = node[2]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, x0, ym, xm, y1));\n if (child = node[3]) quads.push(new _quad__WEBPACK_IMPORTED_MODULE_0__[\"default\"](child, xm, ym, x1, y1));\n }\n next.push(q);\n }\n while (q = next.pop()) {\n callback(q.node, q.x0, q.y0, q.x1, q.y1);\n }\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/visitAfter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/x.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/x.js ***! + \*****************************************************************/ +/*! exports provided: defaultX, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultX\", function() { return defaultX; });\nfunction defaultX(d) {\n return d[0];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._x = _, this) : this._x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/x.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-quadtree/src/y.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-quadtree/src/y.js ***! + \*****************************************************************/ +/*! exports provided: defaultY, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"defaultY\", function() { return defaultY; });\nfunction defaultY(d) {\n return d[1];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(_) {\n return arguments.length ? (this._y = _, this) : this._y;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-quadtree/src/y.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/index.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/index.js ***! + \***************************************************************/ +/*! exports provided: randomUniform, randomNormal, randomLogNormal, randomBates, randomIrwinHall, randomExponential */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_uniform__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/uniform */ \"./node_modules/dagre-d3/node_modules/d3-random/src/uniform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomUniform\", function() { return _src_uniform__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_normal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/normal */ \"./node_modules/dagre-d3/node_modules/d3-random/src/normal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomNormal\", function() { return _src_normal__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_logNormal__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/logNormal */ \"./node_modules/dagre-d3/node_modules/d3-random/src/logNormal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomLogNormal\", function() { return _src_logNormal__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_bates__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/bates */ \"./node_modules/dagre-d3/node_modules/d3-random/src/bates.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomBates\", function() { return _src_bates__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_irwinHall__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/irwinHall */ \"./node_modules/dagre-d3/node_modules/d3-random/src/irwinHall.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomIrwinHall\", function() { return _src_irwinHall__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_exponential__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/exponential */ \"./node_modules/dagre-d3/node_modules/d3-random/src/exponential.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomExponential\", function() { return _src_exponential__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/bates.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/bates.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n/* harmony import */ var _irwinHall__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./irwinHall */ \"./node_modules/dagre-d3/node_modules/d3-random/src/irwinHall.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomBates(source) {\n function randomBates(n) {\n var randomIrwinHall = _irwinHall__WEBPACK_IMPORTED_MODULE_1__[\"default\"].source(source)(n);\n return function() {\n return randomIrwinHall() / n;\n };\n }\n\n randomBates.source = sourceRandomBates;\n\n return randomBates;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/bates.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Math.random();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/exponential.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/exponential.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomExponential(source) {\n function randomExponential(lambda) {\n return function() {\n return -Math.log(1 - source()) / lambda;\n };\n }\n\n randomExponential.source = sourceRandomExponential;\n\n return randomExponential;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/exponential.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/irwinHall.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/irwinHall.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomIrwinHall(source) {\n function randomIrwinHall(n) {\n return function() {\n for (var sum = 0, i = 0; i < n; ++i) sum += source();\n return sum;\n };\n }\n\n randomIrwinHall.source = sourceRandomIrwinHall;\n\n return randomIrwinHall;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/irwinHall.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/logNormal.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/logNormal.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n/* harmony import */ var _normal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./normal */ \"./node_modules/dagre-d3/node_modules/d3-random/src/normal.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomLogNormal(source) {\n function randomLogNormal() {\n var randomNormal = _normal__WEBPACK_IMPORTED_MODULE_1__[\"default\"].source(source).apply(this, arguments);\n return function() {\n return Math.exp(randomNormal());\n };\n }\n\n randomLogNormal.source = sourceRandomLogNormal;\n\n return randomLogNormal;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/logNormal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/normal.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/normal.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomNormal(source) {\n function randomNormal(mu, sigma) {\n var x, r;\n mu = mu == null ? 0 : +mu;\n sigma = sigma == null ? 1 : +sigma;\n return function() {\n var y;\n\n // If available, use the second previously-generated uniform random.\n if (x != null) y = x, x = null;\n\n // Otherwise, generate a new x and y.\n else do {\n x = source() * 2 - 1;\n y = source() * 2 - 1;\n r = x * x + y * y;\n } while (!r || r > 1);\n\n return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r);\n };\n }\n\n randomNormal.source = sourceRandomNormal;\n\n return randomNormal;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/normal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-random/src/uniform.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-random/src/uniform.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultSource__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultSource */ \"./node_modules/dagre-d3/node_modules/d3-random/src/defaultSource.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function sourceRandomUniform(source) {\n function randomUniform(min, max) {\n min = min == null ? 0 : +min;\n max = max == null ? 1 : +max;\n if (arguments.length === 1) max = min, min = 0;\n else max -= min;\n return function() {\n return source() * max + min;\n };\n }\n\n randomUniform.source = sourceRandomUniform;\n\n return randomUniform;\n})(_defaultSource__WEBPACK_IMPORTED_MODULE_0__[\"default\"]));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-random/src/uniform.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/index.js ***! + \**************************************************************/ +/*! exports provided: scaleBand, scalePoint, scaleIdentity, scaleLinear, scaleLog, scaleOrdinal, scaleImplicit, scalePow, scaleSqrt, scaleQuantile, scaleQuantize, scaleThreshold, scaleTime, scaleUtc, schemeCategory10, schemeCategory20b, schemeCategory20c, schemeCategory20, interpolateCubehelixDefault, interpolateRainbow, interpolateWarm, interpolateCool, interpolateViridis, interpolateMagma, interpolateInferno, interpolatePlasma, scaleSequential */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_band__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/band */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/band.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleBand\", function() { return _src_band__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePoint\", function() { return _src_band__WEBPACK_IMPORTED_MODULE_0__[\"point\"]; });\n\n/* harmony import */ var _src_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/identity */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/identity.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleIdentity\", function() { return _src_identity__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/linear */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLinear\", function() { return _src_linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/log */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/log.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLog\", function() { return _src_log__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_ordinal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/ordinal */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/ordinal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleOrdinal\", function() { return _src_ordinal__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleImplicit\", function() { return _src_ordinal__WEBPACK_IMPORTED_MODULE_4__[\"implicit\"]; });\n\n/* harmony import */ var _src_pow__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/pow */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/pow.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePow\", function() { return _src_pow__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSqrt\", function() { return _src_pow__WEBPACK_IMPORTED_MODULE_5__[\"sqrt\"]; });\n\n/* harmony import */ var _src_quantile__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/quantile */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantile\", function() { return _src_quantile__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_quantize__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/quantize */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantize\", function() { return _src_quantize__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_threshold__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/threshold */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/threshold.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleThreshold\", function() { return _src_threshold__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_time__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/time */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/time.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleTime\", function() { return _src_time__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_utcTime__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/utcTime */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/utcTime.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleUtc\", function() { return _src_utcTime__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_category10__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/category10 */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/category10.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory10\", function() { return _src_category10__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_category20b__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/category20b */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/category20b.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20b\", function() { return _src_category20b__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_category20c__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/category20c */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/category20c.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20c\", function() { return _src_category20c__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_category20__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/category20 */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/category20.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20\", function() { return _src_category20__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _src_cubehelix__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixDefault\", function() { return _src_cubehelix__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _src_rainbow__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/rainbow */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/rainbow.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRainbow\", function() { return _src_rainbow__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateWarm\", function() { return _src_rainbow__WEBPACK_IMPORTED_MODULE_16__[\"warm\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCool\", function() { return _src_rainbow__WEBPACK_IMPORTED_MODULE_16__[\"cool\"]; });\n\n/* harmony import */ var _src_viridis__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./src/viridis */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/viridis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateViridis\", function() { return _src_viridis__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateMagma\", function() { return _src_viridis__WEBPACK_IMPORTED_MODULE_17__[\"magma\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateInferno\", function() { return _src_viridis__WEBPACK_IMPORTED_MODULE_17__[\"inferno\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePlasma\", function() { return _src_viridis__WEBPACK_IMPORTED_MODULE_17__[\"plasma\"]; });\n\n/* harmony import */ var _src_sequential__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./src/sequential */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/sequential.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSequential\", function() { return _src_sequential__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/array.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/array.js ***! + \****************************************************************************************/ +/*! exports provided: slice, map */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\nvar array = Array.prototype;\n\nvar slice = array.slice;\nvar map = array.map;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisect.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisect.js ***! + \*****************************************************************************************/ +/*! exports provided: bisectRight, bisectLeft, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return bisectRight; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return bisectLeft; });\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisector.js\");\n\n\n\nvar ascendingBisect = Object(_bisector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\nvar bisectRight = ascendingBisect.right;\nvar bisectLeft = ascendingBisect.left;\n/* harmony default export */ __webpack_exports__[\"default\"] = (bisectRight);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisect.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisector.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisector.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (compare.length === 1) compare = ascendingComparator(compare);\n return {\n left: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) < 0) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n },\n right: function(a, x, lo, hi) {\n if (lo == null) lo = 0;\n if (hi == null) hi = a.length;\n while (lo < hi) {\n var mid = lo + hi >>> 1;\n if (compare(a[mid], x) > 0) hi = mid;\n else lo = mid + 1;\n }\n return lo;\n }\n };\n});\n\nfunction ascendingComparator(f) {\n return function(d, x) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(f(d), x);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/constant.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/constant.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/cross.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/cross.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/pairs.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values0, values1, reduce) {\n var n0 = values0.length,\n n1 = values1.length,\n values = new Array(n0 * n1),\n i0,\n i1,\n i,\n value0;\n\n if (reduce == null) reduce = _pairs__WEBPACK_IMPORTED_MODULE_0__[\"pair\"];\n\n for (i0 = i = 0; i0 < n0; ++i0) {\n for (value0 = values0[i0], i1 = 0; i1 < n1; ++i1, ++i) {\n values[i] = reduce(value0, values1[i1]);\n }\n }\n\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/descending.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/descending.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/deviation.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/deviation.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/variance.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n var v = Object(_variance__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(array, f);\n return v ? Math.sqrt(v) : v;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/deviation.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/extent.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/extent.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null) {\n if (min > value) min = value;\n if (max < value) max = value;\n }\n }\n }\n }\n }\n\n return [min, max];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/extent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/histogram.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/histogram.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisect.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/constant.js\");\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/extent.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/identity.js\");\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/range.js\");\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ticks.js\");\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/sturges.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n domain = _extent__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n threshold = _threshold_sturges__WEBPACK_IMPORTED_MODULE_7__[\"default\"];\n\n function histogram(data) {\n var i,\n n = data.length,\n x,\n values = new Array(n);\n\n for (i = 0; i < n; ++i) {\n values[i] = value(data[i], i, data);\n }\n\n var xz = domain(values),\n x0 = xz[0],\n x1 = xz[1],\n tz = threshold(values, x0, x1);\n\n // Convert number of thresholds into uniform thresholds.\n if (!Array.isArray(tz)) {\n tz = Object(_ticks__WEBPACK_IMPORTED_MODULE_6__[\"tickStep\"])(x0, x1, tz);\n tz = Object(_range__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(Math.ceil(x0 / tz) * tz, x1, tz); // exclusive\n }\n\n // Remove any thresholds outside the domain.\n var m = tz.length;\n while (tz[0] <= x0) tz.shift(), --m;\n while (tz[m - 1] > x1) tz.pop(), --m;\n\n var bins = new Array(m + 1),\n bin;\n\n // Initialize bins.\n for (i = 0; i <= m; ++i) {\n bin = bins[i] = [];\n bin.x0 = i > 0 ? tz[i - 1] : x0;\n bin.x1 = i < m ? tz[i] : x1;\n }\n\n // Assign data to bins by value, ignoring any outside the domain.\n for (i = 0; i < n; ++i) {\n x = values[i];\n if (x0 <= x && x <= x1) {\n bins[Object(_bisect__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(tz, x, 0, m)].push(data[i]);\n }\n }\n\n return bins;\n }\n\n histogram.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : value;\n };\n\n histogram.domain = function(_) {\n return arguments.length ? (domain = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])([_[0], _[1]]), histogram) : domain;\n };\n\n histogram.thresholds = function(_) {\n return arguments.length ? (threshold = typeof _ === \"function\" ? _ : Array.isArray(_) ? Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)) : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_), histogram) : threshold;\n };\n\n return histogram;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/histogram.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/identity.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/identity.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _bisect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./bisect */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisect.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return _bisect__WEBPACK_IMPORTED_MODULE_0__[\"bisectLeft\"]; });\n\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return _ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _bisector__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./bisector */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/bisector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return _bisector__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _cross__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./cross */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return _cross__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./descending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return _descending__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./deviation */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/deviation.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return _deviation__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _extent__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./extent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/extent.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return _extent__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _histogram__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./histogram */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/histogram.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return _histogram__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./threshold/freedmanDiaconis */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/freedmanDiaconis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return _threshold_freedmanDiaconis__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _threshold_scott__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./threshold/scott */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/scott.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return _threshold_scott__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./threshold/sturges */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/sturges.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return _threshold_sturges__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _max__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./max */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/max.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return _max__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _mean__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./mean */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/mean.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return _mean__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _median__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./median */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/median.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return _median__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/merge.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return _merge__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/min.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return _min__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _pairs__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./pairs */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/pairs.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return _pairs__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _permute__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./permute */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/permute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return _permute__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return _quantile__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _range__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./range */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/range.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return _range__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./scan */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/scan.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return _scan__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _shuffle__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./shuffle */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/shuffle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return _shuffle__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./sum */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/sum.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return _sum__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _ticks__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./ticks */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ticks.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return _ticks__WEBPACK_IMPORTED_MODULE_23__[\"tickStep\"]; });\n\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/transpose.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return _transpose__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _variance__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./variance */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/variance.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return _variance__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _zip__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./zip */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/zip.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return _zip__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/max.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/max.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n max;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n max = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && value > max) {\n max = value;\n }\n }\n }\n }\n }\n\n return max;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/max.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/mean.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/mean.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = n,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) sum += value;\n else --m;\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) sum += value;\n else --m;\n }\n }\n\n if (m) return sum / m;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/mean.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/median.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/median.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./quantile */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n numbers = [];\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(values[i]))) {\n numbers.push(value);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(valueof(values[i], i, values)))) {\n numbers.push(value);\n }\n }\n }\n\n return Object(_quantile__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(numbers.sort(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), 0.5);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/median.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/merge.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/merge.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(arrays) {\n var n = arrays.length,\n m,\n i = -1,\n j = 0,\n merged,\n array;\n\n while (++i < n) j += arrays[i].length;\n merged = new Array(j);\n\n while (--n >= 0) {\n array = arrays[n];\n m = array.length;\n while (--m >= 0) {\n merged[--j] = array[m];\n }\n }\n\n return merged;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/min.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/min.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n min;\n\n if (valueof == null) {\n while (++i < n) { // Find the first comparable value.\n if ((value = values[i]) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = values[i]) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n else {\n while (++i < n) { // Find the first comparable value.\n if ((value = valueof(values[i], i, values)) != null && value >= value) {\n min = value;\n while (++i < n) { // Compare the remaining values.\n if ((value = valueof(values[i], i, values)) != null && min > value) {\n min = value;\n }\n }\n }\n }\n }\n\n return min;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/min.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x === null ? NaN : +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/pairs.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/pairs.js ***! + \****************************************************************************************/ +/*! exports provided: default, pair */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pair\", function() { return pair; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, f) {\n if (f == null) f = pair;\n var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);\n while (i < n) pairs[i] = f(p, p = array[++i]);\n return pairs;\n});\n\nfunction pair(a, b) {\n return [a, b];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/pairs.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/permute.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/permute.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, indexes) {\n var i = indexes.length, permutes = new Array(i);\n while (i--) permutes[i] = array[indexes[i]];\n return permutes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/permute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, p, valueof) {\n if (valueof == null) valueof = _number__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n if (!(n = values.length)) return;\n if ((p = +p) <= 0 || n < 2) return +valueof(values[0], 0, values);\n if (p >= 1) return +valueof(values[n - 1], n - 1, values);\n var n,\n i = (n - 1) * p,\n i0 = Math.floor(i),\n value0 = +valueof(values[i0], i0, values),\n value1 = +valueof(values[i0 + 1], i0 + 1, values);\n return value0 + (value1 - value0) * (i - i0);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/range.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/range.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, step) {\n start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;\n\n var i = -1,\n n = Math.max(0, Math.ceil((stop - start) / step)) | 0,\n range = new Array(n);\n\n while (++i < n) {\n range[i] = start + i * step;\n }\n\n return range;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/range.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/scan.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/scan.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, compare) {\n if (!(n = values.length)) return;\n var n,\n i = 0,\n j = 0,\n xi,\n xj = values[j];\n\n if (compare == null) compare = _ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\n\n while (++i < n) {\n if (compare(xi = values[i], xj) < 0 || compare(xj, xj) !== 0) {\n xj = xi, j = i;\n }\n }\n\n if (compare(xj, xj) === 0) return j;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/scan.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/shuffle.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/shuffle.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(array, i0, i1) {\n var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),\n t,\n i;\n\n while (m) {\n i = Math.random() * m-- | 0;\n t = array[m + i0];\n array[m + i0] = array[i + i0];\n array[i + i0] = t;\n }\n\n return array;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/shuffle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/sum.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/sum.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n i = -1,\n value,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (value = +values[i]) sum += value; // Note: zero and null are equivalent.\n }\n }\n\n else {\n while (++i < n) {\n if (value = +valueof(values[i], i, values)) sum += value;\n }\n }\n\n return sum;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/sum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/freedmanDiaconis.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/freedmanDiaconis.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/array.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../ascending */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ascending.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js\");\n/* harmony import */ var _quantile__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../quantile */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/quantile.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n values = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(values, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]).sort(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n return Math.ceil((max - min) / (2 * (Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.75) - Object(_quantile__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(values, 0.25)) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/freedmanDiaconis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/scott.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/scott.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _deviation__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../deviation */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/deviation.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, min, max) {\n return Math.ceil((max - min) / (3.5 * Object(_deviation__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values) * Math.pow(values.length, -1 / 3)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/scott.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/sturges.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/sturges.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n return Math.ceil(Math.log(values.length) / Math.LN2) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/threshold/sturges.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ticks.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ticks.js ***! + \****************************************************************************************/ +/*! exports provided: default, tickIncrement, tickStep */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return tickIncrement; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return tickStep; });\nvar e10 = Math.sqrt(50),\n e5 = Math.sqrt(10),\n e2 = Math.sqrt(2);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(start, stop, count) {\n var reverse,\n i = -1,\n n,\n ticks,\n step;\n\n stop = +stop, start = +start, count = +count;\n if (start === stop && count > 0) return [start];\n if (reverse = stop < start) n = start, start = stop, stop = n;\n if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) return [];\n\n if (step > 0) {\n start = Math.ceil(start / step);\n stop = Math.floor(stop / step);\n ticks = new Array(n = Math.ceil(stop - start + 1));\n while (++i < n) ticks[i] = (start + i) * step;\n } else {\n start = Math.floor(start * step);\n stop = Math.ceil(stop * step);\n ticks = new Array(n = Math.ceil(start - stop + 1));\n while (++i < n) ticks[i] = (start - i) / step;\n }\n\n if (reverse) ticks.reverse();\n\n return ticks;\n});\n\nfunction tickIncrement(start, stop, count) {\n var step = (stop - start) / Math.max(0, count),\n power = Math.floor(Math.log(step) / Math.LN10),\n error = step / Math.pow(10, power);\n return power >= 0\n ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)\n : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);\n}\n\nfunction tickStep(start, stop, count) {\n var step0 = Math.abs(stop - start) / Math.max(0, count),\n step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),\n error = step0 / step1;\n if (error >= e10) step1 *= 10;\n else if (error >= e5) step1 *= 5;\n else if (error >= e2) step1 *= 2;\n return stop < start ? -step1 : step1;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/ticks.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/transpose.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/transpose.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _min__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./min */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/min.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(matrix) {\n if (!(n = matrix.length)) return [];\n for (var i = -1, m = Object(_min__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(matrix, length), transpose = new Array(m); ++i < m;) {\n for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {\n row[j] = matrix[j][i];\n }\n }\n return transpose;\n});\n\nfunction length(d) {\n return d.length;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/transpose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/variance.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/variance.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/number.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values, valueof) {\n var n = values.length,\n m = 0,\n i = -1,\n mean = 0,\n value,\n delta,\n sum = 0;\n\n if (valueof == null) {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(values[i]))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n else {\n while (++i < n) {\n if (!isNaN(value = Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(valueof(values[i], i, values)))) {\n delta = value - mean;\n mean += delta / ++m;\n sum += delta * (value - mean);\n }\n }\n }\n\n if (m > 1) return sum / (m - 1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/variance.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/zip.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/zip.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transpose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transpose */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/transpose.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_transpose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(arguments);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/zip.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/entries.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/entries.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var entries = [];\n for (var key in map) entries.push({key: key, value: map[key]});\n return entries;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/entries.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: nest, set, map, keys, values, entries */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _nest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./nest */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/nest.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return _nest__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _set__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./set */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/set.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return _set__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return _map__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _keys__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./keys */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/keys.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return _keys__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _values__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./values */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/values.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return _values__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _entries__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./entries */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/entries.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return _entries__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/keys.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/keys.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var keys = [];\n for (var key in map) keys.push(key);\n return keys;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/keys.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js ***! + \*******************************************************************************************/ +/*! exports provided: prefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefix\", function() { return prefix; });\nvar prefix = \"$\";\n\nfunction Map() {}\n\nMap.prototype = map.prototype = {\n constructor: Map,\n has: function(key) {\n return (prefix + key) in this;\n },\n get: function(key) {\n return this[prefix + key];\n },\n set: function(key, value) {\n this[prefix + key] = value;\n return this;\n },\n remove: function(key) {\n var property = prefix + key;\n return property in this && delete this[property];\n },\n clear: function() {\n for (var property in this) if (property[0] === prefix) delete this[property];\n },\n keys: function() {\n var keys = [];\n for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));\n return keys;\n },\n values: function() {\n var values = [];\n for (var property in this) if (property[0] === prefix) values.push(this[property]);\n return values;\n },\n entries: function() {\n var entries = [];\n for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});\n return entries;\n },\n size: function() {\n var size = 0;\n for (var property in this) if (property[0] === prefix) ++size;\n return size;\n },\n empty: function() {\n for (var property in this) if (property[0] === prefix) return false;\n return true;\n },\n each: function(f) {\n for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);\n }\n};\n\nfunction map(object, f) {\n var map = new Map;\n\n // Copy constructor.\n if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });\n\n // Index array by numeric index or specified key function.\n else if (Array.isArray(object)) {\n var i = -1,\n n = object.length,\n o;\n\n if (f == null) while (++i < n) map.set(i, object[i]);\n else while (++i < n) map.set(f(o = object[i], i, object), o);\n }\n\n // Convert object to map.\n else if (object) for (var key in object) map.set(key, object[key]);\n\n return map;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (map);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/nest.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/nest.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = [],\n sortKeys = [],\n sortValues,\n rollup,\n nest;\n\n function apply(array, depth, createResult, setResult) {\n if (depth >= keys.length) {\n if (sortValues != null) array.sort(sortValues);\n return rollup != null ? rollup(array) : array;\n }\n\n var i = -1,\n n = array.length,\n key = keys[depth++],\n keyValue,\n value,\n valuesByKey = Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(),\n values,\n result = createResult();\n\n while (++i < n) {\n if (values = valuesByKey.get(keyValue = key(value = array[i]) + \"\")) {\n values.push(value);\n } else {\n valuesByKey.set(keyValue, [value]);\n }\n }\n\n valuesByKey.each(function(values, key) {\n setResult(result, key, apply(values, depth, createResult, setResult));\n });\n\n return result;\n }\n\n function entries(map, depth) {\n if (++depth > keys.length) return map;\n var array, sortKey = sortKeys[depth - 1];\n if (rollup != null && depth >= keys.length) array = map.entries();\n else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });\n return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;\n }\n\n return nest = {\n object: function(array) { return apply(array, 0, createObject, setObject); },\n map: function(array) { return apply(array, 0, createMap, setMap); },\n entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },\n key: function(d) { keys.push(d); return nest; },\n sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },\n sortValues: function(order) { sortValues = order; return nest; },\n rollup: function(f) { rollup = f; return nest; }\n };\n});\n\nfunction createObject() {\n return {};\n}\n\nfunction setObject(object, key, value) {\n object[key] = value;\n}\n\nfunction createMap() {\n return Object(_map__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n}\n\nfunction setMap(map, key, value) {\n map.set(key, value);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/nest.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/set.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/set.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./map */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/map.js\");\n\n\nfunction Set() {}\n\nvar proto = _map__WEBPACK_IMPORTED_MODULE_0__[\"default\"].prototype;\n\nSet.prototype = set.prototype = {\n constructor: Set,\n has: proto.has,\n add: function(value) {\n value += \"\";\n this[_map__WEBPACK_IMPORTED_MODULE_0__[\"prefix\"] + value] = value;\n return this;\n },\n remove: proto.remove,\n clear: proto.clear,\n values: proto.keys,\n size: proto.size,\n empty: proto.empty,\n each: proto.each\n};\n\nfunction set(object, f) {\n var set = new Set;\n\n // Copy constructor.\n if (object instanceof Set) object.each(function(value) { set.add(value); });\n\n // Otherwise, assume it’s an array.\n else if (object) {\n var i = -1, n = object.length;\n if (f == null) while (++i < n) set.add(object[i]);\n else while (++i < n) set.add(f(object[i], i, object));\n }\n\n return set;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (set);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/set.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/values.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/values.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(map) {\n var values = [];\n for (var key in map) values.push(map[key]);\n return values;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/values.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js ***! + \****************************************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/cubehelix.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/cubehelix.js ***! + \********************************************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js ***! + \*****************************************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js ***! + \****************************************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/lab.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/lab.js ***! + \**************************************************************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/math.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/math.js ***! + \***************************************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/defaultLocale.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/defaultLocale.js ***! + \*************************************************************************************************/ +/*! exports provided: format, formatPrefix, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return format; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return formatPrefix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/locale.js\");\n\n\nvar locale;\nvar format;\nvar formatPrefix;\n\ndefaultLocale({\n decimal: \".\",\n thousands: \",\",\n grouping: [3],\n currency: [\"$\", \"\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n format = locale.format;\n formatPrefix = locale.formatPrefix;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(x)), x ? x[1] : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Computes the decimal coefficient and exponent of the specified number x with\n// significant digits p, where x is positive and p is in [1, 21] or undefined.\n// For example, formatDecimal(1.23) returns [\"123\", 0].\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf(\"e\")) < 0) return null; // NaN, ±Infinity\n var i, coefficient = x.slice(0, i);\n\n // The string returned by toExponential either has the form \\d\\.\\d+e[-+]\\d+\n // (e.g., 1.2e+3) or the form \\de[-+]\\d+ (e.g., 1e+3).\n return [\n coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,\n +x.slice(i + 1)\n ];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatGroup.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatGroup.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(grouping, thousands) {\n return function(value, width) {\n var i = value.length,\n t = [],\n j = 0,\n g = grouping[0],\n length = 0;\n\n while (i > 0 && g > 0) {\n if (length + g + 1 > width) g = Math.max(1, width - length);\n t.push(value.substring(i -= g, i + g));\n if ((length += g + 1) > width) break;\n g = grouping[j = (j + 1) % grouping.length];\n }\n\n return t.reverse().join(thousands);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatGroup.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatNumerals.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatNumerals.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(numerals) {\n return function(value) {\n return value.replace(/[0-9]/g, function(i) {\n return numerals[+i];\n });\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatNumerals.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatPrefixAuto.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatPrefixAuto.js ***! + \****************************************************************************************************/ +/*! exports provided: prefixExponent, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"prefixExponent\", function() { return prefixExponent; });\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js\");\n\n\nvar prefixExponent;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1],\n i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,\n n = coefficient.length;\n return i === n ? coefficient\n : i > n ? coefficient + new Array(i - n + 1).join(\"0\")\n : i > 0 ? coefficient.slice(0, i) + \".\" + coefficient.slice(i)\n : \"0.\" + new Array(1 - i).join(\"0\") + Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, Math.max(0, p + i - 1))[0]; // less than 1y!\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatPrefixAuto.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatRounded.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatRounded.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatDecimal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatDecimal */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatDecimal.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, p) {\n var d = Object(_formatDecimal__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(x, p);\n if (!d) return x + \"\";\n var coefficient = d[0],\n exponent = d[1];\n return exponent < 0 ? \"0.\" + new Array(-exponent).join(\"0\") + coefficient\n : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + \".\" + coefficient.slice(exponent + 1)\n : coefficient + new Array(exponent - coefficient.length + 2).join(\"0\");\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatRounded.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatSpecifier.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatSpecifier.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatSpecifier; });\n// [[fill]align][sign][symbol][0][width][,][.precision][~][type]\nvar re = /^(?:(.)?([<>=^]))?([+\\-( ])?([$#])?(0)?(\\d+)?(,)?(\\.\\d+)?(~)?([a-z%])?$/i;\n\nfunction formatSpecifier(specifier) {\n return new FormatSpecifier(specifier);\n}\n\nformatSpecifier.prototype = FormatSpecifier.prototype; // instanceof\n\nfunction FormatSpecifier(specifier) {\n if (!(match = re.exec(specifier))) throw new Error(\"invalid format: \" + specifier);\n var match;\n this.fill = match[1] || \" \";\n this.align = match[2] || \">\";\n this.sign = match[3] || \"-\";\n this.symbol = match[4] || \"\";\n this.zero = !!match[5];\n this.width = match[6] && +match[6];\n this.comma = !!match[7];\n this.precision = match[8] && +match[8].slice(1);\n this.trim = !!match[9];\n this.type = match[10] || \"\";\n}\n\nFormatSpecifier.prototype.toString = function() {\n return this.fill\n + this.align\n + this.sign\n + this.symbol\n + (this.zero ? \"0\" : \"\")\n + (this.width == null ? \"\" : Math.max(1, this.width | 0))\n + (this.comma ? \",\" : \"\")\n + (this.precision == null ? \"\" : \".\" + Math.max(0, this.precision | 0))\n + (this.trim ? \"~\" : \"\")\n + this.type;\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatSpecifier.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTrim.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTrim.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n// Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(s) {\n out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {\n switch (s[i]) {\n case \".\": i0 = i1 = i; break;\n case \"0\": if (i0 === 0) i0 = i; i1 = i; break;\n default: if (i0 > 0) { if (!+s[i]) break out; i0 = 0; } break;\n }\n }\n return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTrim.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTypes.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTypes.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _formatRounded__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatRounded */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatRounded.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n \"%\": function(x, p) { return (x * 100).toFixed(p); },\n \"b\": function(x) { return Math.round(x).toString(2); },\n \"c\": function(x) { return x + \"\"; },\n \"d\": function(x) { return Math.round(x).toString(10); },\n \"e\": function(x, p) { return x.toExponential(p); },\n \"f\": function(x, p) { return x.toFixed(p); },\n \"g\": function(x, p) { return x.toPrecision(p); },\n \"o\": function(x) { return Math.round(x).toString(8); },\n \"p\": function(x, p) { return Object(_formatRounded__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(x * 100, p); },\n \"r\": _formatRounded__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n \"s\": _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n \"X\": function(x) { return Math.round(x).toString(16).toUpperCase(); },\n \"x\": function(x) { return Math.round(x).toString(16); }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTypes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/identity.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/identity.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/index.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/index.js ***! + \*****************************************************************************************/ +/*! exports provided: formatDefaultLocale, format, formatPrefix, formatLocale, formatSpecifier, precisionFixed, precisionPrefix, precisionRound */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatDefaultLocale\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"format\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"formatPrefix\"]; });\n\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatLocale\", function() { return _locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _formatSpecifier__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatSpecifier */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatSpecifier\", function() { return _formatSpecifier__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _precisionFixed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./precisionFixed */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionFixed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionFixed\", function() { return _precisionFixed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _precisionPrefix__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./precisionPrefix */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionPrefix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionPrefix\", function() { return _precisionPrefix__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _precisionRound__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./precisionRound */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionRound.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionRound\", function() { return _precisionRound__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/locale.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/locale.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js\");\n/* harmony import */ var _formatGroup__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./formatGroup */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatGroup.js\");\n/* harmony import */ var _formatNumerals__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./formatNumerals */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatNumerals.js\");\n/* harmony import */ var _formatSpecifier__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./formatSpecifier */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatSpecifier.js\");\n/* harmony import */ var _formatTrim__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./formatTrim */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTrim.js\");\n/* harmony import */ var _formatTypes__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./formatTypes */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatTypes.js\");\n/* harmony import */ var _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./formatPrefixAuto */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/formatPrefixAuto.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/identity.js\");\n\n\n\n\n\n\n\n\n\nvar prefixes = [\"y\",\"z\",\"a\",\"f\",\"p\",\"n\",\"µ\",\"m\",\"\",\"k\",\"M\",\"G\",\"T\",\"P\",\"E\",\"Z\",\"Y\"];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(locale) {\n var group = locale.grouping && locale.thousands ? Object(_formatGroup__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(locale.grouping, locale.thousands) : _identity__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n currency = locale.currency,\n decimal = locale.decimal,\n numerals = locale.numerals ? Object(_formatNumerals__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(locale.numerals) : _identity__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n percent = locale.percent || \"%\";\n\n function newFormat(specifier) {\n specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier);\n\n var fill = specifier.fill,\n align = specifier.align,\n sign = specifier.sign,\n symbol = specifier.symbol,\n zero = specifier.zero,\n width = specifier.width,\n comma = specifier.comma,\n precision = specifier.precision,\n trim = specifier.trim,\n type = specifier.type;\n\n // The \"n\" type is an alias for \",g\".\n if (type === \"n\") comma = true, type = \"g\";\n\n // The \"\" type, and any invalid type, is an alias for \".12~g\".\n else if (!_formatTypes__WEBPACK_IMPORTED_MODULE_5__[\"default\"][type]) precision == null && (precision = 12), trim = true, type = \"g\";\n\n // If zero fill is specified, padding goes after sign and before digits.\n if (zero || (fill === \"0\" && align === \"=\")) zero = true, fill = \"0\", align = \"=\";\n\n // Compute the prefix and suffix.\n // For SI-prefix, the suffix is lazily computed.\n var prefix = symbol === \"$\" ? currency[0] : symbol === \"#\" && /[boxX]/.test(type) ? \"0\" + type.toLowerCase() : \"\",\n suffix = symbol === \"$\" ? currency[1] : /[%p]/.test(type) ? percent : \"\";\n\n // What format function should we use?\n // Is this an integer type?\n // Can this type generate exponential notation?\n var formatType = _formatTypes__WEBPACK_IMPORTED_MODULE_5__[\"default\"][type],\n maybeSuffix = /[defgprs%]/.test(type);\n\n // Set the default precision if not specified,\n // or clamp the specified precision to the supported range.\n // For significant precision, it must be in [1, 21].\n // For fixed precision, it must be in [0, 20].\n precision = precision == null ? 6\n : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))\n : Math.max(0, Math.min(20, precision));\n\n function format(value) {\n var valuePrefix = prefix,\n valueSuffix = suffix,\n i, n, c;\n\n if (type === \"c\") {\n valueSuffix = formatType(value) + valueSuffix;\n value = \"\";\n } else {\n value = +value;\n\n // Perform the initial formatting.\n var valueNegative = value < 0;\n value = formatType(Math.abs(value), precision);\n\n // Trim insignificant zeros.\n if (trim) value = Object(_formatTrim__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(value);\n\n // If a negative value rounds to zero during formatting, treat as positive.\n if (valueNegative && +value === 0) valueNegative = false;\n\n // Compute the prefix and suffix.\n valuePrefix = (valueNegative ? (sign === \"(\" ? sign : \"-\") : sign === \"-\" || sign === \"(\" ? \"\" : sign) + valuePrefix;\n valueSuffix = (type === \"s\" ? prefixes[8 + _formatPrefixAuto__WEBPACK_IMPORTED_MODULE_6__[\"prefixExponent\"] / 3] : \"\") + valueSuffix + (valueNegative && sign === \"(\" ? \")\" : \"\");\n\n // Break the formatted value into the integer “value” part that can be\n // grouped, and fractional or exponential “suffix” part that is not.\n if (maybeSuffix) {\n i = -1, n = value.length;\n while (++i < n) {\n if (c = value.charCodeAt(i), 48 > c || c > 57) {\n valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;\n value = value.slice(0, i);\n break;\n }\n }\n }\n }\n\n // If the fill character is not \"0\", grouping is applied before padding.\n if (comma && !zero) value = group(value, Infinity);\n\n // Compute the padding.\n var length = valuePrefix.length + value.length + valueSuffix.length,\n padding = length < width ? new Array(width - length + 1).join(fill) : \"\";\n\n // If the fill character is \"0\", grouping is applied after padding.\n if (comma && zero) value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = \"\";\n\n // Reconstruct the final output based on the desired alignment.\n switch (align) {\n case \"<\": value = valuePrefix + value + valueSuffix + padding; break;\n case \"=\": value = valuePrefix + padding + value + valueSuffix; break;\n case \"^\": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;\n default: value = padding + valuePrefix + value + valueSuffix; break;\n }\n\n return numerals(value);\n }\n\n format.toString = function() {\n return specifier + \"\";\n };\n\n return format;\n }\n\n function formatPrefix(specifier, value) {\n var f = newFormat((specifier = Object(_formatSpecifier__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(specifier), specifier.type = \"f\", specifier)),\n e = Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3,\n k = Math.pow(10, -e),\n prefix = prefixes[8 + e / 3];\n return function(value) {\n return f(k * value) + prefix;\n };\n }\n\n return {\n format: newFormat,\n formatPrefix: formatPrefix\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionFixed.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionFixed.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step) {\n return Math.max(0, -Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionFixed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionPrefix.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionPrefix.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, value) {\n return Math.max(0, Math.max(-8, Math.min(8, Math.floor(Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value) / 3))) * 3 - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Math.abs(step)));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionPrefix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionRound.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionRound.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _exponent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./exponent */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/exponent.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(step, max) {\n step = Math.abs(step), max = Math.abs(max) - step;\n return Math.max(0, Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(max) - Object(_exponent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(step)) + 1;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/precisionRound.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/array.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/array.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js ***! + \**********************************************************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basisClosed.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basisClosed.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js ***! + \**********************************************************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/constant.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/constant.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/cubehelix.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/cubehelix.js ***! + \**************************************************************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/date.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/date.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/discrete.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/discrete.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/discrete.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hcl.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hcl.js ***! + \********************************************************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hsl.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hsl.js ***! + \********************************************************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hue.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hue.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = Object(_color__WEBPACK_IMPORTED_MODULE_0__[\"hue\"])(+a, +b);\n return function(t) {\n var x = i(t);\n return x - 360 * Math.floor(x / 360);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hue.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js ***! + \**********************************************************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _discrete__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discrete */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/discrete.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return _discrete__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _hue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hue */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return _hue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _number__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _object__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./round */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _round__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _string__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _transform_index__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transform/index */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./zoom */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _hsl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./hsl */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"hslLong\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _hcl__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./hcl */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"hclLong\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _piecewise__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./piecewise */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/piecewise.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return _piecewise__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./quantize */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/lab.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/lab.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/object.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/object.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/piecewise.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/piecewise.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return piecewise; });\nfunction piecewise(interpolate, values) {\n var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n while (i < n) I[i] = interpolate(v, v = values[++i]);\n return function(t) {\n var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n return I[i](t - i);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/piecewise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/quantize.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/quantize.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/rgb.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/rgb.js ***! + \********************************************************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/round.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/round.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/string.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/string.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/decompose.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/decompose.js ***! + \************************************************************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/index.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/index.js ***! + \********************************************************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/parse.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/parse.js ***! + \********************************************************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/zoom.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/zoom.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js ***! + \******************************************************************************************************/ +/*! exports provided: timeFormat, timeParse, utcFormat, utcParse, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return timeFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return timeParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return utcFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return utcParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/locale.js\");\n\n\nvar locale;\nvar timeFormat;\nvar timeParse;\nvar utcFormat;\nvar utcParse;\n\ndefaultLocale({\n dateTime: \"%x, %X\",\n date: \"%-m/%-d/%Y\",\n time: \"%-I:%M:%S %p\",\n periods: [\"AM\", \"PM\"],\n days: [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n shortDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n months: [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"],\n shortMonths: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n timeFormat = locale.format;\n timeParse = locale.parse;\n utcFormat = locale.utcFormat;\n utcParse = locale.utcParse;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/index.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/index.js ***! + \**********************************************************************************************/ +/*! exports provided: timeFormatDefaultLocale, timeFormat, timeParse, utcFormat, utcParse, timeFormatLocale, isoFormat, isoParse */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatDefaultLocale\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return _defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcParse\"]; });\n\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatLocale\", function() { return _locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _isoFormat__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./isoFormat */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoFormat\", function() { return _isoFormat__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _isoParse__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./isoParse */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoParse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoParse\", function() { return _isoParse__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoFormat.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoFormat.js ***! + \**************************************************************************************************/ +/*! exports provided: isoSpecifier, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isoSpecifier\", function() { return isoSpecifier; });\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js\");\n\n\nvar isoSpecifier = \"%Y-%m-%dT%H:%M:%S.%LZ\";\n\nfunction formatIsoNative(date) {\n return date.toISOString();\n}\n\nvar formatIso = Date.prototype.toISOString\n ? formatIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"])(isoSpecifier);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (formatIso);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoFormat.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoParse.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoParse.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _isoFormat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isoFormat */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/defaultLocale.js\");\n\n\n\nfunction parseIsoNative(string) {\n var date = new Date(string);\n return isNaN(date) ? null : date;\n}\n\nvar parseIso = +new Date(\"2000-01-01T00:00:00.000Z\")\n ? parseIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_1__[\"utcParse\"])(_isoFormat__WEBPACK_IMPORTED_MODULE_0__[\"isoSpecifier\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (parseIso);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/isoParse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/locale.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/locale.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatLocale; });\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-time */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js\");\n\n\nfunction localDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);\n date.setFullYear(d.y);\n return date;\n }\n return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);\n}\n\nfunction utcDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));\n date.setUTCFullYear(d.y);\n return date;\n }\n return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));\n}\n\nfunction newYear(y) {\n return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};\n}\n\nfunction formatLocale(locale) {\n var locale_dateTime = locale.dateTime,\n locale_date = locale.date,\n locale_time = locale.time,\n locale_periods = locale.periods,\n locale_weekdays = locale.days,\n locale_shortWeekdays = locale.shortDays,\n locale_months = locale.months,\n locale_shortMonths = locale.shortMonths;\n\n var periodRe = formatRe(locale_periods),\n periodLookup = formatLookup(locale_periods),\n weekdayRe = formatRe(locale_weekdays),\n weekdayLookup = formatLookup(locale_weekdays),\n shortWeekdayRe = formatRe(locale_shortWeekdays),\n shortWeekdayLookup = formatLookup(locale_shortWeekdays),\n monthRe = formatRe(locale_months),\n monthLookup = formatLookup(locale_months),\n shortMonthRe = formatRe(locale_shortMonths),\n shortMonthLookup = formatLookup(locale_shortMonths);\n\n var formats = {\n \"a\": formatShortWeekday,\n \"A\": formatWeekday,\n \"b\": formatShortMonth,\n \"B\": formatMonth,\n \"c\": null,\n \"d\": formatDayOfMonth,\n \"e\": formatDayOfMonth,\n \"f\": formatMicroseconds,\n \"H\": formatHour24,\n \"I\": formatHour12,\n \"j\": formatDayOfYear,\n \"L\": formatMilliseconds,\n \"m\": formatMonthNumber,\n \"M\": formatMinutes,\n \"p\": formatPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatSeconds,\n \"u\": formatWeekdayNumberMonday,\n \"U\": formatWeekNumberSunday,\n \"V\": formatWeekNumberISO,\n \"w\": formatWeekdayNumberSunday,\n \"W\": formatWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatYear,\n \"Y\": formatFullYear,\n \"Z\": formatZone,\n \"%\": formatLiteralPercent\n };\n\n var utcFormats = {\n \"a\": formatUTCShortWeekday,\n \"A\": formatUTCWeekday,\n \"b\": formatUTCShortMonth,\n \"B\": formatUTCMonth,\n \"c\": null,\n \"d\": formatUTCDayOfMonth,\n \"e\": formatUTCDayOfMonth,\n \"f\": formatUTCMicroseconds,\n \"H\": formatUTCHour24,\n \"I\": formatUTCHour12,\n \"j\": formatUTCDayOfYear,\n \"L\": formatUTCMilliseconds,\n \"m\": formatUTCMonthNumber,\n \"M\": formatUTCMinutes,\n \"p\": formatUTCPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatUTCSeconds,\n \"u\": formatUTCWeekdayNumberMonday,\n \"U\": formatUTCWeekNumberSunday,\n \"V\": formatUTCWeekNumberISO,\n \"w\": formatUTCWeekdayNumberSunday,\n \"W\": formatUTCWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatUTCYear,\n \"Y\": formatUTCFullYear,\n \"Z\": formatUTCZone,\n \"%\": formatLiteralPercent\n };\n\n var parses = {\n \"a\": parseShortWeekday,\n \"A\": parseWeekday,\n \"b\": parseShortMonth,\n \"B\": parseMonth,\n \"c\": parseLocaleDateTime,\n \"d\": parseDayOfMonth,\n \"e\": parseDayOfMonth,\n \"f\": parseMicroseconds,\n \"H\": parseHour24,\n \"I\": parseHour24,\n \"j\": parseDayOfYear,\n \"L\": parseMilliseconds,\n \"m\": parseMonthNumber,\n \"M\": parseMinutes,\n \"p\": parsePeriod,\n \"Q\": parseUnixTimestamp,\n \"s\": parseUnixTimestampSeconds,\n \"S\": parseSeconds,\n \"u\": parseWeekdayNumberMonday,\n \"U\": parseWeekNumberSunday,\n \"V\": parseWeekNumberISO,\n \"w\": parseWeekdayNumberSunday,\n \"W\": parseWeekNumberMonday,\n \"x\": parseLocaleDate,\n \"X\": parseLocaleTime,\n \"y\": parseYear,\n \"Y\": parseFullYear,\n \"Z\": parseZone,\n \"%\": parseLiteralPercent\n };\n\n // These recursive directive definitions must be deferred.\n formats.x = newFormat(locale_date, formats);\n formats.X = newFormat(locale_time, formats);\n formats.c = newFormat(locale_dateTime, formats);\n utcFormats.x = newFormat(locale_date, utcFormats);\n utcFormats.X = newFormat(locale_time, utcFormats);\n utcFormats.c = newFormat(locale_dateTime, utcFormats);\n\n function newFormat(specifier, formats) {\n return function(date) {\n var string = [],\n i = -1,\n j = 0,\n n = specifier.length,\n c,\n pad,\n format;\n\n if (!(date instanceof Date)) date = new Date(+date);\n\n while (++i < n) {\n if (specifier.charCodeAt(i) === 37) {\n string.push(specifier.slice(j, i));\n if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);\n else pad = c === \"e\" ? \" \" : \"0\";\n if (format = formats[c]) c = format(date, pad);\n string.push(c);\n j = i + 1;\n }\n }\n\n string.push(specifier.slice(j, i));\n return string.join(\"\");\n };\n }\n\n function newParse(specifier, newDate) {\n return function(string) {\n var d = newYear(1900),\n i = parseSpecifier(d, specifier, string += \"\", 0),\n week, day;\n if (i != string.length) return null;\n\n // If a UNIX timestamp is specified, return it.\n if (\"Q\" in d) return new Date(d.Q);\n\n // The am-pm flag is 0 for AM, and 1 for PM.\n if (\"p\" in d) d.H = d.H % 12 + d.p * 12;\n\n // Convert day-of-week and week-of-year to day-of-year.\n if (\"V\" in d) {\n if (d.V < 1 || d.V > 53) return null;\n if (!(\"w\" in d)) d.w = 1;\n if (\"Z\" in d) {\n week = utcDate(newYear(d.y)), day = week.getUTCDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getUTCFullYear();\n d.m = week.getUTCMonth();\n d.d = week.getUTCDate() + (d.w + 6) % 7;\n } else {\n week = newDate(newYear(d.y)), day = week.getDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getFullYear();\n d.m = week.getMonth();\n d.d = week.getDate() + (d.w + 6) % 7;\n }\n } else if (\"W\" in d || \"U\" in d) {\n if (!(\"w\" in d)) d.w = \"u\" in d ? d.u % 7 : \"W\" in d ? 1 : 0;\n day = \"Z\" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();\n d.m = 0;\n d.d = \"W\" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;\n }\n\n // If a time zone is specified, all fields are interpreted as UTC and then\n // offset according to the specified time zone.\n if (\"Z\" in d) {\n d.H += d.Z / 100 | 0;\n d.M += d.Z % 100;\n return utcDate(d);\n }\n\n // Otherwise, all fields are in local time.\n return newDate(d);\n };\n }\n\n function parseSpecifier(d, specifier, string, j) {\n var i = 0,\n n = specifier.length,\n m = string.length,\n c,\n parse;\n\n while (i < n) {\n if (j >= m) return -1;\n c = specifier.charCodeAt(i++);\n if (c === 37) {\n c = specifier.charAt(i++);\n parse = parses[c in pads ? specifier.charAt(i++) : c];\n if (!parse || ((j = parse(d, string, j)) < 0)) return -1;\n } else if (c != string.charCodeAt(j++)) {\n return -1;\n }\n }\n\n return j;\n }\n\n function parsePeriod(d, string, i) {\n var n = periodRe.exec(string.slice(i));\n return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortWeekday(d, string, i) {\n var n = shortWeekdayRe.exec(string.slice(i));\n return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseWeekday(d, string, i) {\n var n = weekdayRe.exec(string.slice(i));\n return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortMonth(d, string, i) {\n var n = shortMonthRe.exec(string.slice(i));\n return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseMonth(d, string, i) {\n var n = monthRe.exec(string.slice(i));\n return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseLocaleDateTime(d, string, i) {\n return parseSpecifier(d, locale_dateTime, string, i);\n }\n\n function parseLocaleDate(d, string, i) {\n return parseSpecifier(d, locale_date, string, i);\n }\n\n function parseLocaleTime(d, string, i) {\n return parseSpecifier(d, locale_time, string, i);\n }\n\n function formatShortWeekday(d) {\n return locale_shortWeekdays[d.getDay()];\n }\n\n function formatWeekday(d) {\n return locale_weekdays[d.getDay()];\n }\n\n function formatShortMonth(d) {\n return locale_shortMonths[d.getMonth()];\n }\n\n function formatMonth(d) {\n return locale_months[d.getMonth()];\n }\n\n function formatPeriod(d) {\n return locale_periods[+(d.getHours() >= 12)];\n }\n\n function formatUTCShortWeekday(d) {\n return locale_shortWeekdays[d.getUTCDay()];\n }\n\n function formatUTCWeekday(d) {\n return locale_weekdays[d.getUTCDay()];\n }\n\n function formatUTCShortMonth(d) {\n return locale_shortMonths[d.getUTCMonth()];\n }\n\n function formatUTCMonth(d) {\n return locale_months[d.getUTCMonth()];\n }\n\n function formatUTCPeriod(d) {\n return locale_periods[+(d.getUTCHours() >= 12)];\n }\n\n return {\n format: function(specifier) {\n var f = newFormat(specifier += \"\", formats);\n f.toString = function() { return specifier; };\n return f;\n },\n parse: function(specifier) {\n var p = newParse(specifier += \"\", localDate);\n p.toString = function() { return specifier; };\n return p;\n },\n utcFormat: function(specifier) {\n var f = newFormat(specifier += \"\", utcFormats);\n f.toString = function() { return specifier; };\n return f;\n },\n utcParse: function(specifier) {\n var p = newParse(specifier, utcDate);\n p.toString = function() { return specifier; };\n return p;\n }\n };\n}\n\nvar pads = {\"-\": \"\", \"_\": \" \", \"0\": \"0\"},\n numberRe = /^\\s*\\d+/, // note: ignores next directive\n percentRe = /^%/,\n requoteRe = /[\\\\^$*+?|[\\]().{}]/g;\n\nfunction pad(value, fill, width) {\n var sign = value < 0 ? \"-\" : \"\",\n string = (sign ? -value : value) + \"\",\n length = string.length;\n return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);\n}\n\nfunction requote(s) {\n return s.replace(requoteRe, \"\\\\$&\");\n}\n\nfunction formatRe(names) {\n return new RegExp(\"^(?:\" + names.map(requote).join(\"|\") + \")\", \"i\");\n}\n\nfunction formatLookup(names) {\n var map = {}, i = -1, n = names.length;\n while (++i < n) map[names[i].toLowerCase()] = i;\n return map;\n}\n\nfunction parseWeekdayNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.w = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekdayNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.u = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.U = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberISO(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.V = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.W = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseFullYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 4));\n return n ? (d.y = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;\n}\n\nfunction parseZone(d, string, i) {\n var n = /^(Z)|([+-]\\d\\d)(?::?(\\d\\d))?/.exec(string.slice(i, i + 6));\n return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || \"00\")), i + n[0].length) : -1;\n}\n\nfunction parseMonthNumber(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.m = n[0] - 1, i + n[0].length) : -1;\n}\n\nfunction parseDayOfMonth(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseDayOfYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseHour24(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.H = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMinutes(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.M = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.S = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMilliseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.L = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMicroseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 6));\n return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;\n}\n\nfunction parseLiteralPercent(d, string, i) {\n var n = percentRe.exec(string.slice(i, i + 1));\n return n ? i + n[0].length : -1;\n}\n\nfunction parseUnixTimestamp(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseUnixTimestampSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;\n}\n\nfunction formatDayOfMonth(d, p) {\n return pad(d.getDate(), p, 2);\n}\n\nfunction formatHour24(d, p) {\n return pad(d.getHours(), p, 2);\n}\n\nfunction formatHour12(d, p) {\n return pad(d.getHours() % 12 || 12, p, 2);\n}\n\nfunction formatDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 3);\n}\n\nfunction formatMilliseconds(d, p) {\n return pad(d.getMilliseconds(), p, 3);\n}\n\nfunction formatMicroseconds(d, p) {\n return formatMilliseconds(d, p) + \"000\";\n}\n\nfunction formatMonthNumber(d, p) {\n return pad(d.getMonth() + 1, p, 2);\n}\n\nfunction formatMinutes(d, p) {\n return pad(d.getMinutes(), p, 2);\n}\n\nfunction formatSeconds(d, p) {\n return pad(d.getSeconds(), p, 2);\n}\n\nfunction formatWeekdayNumberMonday(d) {\n var day = d.getDay();\n return day === 0 ? 7 : day;\n}\n\nfunction formatWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatWeekNumberISO(d, p) {\n var day = d.getDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d).getDay() === 4), p, 2);\n}\n\nfunction formatWeekdayNumberSunday(d) {\n return d.getDay();\n}\n\nfunction formatWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatYear(d, p) {\n return pad(d.getFullYear() % 100, p, 2);\n}\n\nfunction formatFullYear(d, p) {\n return pad(d.getFullYear() % 10000, p, 4);\n}\n\nfunction formatZone(d) {\n var z = d.getTimezoneOffset();\n return (z > 0 ? \"-\" : (z *= -1, \"+\"))\n + pad(z / 60 | 0, \"0\", 2)\n + pad(z % 60, \"0\", 2);\n}\n\nfunction formatUTCDayOfMonth(d, p) {\n return pad(d.getUTCDate(), p, 2);\n}\n\nfunction formatUTCHour24(d, p) {\n return pad(d.getUTCHours(), p, 2);\n}\n\nfunction formatUTCHour12(d, p) {\n return pad(d.getUTCHours() % 12 || 12, p, 2);\n}\n\nfunction formatUTCDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 3);\n}\n\nfunction formatUTCMilliseconds(d, p) {\n return pad(d.getUTCMilliseconds(), p, 3);\n}\n\nfunction formatUTCMicroseconds(d, p) {\n return formatUTCMilliseconds(d, p) + \"000\";\n}\n\nfunction formatUTCMonthNumber(d, p) {\n return pad(d.getUTCMonth() + 1, p, 2);\n}\n\nfunction formatUTCMinutes(d, p) {\n return pad(d.getUTCMinutes(), p, 2);\n}\n\nfunction formatUTCSeconds(d, p) {\n return pad(d.getUTCSeconds(), p, 2);\n}\n\nfunction formatUTCWeekdayNumberMonday(d) {\n var dow = d.getUTCDay();\n return dow === 0 ? 7 : dow;\n}\n\nfunction formatUTCWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCWeekNumberISO(d, p) {\n var day = d.getUTCDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d).getUTCDay() === 4), p, 2);\n}\n\nfunction formatUTCWeekdayNumberSunday(d) {\n return d.getUTCDay();\n}\n\nfunction formatUTCWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCYear(d, p) {\n return pad(d.getUTCFullYear() % 100, p, 2);\n}\n\nfunction formatUTCFullYear(d, p) {\n return pad(d.getUTCFullYear() % 10000, p, 4);\n}\n\nfunction formatUTCZone() {\n return \"+0000\";\n}\n\nfunction formatLiteralPercent() {\n return \"%\";\n}\n\nfunction formatUnixTimestamp(d) {\n return +d;\n}\n\nfunction formatUnixTimestampSeconds(d) {\n return Math.floor(+d / 1000);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/day.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/day.js ***! + \*************************************************************************************/ +/*! exports provided: default, days */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"days\", function() { return days; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar day = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setDate(date.getDate() + step);\n}, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (day);\nvar days = day.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/day.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js ***! + \******************************************************************************************/ +/*! exports provided: durationSecond, durationMinute, durationHour, durationDay, durationWeek */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationSecond\", function() { return durationSecond; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationMinute\", function() { return durationMinute; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationHour\", function() { return durationHour; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationDay\", function() { return durationDay; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationWeek\", function() { return durationWeek; });\nvar durationSecond = 1e3;\nvar durationMinute = 6e4;\nvar durationHour = 36e5;\nvar durationDay = 864e5;\nvar durationWeek = 6048e5;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/hour.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/hour.js ***! + \**************************************************************************************/ +/*! exports provided: default, hours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hours\", function() { return hours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar hour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n var offset = date.getTimezoneOffset() * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"] % _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n if (offset < 0) offset += _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n date.setTime(Math.floor((+date - offset) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"] + offset);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hour);\nvar hours = hour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/hour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _millisecond__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./millisecond */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/millisecond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony import */ var _second__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./second */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/second.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony import */ var _minute__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./minute */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/minute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"minutes\"]; });\n\n/* harmony import */ var _hour__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./hour */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/hour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"hours\"]; });\n\n/* harmony import */ var _day__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./day */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/day.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"days\"]; });\n\n/* harmony import */ var _week__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./week */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/week.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"monday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"mondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"friday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"fridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturdays\"]; });\n\n/* harmony import */ var _month__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./month */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/month.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"months\"]; });\n\n/* harmony import */ var _year__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./year */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/year.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"years\"]; });\n\n/* harmony import */ var _utcMinute__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./utcMinute */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMinute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"utcMinutes\"]; });\n\n/* harmony import */ var _utcHour__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./utcHour */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcHour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"utcHours\"]; });\n\n/* harmony import */ var _utcDay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./utcDay */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcDay.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"utcDays\"]; });\n\n/* harmony import */ var _utcWeek__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./utcWeek */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcWeek.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturdays\"]; });\n\n/* harmony import */ var _utcMonth__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./utcMonth */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMonth.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"utcMonths\"]; });\n\n/* harmony import */ var _utcYear__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./utcYear */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcYear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"utcYears\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return newInterval; });\nvar t0 = new Date,\n t1 = new Date;\n\nfunction newInterval(floori, offseti, count, field) {\n\n function interval(date) {\n return floori(date = new Date(+date)), date;\n }\n\n interval.floor = interval;\n\n interval.ceil = function(date) {\n return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n };\n\n interval.round = function(date) {\n var d0 = interval(date),\n d1 = interval.ceil(date);\n return date - d0 < d1 - date ? d0 : d1;\n };\n\n interval.offset = function(date, step) {\n return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n };\n\n interval.range = function(start, stop, step) {\n var range = [], previous;\n start = interval.ceil(start);\n step = step == null ? 1 : Math.floor(step);\n if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n while (previous < start && start < stop);\n return range;\n };\n\n interval.filter = function(test) {\n return newInterval(function(date) {\n if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n }, function(date, step) {\n if (date >= date) {\n if (step < 0) while (++step <= 0) {\n while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n } else while (--step >= 0) {\n while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n }\n }\n });\n };\n\n if (count) {\n interval.count = function(start, end) {\n t0.setTime(+start), t1.setTime(+end);\n floori(t0), floori(t1);\n return Math.floor(count(t0, t1));\n };\n\n interval.every = function(step) {\n step = Math.floor(step);\n return !isFinite(step) || !(step > 0) ? null\n : !(step > 1) ? interval\n : interval.filter(field\n ? function(d) { return field(d) % step === 0; }\n : function(d) { return interval.count(0, d) % step === 0; });\n };\n }\n\n return interval;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/millisecond.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/millisecond.js ***! + \*********************************************************************************************/ +/*! exports provided: default, milliseconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"milliseconds\", function() { return milliseconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n\n\nvar millisecond = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function() {\n // noop\n}, function(date, step) {\n date.setTime(+date + step);\n}, function(start, end) {\n return end - start;\n});\n\n// An optimized implementation for this simple case.\nmillisecond.every = function(k) {\n k = Math.floor(k);\n if (!isFinite(k) || !(k > 0)) return null;\n if (!(k > 1)) return millisecond;\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / k) * k);\n }, function(date, step) {\n date.setTime(+date + step * k);\n }, function(start, end) {\n return (end - start) / k;\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (millisecond);\nvar milliseconds = millisecond.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/millisecond.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/minute.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/minute.js ***! + \****************************************************************************************/ +/*! exports provided: default, minutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"minutes\", function() { return minutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar minute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (minute);\nvar minutes = minute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/minute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/month.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/month.js ***! + \***************************************************************************************/ +/*! exports provided: default, months */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"months\", function() { return months; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n\n\nvar month = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setMonth(date.getMonth() + step);\n}, function(start, end) {\n return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;\n}, function(date) {\n return date.getMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (month);\nvar months = month.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/month.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/second.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/second.js ***! + \****************************************************************************************/ +/*! exports provided: default, seconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"seconds\", function() { return seconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar second = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"];\n}, function(date) {\n return date.getUTCSeconds();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (second);\nvar seconds = second.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/second.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcDay.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcDay.js ***! + \****************************************************************************************/ +/*! exports provided: default, utcDays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return utcDays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcDay = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getUTCDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcDay);\nvar utcDays = utcDay.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcDay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcHour.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcHour.js ***! + \*****************************************************************************************/ +/*! exports provided: default, utcHours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return utcHours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcHour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMinutes(0, 0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getUTCHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcHour);\nvar utcHours = utcHour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcHour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMinute.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMinute.js ***! + \*******************************************************************************************/ +/*! exports provided: default, utcMinutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return utcMinutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcMinute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCSeconds(0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getUTCMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMinute);\nvar utcMinutes = utcMinute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMinute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMonth.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMonth.js ***! + \******************************************************************************************/ +/*! exports provided: default, utcMonths */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return utcMonths; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n\n\nvar utcMonth = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCMonth(date.getUTCMonth() + step);\n}, function(start, end) {\n return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n}, function(date) {\n return date.getUTCMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMonth);\nvar utcMonths = utcMonth.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcMonth.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcWeek.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcWeek.js ***! + \*****************************************************************************************/ +/*! exports provided: utcSunday, utcMonday, utcTuesday, utcWednesday, utcThursday, utcFriday, utcSaturday, utcSundays, utcMondays, utcTuesdays, utcWednesdays, utcThursdays, utcFridays, utcSaturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return utcSunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return utcMonday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return utcTuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return utcWednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return utcThursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return utcFriday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return utcSaturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return utcSundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return utcMondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return utcTuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return utcWednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return utcThursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return utcFridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return utcSaturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction utcWeekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step * 7);\n }, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar utcSunday = utcWeekday(0);\nvar utcMonday = utcWeekday(1);\nvar utcTuesday = utcWeekday(2);\nvar utcWednesday = utcWeekday(3);\nvar utcThursday = utcWeekday(4);\nvar utcFriday = utcWeekday(5);\nvar utcSaturday = utcWeekday(6);\n\nvar utcSundays = utcSunday.range;\nvar utcMondays = utcMonday.range;\nvar utcTuesdays = utcTuesday.range;\nvar utcWednesdays = utcWednesday.range;\nvar utcThursdays = utcThursday.range;\nvar utcFridays = utcFriday.range;\nvar utcSaturdays = utcSaturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcWeek.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcYear.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcYear.js ***! + \*****************************************************************************************/ +/*! exports provided: default, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return utcYears; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n\n\nvar utcYear = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step);\n}, function(start, end) {\n return end.getUTCFullYear() - start.getUTCFullYear();\n}, function(date) {\n return date.getUTCFullYear();\n});\n\n// An optimized implementation for this simple case.\nutcYear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcYear);\nvar utcYears = utcYear.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/utcYear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/week.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/week.js ***! + \**************************************************************************************/ +/*! exports provided: sunday, monday, tuesday, wednesday, thursday, friday, saturday, sundays, mondays, tuesdays, wednesdays, thursdays, fridays, saturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sunday\", function() { return sunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monday\", function() { return monday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesday\", function() { return tuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesday\", function() { return wednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursday\", function() { return thursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"friday\", function() { return friday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturday\", function() { return saturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sundays\", function() { return sundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mondays\", function() { return mondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesdays\", function() { return tuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesdays\", function() { return wednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursdays\", function() { return thursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fridays\", function() { return fridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturdays\", function() { return saturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction weekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setDate(date.getDate() + step * 7);\n }, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar sunday = weekday(0);\nvar monday = weekday(1);\nvar tuesday = weekday(2);\nvar wednesday = weekday(3);\nvar thursday = weekday(4);\nvar friday = weekday(5);\nvar saturday = weekday(6);\n\nvar sundays = sunday.range;\nvar mondays = monday.range;\nvar tuesdays = tuesday.range;\nvar wednesdays = wednesday.range;\nvar thursdays = thursday.range;\nvar fridays = friday.range;\nvar saturdays = saturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/week.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/year.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/year.js ***! + \**************************************************************************************/ +/*! exports provided: default, years */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"years\", function() { return years; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/interval.js\");\n\n\nvar year = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setFullYear(date.getFullYear() + step);\n}, function(start, end) {\n return end.getFullYear() - start.getFullYear();\n}, function(date) {\n return date.getFullYear();\n});\n\n// An optimized implementation for this simple case.\nyear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setFullYear(date.getFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (year);\nvar years = year.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/year.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/array.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/array.js ***! + \******************************************************************/ +/*! exports provided: map, slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return map; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar array = Array.prototype;\n\nvar map = array.map;\nvar slice = array.slice;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/band.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/band.js ***! + \*****************************************************************/ +/*! exports provided: default, point */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return band; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _ordinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ordinal */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/ordinal.js\");\n\n\n\nfunction band() {\n var scale = Object(_ordinal__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().unknown(undefined),\n domain = scale.domain,\n ordinalRange = scale.range,\n range = [0, 1],\n step,\n bandwidth,\n round = false,\n paddingInner = 0,\n paddingOuter = 0,\n align = 0.5;\n\n delete scale.unknown;\n\n function rescale() {\n var n = domain().length,\n reverse = range[1] < range[0],\n start = range[reverse - 0],\n stop = range[1 - reverse];\n step = (stop - start) / Math.max(1, n - paddingInner + paddingOuter * 2);\n if (round) step = Math.floor(step);\n start += (stop - start - step * (n - paddingInner)) * align;\n bandwidth = step * (1 - paddingInner);\n if (round) start = Math.round(start), bandwidth = Math.round(bandwidth);\n var values = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"range\"])(n).map(function(i) { return start + step * i; });\n return ordinalRange(reverse ? values.reverse() : values);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (domain(_), rescale()) : domain();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = [+_[0], +_[1]], rescale()) : range.slice();\n };\n\n scale.rangeRound = function(_) {\n return range = [+_[0], +_[1]], round = true, rescale();\n };\n\n scale.bandwidth = function() {\n return bandwidth;\n };\n\n scale.step = function() {\n return step;\n };\n\n scale.round = function(_) {\n return arguments.length ? (round = !!_, rescale()) : round;\n };\n\n scale.padding = function(_) {\n return arguments.length ? (paddingInner = paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;\n };\n\n scale.paddingInner = function(_) {\n return arguments.length ? (paddingInner = Math.max(0, Math.min(1, _)), rescale()) : paddingInner;\n };\n\n scale.paddingOuter = function(_) {\n return arguments.length ? (paddingOuter = Math.max(0, Math.min(1, _)), rescale()) : paddingOuter;\n };\n\n scale.align = function(_) {\n return arguments.length ? (align = Math.max(0, Math.min(1, _)), rescale()) : align;\n };\n\n scale.copy = function() {\n return band()\n .domain(domain())\n .range(range)\n .round(round)\n .paddingInner(paddingInner)\n .paddingOuter(paddingOuter)\n .align(align);\n };\n\n return rescale();\n}\n\nfunction pointish(scale) {\n var copy = scale.copy;\n\n scale.padding = scale.paddingOuter;\n delete scale.paddingInner;\n delete scale.paddingOuter;\n\n scale.copy = function() {\n return pointish(copy());\n };\n\n return scale;\n}\n\nfunction point() {\n return pointish(band().paddingInner(1));\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/band.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/category10.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/category10.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./colors */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf\"));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/category10.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/category20.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/category20.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./colors */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"1f77b4aec7e8ff7f0effbb782ca02c98df8ad62728ff98969467bdc5b0d58c564bc49c94e377c2f7b6d27f7f7fc7c7c7bcbd22dbdb8d17becf9edae5\"));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/category20.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/category20b.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/category20b.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./colors */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"393b795254a36b6ecf9c9ede6379398ca252b5cf6bcedb9c8c6d31bd9e39e7ba52e7cb94843c39ad494ad6616be7969c7b4173a55194ce6dbdde9ed6\"));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/category20b.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/category20c.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/category20c.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./colors */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"3182bd6baed69ecae1c6dbefe6550dfd8d3cfdae6bfdd0a231a35474c476a1d99bc7e9c0756bb19e9ac8bcbddcdadaeb636363969696bdbdbdd9d9d9\"));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/category20c.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(s) {\n return s.match(/.{6}/g).map(function(x) {\n return \"#\" + x;\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js ***! + \***********************************************************************/ +/*! exports provided: deinterpolateLinear, copy, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deinterpolateLinear\", function() { return deinterpolateLinear; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"copy\", function() { return copy; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return continuous; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/number.js\");\n\n\n\n\n\n\nvar unit = [0, 1];\n\nfunction deinterpolateLinear(a, b) {\n return (b -= (a = +a))\n ? function(x) { return (x - a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(b);\n}\n\nfunction deinterpolateClamp(deinterpolate) {\n return function(a, b) {\n var d = deinterpolate(a = +a, b = +b);\n return function(x) { return x <= a ? 0 : x >= b ? 1 : d(x); };\n };\n}\n\nfunction reinterpolateClamp(reinterpolate) {\n return function(a, b) {\n var r = reinterpolate(a = +a, b = +b);\n return function(t) { return t <= 0 ? a : t >= 1 ? b : r(t); };\n };\n}\n\nfunction bimap(domain, range, deinterpolate, reinterpolate) {\n var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];\n if (d1 < d0) d0 = deinterpolate(d1, d0), r0 = reinterpolate(r1, r0);\n else d0 = deinterpolate(d0, d1), r0 = reinterpolate(r0, r1);\n return function(x) { return r0(d0(x)); };\n}\n\nfunction polymap(domain, range, deinterpolate, reinterpolate) {\n var j = Math.min(domain.length, range.length) - 1,\n d = new Array(j),\n r = new Array(j),\n i = -1;\n\n // Reverse descending domains.\n if (domain[j] < domain[0]) {\n domain = domain.slice().reverse();\n range = range.slice().reverse();\n }\n\n while (++i < j) {\n d[i] = deinterpolate(domain[i], domain[i + 1]);\n r[i] = reinterpolate(range[i], range[i + 1]);\n }\n\n return function(x) {\n var i = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 1, j) - 1;\n return r[i](d[i](x));\n };\n}\n\nfunction copy(source, target) {\n return target\n .domain(source.domain())\n .range(source.range())\n .interpolate(source.interpolate())\n .clamp(source.clamp());\n}\n\n// deinterpolate(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].\n// reinterpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding domain value x in [a,b].\nfunction continuous(deinterpolate, reinterpolate) {\n var domain = unit,\n range = unit,\n interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolate\"],\n clamp = false,\n piecewise,\n output,\n input;\n\n function rescale() {\n piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;\n output = input = null;\n return scale;\n }\n\n function scale(x) {\n return (output || (output = piecewise(domain, range, clamp ? deinterpolateClamp(deinterpolate) : deinterpolate, interpolate)))(+x);\n }\n\n scale.invert = function(y) {\n return (input || (input = piecewise(range, domain, deinterpolateLinear, clamp ? reinterpolateClamp(reinterpolate) : reinterpolate)))(+y);\n };\n\n scale.domain = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_2__[\"map\"].call(_, _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]), rescale()) : domain.slice();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_2__[\"slice\"].call(_), rescale()) : range.slice();\n };\n\n scale.rangeRound = function(_) {\n return range = _array__WEBPACK_IMPORTED_MODULE_2__[\"slice\"].call(_), interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRound\"], rescale();\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = !!_, rescale()) : clamp;\n };\n\n scale.interpolate = function(_) {\n return arguments.length ? (interpolate = _, rescale()) : interpolate;\n };\n\n return rescale();\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/cubehelix.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/cubehelix.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(300, 0.5, 0.0), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(-240, 0.5, 1.0)));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/identity.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/identity.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return identity; });\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/number.js\");\n\n\n\n\nfunction identity() {\n var domain = [0, 1];\n\n function scale(x) {\n return +x;\n }\n\n scale.invert = scale;\n\n scale.domain = scale.range = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_0__[\"map\"].call(_, _number__WEBPACK_IMPORTED_MODULE_2__[\"default\"]), scale) : domain.slice();\n };\n\n scale.copy = function() {\n return identity().domain(domain);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_1__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js ***! + \*******************************************************************/ +/*! exports provided: linearish, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linearish\", function() { return linearish; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return linear; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./continuous */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js\");\n/* harmony import */ var _tickFormat__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./tickFormat */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/tickFormat.js\");\n\n\n\n\n\nfunction linearish(scale) {\n var domain = scale.domain;\n\n scale.ticks = function(count) {\n var d = domain();\n return Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ticks\"])(d[0], d[d.length - 1], count == null ? 10 : count);\n };\n\n scale.tickFormat = function(count, specifier) {\n return Object(_tickFormat__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(domain(), count, specifier);\n };\n\n scale.nice = function(count) {\n if (count == null) count = 10;\n\n var d = domain(),\n i0 = 0,\n i1 = d.length - 1,\n start = d[i0],\n stop = d[i1],\n step;\n\n if (stop < start) {\n step = start, start = stop, stop = step;\n step = i0, i0 = i1, i1 = step;\n }\n\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n\n if (step > 0) {\n start = Math.floor(start / step) * step;\n stop = Math.ceil(stop / step) * step;\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n } else if (step < 0) {\n start = Math.ceil(start * step) / step;\n stop = Math.floor(stop * step) / step;\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickIncrement\"])(start, stop, count);\n }\n\n if (step > 0) {\n d[i0] = Math.floor(start / step) * step;\n d[i1] = Math.ceil(stop / step) * step;\n domain(d);\n } else if (step < 0) {\n d[i0] = Math.ceil(start * step) / step;\n d[i1] = Math.floor(stop * step) / step;\n domain(d);\n }\n\n return scale;\n };\n\n return scale;\n}\n\nfunction linear() {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"deinterpolateLinear\"], d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]);\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"copy\"])(scale, linear());\n };\n\n return linearish(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/log.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/log.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return log; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-format */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _nice__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./nice */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/nice.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./continuous */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js\");\n\n\n\n\n\n\nfunction deinterpolate(a, b) {\n return (b = Math.log(b / a))\n ? function(x) { return Math.log(x / a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(b);\n}\n\nfunction reinterpolate(a, b) {\n return a < 0\n ? function(t) { return -Math.pow(-b, t) * Math.pow(-a, 1 - t); }\n : function(t) { return Math.pow(b, t) * Math.pow(a, 1 - t); };\n}\n\nfunction pow10(x) {\n return isFinite(x) ? +(\"1e\" + x) : x < 0 ? 0 : x;\n}\n\nfunction powp(base) {\n return base === 10 ? pow10\n : base === Math.E ? Math.exp\n : function(x) { return Math.pow(base, x); };\n}\n\nfunction logp(base) {\n return base === Math.E ? Math.log\n : base === 10 && Math.log10\n || base === 2 && Math.log2\n || (base = Math.log(base), function(x) { return Math.log(x) / base; });\n}\n\nfunction reflect(f) {\n return function(x) {\n return -f(-x);\n };\n}\n\nfunction log() {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(deinterpolate, reinterpolate).domain([1, 10]),\n domain = scale.domain,\n base = 10,\n logs = logp(10),\n pows = powp(10);\n\n function rescale() {\n logs = logp(base), pows = powp(base);\n if (domain()[0] < 0) logs = reflect(logs), pows = reflect(pows);\n return scale;\n }\n\n scale.base = function(_) {\n return arguments.length ? (base = +_, rescale()) : base;\n };\n\n scale.domain = function(_) {\n return arguments.length ? (domain(_), rescale()) : domain();\n };\n\n scale.ticks = function(count) {\n var d = domain(),\n u = d[0],\n v = d[d.length - 1],\n r;\n\n if (r = v < u) i = u, u = v, v = i;\n\n var i = logs(u),\n j = logs(v),\n p,\n k,\n t,\n n = count == null ? 10 : +count,\n z = [];\n\n if (!(base % 1) && j - i < n) {\n i = Math.round(i) - 1, j = Math.round(j) + 1;\n if (u > 0) for (; i < j; ++i) {\n for (k = 1, p = pows(i); k < base; ++k) {\n t = p * k;\n if (t < u) continue;\n if (t > v) break;\n z.push(t);\n }\n } else for (; i < j; ++i) {\n for (k = base - 1, p = pows(i); k >= 1; --k) {\n t = p * k;\n if (t < u) continue;\n if (t > v) break;\n z.push(t);\n }\n }\n } else {\n z = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ticks\"])(i, j, Math.min(j - i, n)).map(pows);\n }\n\n return r ? z.reverse() : z;\n };\n\n scale.tickFormat = function(count, specifier) {\n if (specifier == null) specifier = base === 10 ? \".0e\" : \",\";\n if (typeof specifier !== \"function\") specifier = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"format\"])(specifier);\n if (count === Infinity) return specifier;\n if (count == null) count = 10;\n var k = Math.max(1, base * count / scale.ticks().length); // TODO fast estimate?\n return function(d) {\n var i = d / pows(Math.round(logs(d)));\n if (i * base < base - 0.5) i *= base;\n return i <= k ? specifier(d) : \"\";\n };\n };\n\n scale.nice = function() {\n return domain(Object(_nice__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(domain(), {\n floor: function(x) { return pows(Math.floor(logs(x))); },\n ceil: function(x) { return pows(Math.ceil(logs(x))); }\n }));\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_4__[\"copy\"])(scale, log().base(base));\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/log.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/nice.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/nice.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(domain, interval) {\n domain = domain.slice();\n\n var i0 = 0,\n i1 = domain.length - 1,\n x0 = domain[i0],\n x1 = domain[i1],\n t;\n\n if (x1 < x0) {\n t = i0, i0 = i1, i1 = t;\n t = x0, x0 = x1, x1 = t;\n }\n\n domain[i0] = interval.floor(x0);\n domain[i1] = interval.ceil(x1);\n return domain;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/nice.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/number.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/number.js ***! + \*******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return +x;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/ordinal.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/ordinal.js ***! + \********************************************************************/ +/*! exports provided: implicit, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"implicit\", function() { return implicit; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return ordinal; });\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-collection */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-collection/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n\n\n\nvar implicit = {name: \"implicit\"};\n\nfunction ordinal(range) {\n var index = Object(d3_collection__WEBPACK_IMPORTED_MODULE_0__[\"map\"])(),\n domain = [],\n unknown = implicit;\n\n range = range == null ? [] : _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(range);\n\n function scale(d) {\n var key = d + \"\", i = index.get(key);\n if (!i) {\n if (unknown !== implicit) return unknown;\n index.set(key, i = domain.push(d));\n }\n return range[(i - 1) % range.length];\n }\n\n scale.domain = function(_) {\n if (!arguments.length) return domain.slice();\n domain = [], index = Object(d3_collection__WEBPACK_IMPORTED_MODULE_0__[\"map\"])();\n var i = -1, n = _.length, d, key;\n while (++i < n) if (!index.has(key = (d = _[i]) + \"\")) index.set(key, domain.push(d));\n return scale;\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), scale) : range.slice();\n };\n\n scale.unknown = function(_) {\n return arguments.length ? (unknown = _, scale) : unknown;\n };\n\n scale.copy = function() {\n return ordinal()\n .domain(domain)\n .range(range)\n .unknown(unknown);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/ordinal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/pow.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/pow.js ***! + \****************************************************************/ +/*! exports provided: default, sqrt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return pow; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/constant.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./continuous */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js\");\n\n\n\n\nfunction raise(x, exponent) {\n return x < 0 ? -Math.pow(-x, exponent) : Math.pow(x, exponent);\n}\n\nfunction pow() {\n var exponent = 1,\n scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(deinterpolate, reinterpolate),\n domain = scale.domain;\n\n function deinterpolate(a, b) {\n return (b = raise(b, exponent) - (a = raise(a, exponent)))\n ? function(x) { return (raise(x, exponent) - a) / b; }\n : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(b);\n }\n\n function reinterpolate(a, b) {\n b = raise(b, exponent) - (a = raise(a, exponent));\n return function(t) { return raise(a + b * t, 1 / exponent); };\n }\n\n scale.exponent = function(_) {\n return arguments.length ? (exponent = +_, domain(domain())) : exponent;\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_2__[\"copy\"])(scale, pow().exponent(exponent));\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_1__[\"linearish\"])(scale);\n}\n\nfunction sqrt() {\n return pow().exponent(0.5);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/pow.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/quantile.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/quantile.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quantile; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n\n\n\nfunction quantile() {\n var domain = [],\n range = [],\n thresholds = [];\n\n function rescale() {\n var i = 0, n = Math.max(1, range.length);\n thresholds = new Array(n - 1);\n while (++i < n) thresholds[i - 1] = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"quantile\"])(domain, i / n);\n return scale;\n }\n\n function scale(x) {\n if (!isNaN(x = +x)) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(thresholds, x)];\n }\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return i < 0 ? [NaN, NaN] : [\n i > 0 ? thresholds[i - 1] : domain[0],\n i < thresholds.length ? thresholds[i] : domain[domain.length - 1]\n ];\n };\n\n scale.domain = function(_) {\n if (!arguments.length) return domain.slice();\n domain = [];\n for (var i = 0, n = _.length, d; i < n; ++i) if (d = _[i], d != null && !isNaN(d = +d)) domain.push(d);\n domain.sort(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"ascending\"]);\n return rescale();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), rescale()) : range.slice();\n };\n\n scale.quantiles = function() {\n return thresholds.slice();\n };\n\n scale.copy = function() {\n return quantile()\n .domain(domain)\n .range(range);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/quantile.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/quantize.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/quantize.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return quantize; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js\");\n\n\n\n\nfunction quantize() {\n var x0 = 0,\n x1 = 1,\n n = 1,\n domain = [0.5],\n range = [0, 1];\n\n function scale(x) {\n if (x <= x) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 0, n)];\n }\n\n function rescale() {\n var i = -1;\n domain = new Array(n);\n while (++i < n) domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1);\n return scale;\n }\n\n scale.domain = function(_) {\n return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];\n };\n\n scale.range = function(_) {\n return arguments.length ? (n = (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_)).length - 1, rescale()) : range.slice();\n };\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return i < 0 ? [NaN, NaN]\n : i < 1 ? [x0, domain[0]]\n : i >= n ? [domain[n - 1], x1]\n : [domain[i - 1], domain[i]];\n };\n\n scale.copy = function() {\n return quantize()\n .domain([x0, x1])\n .range(range);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_2__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/rainbow.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/rainbow.js ***! + \********************************************************************/ +/*! exports provided: warm, cool, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"warm\", function() { return warm; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cool\", function() { return cool; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js\");\n\n\n\nvar warm = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(-100, 0.75, 0.35), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(80, 1.50, 0.8));\n\nvar cool = Object(d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateCubehelixLong\"])(Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(260, 0.75, 0.35), Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(80, 1.50, 0.8));\n\nvar rainbow = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])();\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(t) {\n if (t < 0 || t > 1) t -= Math.floor(t);\n var ts = Math.abs(t - 0.5);\n rainbow.h = 360 * t - 100;\n rainbow.s = 1.5 - 1.5 * ts;\n rainbow.l = 0.8 - 0.9 * ts;\n return rainbow + \"\";\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/rainbow.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/sequential.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/sequential.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return sequential; });\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/linear.js\");\n\n\nfunction sequential(interpolator) {\n var x0 = 0,\n x1 = 1,\n clamp = false;\n\n function scale(x) {\n var t = (x - x0) / (x1 - x0);\n return interpolator(clamp ? Math.max(0, Math.min(1, t)) : t);\n }\n\n scale.domain = function(_) {\n return arguments.length ? (x0 = +_[0], x1 = +_[1], scale) : [x0, x1];\n };\n\n scale.clamp = function(_) {\n return arguments.length ? (clamp = !!_, scale) : clamp;\n };\n\n scale.interpolator = function(_) {\n return arguments.length ? (interpolator = _, scale) : interpolator;\n };\n\n scale.copy = function() {\n return sequential(interpolator).domain([x0, x1]).clamp(clamp);\n };\n\n return Object(_linear__WEBPACK_IMPORTED_MODULE_0__[\"linearish\"])(scale);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/sequential.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/threshold.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/threshold.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return threshold; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n\n\n\nfunction threshold() {\n var domain = [0.5],\n range = [0, 1],\n n = 1;\n\n function scale(x) {\n if (x <= x) return range[Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisect\"])(domain, x, 0, n)];\n }\n\n scale.domain = function(_) {\n return arguments.length ? (domain = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), n = Math.min(domain.length, range.length - 1), scale) : domain.slice();\n };\n\n scale.range = function(_) {\n return arguments.length ? (range = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(_), n = Math.min(domain.length, range.length - 1), scale) : range.slice();\n };\n\n scale.invertExtent = function(y) {\n var i = range.indexOf(y);\n return [domain[i - 1], domain[i]];\n };\n\n scale.copy = function() {\n return threshold()\n .domain(domain)\n .range(range);\n };\n\n return scale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/threshold.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/tickFormat.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/tickFormat.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-format */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-format/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(domain, count, specifier) {\n var start = domain[0],\n stop = domain[domain.length - 1],\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start, stop, count == null ? 10 : count),\n precision;\n specifier = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"formatSpecifier\"])(specifier == null ? \",f\" : specifier);\n switch (specifier.type) {\n case \"s\": {\n var value = Math.max(Math.abs(start), Math.abs(stop));\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionPrefix\"])(step, value))) specifier.precision = precision;\n return Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"formatPrefix\"])(specifier, value);\n }\n case \"\":\n case \"e\":\n case \"g\":\n case \"p\":\n case \"r\": {\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionRound\"])(step, Math.max(Math.abs(start), Math.abs(stop))))) specifier.precision = precision - (specifier.type === \"e\");\n break;\n }\n case \"f\":\n case \"%\": {\n if (specifier.precision == null && !isNaN(precision = Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"precisionFixed\"])(step))) specifier.precision = precision - (specifier.type === \"%\") * 2;\n break;\n }\n }\n return Object(d3_format__WEBPACK_IMPORTED_MODULE_1__[\"format\"])(specifier);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/tickFormat.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/time.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/time.js ***! + \*****************************************************************/ +/*! exports provided: calendar, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"calendar\", function() { return calendar; });\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-array/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-time */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js\");\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/array.js\");\n/* harmony import */ var _continuous__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./continuous */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/continuous.js\");\n/* harmony import */ var _nice__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./nice */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/nice.js\");\n\n\n\n\n\n\n\n\nvar durationSecond = 1000,\n durationMinute = durationSecond * 60,\n durationHour = durationMinute * 60,\n durationDay = durationHour * 24,\n durationWeek = durationDay * 7,\n durationMonth = durationDay * 30,\n durationYear = durationDay * 365;\n\nfunction date(t) {\n return new Date(t);\n}\n\nfunction number(t) {\n return t instanceof Date ? +t : +new Date(+t);\n}\n\nfunction calendar(year, month, week, day, hour, minute, second, millisecond, format) {\n var scale = Object(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"deinterpolateLinear\"], d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]),\n invert = scale.invert,\n domain = scale.domain;\n\n var formatMillisecond = format(\".%L\"),\n formatSecond = format(\":%S\"),\n formatMinute = format(\"%I:%M\"),\n formatHour = format(\"%I %p\"),\n formatDay = format(\"%a %d\"),\n formatWeek = format(\"%b %d\"),\n formatMonth = format(\"%B\"),\n formatYear = format(\"%Y\");\n\n var tickIntervals = [\n [second, 1, durationSecond],\n [second, 5, 5 * durationSecond],\n [second, 15, 15 * durationSecond],\n [second, 30, 30 * durationSecond],\n [minute, 1, durationMinute],\n [minute, 5, 5 * durationMinute],\n [minute, 15, 15 * durationMinute],\n [minute, 30, 30 * durationMinute],\n [ hour, 1, durationHour ],\n [ hour, 3, 3 * durationHour ],\n [ hour, 6, 6 * durationHour ],\n [ hour, 12, 12 * durationHour ],\n [ day, 1, durationDay ],\n [ day, 2, 2 * durationDay ],\n [ week, 1, durationWeek ],\n [ month, 1, durationMonth ],\n [ month, 3, 3 * durationMonth ],\n [ year, 1, durationYear ]\n ];\n\n function tickFormat(date) {\n return (second(date) < date ? formatMillisecond\n : minute(date) < date ? formatSecond\n : hour(date) < date ? formatMinute\n : day(date) < date ? formatHour\n : month(date) < date ? (week(date) < date ? formatDay : formatWeek)\n : year(date) < date ? formatMonth\n : formatYear)(date);\n }\n\n function tickInterval(interval, start, stop, step) {\n if (interval == null) interval = 10;\n\n // If a desired tick count is specified, pick a reasonable tick interval\n // based on the extent of the domain and a rough estimate of tick size.\n // Otherwise, assume interval is already a time interval and use it.\n if (typeof interval === \"number\") {\n var target = Math.abs(stop - start) / interval,\n i = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"bisector\"])(function(i) { return i[2]; }).right(tickIntervals, target);\n if (i === tickIntervals.length) {\n step = Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start / durationYear, stop / durationYear, interval);\n interval = year;\n } else if (i) {\n i = tickIntervals[target / tickIntervals[i - 1][2] < tickIntervals[i][2] / target ? i - 1 : i];\n step = i[1];\n interval = i[0];\n } else {\n step = Math.max(Object(d3_array__WEBPACK_IMPORTED_MODULE_0__[\"tickStep\"])(start, stop, interval), 1);\n interval = millisecond;\n }\n }\n\n return step == null ? interval : interval.every(step);\n }\n\n scale.invert = function(y) {\n return new Date(invert(y));\n };\n\n scale.domain = function(_) {\n return arguments.length ? domain(_array__WEBPACK_IMPORTED_MODULE_4__[\"map\"].call(_, number)) : domain().map(date);\n };\n\n scale.ticks = function(interval, step) {\n var d = domain(),\n t0 = d[0],\n t1 = d[d.length - 1],\n r = t1 < t0,\n t;\n if (r) t = t0, t0 = t1, t1 = t;\n t = tickInterval(interval, t0, t1, step);\n t = t ? t.range(t0, t1 + 1) : []; // inclusive stop\n return r ? t.reverse() : t;\n };\n\n scale.tickFormat = function(count, specifier) {\n return specifier == null ? tickFormat : format(specifier);\n };\n\n scale.nice = function(interval, step) {\n var d = domain();\n return (interval = tickInterval(interval, d[0], d[d.length - 1], step))\n ? domain(Object(_nice__WEBPACK_IMPORTED_MODULE_6__[\"default\"])(d, interval))\n : scale;\n };\n\n scale.copy = function() {\n return Object(_continuous__WEBPACK_IMPORTED_MODULE_5__[\"copy\"])(scale, calendar(year, month, week, day, hour, minute, second, millisecond, format));\n };\n\n return scale;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return calendar(d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeYear\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMonth\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeWeek\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeDay\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeHour\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMinute\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeSecond\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"timeMillisecond\"], d3_time_format__WEBPACK_IMPORTED_MODULE_3__[\"timeFormat\"]).domain([new Date(2000, 0, 1), new Date(2000, 0, 2)]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/time.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/utcTime.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/utcTime.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _time__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./time */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/time.js\");\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time-format/src/index.js\");\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-time */ \"./node_modules/dagre-d3/node_modules/d3-scale/node_modules/d3-time/src/index.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return Object(_time__WEBPACK_IMPORTED_MODULE_0__[\"calendar\"])(d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcYear\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMonth\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcWeek\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcDay\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcHour\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMinute\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcSecond\"], d3_time__WEBPACK_IMPORTED_MODULE_2__[\"utcMillisecond\"], d3_time_format__WEBPACK_IMPORTED_MODULE_1__[\"utcFormat\"]).domain([Date.UTC(2000, 0, 1), Date.UTC(2000, 0, 2)]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/utcTime.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-scale/src/viridis.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-scale/src/viridis.js ***! + \********************************************************************/ +/*! exports provided: default, magma, inferno, plasma */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"magma\", function() { return magma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"inferno\", function() { return inferno; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"plasma\", function() { return plasma; });\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./colors */ \"./node_modules/dagre-d3/node_modules/d3-scale/src/colors.js\");\n\n\nfunction ramp(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725\")));\n\nvar magma = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf\"));\n\nvar inferno = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4\"));\n\nvar plasma = ramp(Object(_colors__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(\"0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921\"));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-scale/src/viridis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/index.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/index.js ***! + \******************************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/create */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _src_create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/creator */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _src_creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/local */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _src_local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/matcher */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _src_matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/mouse */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _src_mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/namespace */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _src_namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/namespaces */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _src_namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/point */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _src_point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _src_select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/select */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _src_select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _src_selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/selectAll */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _src_selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/selection/index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _src_selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/selector */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _src_selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _src_selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/selection/style */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _src_selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _src_touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/touch */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _src_touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _src_touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/touches */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _src_touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _src_window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/window */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _src_window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _src_selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./src/selection/on */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _src_selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _src_selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/constant.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/constant.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/create.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/create.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/local.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/local.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/matcher.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/matcher.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/mouse.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/mouse.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js ***! + \***************************************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/point.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/point.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/select.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/select.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selectAll.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selectAll.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/append.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/append.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/attr.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/attr.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/call.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/call.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/classed.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/classed.js ***! + \**********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/clone.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/clone.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/data.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/data.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/datum.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/datum.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/dispatch.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/dispatch.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/each.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/each.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/empty.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/empty.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/enter.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/enter.js ***! + \********************************************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/exit.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/exit.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/filter.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/filter.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/html.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/html.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js ***! + \********************************************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/insert.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/insert.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/lower.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/lower.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/merge.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/merge.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/node.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/node.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/nodes.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/nodes.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js ***! + \*****************************************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/order.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/order.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/property.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/property.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/raise.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/raise.js ***! + \********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/remove.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/remove.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/select.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/select.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/selectAll.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/selectAll.js ***! + \************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/size.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/size.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sort.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sort.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sparse.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sparse.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/style.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/style.js ***! + \********************************************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selection/text.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selection/text.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/selectorAll.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/selectorAll.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/touch.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/touch.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/touches.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/touches.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-selection/src/window.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-selection/src/window.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/index.js ***! + \**************************************************************/ +/*! exports provided: arc, area, line, pie, areaRadial, radialArea, lineRadial, radialLine, pointRadial, linkHorizontal, linkVertical, linkRadial, symbol, symbols, symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye, curveBasisClosed, curveBasisOpen, curveBasis, curveBundle, curveCardinalClosed, curveCardinalOpen, curveCardinal, curveCatmullRomClosed, curveCatmullRomOpen, curveCatmullRom, curveLinearClosed, curveLinear, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, stack, stackOffsetExpand, stackOffsetDiverging, stackOffsetNone, stackOffsetSilhouette, stackOffsetWiggle, stackOrderAscending, stackOrderDescending, stackOrderInsideOut, stackOrderNone, stackOrderReverse */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_arc__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/arc */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/arc.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"arc\", function() { return _src_arc__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/area */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/area.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"area\", function() { return _src_area__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_line__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/line */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/line.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"line\", function() { return _src_line__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_pie__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/pie */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/pie.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pie\", function() { return _src_pie__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _src_areaRadial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/areaRadial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/areaRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"areaRadial\", function() { return _src_areaRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialArea\", function() { return _src_areaRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _src_lineRadial__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/lineRadial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/lineRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return _src_lineRadial__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialLine\", function() { return _src_lineRadial__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _src_pointRadial__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/pointRadial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/pointRadial.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pointRadial\", function() { return _src_pointRadial__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _src_link_index__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/link/index */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/link/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return _src_link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkHorizontal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return _src_link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkVertical\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return _src_link_index__WEBPACK_IMPORTED_MODULE_7__[\"linkRadial\"]; });\n\n/* harmony import */ var _src_symbol__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/symbol */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbol\", function() { return _src_symbol__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return _src_symbol__WEBPACK_IMPORTED_MODULE_8__[\"symbols\"]; });\n\n/* harmony import */ var _src_symbol_circle__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/symbol/circle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCircle\", function() { return _src_symbol_circle__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_cross__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/symbol/cross */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/cross.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCross\", function() { return _src_symbol_cross__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_diamond__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/symbol/diamond */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/diamond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolDiamond\", function() { return _src_symbol_diamond__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_square__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/symbol/square */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/square.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolSquare\", function() { return _src_symbol_square__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_star__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/symbol/star */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/star.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolStar\", function() { return _src_symbol_star__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_triangle__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/symbol/triangle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/triangle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolTriangle\", function() { return _src_symbol_triangle__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _src_symbol_wye__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./src/symbol/wye */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/wye.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolWye\", function() { return _src_symbol_wye__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _src_curve_basisClosed__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./src/curve/basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisClosed\", function() { return _src_curve_basisClosed__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _src_curve_basisOpen__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./src/curve/basisOpen */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisOpen\", function() { return _src_curve_basisOpen__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony import */ var _src_curve_basis__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./src/curve/basis */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasis\", function() { return _src_curve_basis__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _src_curve_bundle__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./src/curve/bundle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/bundle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBundle\", function() { return _src_curve_bundle__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n/* harmony import */ var _src_curve_cardinalClosed__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./src/curve/cardinalClosed */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalClosed\", function() { return _src_curve_cardinalClosed__WEBPACK_IMPORTED_MODULE_20__[\"default\"]; });\n\n/* harmony import */ var _src_curve_cardinalOpen__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./src/curve/cardinalOpen */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalOpen\", function() { return _src_curve_cardinalOpen__WEBPACK_IMPORTED_MODULE_21__[\"default\"]; });\n\n/* harmony import */ var _src_curve_cardinal__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./src/curve/cardinal */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinal\", function() { return _src_curve_cardinal__WEBPACK_IMPORTED_MODULE_22__[\"default\"]; });\n\n/* harmony import */ var _src_curve_catmullRomClosed__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./src/curve/catmullRomClosed */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomClosed\", function() { return _src_curve_catmullRomClosed__WEBPACK_IMPORTED_MODULE_23__[\"default\"]; });\n\n/* harmony import */ var _src_curve_catmullRomOpen__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./src/curve/catmullRomOpen */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomOpen.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomOpen\", function() { return _src_curve_catmullRomOpen__WEBPACK_IMPORTED_MODULE_24__[\"default\"]; });\n\n/* harmony import */ var _src_curve_catmullRom__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./src/curve/catmullRom */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRom\", function() { return _src_curve_catmullRom__WEBPACK_IMPORTED_MODULE_25__[\"default\"]; });\n\n/* harmony import */ var _src_curve_linearClosed__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./src/curve/linearClosed */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linearClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinearClosed\", function() { return _src_curve_linearClosed__WEBPACK_IMPORTED_MODULE_26__[\"default\"]; });\n\n/* harmony import */ var _src_curve_linear__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./src/curve/linear */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinear\", function() { return _src_curve_linear__WEBPACK_IMPORTED_MODULE_27__[\"default\"]; });\n\n/* harmony import */ var _src_curve_monotone__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./src/curve/monotone */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/monotone.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneX\", function() { return _src_curve_monotone__WEBPACK_IMPORTED_MODULE_28__[\"monotoneX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneY\", function() { return _src_curve_monotone__WEBPACK_IMPORTED_MODULE_28__[\"monotoneY\"]; });\n\n/* harmony import */ var _src_curve_natural__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./src/curve/natural */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/natural.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveNatural\", function() { return _src_curve_natural__WEBPACK_IMPORTED_MODULE_29__[\"default\"]; });\n\n/* harmony import */ var _src_curve_step__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! ./src/curve/step */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/step.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStep\", function() { return _src_curve_step__WEBPACK_IMPORTED_MODULE_30__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepAfter\", function() { return _src_curve_step__WEBPACK_IMPORTED_MODULE_30__[\"stepAfter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepBefore\", function() { return _src_curve_step__WEBPACK_IMPORTED_MODULE_30__[\"stepBefore\"]; });\n\n/* harmony import */ var _src_stack__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(/*! ./src/stack */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/stack.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stack\", function() { return _src_stack__WEBPACK_IMPORTED_MODULE_31__[\"default\"]; });\n\n/* harmony import */ var _src_offset_expand__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(/*! ./src/offset/expand */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/expand.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetExpand\", function() { return _src_offset_expand__WEBPACK_IMPORTED_MODULE_32__[\"default\"]; });\n\n/* harmony import */ var _src_offset_diverging__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(/*! ./src/offset/diverging */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/diverging.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetDiverging\", function() { return _src_offset_diverging__WEBPACK_IMPORTED_MODULE_33__[\"default\"]; });\n\n/* harmony import */ var _src_offset_none__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(/*! ./src/offset/none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetNone\", function() { return _src_offset_none__WEBPACK_IMPORTED_MODULE_34__[\"default\"]; });\n\n/* harmony import */ var _src_offset_silhouette__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(/*! ./src/offset/silhouette */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/silhouette.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetSilhouette\", function() { return _src_offset_silhouette__WEBPACK_IMPORTED_MODULE_35__[\"default\"]; });\n\n/* harmony import */ var _src_offset_wiggle__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(/*! ./src/offset/wiggle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/wiggle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetWiggle\", function() { return _src_offset_wiggle__WEBPACK_IMPORTED_MODULE_36__[\"default\"]; });\n\n/* harmony import */ var _src_order_ascending__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(/*! ./src/order/ascending */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderAscending\", function() { return _src_order_ascending__WEBPACK_IMPORTED_MODULE_37__[\"default\"]; });\n\n/* harmony import */ var _src_order_descending__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(/*! ./src/order/descending */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/descending.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderDescending\", function() { return _src_order_descending__WEBPACK_IMPORTED_MODULE_38__[\"default\"]; });\n\n/* harmony import */ var _src_order_insideOut__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(/*! ./src/order/insideOut */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/insideOut.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderInsideOut\", function() { return _src_order_insideOut__WEBPACK_IMPORTED_MODULE_39__[\"default\"]; });\n\n/* harmony import */ var _src_order_none__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(/*! ./src/order/none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderNone\", function() { return _src_order_none__WEBPACK_IMPORTED_MODULE_40__[\"default\"]; });\n\n/* harmony import */ var _src_order_reverse__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(/*! ./src/order/reverse */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/reverse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderReverse\", function() { return _src_order_reverse__WEBPACK_IMPORTED_MODULE_41__[\"default\"]; });\n\n\n\n\n\n // Note: radialArea is deprecated!\n // Note: radialLine is deprecated!\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: path */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/path.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return _path__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/path.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/path.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar pi = Math.PI,\n tau = 2 * pi,\n epsilon = 1e-6,\n tauEpsilon = tau - epsilon;\n\nfunction Path() {\n this._x0 = this._y0 = // start of current subpath\n this._x1 = this._y1 = null; // end of current subpath\n this._ = \"\";\n}\n\nfunction path() {\n return new Path;\n}\n\nPath.prototype = path.prototype = {\n constructor: Path,\n moveTo: function(x, y) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y);\n },\n closePath: function() {\n if (this._x1 !== null) {\n this._x1 = this._x0, this._y1 = this._y0;\n this._ += \"Z\";\n }\n },\n lineTo: function(x, y) {\n this._ += \"L\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n quadraticCurveTo: function(x1, y1, x, y) {\n this._ += \"Q\" + (+x1) + \",\" + (+y1) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) {\n this._ += \"C\" + (+x1) + \",\" + (+y1) + \",\" + (+x2) + \",\" + (+y2) + \",\" + (this._x1 = +x) + \",\" + (this._y1 = +y);\n },\n arcTo: function(x1, y1, x2, y2, r) {\n x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r;\n var x0 = this._x1,\n y0 = this._y1,\n x21 = x2 - x1,\n y21 = y2 - y1,\n x01 = x0 - x1,\n y01 = y0 - y1,\n l01_2 = x01 * x01 + y01 * y01;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x1,y1).\n if (this._x1 === null) {\n this._ += \"M\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Or, is (x1,y1) coincident with (x0,y0)? Do nothing.\n else if (!(l01_2 > epsilon));\n\n // Or, are (x0,y0), (x1,y1) and (x2,y2) collinear?\n // Equivalently, is (x1,y1) coincident with (x2,y2)?\n // Or, is the radius zero? Line to (x1,y1).\n else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) {\n this._ += \"L\" + (this._x1 = x1) + \",\" + (this._y1 = y1);\n }\n\n // Otherwise, draw an arc!\n else {\n var x20 = x2 - x0,\n y20 = y2 - y0,\n l21_2 = x21 * x21 + y21 * y21,\n l20_2 = x20 * x20 + y20 * y20,\n l21 = Math.sqrt(l21_2),\n l01 = Math.sqrt(l01_2),\n l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2),\n t01 = l / l01,\n t21 = l / l21;\n\n // If the start tangent is not coincident with (x0,y0), line to.\n if (Math.abs(t01 - 1) > epsilon) {\n this._ += \"L\" + (x1 + t01 * x01) + \",\" + (y1 + t01 * y01);\n }\n\n this._ += \"A\" + r + \",\" + r + \",0,0,\" + (+(y01 * x20 > x01 * y20)) + \",\" + (this._x1 = x1 + t21 * x21) + \",\" + (this._y1 = y1 + t21 * y21);\n }\n },\n arc: function(x, y, r, a0, a1, ccw) {\n x = +x, y = +y, r = +r;\n var dx = r * Math.cos(a0),\n dy = r * Math.sin(a0),\n x0 = x + dx,\n y0 = y + dy,\n cw = 1 ^ ccw,\n da = ccw ? a0 - a1 : a1 - a0;\n\n // Is the radius negative? Error.\n if (r < 0) throw new Error(\"negative radius: \" + r);\n\n // Is this path empty? Move to (x0,y0).\n if (this._x1 === null) {\n this._ += \"M\" + x0 + \",\" + y0;\n }\n\n // Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0).\n else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) {\n this._ += \"L\" + x0 + \",\" + y0;\n }\n\n // Is this arc empty? We’re done.\n if (!r) return;\n\n // Does the angle go the wrong way? Flip the direction.\n if (da < 0) da = da % tau + tau;\n\n // Is this a complete circle? Draw two arcs to complete the circle.\n if (da > tauEpsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (x - dx) + \",\" + (y - dy) + \"A\" + r + \",\" + r + \",0,1,\" + cw + \",\" + (this._x1 = x0) + \",\" + (this._y1 = y0);\n }\n\n // Is this arc non-empty? Draw an arc!\n else if (da > epsilon) {\n this._ += \"A\" + r + \",\" + r + \",0,\" + (+(da >= pi)) + \",\" + cw + \",\" + (this._x1 = x + r * Math.cos(a1)) + \",\" + (this._y1 = y + r * Math.sin(a1));\n }\n },\n rect: function(x, y, w, h) {\n this._ += \"M\" + (this._x0 = this._x1 = +x) + \",\" + (this._y0 = this._y1 = +y) + \"h\" + (+w) + \"v\" + (+h) + \"h\" + (-w) + \"Z\";\n },\n toString: function() {\n return this._;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (path);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/path.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/arc.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/arc.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/math.js\");\n\n\n\n\nfunction arcInnerRadius(d) {\n return d.innerRadius;\n}\n\nfunction arcOuterRadius(d) {\n return d.outerRadius;\n}\n\nfunction arcStartAngle(d) {\n return d.startAngle;\n}\n\nfunction arcEndAngle(d) {\n return d.endAngle;\n}\n\nfunction arcPadAngle(d) {\n return d && d.padAngle; // Note: optional!\n}\n\nfunction intersect(x0, y0, x1, y1, x2, y2, x3, y3) {\n var x10 = x1 - x0, y10 = y1 - y0,\n x32 = x3 - x2, y32 = y3 - y2,\n t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10);\n return [x0 + t * x10, y0 + t * y10];\n}\n\n// Compute perpendicular offset line of length rc.\n// http://mathworld.wolfram.com/Circle-LineIntersection.html\nfunction cornerTangents(x0, y0, x1, y1, r1, rc, cw) {\n var x01 = x0 - x1,\n y01 = y0 - y1,\n lo = (cw ? rc : -rc) / Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(x01 * x01 + y01 * y01),\n ox = lo * y01,\n oy = -lo * x01,\n x11 = x0 + ox,\n y11 = y0 + oy,\n x10 = x1 + ox,\n y10 = y1 + oy,\n x00 = (x11 + x10) / 2,\n y00 = (y11 + y10) / 2,\n dx = x10 - x11,\n dy = y10 - y11,\n d2 = dx * dx + dy * dy,\n r = r1 - rc,\n D = x11 * y10 - x10 * y11,\n d = (dy < 0 ? -1 : 1) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"max\"])(0, r * r * d2 - D * D)),\n cx0 = (D * dy - dx * d) / d2,\n cy0 = (-D * dx - dy * d) / d2,\n cx1 = (D * dy + dx * d) / d2,\n cy1 = (-D * dx + dy * d) / d2,\n dx0 = cx0 - x00,\n dy0 = cy0 - y00,\n dx1 = cx1 - x00,\n dy1 = cy1 - y00;\n\n // Pick the closer of the two intersection points.\n // TODO Is there a faster way to determine which intersection to use?\n if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1;\n\n return {\n cx: cx0,\n cy: cy0,\n x01: -ox,\n y01: -oy,\n x11: cx0 * (r1 / r - 1),\n y11: cy0 * (r1 / r - 1)\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var innerRadius = arcInnerRadius,\n outerRadius = arcOuterRadius,\n cornerRadius = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(0),\n padRadius = null,\n startAngle = arcStartAngle,\n endAngle = arcEndAngle,\n padAngle = arcPadAngle,\n context = null;\n\n function arc() {\n var buffer,\n r,\n r0 = +innerRadius.apply(this, arguments),\n r1 = +outerRadius.apply(this, arguments),\n a0 = startAngle.apply(this, arguments) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n a1 = endAngle.apply(this, arguments) - _math__WEBPACK_IMPORTED_MODULE_2__[\"halfPi\"],\n da = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(a1 - a0),\n cw = a1 > a0;\n\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n\n // Ensure that the outer radius is always larger than the inner radius.\n if (r1 < r0) r = r1, r1 = r0, r0 = r;\n\n // Is it a point?\n if (!(r1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.moveTo(0, 0);\n\n // Or is it a circle or annulus?\n else if (da > _math__WEBPACK_IMPORTED_MODULE_2__[\"tau\"] - _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n context.moveTo(r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a0), r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a0));\n context.arc(0, 0, r1, a0, a1, !cw);\n if (r0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n context.moveTo(r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a1), r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a1));\n context.arc(0, 0, r0, a1, a0, cw);\n }\n }\n\n // Or is it a circular or annular sector?\n else {\n var a01 = a0,\n a11 = a1,\n a00 = a0,\n a10 = a1,\n da0 = da,\n da1 = da,\n ap = padAngle.apply(this, arguments) / 2,\n rp = (ap > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) && (padRadius ? +padRadius.apply(this, arguments) : Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(r0 * r0 + r1 * r1)),\n rc = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"abs\"])(r1 - r0) / 2, +cornerRadius.apply(this, arguments)),\n rc0 = rc,\n rc1 = rc,\n t0,\n t1;\n\n // Apply padding? Note that since r1 ≥ r0, da1 ≥ da0.\n if (rp > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n var p0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(rp / r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ap)),\n p1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"asin\"])(rp / r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(ap));\n if ((da0 -= p0 * 2) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0;\n else da0 = 0, a00 = a10 = (a0 + a1) / 2;\n if ((da1 -= p1 * 2) > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1;\n else da1 = 0, a01 = a11 = (a0 + a1) / 2;\n }\n\n var x01 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a01),\n y01 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a01),\n x10 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a10),\n y10 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a10);\n\n // Apply rounded corners?\n if (rc > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n var x11 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a11),\n y11 = r1 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a11),\n x00 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a00),\n y00 = r0 * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a00);\n\n // Restrict the corner radius according to the sector angle.\n if (da < _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"]) {\n var oc = da0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"] ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10],\n ax = x01 - oc[0],\n ay = y01 - oc[1],\n bx = x11 - oc[0],\n by = y11 - oc[1],\n kc = 1 / Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"acos\"])((ax * bx + ay * by) / (Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(ax * ax + ay * ay) * Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(bx * bx + by * by))) / 2),\n lc = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sqrt\"])(oc[0] * oc[0] + oc[1] * oc[1]);\n rc0 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(rc, (r0 - lc) / (kc - 1));\n rc1 = Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"min\"])(rc, (r1 - lc) / (kc + 1));\n }\n }\n\n // Is the sector collapsed to a line?\n if (!(da1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.moveTo(x01, y01);\n\n // Does the sector’s outer ring have rounded corners?\n else if (rc1 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw);\n t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw);\n\n context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.cy + t0.y11, t0.cx + t0.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.cy + t1.y11, t1.cx + t1.x11), !cw);\n context.arc(t1.cx, t1.cy, rc1, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y11, t1.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the outer ring just a circular arc?\n else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw);\n\n // Is there no inner ring, and it’s a circular sector?\n // Or perhaps it’s an annular sector collapsed due to padding?\n if (!(r0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) || !(da0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"])) context.lineTo(x10, y10);\n\n // Does the sector’s inner ring (or point) have rounded corners?\n else if (rc0 > _math__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) {\n t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw);\n t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw);\n\n context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01);\n\n // Have the corners merged?\n if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n\n // Otherwise, draw the two corners and the ring.\n else {\n context.arc(t0.cx, t0.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y01, t0.x01), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.y11, t0.x11), !cw);\n context.arc(0, 0, r0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t0.cy + t0.y11, t0.cx + t0.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.cy + t1.y11, t1.cx + t1.x11), cw);\n context.arc(t1.cx, t1.cy, rc0, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y11, t1.x11), Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"atan2\"])(t1.y01, t1.x01), !cw);\n }\n }\n\n // Or is the inner ring just a circular arc?\n else context.arc(0, 0, r0, a10, a00, cw);\n }\n\n context.closePath();\n\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n arc.centroid = function() {\n var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2,\n a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - _math__WEBPACK_IMPORTED_MODULE_2__[\"pi\"] / 2;\n return [Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"cos\"])(a) * r, Object(_math__WEBPACK_IMPORTED_MODULE_2__[\"sin\"])(a) * r];\n };\n\n arc.innerRadius = function(_) {\n return arguments.length ? (innerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : innerRadius;\n };\n\n arc.outerRadius = function(_) {\n return arguments.length ? (outerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : outerRadius;\n };\n\n arc.cornerRadius = function(_) {\n return arguments.length ? (cornerRadius = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : cornerRadius;\n };\n\n arc.padRadius = function(_) {\n return arguments.length ? (padRadius = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : padRadius;\n };\n\n arc.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : startAngle;\n };\n\n arc.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : endAngle;\n };\n\n arc.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), arc) : padAngle;\n };\n\n arc.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), arc) : context;\n };\n\n return arc;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/arc.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/area.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/area.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _curve_linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./curve/linear */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./line */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/line.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/point.js\");\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x0 = _point__WEBPACK_IMPORTED_MODULE_4__[\"x\"],\n x1 = null,\n y0 = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(0),\n y1 = _point__WEBPACK_IMPORTED_MODULE_4__[\"y\"],\n defined = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(true),\n context = null,\n curve = _curve_linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n output = null;\n\n function area(data) {\n var i,\n j,\n k,\n n = data.length,\n d,\n defined0 = false,\n buffer,\n x0z = new Array(n),\n y0z = new Array(n);\n\n if (context == null) output = curve(buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])());\n\n for (i = 0; i <= n; ++i) {\n if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n if (defined0 = !defined0) {\n j = i;\n output.areaStart();\n output.lineStart();\n } else {\n output.lineEnd();\n output.lineStart();\n for (k = i - 1; k >= j; --k) {\n output.point(x0z[k], y0z[k]);\n }\n output.lineEnd();\n output.areaEnd();\n }\n }\n if (defined0) {\n x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data);\n output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]);\n }\n }\n\n if (buffer) return output = null, buffer + \"\" || null;\n }\n\n function arealine() {\n return Object(_line__WEBPACK_IMPORTED_MODULE_3__[\"default\"])().defined(defined).curve(curve).context(context);\n }\n\n area.x = function(_) {\n return arguments.length ? (x0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), x1 = null, area) : x0;\n };\n\n area.x0 = function(_) {\n return arguments.length ? (x0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : x0;\n };\n\n area.x1 = function(_) {\n return arguments.length ? (x1 = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : x1;\n };\n\n area.y = function(_) {\n return arguments.length ? (y0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), y1 = null, area) : y0;\n };\n\n area.y0 = function(_) {\n return arguments.length ? (y0 = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : y0;\n };\n\n area.y1 = function(_) {\n return arguments.length ? (y1 = _ == null ? null : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), area) : y1;\n };\n\n area.lineX0 =\n area.lineY0 = function() {\n return arealine().x(x0).y(y0);\n };\n\n area.lineY1 = function() {\n return arealine().x(x0).y(y1);\n };\n\n area.lineX1 = function() {\n return arealine().x(x1).y(y0);\n };\n\n area.defined = function(_) {\n return arguments.length ? (defined = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(!!_), area) : defined;\n };\n\n area.curve = function(_) {\n return arguments.length ? (curve = _, context != null && (output = curve(context)), area) : curve;\n };\n\n area.context = function(_) {\n return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context;\n };\n\n return area;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/area.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/areaRadial.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/areaRadial.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _curve_radial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./curve/radial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/radial.js\");\n/* harmony import */ var _area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./area */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/area.js\");\n/* harmony import */ var _lineRadial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./lineRadial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/lineRadial.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var a = Object(_area__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().curve(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"curveRadialLinear\"]),\n c = a.curve,\n x0 = a.lineX0,\n x1 = a.lineX1,\n y0 = a.lineY0,\n y1 = a.lineY1;\n\n a.angle = a.x, delete a.x;\n a.startAngle = a.x0, delete a.x0;\n a.endAngle = a.x1, delete a.x1;\n a.radius = a.y, delete a.y;\n a.innerRadius = a.y0, delete a.y0;\n a.outerRadius = a.y1, delete a.y1;\n a.lineStartAngle = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(x0()); }, delete a.lineX0;\n a.lineEndAngle = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(x1()); }, delete a.lineX1;\n a.lineInnerRadius = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(y0()); }, delete a.lineY0;\n a.lineOuterRadius = function() { return Object(_lineRadial__WEBPACK_IMPORTED_MODULE_2__[\"lineRadial\"])(y1()); }, delete a.lineY1;\n\n a.curve = function(_) {\n return arguments.length ? c(Object(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_)) : c()._curve;\n };\n\n return a;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/areaRadial.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/array.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/array.js ***! + \******************************************************************/ +/*! exports provided: slice */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"slice\", function() { return slice; });\nvar slice = Array.prototype.slice;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function constant() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js ***! + \************************************************************************/ +/*! exports provided: point, Basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Basis\", function() { return Basis; });\nfunction point(that, x, y) {\n that._context.bezierCurveTo(\n (2 * that._x0 + that._x1) / 3,\n (2 * that._y0 + that._y1) / 3,\n (that._x0 + 2 * that._x1) / 3,\n (that._y0 + 2 * that._y1) / 3,\n (that._x0 + 4 * that._x1 + x) / 6,\n (that._y0 + 4 * that._y1 + y) / 6\n );\n}\n\nfunction Basis(context) {\n this._context = context;\n}\n\nBasis.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 3: point(this, this._x1, this._y1); // proceed\n case 2: this._context.lineTo(this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Basis(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisClosed.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisClosed.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js\");\n\n\n\nfunction BasisClosed(context) {\n this._context = context;\n}\n\nBasisClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x2, this._y2);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3);\n this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x2, this._y2);\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x2 = x, this._y2 = y; break;\n case 1: this._point = 2; this._x3 = x, this._y3 = y; break;\n case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break;\n default: Object(_basis__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new BasisClosed(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisOpen.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisOpen.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js\");\n\n\nfunction BasisOpen(context) {\n this._context = context;\n}\n\nBasisOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break;\n case 3: this._point = 4; // proceed\n default: Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new BasisOpen(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basisOpen.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/bundle.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/bundle.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/basis.js\");\n\n\nfunction Bundle(context, beta) {\n this._basis = new _basis__WEBPACK_IMPORTED_MODULE_0__[\"Basis\"](context);\n this._beta = beta;\n}\n\nBundle.prototype = {\n lineStart: function() {\n this._x = [];\n this._y = [];\n this._basis.lineStart();\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n j = x.length - 1;\n\n if (j > 0) {\n var x0 = x[0],\n y0 = y[0],\n dx = x[j] - x0,\n dy = y[j] - y0,\n i = -1,\n t;\n\n while (++i <= j) {\n t = i / j;\n this._basis.point(\n this._beta * x[i] + (1 - this._beta) * (x0 + t * dx),\n this._beta * y[i] + (1 - this._beta) * (y0 + t * dy)\n );\n }\n }\n\n this._x = this._y = null;\n this._basis.lineEnd();\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(beta) {\n\n function bundle(context) {\n return beta === 1 ? new _basis__WEBPACK_IMPORTED_MODULE_0__[\"Basis\"](context) : new Bundle(context, beta);\n }\n\n bundle.beta = function(beta) {\n return custom(+beta);\n };\n\n return bundle;\n})(0.85));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/bundle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js ***! + \***************************************************************************/ +/*! exports provided: point, Cardinal, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cardinal\", function() { return Cardinal; });\nfunction point(that, x, y) {\n that._context.bezierCurveTo(\n that._x1 + that._k * (that._x2 - that._x0),\n that._y1 + that._k * (that._y2 - that._y0),\n that._x2 + that._k * (that._x1 - x),\n that._y2 + that._k * (that._y1 - y),\n that._x2,\n that._y2\n );\n}\n\nfunction Cardinal(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinal.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: point(this, this._x1, this._y1); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; this._x1 = x, this._y1 = y; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new Cardinal(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalClosed.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalClosed.js ***! + \*********************************************************************************/ +/*! exports provided: CardinalClosed, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CardinalClosed\", function() { return CardinalClosed; });\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js\");\n\n\n\nfunction CardinalClosed(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: Object(_cardinal__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new CardinalClosed(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalOpen.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalOpen.js ***! + \*******************************************************************************/ +/*! exports provided: CardinalOpen, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CardinalOpen\", function() { return CardinalOpen; });\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js\");\n\n\nfunction CardinalOpen(context, tension) {\n this._context = context;\n this._k = (1 - tension) / 6;\n}\n\nCardinalOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: Object(_cardinal__WEBPACK_IMPORTED_MODULE_0__[\"point\"])(this, x, y); break;\n }\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(tension) {\n\n function cardinal(context) {\n return new CardinalOpen(context, tension);\n }\n\n cardinal.tension = function(tension) {\n return custom(+tension);\n };\n\n return cardinal;\n})(0));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalOpen.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js ***! + \*****************************************************************************/ +/*! exports provided: point, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"point\", function() { return point; });\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/math.js\");\n/* harmony import */ var _cardinal__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./cardinal */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinal.js\");\n\n\n\nfunction point(that, x, y) {\n var x1 = that._x1,\n y1 = that._y1,\n x2 = that._x2,\n y2 = that._y2;\n\n if (that._l01_a > _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) {\n var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a,\n n = 3 * that._l01_a * (that._l01_a + that._l12_a);\n x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n;\n y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n;\n }\n\n if (that._l23_a > _math__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]) {\n var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a,\n m = 3 * that._l23_a * (that._l23_a + that._l12_a);\n x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m;\n y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m;\n }\n\n that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2);\n}\n\nfunction CatmullRom(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRom.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x2, this._y2); break;\n case 3: this.point(this._x2, this._y2); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; // proceed\n default: point(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRom(context, alpha) : new _cardinal__WEBPACK_IMPORTED_MODULE_1__[\"Cardinal\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomClosed.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomClosed.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cardinalClosed__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinalClosed */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalClosed.js\");\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js\");\n/* harmony import */ var _catmullRom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./catmullRom */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js\");\n\n\n\n\nfunction CatmullRomClosed(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n lineStart: function() {\n this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 =\n this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 1: {\n this._context.moveTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 2: {\n this._context.lineTo(this._x3, this._y3);\n this._context.closePath();\n break;\n }\n case 3: {\n this.point(this._x3, this._y3);\n this.point(this._x4, this._y4);\n this.point(this._x5, this._y5);\n break;\n }\n }\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; this._x3 = x, this._y3 = y; break;\n case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break;\n case 2: this._point = 3; this._x5 = x, this._y5 = y; break;\n default: Object(_catmullRom__WEBPACK_IMPORTED_MODULE_2__[\"point\"])(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomClosed(context, alpha) : new _cardinalClosed__WEBPACK_IMPORTED_MODULE_0__[\"CardinalClosed\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomOpen.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomOpen.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _cardinalOpen__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./cardinalOpen */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/cardinalOpen.js\");\n/* harmony import */ var _catmullRom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./catmullRom */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRom.js\");\n\n\n\nfunction CatmullRomOpen(context, alpha) {\n this._context = context;\n this._alpha = alpha;\n}\n\nCatmullRomOpen.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 = this._x2 =\n this._y0 = this._y1 = this._y2 = NaN;\n this._l01_a = this._l12_a = this._l23_a =\n this._l01_2a = this._l12_2a = this._l23_2a =\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n\n if (this._point) {\n var x23 = this._x2 - x,\n y23 = this._y2 - y;\n this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha));\n }\n\n switch (this._point) {\n case 0: this._point = 1; break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break;\n case 3: this._point = 4; // proceed\n default: Object(_catmullRom__WEBPACK_IMPORTED_MODULE_1__[\"point\"])(this, x, y); break;\n }\n\n this._l01_a = this._l12_a, this._l12_a = this._l23_a;\n this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a;\n this._x0 = this._x1, this._x1 = this._x2, this._x2 = x;\n this._y0 = this._y1, this._y1 = this._y2, this._y2 = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function custom(alpha) {\n\n function catmullRom(context) {\n return alpha ? new CatmullRomOpen(context, alpha) : new _cardinalOpen__WEBPACK_IMPORTED_MODULE_0__[\"CardinalOpen\"](context, 0);\n }\n\n catmullRom.alpha = function(alpha) {\n return custom(+alpha);\n };\n\n return catmullRom;\n})(0.5));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/catmullRomOpen.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction Linear(context) {\n this._context = context;\n}\n\nLinear.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: this._context.lineTo(x, y); break;\n }\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Linear(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linearClosed.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linearClosed.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _noop__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../noop */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js\");\n\n\nfunction LinearClosed(context) {\n this._context = context;\n}\n\nLinearClosed.prototype = {\n areaStart: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n areaEnd: _noop__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n lineStart: function() {\n this._point = 0;\n },\n lineEnd: function() {\n if (this._point) this._context.closePath();\n },\n point: function(x, y) {\n x = +x, y = +y;\n if (this._point) this._context.lineTo(x, y);\n else this._point = 1, this._context.moveTo(x, y);\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new LinearClosed(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linearClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/monotone.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/monotone.js ***! + \***************************************************************************/ +/*! exports provided: monotoneX, monotoneY */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monotoneX\", function() { return monotoneX; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monotoneY\", function() { return monotoneY; });\nfunction sign(x) {\n return x < 0 ? -1 : 1;\n}\n\n// Calculate the slopes of the tangents (Hermite-type interpolation) based on\n// the following paper: Steffen, M. 1990. A Simple Method for Monotonic\n// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO.\n// NOV(II), P. 443, 1990.\nfunction slope3(that, x2, y2) {\n var h0 = that._x1 - that._x0,\n h1 = x2 - that._x1,\n s0 = (that._y1 - that._y0) / (h0 || h1 < 0 && -0),\n s1 = (y2 - that._y1) / (h1 || h0 < 0 && -0),\n p = (s0 * h1 + s1 * h0) / (h0 + h1);\n return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0;\n}\n\n// Calculate a one-sided slope.\nfunction slope2(that, t) {\n var h = that._x1 - that._x0;\n return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t;\n}\n\n// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations\n// \"you can express cubic Hermite interpolation in terms of cubic Bézier curves\n// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1\".\nfunction point(that, t0, t1) {\n var x0 = that._x0,\n y0 = that._y0,\n x1 = that._x1,\n y1 = that._y1,\n dx = (x1 - x0) / 3;\n that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1);\n}\n\nfunction MonotoneX(context) {\n this._context = context;\n}\n\nMonotoneX.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x0 = this._x1 =\n this._y0 = this._y1 =\n this._t0 = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n switch (this._point) {\n case 2: this._context.lineTo(this._x1, this._y1); break;\n case 3: point(this, this._t0, slope2(this, this._t0)); break;\n }\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n this._line = 1 - this._line;\n },\n point: function(x, y) {\n var t1 = NaN;\n\n x = +x, y = +y;\n if (x === this._x1 && y === this._y1) return; // Ignore coincident points.\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; break;\n case 2: this._point = 3; point(this, slope2(this, t1 = slope3(this, x, y)), t1); break;\n default: point(this, this._t0, t1 = slope3(this, x, y)); break;\n }\n\n this._x0 = this._x1, this._x1 = x;\n this._y0 = this._y1, this._y1 = y;\n this._t0 = t1;\n }\n}\n\nfunction MonotoneY(context) {\n this._context = new ReflectContext(context);\n}\n\n(MonotoneY.prototype = Object.create(MonotoneX.prototype)).point = function(x, y) {\n MonotoneX.prototype.point.call(this, y, x);\n};\n\nfunction ReflectContext(context) {\n this._context = context;\n}\n\nReflectContext.prototype = {\n moveTo: function(x, y) { this._context.moveTo(y, x); },\n closePath: function() { this._context.closePath(); },\n lineTo: function(x, y) { this._context.lineTo(y, x); },\n bezierCurveTo: function(x1, y1, x2, y2, x, y) { this._context.bezierCurveTo(y1, x1, y2, x2, y, x); }\n};\n\nfunction monotoneX(context) {\n return new MonotoneX(context);\n}\n\nfunction monotoneY(context) {\n return new MonotoneY(context);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/monotone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/natural.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/natural.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction Natural(context) {\n this._context = context;\n}\n\nNatural.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = [];\n this._y = [];\n },\n lineEnd: function() {\n var x = this._x,\n y = this._y,\n n = x.length;\n\n if (n) {\n this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]);\n if (n === 2) {\n this._context.lineTo(x[1], y[1]);\n } else {\n var px = controlPoints(x),\n py = controlPoints(y);\n for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) {\n this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]);\n }\n }\n }\n\n if (this._line || (this._line !== 0 && n === 1)) this._context.closePath();\n this._line = 1 - this._line;\n this._x = this._y = null;\n },\n point: function(x, y) {\n this._x.push(+x);\n this._y.push(+y);\n }\n};\n\n// See https://www.particleincell.com/2012/bezier-splines/ for derivation.\nfunction controlPoints(x) {\n var i,\n n = x.length - 1,\n m,\n a = new Array(n),\n b = new Array(n),\n r = new Array(n);\n a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1];\n for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1];\n a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n];\n for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1];\n a[n - 1] = r[n - 1] / b[n - 1];\n for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i];\n b[n - 1] = (x[n] + a[n - 1]) / 2;\n for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1];\n return [a, b];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Natural(context);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/natural.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/radial.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/radial.js ***! + \*************************************************************************/ +/*! exports provided: curveRadialLinear, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"curveRadialLinear\", function() { return curveRadialLinear; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return curveRadial; });\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js\");\n\n\nvar curveRadialLinear = curveRadial(_linear__WEBPACK_IMPORTED_MODULE_0__[\"default\"]);\n\nfunction Radial(curve) {\n this._curve = curve;\n}\n\nRadial.prototype = {\n areaStart: function() {\n this._curve.areaStart();\n },\n areaEnd: function() {\n this._curve.areaEnd();\n },\n lineStart: function() {\n this._curve.lineStart();\n },\n lineEnd: function() {\n this._curve.lineEnd();\n },\n point: function(a, r) {\n this._curve.point(r * Math.sin(a), r * -Math.cos(a));\n }\n};\n\nfunction curveRadial(curve) {\n\n function radial(context) {\n return new Radial(curve(context));\n }\n\n radial._curve = curve;\n\n return radial;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/radial.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/curve/step.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/curve/step.js ***! + \***********************************************************************/ +/*! exports provided: default, stepBefore, stepAfter */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stepBefore\", function() { return stepBefore; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"stepAfter\", function() { return stepAfter; });\nfunction Step(context, t) {\n this._context = context;\n this._t = t;\n}\n\nStep.prototype = {\n areaStart: function() {\n this._line = 0;\n },\n areaEnd: function() {\n this._line = NaN;\n },\n lineStart: function() {\n this._x = this._y = NaN;\n this._point = 0;\n },\n lineEnd: function() {\n if (0 < this._t && this._t < 1 && this._point === 2) this._context.lineTo(this._x, this._y);\n if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath();\n if (this._line >= 0) this._t = 1 - this._t, this._line = 1 - this._line;\n },\n point: function(x, y) {\n x = +x, y = +y;\n switch (this._point) {\n case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break;\n case 1: this._point = 2; // proceed\n default: {\n if (this._t <= 0) {\n this._context.lineTo(this._x, y);\n this._context.lineTo(x, y);\n } else {\n var x1 = this._x * (1 - this._t) + x * this._t;\n this._context.lineTo(x1, this._y);\n this._context.lineTo(x1, y);\n }\n break;\n }\n }\n this._x = x, this._y = y;\n }\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(context) {\n return new Step(context, 0.5);\n});\n\nfunction stepBefore(context) {\n return new Step(context, 0);\n}\n\nfunction stepAfter(context) {\n return new Step(context, 1);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/curve/step.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/descending.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/descending.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/identity.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/identity.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(d) {\n return d;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/identity.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/line.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/line.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _curve_linear__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./curve/linear */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/linear.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/point.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x = _point__WEBPACK_IMPORTED_MODULE_3__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_3__[\"y\"],\n defined = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(true),\n context = null,\n curve = _curve_linear__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n output = null;\n\n function line(data) {\n var i,\n n = data.length,\n d,\n defined0 = false,\n buffer;\n\n if (context == null) output = curve(buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])());\n\n for (i = 0; i <= n; ++i) {\n if (!(i < n && defined(d = data[i], i, data)) === defined0) {\n if (defined0 = !defined0) output.lineStart();\n else output.lineEnd();\n }\n if (defined0) output.point(+x(d, i, data), +y(d, i, data));\n }\n\n if (buffer) return output = null, buffer + \"\" || null;\n }\n\n line.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), line) : x;\n };\n\n line.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), line) : y;\n };\n\n line.defined = function(_) {\n return arguments.length ? (defined = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(!!_), line) : defined;\n };\n\n line.curve = function(_) {\n return arguments.length ? (curve = _, context != null && (output = curve(context)), line) : curve;\n };\n\n line.context = function(_) {\n return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context;\n };\n\n return line;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/line.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/lineRadial.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/lineRadial.js ***! + \***********************************************************************/ +/*! exports provided: lineRadial, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return lineRadial; });\n/* harmony import */ var _curve_radial__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./curve/radial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/curve/radial.js\");\n/* harmony import */ var _line__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./line */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/line.js\");\n\n\n\nfunction lineRadial(l) {\n var c = l.curve;\n\n l.angle = l.x, delete l.x;\n l.radius = l.y, delete l.y;\n\n l.curve = function(_) {\n return arguments.length ? c(Object(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_)) : c()._curve;\n };\n\n return l;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return lineRadial(Object(_line__WEBPACK_IMPORTED_MODULE_1__[\"default\"])().curve(_curve_radial__WEBPACK_IMPORTED_MODULE_0__[\"curveRadialLinear\"]));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/lineRadial.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/link/index.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/link/index.js ***! + \***********************************************************************/ +/*! exports provided: linkHorizontal, linkVertical, linkRadial */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return linkHorizontal; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return linkVertical; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return linkRadial; });\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../array */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../point */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/point.js\");\n/* harmony import */ var _pointRadial__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../pointRadial */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/pointRadial.js\");\n\n\n\n\n\n\nfunction linkSource(d) {\n return d.source;\n}\n\nfunction linkTarget(d) {\n return d.target;\n}\n\nfunction link(curve) {\n var source = linkSource,\n target = linkTarget,\n x = _point__WEBPACK_IMPORTED_MODULE_3__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_3__[\"y\"],\n context = null;\n\n function link() {\n var buffer, argv = _array__WEBPACK_IMPORTED_MODULE_1__[\"slice\"].call(arguments), s = source.apply(this, argv), t = target.apply(this, argv);\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n curve(context, +x.apply(this, (argv[0] = s, argv)), +y.apply(this, argv), +x.apply(this, (argv[0] = t, argv)), +y.apply(this, argv));\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n link.source = function(_) {\n return arguments.length ? (source = _, link) : source;\n };\n\n link.target = function(_) {\n return arguments.length ? (target = _, link) : target;\n };\n\n link.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+_), link) : x;\n };\n\n link.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(+_), link) : y;\n };\n\n link.context = function(_) {\n return arguments.length ? ((context = _ == null ? null : _), link) : context;\n };\n\n return link;\n}\n\nfunction curveHorizontal(context, x0, y0, x1, y1) {\n context.moveTo(x0, y0);\n context.bezierCurveTo(x0 = (x0 + x1) / 2, y0, x0, y1, x1, y1);\n}\n\nfunction curveVertical(context, x0, y0, x1, y1) {\n context.moveTo(x0, y0);\n context.bezierCurveTo(x0, y0 = (y0 + y1) / 2, x1, y0, x1, y1);\n}\n\nfunction curveRadial(context, x0, y0, x1, y1) {\n var p0 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x0, y0),\n p1 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x0, y0 = (y0 + y1) / 2),\n p2 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x1, y0),\n p3 = Object(_pointRadial__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(x1, y1);\n context.moveTo(p0[0], p0[1]);\n context.bezierCurveTo(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]);\n}\n\nfunction linkHorizontal() {\n return link(curveHorizontal);\n}\n\nfunction linkVertical() {\n return link(curveVertical);\n}\n\nfunction linkRadial() {\n var l = link(curveRadial);\n l.angle = l.x, delete l.x;\n l.radius = l.y, delete l.y;\n return l;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/link/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/math.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/math.js ***! + \*****************************************************************/ +/*! exports provided: abs, atan2, cos, max, min, sin, sqrt, epsilon, pi, halfPi, tau, acos, asin */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"abs\", function() { return abs; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"atan2\", function() { return atan2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cos\", function() { return cos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return max; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return min; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sin\", function() { return sin; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sqrt\", function() { return sqrt; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"pi\", function() { return pi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"halfPi\", function() { return halfPi; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tau\", function() { return tau; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"acos\", function() { return acos; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"asin\", function() { return asin; });\nvar abs = Math.abs;\nvar atan2 = Math.atan2;\nvar cos = Math.cos;\nvar max = Math.max;\nvar min = Math.min;\nvar sin = Math.sin;\nvar sqrt = Math.sqrt;\n\nvar epsilon = 1e-12;\nvar pi = Math.PI;\nvar halfPi = pi / 2;\nvar tau = 2 * pi;\n\nfunction acos(x) {\n return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);\n}\n\nfunction asin(x) {\n return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/noop.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/offset/diverging.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/offset/diverging.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 1)) return;\n for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {\n for (yp = yn = 0, i = 0; i < n; ++i) {\n if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {\n d[0] = yp, d[1] = yp += dy;\n } else if (dy < 0) {\n d[1] = yn, d[0] = yn += dy;\n } else {\n d[0] = yp;\n }\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/offset/diverging.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/offset/expand.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/offset/expand.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0)) return;\n for (var i, n, j = 0, m = series[0].length, y; j < m; ++j) {\n for (y = i = 0; i < n; ++i) y += series[i][j][1] || 0;\n if (y) for (i = 0; i < n; ++i) series[i][j][1] /= y;\n }\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/offset/expand.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 1)) return;\n for (var i = 1, j, s0, s1 = series[order[0]], n, m = s1.length; i < n; ++i) {\n s0 = s1, s1 = series[order[i]];\n for (j = 0; j < m; ++j) {\n s1[j][1] += s1[j][0] = isNaN(s0[j][1]) ? s0[j][0] : s0[j][1];\n }\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/offset/silhouette.js": +/*!******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/offset/silhouette.js ***! + \******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0)) return;\n for (var j = 0, s0 = series[order[0]], n, m = s0.length; j < m; ++j) {\n for (var i = 0, y = 0; i < n; ++i) y += series[i][j][1] || 0;\n s0[j][1] += s0[j][0] = -y / 2;\n }\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/offset/silhouette.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/offset/wiggle.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/offset/wiggle.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series, order) {\n if (!((n = series.length) > 0) || !((m = (s0 = series[order[0]]).length) > 0)) return;\n for (var y = 0, j = 1, s0, m, n; j < m; ++j) {\n for (var i = 0, s1 = 0, s2 = 0; i < n; ++i) {\n var si = series[order[i]],\n sij0 = si[j][1] || 0,\n sij1 = si[j - 1][1] || 0,\n s3 = (sij0 - sij1) / 2;\n for (var k = 0; k < i; ++k) {\n var sk = series[order[k]],\n skj0 = sk[j][1] || 0,\n skj1 = sk[j - 1][1] || 0;\n s3 += skj0 - skj1;\n }\n s1 += sij0, s2 += s3 * sij0;\n }\n s0[j - 1][1] += s0[j - 1][0] = y;\n if (s1) y -= s2 / s1;\n }\n s0[j - 1][1] += s0[j - 1][0] = y;\n Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series, order);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/offset/wiggle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js ***! + \****************************************************************************/ +/*! exports provided: default, sum */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return sum; });\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var sums = series.map(sum);\n return Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).sort(function(a, b) { return sums[a] - sums[b]; });\n});\n\nfunction sum(series) {\n var s = 0, i = -1, n = series.length, v;\n while (++i < n) if (v = +series[i][1]) s += v;\n return s;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/order/descending.js": +/*!*****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/order/descending.js ***! + \*****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n return Object(_ascending__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).reverse();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/order/descending.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/order/insideOut.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/order/insideOut.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js\");\n/* harmony import */ var _ascending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./ascending */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/ascending.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var n = series.length,\n i,\n j,\n sums = series.map(_ascending__WEBPACK_IMPORTED_MODULE_1__[\"sum\"]),\n order = Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).sort(function(a, b) { return sums[b] - sums[a]; }),\n top = 0,\n bottom = 0,\n tops = [],\n bottoms = [];\n\n for (i = 0; i < n; ++i) {\n j = order[i];\n if (top < bottom) {\n top += sums[j];\n tops.push(j);\n } else {\n bottom += sums[j];\n bottoms.push(j);\n }\n }\n\n return bottoms.reverse().concat(tops);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/order/insideOut.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n var n = series.length, o = new Array(n);\n while (--n >= 0) o[n] = n;\n return o;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/order/reverse.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/order/reverse.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _none__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(series) {\n return Object(_none__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(series).reverse();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/order/reverse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/pie.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/pie.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _descending__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./descending */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/descending.js\");\n/* harmony import */ var _identity__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./identity */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/identity.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/math.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var value = _identity__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n sortValues = _descending__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n sort = null,\n startAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0),\n endAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"]),\n padAngle = Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(0);\n\n function pie(data) {\n var i,\n n = data.length,\n j,\n k,\n sum = 0,\n index = new Array(n),\n arcs = new Array(n),\n a0 = +startAngle.apply(this, arguments),\n da = Math.min(_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"], Math.max(-_math__WEBPACK_IMPORTED_MODULE_3__[\"tau\"], endAngle.apply(this, arguments) - a0)),\n a1,\n p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)),\n pa = p * (da < 0 ? -1 : 1),\n v;\n\n for (i = 0; i < n; ++i) {\n if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) {\n sum += v;\n }\n }\n\n // Optionally sort the arcs by previously-computed values or by data.\n if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); });\n else if (sort != null) index.sort(function(i, j) { return sort(data[i], data[j]); });\n\n // Compute the arcs! They are stored in the original data's order.\n for (i = 0, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) {\n j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = {\n data: data[j],\n index: i,\n value: v,\n startAngle: a0,\n endAngle: a1,\n padAngle: p\n };\n }\n\n return arcs;\n }\n\n pie.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : value;\n };\n\n pie.sortValues = function(_) {\n return arguments.length ? (sortValues = _, sort = null, pie) : sortValues;\n };\n\n pie.sort = function(_) {\n return arguments.length ? (sort = _, sortValues = null, pie) : sort;\n };\n\n pie.startAngle = function(_) {\n return arguments.length ? (startAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : startAngle;\n };\n\n pie.endAngle = function(_) {\n return arguments.length ? (endAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : endAngle;\n };\n\n pie.padAngle = function(_) {\n return arguments.length ? (padAngle = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), pie) : padAngle;\n };\n\n return pie;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/pie.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/point.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/point.js ***! + \******************************************************************/ +/*! exports provided: x, y */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\nfunction x(p) {\n return p[0];\n}\n\nfunction y(p) {\n return p[1];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/pointRadial.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/pointRadial.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x, y) {\n return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/pointRadial.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/stack.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/stack.js ***! + \******************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/array.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n/* harmony import */ var _offset_none__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./offset/none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/offset/none.js\");\n/* harmony import */ var _order_none__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./order/none */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/order/none.js\");\n\n\n\n\n\nfunction stackValue(d, key) {\n return d[key];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var keys = Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])([]),\n order = _order_none__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n offset = _offset_none__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n value = stackValue;\n\n function stack(data) {\n var kz = keys.apply(this, arguments),\n i,\n m = data.length,\n n = kz.length,\n sz = new Array(n),\n oz;\n\n for (i = 0; i < n; ++i) {\n for (var ki = kz[i], si = sz[i] = new Array(m), j = 0, sij; j < m; ++j) {\n si[j] = sij = [0, +value(data[j], ki, j, data)];\n sij.data = data[j];\n }\n si.key = ki;\n }\n\n for (i = 0, oz = order(sz); i < n; ++i) {\n sz[oz[i]].index = i;\n }\n\n offset(sz, oz);\n return sz;\n }\n\n stack.keys = function(_) {\n return arguments.length ? (keys = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)), stack) : keys;\n };\n\n stack.value = function(_) {\n return arguments.length ? (value = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(+_), stack) : value;\n };\n\n stack.order = function(_) {\n return arguments.length ? (order = _ == null ? _order_none__WEBPACK_IMPORTED_MODULE_3__[\"default\"] : typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(_array__WEBPACK_IMPORTED_MODULE_0__[\"slice\"].call(_)), stack) : order;\n };\n\n stack.offset = function(_) {\n return arguments.length ? (offset = _ == null ? _offset_none__WEBPACK_IMPORTED_MODULE_2__[\"default\"] : _, stack) : offset;\n };\n\n return stack;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/stack.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol.js ***! + \*******************************************************************/ +/*! exports provided: symbols, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return symbols; });\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-shape/node_modules/d3-path/src/index.js\");\n/* harmony import */ var _symbol_circle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./symbol/circle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/circle.js\");\n/* harmony import */ var _symbol_cross__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./symbol/cross */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/cross.js\");\n/* harmony import */ var _symbol_diamond__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./symbol/diamond */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/diamond.js\");\n/* harmony import */ var _symbol_star__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./symbol/star */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/star.js\");\n/* harmony import */ var _symbol_square__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./symbol/square */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/square.js\");\n/* harmony import */ var _symbol_triangle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./symbol/triangle */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/triangle.js\");\n/* harmony import */ var _symbol_wye__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./symbol/wye */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/wye.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/constant.js\");\n\n\n\n\n\n\n\n\n\n\nvar symbols = [\n _symbol_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n _symbol_cross__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n _symbol_diamond__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n _symbol_square__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n _symbol_star__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n _symbol_triangle__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n _symbol_wye__WEBPACK_IMPORTED_MODULE_7__[\"default\"]\n];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var type = Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(_symbol_circle__WEBPACK_IMPORTED_MODULE_1__[\"default\"]),\n size = Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(64),\n context = null;\n\n function symbol() {\n var buffer;\n if (!context) context = buffer = Object(d3_path__WEBPACK_IMPORTED_MODULE_0__[\"path\"])();\n type.apply(this, arguments).draw(context, +size.apply(this, arguments));\n if (buffer) return context = null, buffer + \"\" || null;\n }\n\n symbol.type = function(_) {\n return arguments.length ? (type = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(_), symbol) : type;\n };\n\n symbol.size = function(_) {\n return arguments.length ? (size = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_8__[\"default\"])(+_), symbol) : size;\n };\n\n symbol.context = function(_) {\n return arguments.length ? (context = _ == null ? null : _, symbol) : context;\n };\n\n return symbol;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/circle.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/circle.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/math.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"]);\n context.moveTo(r, 0);\n context.arc(0, 0, r, 0, _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"]);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/cross.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/cross.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / 5) / 2;\n context.moveTo(-3 * r, -r);\n context.lineTo(-r, -r);\n context.lineTo(-r, -3 * r);\n context.lineTo(r, -3 * r);\n context.lineTo(r, -r);\n context.lineTo(3 * r, -r);\n context.lineTo(3 * r, r);\n context.lineTo(r, r);\n context.lineTo(r, 3 * r);\n context.lineTo(-r, 3 * r);\n context.lineTo(-r, r);\n context.lineTo(-3 * r, r);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/cross.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/diamond.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/diamond.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar tan30 = Math.sqrt(1 / 3),\n tan30_2 = tan30 * 2;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var y = Math.sqrt(size / tan30_2),\n x = y * tan30;\n context.moveTo(0, -y);\n context.lineTo(x, 0);\n context.lineTo(0, y);\n context.lineTo(-x, 0);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/diamond.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/square.js": +/*!**************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/square.js ***! + \**************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var w = Math.sqrt(size),\n x = -w / 2;\n context.rect(x, x, w, w);\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/square.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/star.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/star.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../math */ \"./node_modules/dagre-d3/node_modules/d3-shape/src/math.js\");\n\n\nvar ka = 0.89081309152928522810,\n kr = Math.sin(_math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 10) / Math.sin(7 * _math__WEBPACK_IMPORTED_MODULE_0__[\"pi\"] / 10),\n kx = Math.sin(_math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] / 10) * kr,\n ky = -Math.cos(_math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] / 10) * kr;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size * ka),\n x = kx * r,\n y = ky * r;\n context.moveTo(0, -r);\n context.lineTo(x, y);\n for (var i = 1; i < 5; ++i) {\n var a = _math__WEBPACK_IMPORTED_MODULE_0__[\"tau\"] * i / 5,\n c = Math.cos(a),\n s = Math.sin(a);\n context.lineTo(s * r, -c * r);\n context.lineTo(c * x - s * y, s * x + c * y);\n }\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/star.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/triangle.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/triangle.js ***! + \****************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar sqrt3 = Math.sqrt(3);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var y = -Math.sqrt(size / (sqrt3 * 3));\n context.moveTo(0, y * 2);\n context.lineTo(-sqrt3 * y, -y);\n context.lineTo(sqrt3 * y, -y);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/triangle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/wye.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/wye.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar c = -0.5,\n s = Math.sqrt(3) / 2,\n k = 1 / Math.sqrt(12),\n a = (k / 2 + 1) * 3;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n draw: function(context, size) {\n var r = Math.sqrt(size / a),\n x0 = r / 2,\n y0 = r * k,\n x1 = x0,\n y1 = r * k + r,\n x2 = -x1,\n y2 = y1;\n context.moveTo(x0, y0);\n context.lineTo(x1, y1);\n context.lineTo(x2, y2);\n context.lineTo(c * x0 - s * y0, s * x0 + c * y0);\n context.lineTo(c * x1 - s * y1, s * x1 + c * y1);\n context.lineTo(c * x2 - s * y2, s * x2 + c * y2);\n context.lineTo(c * x0 + s * y0, c * y0 - s * x0);\n context.lineTo(c * x1 + s * y1, c * y1 - s * x1);\n context.lineTo(c * x2 + s * y2, c * y2 - s * x2);\n context.closePath();\n }\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-shape/src/symbol/wye.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/index.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/index.js ***! + \********************************************************************/ +/*! exports provided: timeFormatDefaultLocale, timeFormat, timeParse, utcFormat, utcParse, timeFormatLocale, isoFormat, isoParse */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatDefaultLocale\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"timeParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return _src_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcParse\"]; });\n\n/* harmony import */ var _src_locale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/locale */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/locale.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatLocale\", function() { return _src_locale__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_isoFormat__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/isoFormat */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoFormat\", function() { return _src_isoFormat__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_isoParse__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/isoParse */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/isoParse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoParse\", function() { return _src_isoParse__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/day.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/day.js ***! + \*******************************************************************************************/ +/*! exports provided: default, days */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"days\", function() { return days; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar day = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setDate(date.getDate() + step);\n}, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (day);\nvar days = day.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/day.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js ***! + \************************************************************************************************/ +/*! exports provided: durationSecond, durationMinute, durationHour, durationDay, durationWeek */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationSecond\", function() { return durationSecond; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationMinute\", function() { return durationMinute; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationHour\", function() { return durationHour; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationDay\", function() { return durationDay; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationWeek\", function() { return durationWeek; });\nvar durationSecond = 1e3;\nvar durationMinute = 6e4;\nvar durationHour = 36e5;\nvar durationDay = 864e5;\nvar durationWeek = 6048e5;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/hour.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/hour.js ***! + \********************************************************************************************/ +/*! exports provided: default, hours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hours\", function() { return hours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar hour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n var offset = date.getTimezoneOffset() * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"] % _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n if (offset < 0) offset += _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n date.setTime(Math.floor((+date - offset) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"] + offset);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hour);\nvar hours = hour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/hour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _millisecond__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./millisecond */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/millisecond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return _millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony import */ var _second__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./second */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/second.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return _second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony import */ var _minute__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./minute */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/minute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return _minute__WEBPACK_IMPORTED_MODULE_3__[\"minutes\"]; });\n\n/* harmony import */ var _hour__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./hour */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/hour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return _hour__WEBPACK_IMPORTED_MODULE_4__[\"hours\"]; });\n\n/* harmony import */ var _day__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./day */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/day.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return _day__WEBPACK_IMPORTED_MODULE_5__[\"days\"]; });\n\n/* harmony import */ var _week__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./week */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/week.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"monday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"mondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"tuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"wednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"thursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"friday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"fridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return _week__WEBPACK_IMPORTED_MODULE_6__[\"saturdays\"]; });\n\n/* harmony import */ var _month__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./month */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/month.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return _month__WEBPACK_IMPORTED_MODULE_7__[\"months\"]; });\n\n/* harmony import */ var _year__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./year */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/year.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return _year__WEBPACK_IMPORTED_MODULE_8__[\"years\"]; });\n\n/* harmony import */ var _utcMinute__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./utcMinute */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMinute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return _utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"utcMinutes\"]; });\n\n/* harmony import */ var _utcHour__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./utcHour */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcHour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return _utcHour__WEBPACK_IMPORTED_MODULE_10__[\"utcHours\"]; });\n\n/* harmony import */ var _utcDay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./utcDay */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcDay.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return _utcDay__WEBPACK_IMPORTED_MODULE_11__[\"utcDays\"]; });\n\n/* harmony import */ var _utcWeek__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./utcWeek */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcWeek.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return _utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturdays\"]; });\n\n/* harmony import */ var _utcMonth__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./utcMonth */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMonth.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return _utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"utcMonths\"]; });\n\n/* harmony import */ var _utcYear__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./utcYear */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcYear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return _utcYear__WEBPACK_IMPORTED_MODULE_14__[\"utcYears\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return newInterval; });\nvar t0 = new Date,\n t1 = new Date;\n\nfunction newInterval(floori, offseti, count, field) {\n\n function interval(date) {\n return floori(date = new Date(+date)), date;\n }\n\n interval.floor = interval;\n\n interval.ceil = function(date) {\n return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n };\n\n interval.round = function(date) {\n var d0 = interval(date),\n d1 = interval.ceil(date);\n return date - d0 < d1 - date ? d0 : d1;\n };\n\n interval.offset = function(date, step) {\n return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n };\n\n interval.range = function(start, stop, step) {\n var range = [], previous;\n start = interval.ceil(start);\n step = step == null ? 1 : Math.floor(step);\n if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n while (previous < start && start < stop);\n return range;\n };\n\n interval.filter = function(test) {\n return newInterval(function(date) {\n if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n }, function(date, step) {\n if (date >= date) {\n if (step < 0) while (++step <= 0) {\n while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n } else while (--step >= 0) {\n while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n }\n }\n });\n };\n\n if (count) {\n interval.count = function(start, end) {\n t0.setTime(+start), t1.setTime(+end);\n floori(t0), floori(t1);\n return Math.floor(count(t0, t1));\n };\n\n interval.every = function(step) {\n step = Math.floor(step);\n return !isFinite(step) || !(step > 0) ? null\n : !(step > 1) ? interval\n : interval.filter(field\n ? function(d) { return field(d) % step === 0; }\n : function(d) { return interval.count(0, d) % step === 0; });\n };\n }\n\n return interval;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/millisecond.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/millisecond.js ***! + \***************************************************************************************************/ +/*! exports provided: default, milliseconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"milliseconds\", function() { return milliseconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n\n\nvar millisecond = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function() {\n // noop\n}, function(date, step) {\n date.setTime(+date + step);\n}, function(start, end) {\n return end - start;\n});\n\n// An optimized implementation for this simple case.\nmillisecond.every = function(k) {\n k = Math.floor(k);\n if (!isFinite(k) || !(k > 0)) return null;\n if (!(k > 1)) return millisecond;\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / k) * k);\n }, function(date, step) {\n date.setTime(+date + step * k);\n }, function(start, end) {\n return (end - start) / k;\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (millisecond);\nvar milliseconds = millisecond.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/millisecond.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/minute.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/minute.js ***! + \**********************************************************************************************/ +/*! exports provided: default, minutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"minutes\", function() { return minutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar minute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (minute);\nvar minutes = minute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/minute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/month.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/month.js ***! + \*********************************************************************************************/ +/*! exports provided: default, months */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"months\", function() { return months; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n\n\nvar month = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setMonth(date.getMonth() + step);\n}, function(start, end) {\n return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;\n}, function(date) {\n return date.getMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (month);\nvar months = month.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/month.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/second.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/second.js ***! + \**********************************************************************************************/ +/*! exports provided: default, seconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"seconds\", function() { return seconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar second = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"];\n}, function(date) {\n return date.getUTCSeconds();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (second);\nvar seconds = second.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/second.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcDay.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcDay.js ***! + \**********************************************************************************************/ +/*! exports provided: default, utcDays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return utcDays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcDay = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getUTCDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcDay);\nvar utcDays = utcDay.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcDay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcHour.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcHour.js ***! + \***********************************************************************************************/ +/*! exports provided: default, utcHours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return utcHours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcHour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMinutes(0, 0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getUTCHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcHour);\nvar utcHours = utcHour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcHour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMinute.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMinute.js ***! + \*************************************************************************************************/ +/*! exports provided: default, utcMinutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return utcMinutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcMinute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCSeconds(0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getUTCMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMinute);\nvar utcMinutes = utcMinute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMinute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMonth.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMonth.js ***! + \************************************************************************************************/ +/*! exports provided: default, utcMonths */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return utcMonths; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n\n\nvar utcMonth = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCMonth(date.getUTCMonth() + step);\n}, function(start, end) {\n return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n}, function(date) {\n return date.getUTCMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMonth);\nvar utcMonths = utcMonth.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcMonth.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcWeek.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcWeek.js ***! + \***********************************************************************************************/ +/*! exports provided: utcSunday, utcMonday, utcTuesday, utcWednesday, utcThursday, utcFriday, utcSaturday, utcSundays, utcMondays, utcTuesdays, utcWednesdays, utcThursdays, utcFridays, utcSaturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return utcSunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return utcMonday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return utcTuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return utcWednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return utcThursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return utcFriday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return utcSaturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return utcSundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return utcMondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return utcTuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return utcWednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return utcThursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return utcFridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return utcSaturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction utcWeekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step * 7);\n }, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar utcSunday = utcWeekday(0);\nvar utcMonday = utcWeekday(1);\nvar utcTuesday = utcWeekday(2);\nvar utcWednesday = utcWeekday(3);\nvar utcThursday = utcWeekday(4);\nvar utcFriday = utcWeekday(5);\nvar utcSaturday = utcWeekday(6);\n\nvar utcSundays = utcSunday.range;\nvar utcMondays = utcMonday.range;\nvar utcTuesdays = utcTuesday.range;\nvar utcWednesdays = utcWednesday.range;\nvar utcThursdays = utcThursday.range;\nvar utcFridays = utcFriday.range;\nvar utcSaturdays = utcSaturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcWeek.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcYear.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcYear.js ***! + \***********************************************************************************************/ +/*! exports provided: default, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return utcYears; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n\n\nvar utcYear = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step);\n}, function(start, end) {\n return end.getUTCFullYear() - start.getUTCFullYear();\n}, function(date) {\n return date.getUTCFullYear();\n});\n\n// An optimized implementation for this simple case.\nutcYear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcYear);\nvar utcYears = utcYear.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/utcYear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/week.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/week.js ***! + \********************************************************************************************/ +/*! exports provided: sunday, monday, tuesday, wednesday, thursday, friday, saturday, sundays, mondays, tuesdays, wednesdays, thursdays, fridays, saturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sunday\", function() { return sunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monday\", function() { return monday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesday\", function() { return tuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesday\", function() { return wednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursday\", function() { return thursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"friday\", function() { return friday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturday\", function() { return saturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sundays\", function() { return sundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mondays\", function() { return mondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesdays\", function() { return tuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesdays\", function() { return wednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursdays\", function() { return thursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fridays\", function() { return fridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturdays\", function() { return saturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction weekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setDate(date.getDate() + step * 7);\n }, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar sunday = weekday(0);\nvar monday = weekday(1);\nvar tuesday = weekday(2);\nvar wednesday = weekday(3);\nvar thursday = weekday(4);\nvar friday = weekday(5);\nvar saturday = weekday(6);\n\nvar sundays = sunday.range;\nvar mondays = monday.range;\nvar tuesdays = tuesday.range;\nvar wednesdays = wednesday.range;\nvar thursdays = thursday.range;\nvar fridays = friday.range;\nvar saturdays = saturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/week.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/year.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/year.js ***! + \********************************************************************************************/ +/*! exports provided: default, years */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"years\", function() { return years; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/interval.js\");\n\n\nvar year = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setFullYear(date.getFullYear() + step);\n}, function(start, end) {\n return end.getFullYear() - start.getFullYear();\n}, function(date) {\n return date.getFullYear();\n});\n\n// An optimized implementation for this simple case.\nyear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setFullYear(date.getFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (year);\nvar years = year.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/year.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js": +/*!********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js ***! + \********************************************************************************/ +/*! exports provided: timeFormat, timeParse, utcFormat, utcParse, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return timeFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return timeParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return utcFormat; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return utcParse; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return defaultLocale; });\n/* harmony import */ var _locale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./locale */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/locale.js\");\n\n\nvar locale;\nvar timeFormat;\nvar timeParse;\nvar utcFormat;\nvar utcParse;\n\ndefaultLocale({\n dateTime: \"%x, %X\",\n date: \"%-m/%-d/%Y\",\n time: \"%-I:%M:%S %p\",\n periods: [\"AM\", \"PM\"],\n days: [\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"],\n shortDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n months: [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"],\n shortMonths: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n});\n\nfunction defaultLocale(definition) {\n locale = Object(_locale__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(definition);\n timeFormat = locale.format;\n timeParse = locale.parse;\n utcFormat = locale.utcFormat;\n utcParse = locale.utcParse;\n return locale;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/src/isoFormat.js": +/*!****************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/src/isoFormat.js ***! + \****************************************************************************/ +/*! exports provided: isoSpecifier, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isoSpecifier\", function() { return isoSpecifier; });\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js\");\n\n\nvar isoSpecifier = \"%Y-%m-%dT%H:%M:%S.%LZ\";\n\nfunction formatIsoNative(date) {\n return date.toISOString();\n}\n\nvar formatIso = Date.prototype.toISOString\n ? formatIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_0__[\"utcFormat\"])(isoSpecifier);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (formatIso);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/src/isoFormat.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/src/isoParse.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/src/isoParse.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _isoFormat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./isoFormat */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/isoFormat.js\");\n/* harmony import */ var _defaultLocale__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./defaultLocale */ \"./node_modules/dagre-d3/node_modules/d3-time-format/src/defaultLocale.js\");\n\n\n\nfunction parseIsoNative(string) {\n var date = new Date(string);\n return isNaN(date) ? null : date;\n}\n\nvar parseIso = +new Date(\"2000-01-01T00:00:00.000Z\")\n ? parseIsoNative\n : Object(_defaultLocale__WEBPACK_IMPORTED_MODULE_1__[\"utcParse\"])(_isoFormat__WEBPACK_IMPORTED_MODULE_0__[\"isoSpecifier\"]);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (parseIso);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/src/isoParse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time-format/src/locale.js": +/*!*************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time-format/src/locale.js ***! + \*************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return formatLocale; });\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-time */ \"./node_modules/dagre-d3/node_modules/d3-time-format/node_modules/d3-time/src/index.js\");\n\n\nfunction localDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(-1, d.m, d.d, d.H, d.M, d.S, d.L);\n date.setFullYear(d.y);\n return date;\n }\n return new Date(d.y, d.m, d.d, d.H, d.M, d.S, d.L);\n}\n\nfunction utcDate(d) {\n if (0 <= d.y && d.y < 100) {\n var date = new Date(Date.UTC(-1, d.m, d.d, d.H, d.M, d.S, d.L));\n date.setUTCFullYear(d.y);\n return date;\n }\n return new Date(Date.UTC(d.y, d.m, d.d, d.H, d.M, d.S, d.L));\n}\n\nfunction newYear(y) {\n return {y: y, m: 0, d: 1, H: 0, M: 0, S: 0, L: 0};\n}\n\nfunction formatLocale(locale) {\n var locale_dateTime = locale.dateTime,\n locale_date = locale.date,\n locale_time = locale.time,\n locale_periods = locale.periods,\n locale_weekdays = locale.days,\n locale_shortWeekdays = locale.shortDays,\n locale_months = locale.months,\n locale_shortMonths = locale.shortMonths;\n\n var periodRe = formatRe(locale_periods),\n periodLookup = formatLookup(locale_periods),\n weekdayRe = formatRe(locale_weekdays),\n weekdayLookup = formatLookup(locale_weekdays),\n shortWeekdayRe = formatRe(locale_shortWeekdays),\n shortWeekdayLookup = formatLookup(locale_shortWeekdays),\n monthRe = formatRe(locale_months),\n monthLookup = formatLookup(locale_months),\n shortMonthRe = formatRe(locale_shortMonths),\n shortMonthLookup = formatLookup(locale_shortMonths);\n\n var formats = {\n \"a\": formatShortWeekday,\n \"A\": formatWeekday,\n \"b\": formatShortMonth,\n \"B\": formatMonth,\n \"c\": null,\n \"d\": formatDayOfMonth,\n \"e\": formatDayOfMonth,\n \"f\": formatMicroseconds,\n \"H\": formatHour24,\n \"I\": formatHour12,\n \"j\": formatDayOfYear,\n \"L\": formatMilliseconds,\n \"m\": formatMonthNumber,\n \"M\": formatMinutes,\n \"p\": formatPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatSeconds,\n \"u\": formatWeekdayNumberMonday,\n \"U\": formatWeekNumberSunday,\n \"V\": formatWeekNumberISO,\n \"w\": formatWeekdayNumberSunday,\n \"W\": formatWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatYear,\n \"Y\": formatFullYear,\n \"Z\": formatZone,\n \"%\": formatLiteralPercent\n };\n\n var utcFormats = {\n \"a\": formatUTCShortWeekday,\n \"A\": formatUTCWeekday,\n \"b\": formatUTCShortMonth,\n \"B\": formatUTCMonth,\n \"c\": null,\n \"d\": formatUTCDayOfMonth,\n \"e\": formatUTCDayOfMonth,\n \"f\": formatUTCMicroseconds,\n \"H\": formatUTCHour24,\n \"I\": formatUTCHour12,\n \"j\": formatUTCDayOfYear,\n \"L\": formatUTCMilliseconds,\n \"m\": formatUTCMonthNumber,\n \"M\": formatUTCMinutes,\n \"p\": formatUTCPeriod,\n \"Q\": formatUnixTimestamp,\n \"s\": formatUnixTimestampSeconds,\n \"S\": formatUTCSeconds,\n \"u\": formatUTCWeekdayNumberMonday,\n \"U\": formatUTCWeekNumberSunday,\n \"V\": formatUTCWeekNumberISO,\n \"w\": formatUTCWeekdayNumberSunday,\n \"W\": formatUTCWeekNumberMonday,\n \"x\": null,\n \"X\": null,\n \"y\": formatUTCYear,\n \"Y\": formatUTCFullYear,\n \"Z\": formatUTCZone,\n \"%\": formatLiteralPercent\n };\n\n var parses = {\n \"a\": parseShortWeekday,\n \"A\": parseWeekday,\n \"b\": parseShortMonth,\n \"B\": parseMonth,\n \"c\": parseLocaleDateTime,\n \"d\": parseDayOfMonth,\n \"e\": parseDayOfMonth,\n \"f\": parseMicroseconds,\n \"H\": parseHour24,\n \"I\": parseHour24,\n \"j\": parseDayOfYear,\n \"L\": parseMilliseconds,\n \"m\": parseMonthNumber,\n \"M\": parseMinutes,\n \"p\": parsePeriod,\n \"Q\": parseUnixTimestamp,\n \"s\": parseUnixTimestampSeconds,\n \"S\": parseSeconds,\n \"u\": parseWeekdayNumberMonday,\n \"U\": parseWeekNumberSunday,\n \"V\": parseWeekNumberISO,\n \"w\": parseWeekdayNumberSunday,\n \"W\": parseWeekNumberMonday,\n \"x\": parseLocaleDate,\n \"X\": parseLocaleTime,\n \"y\": parseYear,\n \"Y\": parseFullYear,\n \"Z\": parseZone,\n \"%\": parseLiteralPercent\n };\n\n // These recursive directive definitions must be deferred.\n formats.x = newFormat(locale_date, formats);\n formats.X = newFormat(locale_time, formats);\n formats.c = newFormat(locale_dateTime, formats);\n utcFormats.x = newFormat(locale_date, utcFormats);\n utcFormats.X = newFormat(locale_time, utcFormats);\n utcFormats.c = newFormat(locale_dateTime, utcFormats);\n\n function newFormat(specifier, formats) {\n return function(date) {\n var string = [],\n i = -1,\n j = 0,\n n = specifier.length,\n c,\n pad,\n format;\n\n if (!(date instanceof Date)) date = new Date(+date);\n\n while (++i < n) {\n if (specifier.charCodeAt(i) === 37) {\n string.push(specifier.slice(j, i));\n if ((pad = pads[c = specifier.charAt(++i)]) != null) c = specifier.charAt(++i);\n else pad = c === \"e\" ? \" \" : \"0\";\n if (format = formats[c]) c = format(date, pad);\n string.push(c);\n j = i + 1;\n }\n }\n\n string.push(specifier.slice(j, i));\n return string.join(\"\");\n };\n }\n\n function newParse(specifier, newDate) {\n return function(string) {\n var d = newYear(1900),\n i = parseSpecifier(d, specifier, string += \"\", 0),\n week, day;\n if (i != string.length) return null;\n\n // If a UNIX timestamp is specified, return it.\n if (\"Q\" in d) return new Date(d.Q);\n\n // The am-pm flag is 0 for AM, and 1 for PM.\n if (\"p\" in d) d.H = d.H % 12 + d.p * 12;\n\n // Convert day-of-week and week-of-year to day-of-year.\n if (\"V\" in d) {\n if (d.V < 1 || d.V > 53) return null;\n if (!(\"w\" in d)) d.w = 1;\n if (\"Z\" in d) {\n week = utcDate(newYear(d.y)), day = week.getUTCDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getUTCFullYear();\n d.m = week.getUTCMonth();\n d.d = week.getUTCDate() + (d.w + 6) % 7;\n } else {\n week = newDate(newYear(d.y)), day = week.getDay();\n week = day > 4 || day === 0 ? d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].ceil(week) : Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"])(week);\n week = d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].offset(week, (d.V - 1) * 7);\n d.y = week.getFullYear();\n d.m = week.getMonth();\n d.d = week.getDate() + (d.w + 6) % 7;\n }\n } else if (\"W\" in d || \"U\" in d) {\n if (!(\"w\" in d)) d.w = \"u\" in d ? d.u % 7 : \"W\" in d ? 1 : 0;\n day = \"Z\" in d ? utcDate(newYear(d.y)).getUTCDay() : newDate(newYear(d.y)).getDay();\n d.m = 0;\n d.d = \"W\" in d ? (d.w + 6) % 7 + d.W * 7 - (day + 5) % 7 : d.w + d.U * 7 - (day + 6) % 7;\n }\n\n // If a time zone is specified, all fields are interpreted as UTC and then\n // offset according to the specified time zone.\n if (\"Z\" in d) {\n d.H += d.Z / 100 | 0;\n d.M += d.Z % 100;\n return utcDate(d);\n }\n\n // Otherwise, all fields are in local time.\n return newDate(d);\n };\n }\n\n function parseSpecifier(d, specifier, string, j) {\n var i = 0,\n n = specifier.length,\n m = string.length,\n c,\n parse;\n\n while (i < n) {\n if (j >= m) return -1;\n c = specifier.charCodeAt(i++);\n if (c === 37) {\n c = specifier.charAt(i++);\n parse = parses[c in pads ? specifier.charAt(i++) : c];\n if (!parse || ((j = parse(d, string, j)) < 0)) return -1;\n } else if (c != string.charCodeAt(j++)) {\n return -1;\n }\n }\n\n return j;\n }\n\n function parsePeriod(d, string, i) {\n var n = periodRe.exec(string.slice(i));\n return n ? (d.p = periodLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortWeekday(d, string, i) {\n var n = shortWeekdayRe.exec(string.slice(i));\n return n ? (d.w = shortWeekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseWeekday(d, string, i) {\n var n = weekdayRe.exec(string.slice(i));\n return n ? (d.w = weekdayLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseShortMonth(d, string, i) {\n var n = shortMonthRe.exec(string.slice(i));\n return n ? (d.m = shortMonthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseMonth(d, string, i) {\n var n = monthRe.exec(string.slice(i));\n return n ? (d.m = monthLookup[n[0].toLowerCase()], i + n[0].length) : -1;\n }\n\n function parseLocaleDateTime(d, string, i) {\n return parseSpecifier(d, locale_dateTime, string, i);\n }\n\n function parseLocaleDate(d, string, i) {\n return parseSpecifier(d, locale_date, string, i);\n }\n\n function parseLocaleTime(d, string, i) {\n return parseSpecifier(d, locale_time, string, i);\n }\n\n function formatShortWeekday(d) {\n return locale_shortWeekdays[d.getDay()];\n }\n\n function formatWeekday(d) {\n return locale_weekdays[d.getDay()];\n }\n\n function formatShortMonth(d) {\n return locale_shortMonths[d.getMonth()];\n }\n\n function formatMonth(d) {\n return locale_months[d.getMonth()];\n }\n\n function formatPeriod(d) {\n return locale_periods[+(d.getHours() >= 12)];\n }\n\n function formatUTCShortWeekday(d) {\n return locale_shortWeekdays[d.getUTCDay()];\n }\n\n function formatUTCWeekday(d) {\n return locale_weekdays[d.getUTCDay()];\n }\n\n function formatUTCShortMonth(d) {\n return locale_shortMonths[d.getUTCMonth()];\n }\n\n function formatUTCMonth(d) {\n return locale_months[d.getUTCMonth()];\n }\n\n function formatUTCPeriod(d) {\n return locale_periods[+(d.getUTCHours() >= 12)];\n }\n\n return {\n format: function(specifier) {\n var f = newFormat(specifier += \"\", formats);\n f.toString = function() { return specifier; };\n return f;\n },\n parse: function(specifier) {\n var p = newParse(specifier += \"\", localDate);\n p.toString = function() { return specifier; };\n return p;\n },\n utcFormat: function(specifier) {\n var f = newFormat(specifier += \"\", utcFormats);\n f.toString = function() { return specifier; };\n return f;\n },\n utcParse: function(specifier) {\n var p = newParse(specifier, utcDate);\n p.toString = function() { return specifier; };\n return p;\n }\n };\n}\n\nvar pads = {\"-\": \"\", \"_\": \" \", \"0\": \"0\"},\n numberRe = /^\\s*\\d+/, // note: ignores next directive\n percentRe = /^%/,\n requoteRe = /[\\\\^$*+?|[\\]().{}]/g;\n\nfunction pad(value, fill, width) {\n var sign = value < 0 ? \"-\" : \"\",\n string = (sign ? -value : value) + \"\",\n length = string.length;\n return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);\n}\n\nfunction requote(s) {\n return s.replace(requoteRe, \"\\\\$&\");\n}\n\nfunction formatRe(names) {\n return new RegExp(\"^(?:\" + names.map(requote).join(\"|\") + \")\", \"i\");\n}\n\nfunction formatLookup(names) {\n var map = {}, i = -1, n = names.length;\n while (++i < n) map[names[i].toLowerCase()] = i;\n return map;\n}\n\nfunction parseWeekdayNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.w = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekdayNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 1));\n return n ? (d.u = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberSunday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.U = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberISO(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.V = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseWeekNumberMonday(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.W = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseFullYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 4));\n return n ? (d.y = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.y = +n[0] + (+n[0] > 68 ? 1900 : 2000), i + n[0].length) : -1;\n}\n\nfunction parseZone(d, string, i) {\n var n = /^(Z)|([+-]\\d\\d)(?::?(\\d\\d))?/.exec(string.slice(i, i + 6));\n return n ? (d.Z = n[1] ? 0 : -(n[2] + (n[3] || \"00\")), i + n[0].length) : -1;\n}\n\nfunction parseMonthNumber(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.m = n[0] - 1, i + n[0].length) : -1;\n}\n\nfunction parseDayOfMonth(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseDayOfYear(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.m = 0, d.d = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseHour24(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.H = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMinutes(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.M = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 2));\n return n ? (d.S = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMilliseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 3));\n return n ? (d.L = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseMicroseconds(d, string, i) {\n var n = numberRe.exec(string.slice(i, i + 6));\n return n ? (d.L = Math.floor(n[0] / 1000), i + n[0].length) : -1;\n}\n\nfunction parseLiteralPercent(d, string, i) {\n var n = percentRe.exec(string.slice(i, i + 1));\n return n ? i + n[0].length : -1;\n}\n\nfunction parseUnixTimestamp(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = +n[0], i + n[0].length) : -1;\n}\n\nfunction parseUnixTimestampSeconds(d, string, i) {\n var n = numberRe.exec(string.slice(i));\n return n ? (d.Q = (+n[0]) * 1000, i + n[0].length) : -1;\n}\n\nfunction formatDayOfMonth(d, p) {\n return pad(d.getDate(), p, 2);\n}\n\nfunction formatHour24(d, p) {\n return pad(d.getHours(), p, 2);\n}\n\nfunction formatHour12(d, p) {\n return pad(d.getHours() % 12 || 12, p, 2);\n}\n\nfunction formatDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 3);\n}\n\nfunction formatMilliseconds(d, p) {\n return pad(d.getMilliseconds(), p, 3);\n}\n\nfunction formatMicroseconds(d, p) {\n return formatMilliseconds(d, p) + \"000\";\n}\n\nfunction formatMonthNumber(d, p) {\n return pad(d.getMonth() + 1, p, 2);\n}\n\nfunction formatMinutes(d, p) {\n return pad(d.getMinutes(), p, 2);\n}\n\nfunction formatSeconds(d, p) {\n return pad(d.getSeconds(), p, 2);\n}\n\nfunction formatWeekdayNumberMonday(d) {\n var day = d.getDay();\n return day === 0 ? 7 : day;\n}\n\nfunction formatWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatWeekNumberISO(d, p) {\n var day = d.getDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d).getDay() === 4), p, 2);\n}\n\nfunction formatWeekdayNumberSunday(d) {\n return d.getDay();\n}\n\nfunction formatWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"timeYear\"])(d), d), p, 2);\n}\n\nfunction formatYear(d, p) {\n return pad(d.getFullYear() % 100, p, 2);\n}\n\nfunction formatFullYear(d, p) {\n return pad(d.getFullYear() % 10000, p, 4);\n}\n\nfunction formatZone(d) {\n var z = d.getTimezoneOffset();\n return (z > 0 ? \"-\" : (z *= -1, \"+\"))\n + pad(z / 60 | 0, \"0\", 2)\n + pad(z % 60, \"0\", 2);\n}\n\nfunction formatUTCDayOfMonth(d, p) {\n return pad(d.getUTCDate(), p, 2);\n}\n\nfunction formatUTCHour24(d, p) {\n return pad(d.getUTCHours(), p, 2);\n}\n\nfunction formatUTCHour12(d, p) {\n return pad(d.getUTCHours() % 12 || 12, p, 2);\n}\n\nfunction formatUTCDayOfYear(d, p) {\n return pad(1 + d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcDay\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 3);\n}\n\nfunction formatUTCMilliseconds(d, p) {\n return pad(d.getUTCMilliseconds(), p, 3);\n}\n\nfunction formatUTCMicroseconds(d, p) {\n return formatUTCMilliseconds(d, p) + \"000\";\n}\n\nfunction formatUTCMonthNumber(d, p) {\n return pad(d.getUTCMonth() + 1, p, 2);\n}\n\nfunction formatUTCMinutes(d, p) {\n return pad(d.getUTCMinutes(), p, 2);\n}\n\nfunction formatUTCSeconds(d, p) {\n return pad(d.getUTCSeconds(), p, 2);\n}\n\nfunction formatUTCWeekdayNumberMonday(d) {\n var dow = d.getUTCDay();\n return dow === 0 ? 7 : dow;\n}\n\nfunction formatUTCWeekNumberSunday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcSunday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCWeekNumberISO(d, p) {\n var day = d.getUTCDay();\n d = (day >= 4 || day === 0) ? Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"])(d) : d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].ceil(d);\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcThursday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d) + (Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d).getUTCDay() === 4), p, 2);\n}\n\nfunction formatUTCWeekdayNumberSunday(d) {\n return d.getUTCDay();\n}\n\nfunction formatUTCWeekNumberMonday(d, p) {\n return pad(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcMonday\"].count(Object(d3_time__WEBPACK_IMPORTED_MODULE_0__[\"utcYear\"])(d), d), p, 2);\n}\n\nfunction formatUTCYear(d, p) {\n return pad(d.getUTCFullYear() % 100, p, 2);\n}\n\nfunction formatUTCFullYear(d, p) {\n return pad(d.getUTCFullYear() % 10000, p, 4);\n}\n\nfunction formatUTCZone() {\n return \"+0000\";\n}\n\nfunction formatLiteralPercent() {\n return \"%\";\n}\n\nfunction formatUnixTimestamp(d) {\n return +d;\n}\n\nfunction formatUnixTimestampSeconds(d) {\n return Math.floor(+d / 1000);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time-format/src/locale.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/index.js ***! + \*************************************************************/ +/*! exports provided: timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return _src_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_millisecond__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/millisecond */ \"./node_modules/dagre-d3/node_modules/d3-time/src/millisecond.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return _src_millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return _src_millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return _src_millisecond__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return _src_millisecond__WEBPACK_IMPORTED_MODULE_1__[\"milliseconds\"]; });\n\n/* harmony import */ var _src_second__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/second */ \"./node_modules/dagre-d3/node_modules/d3-time/src/second.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return _src_second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return _src_second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return _src_second__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return _src_second__WEBPACK_IMPORTED_MODULE_2__[\"seconds\"]; });\n\n/* harmony import */ var _src_minute__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/minute */ \"./node_modules/dagre-d3/node_modules/d3-time/src/minute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return _src_minute__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return _src_minute__WEBPACK_IMPORTED_MODULE_3__[\"minutes\"]; });\n\n/* harmony import */ var _src_hour__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./src/hour */ \"./node_modules/dagre-d3/node_modules/d3-time/src/hour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return _src_hour__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return _src_hour__WEBPACK_IMPORTED_MODULE_4__[\"hours\"]; });\n\n/* harmony import */ var _src_day__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./src/day */ \"./node_modules/dagre-d3/node_modules/d3-time/src/day.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return _src_day__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return _src_day__WEBPACK_IMPORTED_MODULE_5__[\"days\"]; });\n\n/* harmony import */ var _src_week__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./src/week */ \"./node_modules/dagre-d3/node_modules/d3-time/src/week.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"sunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"sundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"monday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"mondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"tuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"tuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"wednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"wednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"thursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"thursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"friday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"fridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"saturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return _src_week__WEBPACK_IMPORTED_MODULE_6__[\"saturdays\"]; });\n\n/* harmony import */ var _src_month__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./src/month */ \"./node_modules/dagre-d3/node_modules/d3-time/src/month.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return _src_month__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return _src_month__WEBPACK_IMPORTED_MODULE_7__[\"months\"]; });\n\n/* harmony import */ var _src_year__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./src/year */ \"./node_modules/dagre-d3/node_modules/d3-time/src/year.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return _src_year__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return _src_year__WEBPACK_IMPORTED_MODULE_8__[\"years\"]; });\n\n/* harmony import */ var _src_utcMinute__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./src/utcMinute */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcMinute.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return _src_utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return _src_utcMinute__WEBPACK_IMPORTED_MODULE_9__[\"utcMinutes\"]; });\n\n/* harmony import */ var _src_utcHour__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./src/utcHour */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcHour.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return _src_utcHour__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return _src_utcHour__WEBPACK_IMPORTED_MODULE_10__[\"utcHours\"]; });\n\n/* harmony import */ var _src_utcDay__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./src/utcDay */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcDay.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return _src_utcDay__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return _src_utcDay__WEBPACK_IMPORTED_MODULE_11__[\"utcDays\"]; });\n\n/* harmony import */ var _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./src/utcWeek */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcWeek.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return _src_utcWeek__WEBPACK_IMPORTED_MODULE_12__[\"utcSaturdays\"]; });\n\n/* harmony import */ var _src_utcMonth__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./src/utcMonth */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcMonth.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return _src_utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return _src_utcMonth__WEBPACK_IMPORTED_MODULE_13__[\"utcMonths\"]; });\n\n/* harmony import */ var _src_utcYear__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./src/utcYear */ \"./node_modules/dagre-d3/node_modules/d3-time/src/utcYear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return _src_utcYear__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return _src_utcYear__WEBPACK_IMPORTED_MODULE_14__[\"utcYears\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/day.js": +/*!***************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/day.js ***! + \***************************************************************/ +/*! exports provided: default, days */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"days\", function() { return days; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar day = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setDate(date.getDate() + step);\n}, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (day);\nvar days = day.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/day.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/duration.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/duration.js ***! + \********************************************************************/ +/*! exports provided: durationSecond, durationMinute, durationHour, durationDay, durationWeek */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationSecond\", function() { return durationSecond; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationMinute\", function() { return durationMinute; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationHour\", function() { return durationHour; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationDay\", function() { return durationDay; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"durationWeek\", function() { return durationWeek; });\nvar durationSecond = 1e3;\nvar durationMinute = 6e4;\nvar durationHour = 36e5;\nvar durationDay = 864e5;\nvar durationWeek = 6048e5;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/hour.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/hour.js ***! + \****************************************************************/ +/*! exports provided: default, hours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hours\", function() { return hours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar hour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n var offset = date.getTimezoneOffset() * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"] % _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n if (offset < 0) offset += _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n date.setTime(Math.floor((+date - offset) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"] + offset);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hour);\nvar hours = hour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/hour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/interval.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/interval.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return newInterval; });\nvar t0 = new Date,\n t1 = new Date;\n\nfunction newInterval(floori, offseti, count, field) {\n\n function interval(date) {\n return floori(date = new Date(+date)), date;\n }\n\n interval.floor = interval;\n\n interval.ceil = function(date) {\n return floori(date = new Date(date - 1)), offseti(date, 1), floori(date), date;\n };\n\n interval.round = function(date) {\n var d0 = interval(date),\n d1 = interval.ceil(date);\n return date - d0 < d1 - date ? d0 : d1;\n };\n\n interval.offset = function(date, step) {\n return offseti(date = new Date(+date), step == null ? 1 : Math.floor(step)), date;\n };\n\n interval.range = function(start, stop, step) {\n var range = [], previous;\n start = interval.ceil(start);\n step = step == null ? 1 : Math.floor(step);\n if (!(start < stop) || !(step > 0)) return range; // also handles Invalid Date\n do range.push(previous = new Date(+start)), offseti(start, step), floori(start);\n while (previous < start && start < stop);\n return range;\n };\n\n interval.filter = function(test) {\n return newInterval(function(date) {\n if (date >= date) while (floori(date), !test(date)) date.setTime(date - 1);\n }, function(date, step) {\n if (date >= date) {\n if (step < 0) while (++step <= 0) {\n while (offseti(date, -1), !test(date)) {} // eslint-disable-line no-empty\n } else while (--step >= 0) {\n while (offseti(date, +1), !test(date)) {} // eslint-disable-line no-empty\n }\n }\n });\n };\n\n if (count) {\n interval.count = function(start, end) {\n t0.setTime(+start), t1.setTime(+end);\n floori(t0), floori(t1);\n return Math.floor(count(t0, t1));\n };\n\n interval.every = function(step) {\n step = Math.floor(step);\n return !isFinite(step) || !(step > 0) ? null\n : !(step > 1) ? interval\n : interval.filter(field\n ? function(d) { return field(d) % step === 0; }\n : function(d) { return interval.count(0, d) % step === 0; });\n };\n }\n\n return interval;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/millisecond.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/millisecond.js ***! + \***********************************************************************/ +/*! exports provided: default, milliseconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"milliseconds\", function() { return milliseconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n\n\nvar millisecond = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function() {\n // noop\n}, function(date, step) {\n date.setTime(+date + step);\n}, function(start, end) {\n return end - start;\n});\n\n// An optimized implementation for this simple case.\nmillisecond.every = function(k) {\n k = Math.floor(k);\n if (!isFinite(k) || !(k > 0)) return null;\n if (!(k > 1)) return millisecond;\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / k) * k);\n }, function(date, step) {\n date.setTime(+date + step * k);\n }, function(start, end) {\n return (end - start) / k;\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (millisecond);\nvar milliseconds = millisecond.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/millisecond.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/minute.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/minute.js ***! + \******************************************************************/ +/*! exports provided: default, minutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"minutes\", function() { return minutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar minute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (minute);\nvar minutes = minute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/minute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/month.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/month.js ***! + \*****************************************************************/ +/*! exports provided: default, months */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"months\", function() { return months; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n\n\nvar month = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setMonth(date.getMonth() + step);\n}, function(start, end) {\n return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12;\n}, function(date) {\n return date.getMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (month);\nvar months = month.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/month.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/second.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/second.js ***! + \******************************************************************/ +/*! exports provided: default, seconds */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"seconds\", function() { return seconds; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar second = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setTime(Math.floor(date / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationSecond\"];\n}, function(date) {\n return date.getUTCSeconds();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (second);\nvar seconds = second.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/second.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcDay.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcDay.js ***! + \******************************************************************/ +/*! exports provided: default, utcDays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return utcDays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcDay = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationDay\"];\n}, function(date) {\n return date.getUTCDate() - 1;\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcDay);\nvar utcDays = utcDay.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcDay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcHour.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcHour.js ***! + \*******************************************************************/ +/*! exports provided: default, utcHours */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return utcHours; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcHour = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMinutes(0, 0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationHour\"];\n}, function(date) {\n return date.getUTCHours();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcHour);\nvar utcHours = utcHour.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcHour.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcMinute.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcMinute.js ***! + \*********************************************************************/ +/*! exports provided: default, utcMinutes */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return utcMinutes; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nvar utcMinute = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCSeconds(0, 0);\n}, function(date, step) {\n date.setTime(+date + step * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]);\n}, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"];\n}, function(date) {\n return date.getUTCMinutes();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMinute);\nvar utcMinutes = utcMinute.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcMinute.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcMonth.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcMonth.js ***! + \********************************************************************/ +/*! exports provided: default, utcMonths */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return utcMonths; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n\n\nvar utcMonth = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCMonth(date.getUTCMonth() + step);\n}, function(start, end) {\n return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12;\n}, function(date) {\n return date.getUTCMonth();\n});\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcMonth);\nvar utcMonths = utcMonth.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcMonth.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcWeek.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcWeek.js ***! + \*******************************************************************/ +/*! exports provided: utcSunday, utcMonday, utcTuesday, utcWednesday, utcThursday, utcFriday, utcSaturday, utcSundays, utcMondays, utcTuesdays, utcWednesdays, utcThursdays, utcFridays, utcSaturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return utcSunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return utcMonday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return utcTuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return utcWednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return utcThursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return utcFriday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return utcSaturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return utcSundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return utcMondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return utcTuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return utcWednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return utcThursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return utcFridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return utcSaturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction utcWeekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCDate(date.getUTCDate() + step * 7);\n }, function(start, end) {\n return (end - start) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar utcSunday = utcWeekday(0);\nvar utcMonday = utcWeekday(1);\nvar utcTuesday = utcWeekday(2);\nvar utcWednesday = utcWeekday(3);\nvar utcThursday = utcWeekday(4);\nvar utcFriday = utcWeekday(5);\nvar utcSaturday = utcWeekday(6);\n\nvar utcSundays = utcSunday.range;\nvar utcMondays = utcMonday.range;\nvar utcTuesdays = utcTuesday.range;\nvar utcWednesdays = utcWednesday.range;\nvar utcThursdays = utcThursday.range;\nvar utcFridays = utcFriday.range;\nvar utcSaturdays = utcSaturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcWeek.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/utcYear.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/utcYear.js ***! + \*******************************************************************/ +/*! exports provided: default, utcYears */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return utcYears; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n\n\nvar utcYear = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step);\n}, function(start, end) {\n return end.getUTCFullYear() - start.getUTCFullYear();\n}, function(date) {\n return date.getUTCFullYear();\n});\n\n// An optimized implementation for this simple case.\nutcYear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k);\n date.setUTCMonth(0, 1);\n date.setUTCHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setUTCFullYear(date.getUTCFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (utcYear);\nvar utcYears = utcYear.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/utcYear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/week.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/week.js ***! + \****************************************************************/ +/*! exports provided: sunday, monday, tuesday, wednesday, thursday, friday, saturday, sundays, mondays, tuesdays, wednesdays, thursdays, fridays, saturdays */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sunday\", function() { return sunday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"monday\", function() { return monday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesday\", function() { return tuesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesday\", function() { return wednesday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursday\", function() { return thursday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"friday\", function() { return friday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturday\", function() { return saturday; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sundays\", function() { return sundays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"mondays\", function() { return mondays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tuesdays\", function() { return tuesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"wednesdays\", function() { return wednesdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"thursdays\", function() { return thursdays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"fridays\", function() { return fridays; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"saturdays\", function() { return saturdays; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-time/src/duration.js\");\n\n\n\nfunction weekday(i) {\n return Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setDate(date.getDate() + step * 7);\n }, function(start, end) {\n return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationMinute\"]) / _duration__WEBPACK_IMPORTED_MODULE_1__[\"durationWeek\"];\n });\n}\n\nvar sunday = weekday(0);\nvar monday = weekday(1);\nvar tuesday = weekday(2);\nvar wednesday = weekday(3);\nvar thursday = weekday(4);\nvar friday = weekday(5);\nvar saturday = weekday(6);\n\nvar sundays = sunday.range;\nvar mondays = monday.range;\nvar tuesdays = tuesday.range;\nvar wednesdays = wednesday.range;\nvar thursdays = thursday.range;\nvar fridays = friday.range;\nvar saturdays = saturday.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/week.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-time/src/year.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-time/src/year.js ***! + \****************************************************************/ +/*! exports provided: default, years */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"years\", function() { return years; });\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-time/src/interval.js\");\n\n\nvar year = Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n}, function(date, step) {\n date.setFullYear(date.getFullYear() + step);\n}, function(start, end) {\n return end.getFullYear() - start.getFullYear();\n}, function(date) {\n return date.getFullYear();\n});\n\n// An optimized implementation for this simple case.\nyear.every = function(k) {\n return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : Object(_interval__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(function(date) {\n date.setFullYear(Math.floor(date.getFullYear() / k) * k);\n date.setMonth(0, 1);\n date.setHours(0, 0, 0, 0);\n }, function(date, step) {\n date.setFullYear(date.getFullYear() + step * k);\n });\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (year);\nvar years = year.range;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-time/src/year.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-timer/index.js": +/*!**************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-timer/index.js ***! + \**************************************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/timer */ \"./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _src_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _src_timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _src_timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _src_timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/timeout */ \"./node_modules/dagre-d3/node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _src_timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/interval */ \"./node_modules/dagre-d3/node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _src_interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-timer/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-timer/src/interval.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-timer/src/interval.js ***! + \*********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-timer/src/timeout.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-timer/src/timeout.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js ***! + \******************************************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/index.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/index.js ***! + \*******************************************************************/ +/*! exports provided: transition, active, interrupt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/selection/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/selection/index.js\");\n/* harmony import */ var _src_transition_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/transition/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return _src_transition_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _src_active__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./src/active */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/active.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return _src_active__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _src_interrupt__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./src/interrupt */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/interrupt.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return _src_interrupt__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js ***! + \*********************************************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/cubehelix.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/cubehelix.js ***! + \*************************************************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js ***! + \**********************************************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/lab.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/lab.js ***! + \*******************************************************************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/math.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/math.js ***! + \********************************************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/dispatch.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/dispatch.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/index.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/index.js ***! + \************************************************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/back.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/back.js ***! + \*******************************************************************************************/ +/*! exports provided: backIn, backOut, backInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backIn\", function() { return backIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backOut\", function() { return backOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backInOut\", function() { return backInOut; });\nvar overshoot = 1.70158;\n\nvar backIn = (function custom(s) {\n s = +s;\n\n function backIn(t) {\n return t * t * ((s + 1) * t - s);\n }\n\n backIn.overshoot = custom;\n\n return backIn;\n})(overshoot);\n\nvar backOut = (function custom(s) {\n s = +s;\n\n function backOut(t) {\n return --t * t * ((s + 1) * t + s) + 1;\n }\n\n backOut.overshoot = custom;\n\n return backOut;\n})(overshoot);\n\nvar backInOut = (function custom(s) {\n s = +s;\n\n function backInOut(t) {\n return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n }\n\n backInOut.overshoot = custom;\n\n return backInOut;\n})(overshoot);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/back.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/bounce.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/bounce.js ***! + \*********************************************************************************************/ +/*! exports provided: bounceIn, bounceOut, bounceInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceIn\", function() { return bounceIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceOut\", function() { return bounceOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceInOut\", function() { return bounceInOut; });\nvar b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\n\nfunction bounceIn(t) {\n return 1 - bounceOut(1 - t);\n}\n\nfunction bounceOut(t) {\n return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n}\n\nfunction bounceInOut(t) {\n return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/bounce.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/circle.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/circle.js ***! + \*********************************************************************************************/ +/*! exports provided: circleIn, circleOut, circleInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleIn\", function() { return circleIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleOut\", function() { return circleOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleInOut\", function() { return circleInOut; });\nfunction circleIn(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n\nfunction circleOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n\nfunction circleInOut(t) {\n return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/cubic.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/cubic.js ***! + \********************************************************************************************/ +/*! exports provided: cubicIn, cubicOut, cubicInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicIn\", function() { return cubicIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicOut\", function() { return cubicOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicInOut\", function() { return cubicInOut; });\nfunction cubicIn(t) {\n return t * t * t;\n}\n\nfunction cubicOut(t) {\n return --t * t * t + 1;\n}\n\nfunction cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/cubic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/elastic.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/elastic.js ***! + \**********************************************************************************************/ +/*! exports provided: elasticIn, elasticOut, elasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticIn\", function() { return elasticIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticOut\", function() { return elasticOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticInOut\", function() { return elasticInOut; });\nvar tau = 2 * Math.PI,\n amplitude = 1,\n period = 0.3;\n\nvar elasticIn = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticIn(t) {\n return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n }\n\n elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n elasticIn.period = function(p) { return custom(a, p); };\n\n return elasticIn;\n})(amplitude, period);\n\nvar elasticOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticOut(t) {\n return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n }\n\n elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticOut.period = function(p) { return custom(a, p); };\n\n return elasticOut;\n})(amplitude, period);\n\nvar elasticInOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticInOut(t) {\n return ((t = t * 2 - 1) < 0\n ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n }\n\n elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticInOut.period = function(p) { return custom(a, p); };\n\n return elasticInOut;\n})(amplitude, period);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/elastic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/exp.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/exp.js ***! + \******************************************************************************************/ +/*! exports provided: expIn, expOut, expInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expIn\", function() { return expIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expOut\", function() { return expOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expInOut\", function() { return expInOut; });\nfunction expIn(t) {\n return Math.pow(2, 10 * t - 10);\n}\n\nfunction expOut(t) {\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction expInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/exp.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/index.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/index.js ***! + \********************************************************************************************/ +/*! exports provided: easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return _linear__WEBPACK_IMPORTED_MODULE_0__[\"linear\"]; });\n\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/quad.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony import */ var _cubic__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubic */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/cubic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony import */ var _poly__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./poly */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/poly.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony import */ var _sin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sin */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/sin.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony import */ var _exp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exp */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/exp.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./circle */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony import */ var _bounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./bounce */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/bounce.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceInOut\"]; });\n\n/* harmony import */ var _back__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./back */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/back.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony import */ var _elastic__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./elastic */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/elastic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticInOut\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/linear.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/linear.js ***! + \*********************************************************************************************/ +/*! exports provided: linear */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linear\", function() { return linear; });\nfunction linear(t) {\n return +t;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/poly.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/poly.js ***! + \*******************************************************************************************/ +/*! exports provided: polyIn, polyOut, polyInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyIn\", function() { return polyIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyOut\", function() { return polyOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyInOut\", function() { return polyInOut; });\nvar exponent = 3;\n\nvar polyIn = (function custom(e) {\n e = +e;\n\n function polyIn(t) {\n return Math.pow(t, e);\n }\n\n polyIn.exponent = custom;\n\n return polyIn;\n})(exponent);\n\nvar polyOut = (function custom(e) {\n e = +e;\n\n function polyOut(t) {\n return 1 - Math.pow(1 - t, e);\n }\n\n polyOut.exponent = custom;\n\n return polyOut;\n})(exponent);\n\nvar polyInOut = (function custom(e) {\n e = +e;\n\n function polyInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n }\n\n polyInOut.exponent = custom;\n\n return polyInOut;\n})(exponent);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/poly.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/quad.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/quad.js ***! + \*******************************************************************************************/ +/*! exports provided: quadIn, quadOut, quadInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadIn\", function() { return quadIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadOut\", function() { return quadOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadInOut\", function() { return quadInOut; });\nfunction quadIn(t) {\n return t * t;\n}\n\nfunction quadOut(t) {\n return t * (2 - t);\n}\n\nfunction quadInOut(t) {\n return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/sin.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/sin.js ***! + \******************************************************************************************/ +/*! exports provided: sinIn, sinOut, sinInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinIn\", function() { return sinIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinOut\", function() { return sinOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinInOut\", function() { return sinInOut; });\nvar pi = Math.PI,\n halfPi = pi / 2;\n\nfunction sinIn(t) {\n return 1 - Math.cos(t * halfPi);\n}\n\nfunction sinOut(t) {\n return Math.sin(t * halfPi);\n}\n\nfunction sinInOut(t) {\n return (1 - Math.cos(pi * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/sin.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/array.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/array.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js ***! + \***************************************************************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basisClosed.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basisClosed.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js ***! + \***************************************************************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/constant.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/constant.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/cubehelix.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/cubehelix.js ***! + \*******************************************************************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/date.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/date.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/discrete.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/discrete.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/discrete.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hcl.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hcl.js ***! + \*************************************************************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hsl.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hsl.js ***! + \*************************************************************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hue.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hue.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = Object(_color__WEBPACK_IMPORTED_MODULE_0__[\"hue\"])(+a, +b);\n return function(t) {\n var x = i(t);\n return x - 360 * Math.floor(x / 360);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hue.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js ***! + \***************************************************************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _discrete__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discrete */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/discrete.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return _discrete__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _hue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hue */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return _hue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _number__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _object__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./round */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _round__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _string__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _transform_index__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transform/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./zoom */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _hsl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./hsl */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"hslLong\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _hcl__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./hcl */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"hclLong\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _piecewise__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./piecewise */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/piecewise.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return _piecewise__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./quantize */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/lab.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/lab.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/object.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/object.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/piecewise.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/piecewise.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return piecewise; });\nfunction piecewise(interpolate, values) {\n var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n while (i < n) I[i] = interpolate(v, v = values[++i]);\n return function(t) {\n var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n return I[i](t - i);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/piecewise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/quantize.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/quantize.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/rgb.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/rgb.js ***! + \*************************************************************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/round.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/round.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/string.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/string.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/decompose.js": +/*!*****************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/decompose.js ***! + \*****************************************************************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/index.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/index.js ***! + \*************************************************************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/parse.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/parse.js ***! + \*************************************************************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/zoom.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/zoom.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/constant.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/constant.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/create.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/create.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js ***! + \*************************************************************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./create */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./local */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./matcher */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mouse */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selector */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./selection/style */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./touch */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./touches */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./window */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/local.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/local.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/matcher.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/matcher.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/mouse.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/mouse.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js ***! + \******************************************************************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/select.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/select.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectAll.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectAll.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/append.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/append.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/attr.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/attr.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/call.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/call.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/classed.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/classed.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/clone.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/clone.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/data.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/data.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/datum.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/datum.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/dispatch.js": +/*!**************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/dispatch.js ***! + \**************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/each.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/each.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/empty.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/empty.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/enter.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/enter.js ***! + \***********************************************************************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/exit.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/exit.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/filter.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/filter.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/html.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/html.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js ***! + \***********************************************************************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/insert.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/insert.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/lower.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/lower.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/merge.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/merge.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/node.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/node.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/nodes.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/nodes.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js ***! + \********************************************************************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/order.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/order.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/property.js": +/*!**************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/property.js ***! + \**************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/raise.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/raise.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/remove.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/remove.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/select.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/select.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/selectAll.js": +/*!***************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/selectAll.js ***! + \***************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/size.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/size.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sort.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sort.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sparse.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sparse.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/style.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/style.js ***! + \***********************************************************************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/text.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/text.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectorAll.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectorAll.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touch.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touch.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touches.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touches.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js ***! + \**************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./timeout */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/interval.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/interval.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timeout.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timeout.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js ***! + \*********************************************************************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/active.js": +/*!************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/active.js ***! + \************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\nvar root = [null];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n i;\n\n if (schedules) {\n name = name == null ? null : name + \"\";\n for (i in schedules) {\n if ((schedule = schedules[i]).state > _transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"SCHEDULED\"] && schedule.name === name) {\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]([[node]], root, name, +i);\n }\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/active.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/interrupt.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/interrupt.js ***! + \***************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n active,\n empty = true,\n i;\n\n if (!schedules) return;\n\n name = name == null ? null : name + \"\";\n\n for (i in schedules) {\n if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n active = schedule.state > _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"STARTING\"] && schedule.state < _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDING\"];\n schedule.state = _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDED\"];\n schedule.timer.stop();\n if (active) schedule.on.call(\"interrupt\", node, node.__data__, schedule.index, schedule.group);\n delete schedules[i];\n }\n\n if (empty) delete node.__transition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/selection/index.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/selection/index.js ***! + \*********************************************************************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/selection/interrupt.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/selection/transition.js\");\n\n\n\n\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.interrupt = _interrupt__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.transition = _transition__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/selection/interrupt.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/selection/interrupt.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../interrupt */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/interrupt.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return this.each(function() {\n Object(_interrupt__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(this, name);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/selection/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/selection/transition.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/selection/transition.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../transition/index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-ease */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-ease/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/index.js\");\n\n\n\n\n\nvar defaultTiming = {\n time: null, // Set on use.\n delay: 0,\n duration: 250,\n ease: d3_ease__WEBPACK_IMPORTED_MODULE_2__[\"easeCubicInOut\"]\n};\n\nfunction inherit(node, id) {\n var timing;\n while (!(timing = node.__transition) || !(timing = timing[id])) {\n if (!(node = node.parentNode)) {\n return defaultTiming.time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), defaultTiming;\n }\n }\n return timing;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var id,\n timing;\n\n if (name instanceof _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]) {\n id = name._id, name = name._name;\n } else {\n id = Object(_transition_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])(), (timing = defaultTiming).time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), name = name == null ? null : name + \"\";\n }\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n Object(_transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id, i, group, timing || inherit(node, id));\n }\n }\n }\n\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/selection/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attr.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attr.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrConstantNS(fullname, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttribute(name);\n value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction attrFunctionNS(fullname, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"namespace\"])(name), i = fullname === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformSvg\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return this.attrTween(name, typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"attr.\" + name, value))\n : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attrTween.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attrTween.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n\n\nfunction attrTweenNS(fullname, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttributeNS(fullname.space, fullname.local, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\nfunction attrTween(name, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttribute(name, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var key = \"attr.\" + name;\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"namespace\"])(name);\n return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attrTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/delay.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/delay.js ***! + \**********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction delayFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = +value.apply(this, arguments);\n };\n}\n\nfunction delayConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? delayFunction\n : delayConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).delay;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/delay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/duration.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/duration.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction durationFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = +value.apply(this, arguments);\n };\n}\n\nfunction durationConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? durationFunction\n : durationConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).duration;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/ease.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/ease.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new Error;\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).ease = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each(easeConstant(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).ease;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/ease.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/filter.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/filter.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"matcher\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js ***! + \**********************************************************************************/ +/*! exports provided: Transition, default, newId */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transition\", function() { return Transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"newId\", function() { return newId; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attr.js\");\n/* harmony import */ var _attrTween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./attrTween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/attrTween.js\");\n/* harmony import */ var _delay__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./delay */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/delay.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/duration.js\");\n/* harmony import */ var _ease__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ease */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/ease.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/filter.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/merge.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/on.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/remove.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selectAll.js\");\n/* harmony import */ var _selection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selection.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/style.js\");\n/* harmony import */ var _styleTween__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./styleTween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/styleTween.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/text.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/transition.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar id = 0;\n\nfunction Transition(groups, parents, name, id) {\n this._groups = groups;\n this._parents = parents;\n this._name = name;\n this._id = id;\n}\n\nfunction transition(name) {\n return Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"])().transition(name);\n}\n\nfunction newId() {\n return ++id;\n}\n\nvar selection_prototype = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype;\n\nTransition.prototype = transition.prototype = {\n constructor: Transition,\n select: _select__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n selection: _selection__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n transition: _transition__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n call: selection_prototype.call,\n nodes: selection_prototype.nodes,\n node: selection_prototype.node,\n size: selection_prototype.size,\n empty: selection_prototype.empty,\n each: selection_prototype.each,\n on: _on__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n attrTween: _attrTween__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n styleTween: _styleTween__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n tween: _tween__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n delay: _delay__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n duration: _duration__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n ease: _ease__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/interpolate.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/interpolate.js ***! + \****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var c;\n return (typeof b === \"number\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"]\n : (c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"])\n : d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateString\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/merge.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/merge.js ***! + \**********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(transition) {\n if (transition._id !== this._id) throw new Error;\n\n for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](merges, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/on.js": +/*!*******************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/on.js ***! + \*******************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n var i = t.indexOf(\".\");\n if (i >= 0) t = t.slice(0, i);\n return !t || t === \"start\";\n });\n}\n\nfunction onFunction(id, name, listener) {\n var on0, on1, sit = start(name) ? _schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"] : _schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"];\n return function() {\n var schedule = sit(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n\n schedule.on = on1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, listener) {\n var id = this._id;\n\n return arguments.length < 2\n ? Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).on.on(name)\n : this.each(onFunction(id, name, listener));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/remove.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/remove.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transition) if (+i !== id) return;\n if (parent) parent.removeChild(this);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.on(\"end.remove\", removeFunction(this._id));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js ***! + \*************************************************************************************/ +/*! exports provided: CREATED, SCHEDULED, STARTING, STARTED, RUNNING, ENDING, ENDED, default, init, set, get */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CREATED\", function() { return CREATED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SCHEDULED\", function() { return SCHEDULED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTING\", function() { return STARTING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTED\", function() { return STARTED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RUNNING\", function() { return RUNNING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDING\", function() { return ENDING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDED\", function() { return ENDED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return set; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"get\", function() { return get; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-timer/src/index.js\");\n\n\n\nvar emptyOn = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"end\", \"interrupt\");\nvar emptyTween = [];\n\nvar CREATED = 0;\nvar SCHEDULED = 1;\nvar STARTING = 2;\nvar STARTED = 3;\nvar RUNNING = 4;\nvar ENDING = 5;\nvar ENDED = 6;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name, id, index, group, timing) {\n var schedules = node.__transition;\n if (!schedules) node.__transition = {};\n else if (id in schedules) return;\n create(node, id, {\n name: name,\n index: index, // For context during callback.\n group: group, // For context during callback.\n on: emptyOn,\n tween: emptyTween,\n time: timing.time,\n delay: timing.delay,\n duration: timing.duration,\n ease: timing.ease,\n timer: null,\n state: CREATED\n });\n});\n\nfunction init(node, id) {\n var schedule = get(node, id);\n if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n return schedule;\n}\n\nfunction set(node, id) {\n var schedule = get(node, id);\n if (schedule.state > STARTING) throw new Error(\"too late; already started\");\n return schedule;\n}\n\nfunction get(node, id) {\n var schedule = node.__transition;\n if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n return schedule;\n}\n\nfunction create(node, id, self) {\n var schedules = node.__transition,\n tween;\n\n // Initialize the self timer when the transition is created.\n // Note the actual delay is not known until the first callback!\n schedules[id] = self;\n self.timer = Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timer\"])(schedule, 0, self.time);\n\n function schedule(elapsed) {\n self.state = SCHEDULED;\n self.timer.restart(start, self.delay, self.time);\n\n // If the elapsed delay is less than our first sleep, start immediately.\n if (self.delay <= elapsed) start(elapsed - self.delay);\n }\n\n function start(elapsed) {\n var i, j, n, o;\n\n // If the state is not SCHEDULED, then we previously errored on start.\n if (self.state !== SCHEDULED) return stop();\n\n for (i in schedules) {\n o = schedules[i];\n if (o.name !== self.name) continue;\n\n // While this element already has a starting transition during this frame,\n // defer starting an interrupting transition until that transition has a\n // chance to tick (and possibly end); see d3/d3-transition#54!\n if (o.state === STARTED) return Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(start);\n\n // Interrupt the active transition, if any.\n // Dispatch the interrupt event.\n if (o.state === RUNNING) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n\n // Cancel any pre-empted transitions. No interrupt event is dispatched\n // because the cancelled transitions never started. Note that this also\n // removes this transition from the pending list!\n else if (+i < id) {\n o.state = ENDED;\n o.timer.stop();\n delete schedules[i];\n }\n }\n\n // Defer the first tick to end of the current frame; see d3/d3#1576.\n // Note the transition may be canceled after start and before the first tick!\n // Note this must be scheduled before the start event; see d3/d3-transition#16!\n // Assuming this is successful, subsequent callbacks go straight to tick.\n Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(function() {\n if (self.state === STARTED) {\n self.state = RUNNING;\n self.timer.restart(tick, self.delay, self.time);\n tick(elapsed);\n }\n });\n\n // Dispatch the start event.\n // Note this must be done before the tween are initialized.\n self.state = STARTING;\n self.on.call(\"start\", node, node.__data__, self.index, self.group);\n if (self.state !== STARTING) return; // interrupted\n self.state = STARTED;\n\n // Initialize the tween, deleting null tween.\n tween = new Array(n = self.tween.length);\n for (i = 0, j = -1; i < n; ++i) {\n if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n tween[++j] = o;\n }\n }\n tween.length = j + 1;\n }\n\n function tick(elapsed) {\n var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n i = -1,\n n = tween.length;\n\n while (++i < n) {\n tween[i].call(null, t);\n }\n\n // Dispatch the end event.\n if (self.state === ENDING) {\n self.on.call(\"end\", node, node.__data__, self.index, self.group);\n stop();\n }\n }\n\n function stop() {\n self.state = ENDED;\n self.timer.stop();\n delete schedules[id];\n for (var i in schedules) return; // eslint-disable-line no-unused-vars\n delete node.__transition;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/select.js": +/*!***********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/select.js ***! + \***********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selector\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(subgroup[i], name, id, i, subgroup, Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id));\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selectAll.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selectAll.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selectorAll\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n for (var children = select.call(node, node.__data__, i, group), child, inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id), k = 0, l = children.length; k < l; ++k) {\n if (child = children[k]) {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(child, name, id, k, children, inherit);\n }\n }\n subgroups.push(children);\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selection.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selection.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n\n\nvar Selection = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.constructor;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Selection(this._groups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/selection.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/style.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/style.js ***! + \**********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-transition/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction styleRemove(name, interpolate) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction styleRemoveEnd(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction styleFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = value(this);\n if (value1 == null) value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var i = (name += \"\") === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformCss\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return value == null ? this\n .styleTween(name, styleRemove(name, i))\n .on(\"end.style.\" + name, styleRemoveEnd(name))\n : this.styleTween(name, typeof value === \"function\"\n ? styleFunction(name, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"style.\" + name, value))\n : styleConstant(name, i, value + \"\"), priority);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/styleTween.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/styleTween.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction styleTween(name, value, priority) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.style.setProperty(name, i(t), priority);\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var key = \"style.\" + (name += \"\");\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/styleTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/text.js": +/*!*********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/text.js ***! + \*********************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js\");\n\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var value1 = value(this);\n this.textContent = value1 == null ? \"\" : value1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.tween(\"text\", typeof value === \"function\"\n ? textFunction(Object(_tween__WEBPACK_IMPORTED_MODULE_0__[\"tweenValue\"])(this, \"text\", value))\n : textConstant(value == null ? \"\" : value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/transition.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/transition.js ***! + \***************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var name = this._name,\n id0 = this._id,\n id1 = Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])();\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n var inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"get\"])(node, id0);\n Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id1, i, group, {\n time: inherit.time + inherit.delay + inherit.duration,\n delay: 0,\n duration: inherit.duration,\n ease: inherit.ease\n });\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js": +/*!**********************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js ***! + \**********************************************************************************/ +/*! exports provided: default, tweenValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tweenValue\", function() { return tweenValue; });\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = tween0 = tween;\n for (var i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1 = tween1.slice();\n tween1.splice(i, 1);\n break;\n }\n }\n }\n\n schedule.tween = tween1;\n };\n}\n\nfunction tweenFunction(id, name, value) {\n var tween0, tween1;\n if (typeof value !== \"function\") throw new Error;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = (tween0 = tween).slice();\n for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1[i] = t;\n break;\n }\n }\n if (i === n) tween1.push(t);\n }\n\n schedule.tween = tween1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var id = this._id;\n\n name += \"\";\n\n if (arguments.length < 2) {\n var tween = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).tween;\n for (var i = 0, n = tween.length, t; i < n; ++i) {\n if ((t = tween[i]).name === name) {\n return t.value;\n }\n }\n return null;\n }\n\n return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n});\n\nfunction tweenValue(transition, name, value) {\n var id = transition._id;\n\n transition.each(function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id);\n (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n });\n\n return function(node) {\n return Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(node, id).value[name];\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-transition/src/transition/tween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/index.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/index.js ***! + \****************************************************************/ +/*! exports provided: voronoi */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_voronoi__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/voronoi */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/voronoi.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"voronoi\", function() { return _src_voronoi__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/Beach.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/Beach.js ***! + \********************************************************************/ +/*! exports provided: removeBeach, addBeach */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"removeBeach\", function() { return removeBeach; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"addBeach\", function() { return addBeach; });\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js\");\n/* harmony import */ var _Cell__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Cell */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Cell.js\");\n/* harmony import */ var _Circle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Circle */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Circle.js\");\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Edge */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\n\n\n\nvar beachPool = [];\n\nfunction Beach() {\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(this);\n this.edge =\n this.site =\n this.circle = null;\n}\n\nfunction createBeach(site) {\n var beach = beachPool.pop() || new Beach;\n beach.site = site;\n return beach;\n}\n\nfunction detachBeach(beach) {\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(beach);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].remove(beach);\n beachPool.push(beach);\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(beach);\n}\n\nfunction removeBeach(beach) {\n var circle = beach.circle,\n x = circle.x,\n y = circle.cy,\n vertex = [x, y],\n previous = beach.P,\n next = beach.N,\n disappearing = [beach];\n\n detachBeach(beach);\n\n var lArc = previous;\n while (lArc.circle\n && Math.abs(x - lArc.circle.x) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]\n && Math.abs(y - lArc.circle.cy) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n previous = lArc.P;\n disappearing.unshift(lArc);\n detachBeach(lArc);\n lArc = previous;\n }\n\n disappearing.unshift(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n\n var rArc = next;\n while (rArc.circle\n && Math.abs(x - rArc.circle.x) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]\n && Math.abs(y - rArc.circle.cy) < _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n next = rArc.N;\n disappearing.push(rArc);\n detachBeach(rArc);\n rArc = next;\n }\n\n disappearing.push(rArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(rArc);\n\n var nArcs = disappearing.length,\n iArc;\n for (iArc = 1; iArc < nArcs; ++iArc) {\n rArc = disappearing[iArc];\n lArc = disappearing[iArc - 1];\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"setEdgeEnd\"])(rArc.edge, lArc.site, rArc.site, vertex);\n }\n\n lArc = disappearing[0];\n rArc = disappearing[nArcs - 1];\n rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, rArc.site, null, vertex);\n\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n}\n\nfunction addBeach(site) {\n var x = site[0],\n directrix = site[1],\n lArc,\n rArc,\n dxl,\n dxr,\n node = _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"]._;\n\n while (node) {\n dxl = leftBreakPoint(node, directrix) - x;\n if (dxl > _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) node = node.L; else {\n dxr = x - rightBreakPoint(node, directrix);\n if (dxr > _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n if (!node.R) {\n lArc = node;\n break;\n }\n node = node.R;\n } else {\n if (dxl > -_Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n lArc = node.P;\n rArc = node;\n } else if (dxr > -_Diagram__WEBPACK_IMPORTED_MODULE_4__[\"epsilon\"]) {\n lArc = node;\n rArc = node.N;\n } else {\n lArc = rArc = node;\n }\n break;\n }\n }\n }\n\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"createCell\"])(site);\n var newArc = createBeach(site);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].insert(lArc, newArc);\n\n if (!lArc && !rArc) return;\n\n if (lArc === rArc) {\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n rArc = createBeach(lArc.site);\n _Diagram__WEBPACK_IMPORTED_MODULE_4__[\"beaches\"].insert(newArc, rArc);\n newArc.edge = rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, newArc.site);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n return;\n }\n\n if (!rArc) { // && lArc\n newArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lArc.site, newArc.site);\n return;\n }\n\n // else lArc !== rArc\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"detachCircle\"])(rArc);\n\n var lSite = lArc.site,\n ax = lSite[0],\n ay = lSite[1],\n bx = site[0] - ax,\n by = site[1] - ay,\n rSite = rArc.site,\n cx = rSite[0] - ax,\n cy = rSite[1] - ay,\n d = 2 * (bx * cy - by * cx),\n hb = bx * bx + by * by,\n hc = cx * cx + cy * cy,\n vertex = [(cy * hb - by * hc) / d + ax, (bx * hc - cx * hb) / d + ay];\n\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"setEdgeEnd\"])(rArc.edge, lSite, rSite, vertex);\n newArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(lSite, site, null, vertex);\n rArc.edge = Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"createEdge\"])(site, rSite, null, vertex);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(lArc);\n Object(_Circle__WEBPACK_IMPORTED_MODULE_2__[\"attachCircle\"])(rArc);\n}\n\nfunction leftBreakPoint(arc, directrix) {\n var site = arc.site,\n rfocx = site[0],\n rfocy = site[1],\n pby2 = rfocy - directrix;\n\n if (!pby2) return rfocx;\n\n var lArc = arc.P;\n if (!lArc) return -Infinity;\n\n site = lArc.site;\n var lfocx = site[0],\n lfocy = site[1],\n plby2 = lfocy - directrix;\n\n if (!plby2) return lfocx;\n\n var hl = lfocx - rfocx,\n aby2 = 1 / pby2 - 1 / plby2,\n b = hl / plby2;\n\n if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;\n\n return (rfocx + lfocx) / 2;\n}\n\nfunction rightBreakPoint(arc, directrix) {\n var rArc = arc.N;\n if (rArc) return leftBreakPoint(rArc, directrix);\n var site = arc.site;\n return site[1] === directrix ? site[0] : Infinity;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/Beach.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/Cell.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/Cell.js ***! + \*******************************************************************/ +/*! exports provided: createCell, cellHalfedgeStart, cellHalfedgeEnd, sortCellHalfedges, clipCells */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createCell\", function() { return createCell; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cellHalfedgeStart\", function() { return cellHalfedgeStart; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cellHalfedgeEnd\", function() { return cellHalfedgeEnd; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sortCellHalfedges\", function() { return sortCellHalfedges; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clipCells\", function() { return clipCells; });\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Edge */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\nfunction createCell(site) {\n return _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][site.index] = {\n site: site,\n halfedges: []\n };\n}\n\nfunction cellHalfedgeAngle(cell, edge) {\n var site = cell.site,\n va = edge.left,\n vb = edge.right;\n if (site === vb) vb = va, va = site;\n if (vb) return Math.atan2(vb[1] - va[1], vb[0] - va[0]);\n if (site === va) va = edge[1], vb = edge[0];\n else va = edge[0], vb = edge[1];\n return Math.atan2(va[0] - vb[0], vb[1] - va[1]);\n}\n\nfunction cellHalfedgeStart(cell, edge) {\n return edge[+(edge.left !== cell.site)];\n}\n\nfunction cellHalfedgeEnd(cell, edge) {\n return edge[+(edge.left === cell.site)];\n}\n\nfunction sortCellHalfedges() {\n for (var i = 0, n = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"].length, cell, halfedges, j, m; i < n; ++i) {\n if ((cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][i]) && (m = (halfedges = cell.halfedges).length)) {\n var index = new Array(m),\n array = new Array(m);\n for (j = 0; j < m; ++j) index[j] = j, array[j] = cellHalfedgeAngle(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[j]]);\n index.sort(function(i, j) { return array[j] - array[i]; });\n for (j = 0; j < m; ++j) array[j] = halfedges[index[j]];\n for (j = 0; j < m; ++j) halfedges[j] = array[j];\n }\n }\n}\n\nfunction clipCells(x0, y0, x1, y1) {\n var nCells = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"].length,\n iCell,\n cell,\n site,\n iHalfedge,\n halfedges,\n nHalfedges,\n start,\n startX,\n startY,\n end,\n endX,\n endY,\n cover = true;\n\n for (iCell = 0; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n site = cell.site;\n halfedges = cell.halfedges;\n iHalfedge = halfedges.length;\n\n // Remove any dangling clipped edges.\n while (iHalfedge--) {\n if (!_Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[iHalfedge]]) {\n halfedges.splice(iHalfedge, 1);\n }\n }\n\n // Insert any border edges as necessary.\n iHalfedge = 0, nHalfedges = halfedges.length;\n while (iHalfedge < nHalfedges) {\n end = cellHalfedgeEnd(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[iHalfedge]]), endX = end[0], endY = end[1];\n start = cellHalfedgeStart(cell, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"][halfedges[++iHalfedge % nHalfedges]]), startX = start[0], startY = start[1];\n if (Math.abs(endX - startX) > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] || Math.abs(endY - startY) > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"]) {\n halfedges.splice(iHalfedge, 0, _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, end,\n Math.abs(endX - x0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && y1 - endY > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [x0, Math.abs(startX - x0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startY : y1]\n : Math.abs(endY - y1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && x1 - endX > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [Math.abs(startY - y1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startX : x1, y1]\n : Math.abs(endX - x1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && endY - y0 > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [x1, Math.abs(startX - x1) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startY : y0]\n : Math.abs(endY - y0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] && endX - x0 > _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? [Math.abs(startY - y0) < _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon\"] ? startX : x0, y0]\n : null)) - 1);\n ++nHalfedges;\n }\n }\n\n if (nHalfedges) cover = false;\n }\n }\n\n // If there weren’t any edges, have the closest site cover the extent.\n // It doesn’t matter which corner of the extent we measure!\n if (cover) {\n var dx, dy, d2, dc = Infinity;\n\n for (iCell = 0, cover = null; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n site = cell.site;\n dx = site[0] - x0;\n dy = site[1] - y0;\n d2 = dx * dx + dy * dy;\n if (d2 < dc) dc = d2, cover = cell;\n }\n }\n\n if (cover) {\n var v00 = [x0, y0], v01 = [x0, y1], v11 = [x1, y1], v10 = [x1, y0];\n cover.halfedges.push(\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site = cover.site, v00, v01)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v01, v11)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v11, v10)) - 1,\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"edges\"].push(Object(_Edge__WEBPACK_IMPORTED_MODULE_0__[\"createBorderEdge\"])(site, v10, v00)) - 1\n );\n }\n }\n\n // Lastly delete any cells with no edges; these were entirely clipped.\n for (iCell = 0; iCell < nCells; ++iCell) {\n if (cell = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell]) {\n if (!cell.halfedges.length) {\n delete _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"cells\"][iCell];\n }\n }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/Cell.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/Circle.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/Circle.js ***! + \*********************************************************************/ +/*! exports provided: firstCircle, attachCircle, detachCircle */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"firstCircle\", function() { return firstCircle; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"attachCircle\", function() { return attachCircle; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"detachCircle\", function() { return detachCircle; });\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\nvar circlePool = [];\n\nvar firstCircle;\n\nfunction Circle() {\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(this);\n this.x =\n this.y =\n this.arc =\n this.site =\n this.cy = null;\n}\n\nfunction attachCircle(arc) {\n var lArc = arc.P,\n rArc = arc.N;\n\n if (!lArc || !rArc) return;\n\n var lSite = lArc.site,\n cSite = arc.site,\n rSite = rArc.site;\n\n if (lSite === rSite) return;\n\n var bx = cSite[0],\n by = cSite[1],\n ax = lSite[0] - bx,\n ay = lSite[1] - by,\n cx = rSite[0] - bx,\n cy = rSite[1] - by;\n\n var d = 2 * (ax * cy - ay * cx);\n if (d >= -_Diagram__WEBPACK_IMPORTED_MODULE_1__[\"epsilon2\"]) return;\n\n var ha = ax * ax + ay * ay,\n hc = cx * cx + cy * cy,\n x = (cy * ha - ay * hc) / d,\n y = (ax * hc - cx * ha) / d;\n\n var circle = circlePool.pop() || new Circle;\n circle.arc = arc;\n circle.site = cSite;\n circle.x = x + bx;\n circle.y = (circle.cy = y + by) + Math.sqrt(x * x + y * y); // y bottom\n\n arc.circle = circle;\n\n var before = null,\n node = _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"]._;\n\n while (node) {\n if (circle.y < node.y || (circle.y === node.y && circle.x <= node.x)) {\n if (node.L) node = node.L;\n else { before = node.P; break; }\n } else {\n if (node.R) node = node.R;\n else { before = node; break; }\n }\n }\n\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"].insert(before, circle);\n if (!before) firstCircle = circle;\n}\n\nfunction detachCircle(arc) {\n var circle = arc.circle;\n if (circle) {\n if (!circle.P) firstCircle = circle.N;\n _Diagram__WEBPACK_IMPORTED_MODULE_1__[\"circles\"].remove(circle);\n circlePool.push(circle);\n Object(_RedBlackTree__WEBPACK_IMPORTED_MODULE_0__[\"RedBlackNode\"])(circle);\n arc.circle = null;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/Circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js ***! + \**********************************************************************/ +/*! exports provided: epsilon, epsilon2, beaches, cells, circles, edges, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon\", function() { return epsilon; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"epsilon2\", function() { return epsilon2; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"beaches\", function() { return beaches; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cells\", function() { return cells; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circles\", function() { return circles; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"edges\", function() { return edges; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return Diagram; });\n/* harmony import */ var _Beach__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Beach */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Beach.js\");\n/* harmony import */ var _Cell__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Cell */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Cell.js\");\n/* harmony import */ var _Circle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Circle */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Circle.js\");\n/* harmony import */ var _Edge__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Edge */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js\");\n/* harmony import */ var _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./RedBlackTree */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js\");\n\n\n\n\n\n\nvar epsilon = 1e-6;\nvar epsilon2 = 1e-12;\nvar beaches;\nvar cells;\nvar circles;\nvar edges;\n\nfunction triangleArea(a, b, c) {\n return (a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]);\n}\n\nfunction lexicographic(a, b) {\n return b[1] - a[1]\n || b[0] - a[0];\n}\n\nfunction Diagram(sites, extent) {\n var site = sites.sort(lexicographic).pop(),\n x,\n y,\n circle;\n\n edges = [];\n cells = new Array(sites.length);\n beaches = new _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n circles = new _RedBlackTree__WEBPACK_IMPORTED_MODULE_4__[\"default\"];\n\n while (true) {\n circle = _Circle__WEBPACK_IMPORTED_MODULE_2__[\"firstCircle\"];\n if (site && (!circle || site[1] < circle.y || (site[1] === circle.y && site[0] < circle.x))) {\n if (site[0] !== x || site[1] !== y) {\n Object(_Beach__WEBPACK_IMPORTED_MODULE_0__[\"addBeach\"])(site);\n x = site[0], y = site[1];\n }\n site = sites.pop();\n } else if (circle) {\n Object(_Beach__WEBPACK_IMPORTED_MODULE_0__[\"removeBeach\"])(circle.arc);\n } else {\n break;\n }\n }\n\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"sortCellHalfedges\"])();\n\n if (extent) {\n var x0 = +extent[0][0],\n y0 = +extent[0][1],\n x1 = +extent[1][0],\n y1 = +extent[1][1];\n Object(_Edge__WEBPACK_IMPORTED_MODULE_3__[\"clipEdges\"])(x0, y0, x1, y1);\n Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"clipCells\"])(x0, y0, x1, y1);\n }\n\n this.edges = edges;\n this.cells = cells;\n\n beaches =\n circles =\n edges =\n cells = null;\n}\n\nDiagram.prototype = {\n constructor: Diagram,\n\n polygons: function() {\n var edges = this.edges;\n\n return this.cells.map(function(cell) {\n var polygon = cell.halfedges.map(function(i) { return Object(_Cell__WEBPACK_IMPORTED_MODULE_1__[\"cellHalfedgeStart\"])(cell, edges[i]); });\n polygon.data = cell.site.data;\n return polygon;\n });\n },\n\n triangles: function() {\n var triangles = [],\n edges = this.edges;\n\n this.cells.forEach(function(cell, i) {\n if (!(m = (halfedges = cell.halfedges).length)) return;\n var site = cell.site,\n halfedges,\n j = -1,\n m,\n s0,\n e1 = edges[halfedges[m - 1]],\n s1 = e1.left === site ? e1.right : e1.left;\n\n while (++j < m) {\n s0 = s1;\n e1 = edges[halfedges[j]];\n s1 = e1.left === site ? e1.right : e1.left;\n if (s0 && s1 && i < s0.index && i < s1.index && triangleArea(site, s0, s1) < 0) {\n triangles.push([site.data, s0.data, s1.data]);\n }\n }\n });\n\n return triangles;\n },\n\n links: function() {\n return this.edges.filter(function(edge) {\n return edge.right;\n }).map(function(edge) {\n return {\n source: edge.left.data,\n target: edge.right.data\n };\n });\n },\n\n find: function(x, y, radius) {\n var that = this, i0, i1 = that._found || 0, n = that.cells.length, cell;\n\n // Use the previously-found cell, or start with an arbitrary one.\n while (!(cell = that.cells[i1])) if (++i1 >= n) return null;\n var dx = x - cell.site[0], dy = y - cell.site[1], d2 = dx * dx + dy * dy;\n\n // Traverse the half-edges to find a closer cell, if any.\n do {\n cell = that.cells[i0 = i1], i1 = null;\n cell.halfedges.forEach(function(e) {\n var edge = that.edges[e], v = edge.left;\n if ((v === cell.site || !v) && !(v = edge.right)) return;\n var vx = x - v[0], vy = y - v[1], v2 = vx * vx + vy * vy;\n if (v2 < d2) d2 = v2, i1 = v.index;\n });\n } while (i1 !== null);\n\n that._found = i0;\n\n return radius == null || d2 <= radius * radius ? cell.site : null;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js ***! + \*******************************************************************/ +/*! exports provided: createEdge, createBorderEdge, setEdgeEnd, clipEdges */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createEdge\", function() { return createEdge; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"createBorderEdge\", function() { return createBorderEdge; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"setEdgeEnd\", function() { return setEdgeEnd; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"clipEdges\", function() { return clipEdges; });\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js\");\n\n\nfunction createEdge(left, right, v0, v1) {\n var edge = [null, null],\n index = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"].push(edge) - 1;\n edge.left = left;\n edge.right = right;\n if (v0) setEdgeEnd(edge, left, right, v0);\n if (v1) setEdgeEnd(edge, right, left, v1);\n _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"cells\"][left.index].halfedges.push(index);\n _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"cells\"][right.index].halfedges.push(index);\n return edge;\n}\n\nfunction createBorderEdge(left, v0, v1) {\n var edge = [v0, v1];\n edge.left = left;\n return edge;\n}\n\nfunction setEdgeEnd(edge, left, right, vertex) {\n if (!edge[0] && !edge[1]) {\n edge[0] = vertex;\n edge.left = left;\n edge.right = right;\n } else if (edge.left === right) {\n edge[1] = vertex;\n } else {\n edge[0] = vertex;\n }\n}\n\n// Liang–Barsky line clipping.\nfunction clipEdge(edge, x0, y0, x1, y1) {\n var a = edge[0],\n b = edge[1],\n ax = a[0],\n ay = a[1],\n bx = b[0],\n by = b[1],\n t0 = 0,\n t1 = 1,\n dx = bx - ax,\n dy = by - ay,\n r;\n\n r = x0 - ax;\n if (!dx && r > 0) return;\n r /= dx;\n if (dx < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dx > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = x1 - ax;\n if (!dx && r < 0) return;\n r /= dx;\n if (dx < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dx > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n r = y0 - ay;\n if (!dy && r > 0) return;\n r /= dy;\n if (dy < 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n } else if (dy > 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n }\n\n r = y1 - ay;\n if (!dy && r < 0) return;\n r /= dy;\n if (dy < 0) {\n if (r > t1) return;\n if (r > t0) t0 = r;\n } else if (dy > 0) {\n if (r < t0) return;\n if (r < t1) t1 = r;\n }\n\n if (!(t0 > 0) && !(t1 < 1)) return true; // TODO Better check?\n\n if (t0 > 0) edge[0] = [ax + t0 * dx, ay + t0 * dy];\n if (t1 < 1) edge[1] = [ax + t1 * dx, ay + t1 * dy];\n return true;\n}\n\nfunction connectEdge(edge, x0, y0, x1, y1) {\n var v1 = edge[1];\n if (v1) return true;\n\n var v0 = edge[0],\n left = edge.left,\n right = edge.right,\n lx = left[0],\n ly = left[1],\n rx = right[0],\n ry = right[1],\n fx = (lx + rx) / 2,\n fy = (ly + ry) / 2,\n fm,\n fb;\n\n if (ry === ly) {\n if (fx < x0 || fx >= x1) return;\n if (lx > rx) {\n if (!v0) v0 = [fx, y0];\n else if (v0[1] >= y1) return;\n v1 = [fx, y1];\n } else {\n if (!v0) v0 = [fx, y1];\n else if (v0[1] < y0) return;\n v1 = [fx, y0];\n }\n } else {\n fm = (lx - rx) / (ry - ly);\n fb = fy - fm * fx;\n if (fm < -1 || fm > 1) {\n if (lx > rx) {\n if (!v0) v0 = [(y0 - fb) / fm, y0];\n else if (v0[1] >= y1) return;\n v1 = [(y1 - fb) / fm, y1];\n } else {\n if (!v0) v0 = [(y1 - fb) / fm, y1];\n else if (v0[1] < y0) return;\n v1 = [(y0 - fb) / fm, y0];\n }\n } else {\n if (ly < ry) {\n if (!v0) v0 = [x0, fm * x0 + fb];\n else if (v0[0] >= x1) return;\n v1 = [x1, fm * x1 + fb];\n } else {\n if (!v0) v0 = [x1, fm * x1 + fb];\n else if (v0[0] < x0) return;\n v1 = [x0, fm * x0 + fb];\n }\n }\n }\n\n edge[0] = v0;\n edge[1] = v1;\n return true;\n}\n\nfunction clipEdges(x0, y0, x1, y1) {\n var i = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"].length,\n edge;\n\n while (i--) {\n if (!connectEdge(edge = _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"][i], x0, y0, x1, y1)\n || !clipEdge(edge, x0, y0, x1, y1)\n || !(Math.abs(edge[0][0] - edge[1][0]) > _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"]\n || Math.abs(edge[0][1] - edge[1][1]) > _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"epsilon\"])) {\n delete _Diagram__WEBPACK_IMPORTED_MODULE_0__[\"edges\"][i];\n }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/Edge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js": +/*!***************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js ***! + \***************************************************************************/ +/*! exports provided: RedBlackNode, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RedBlackNode\", function() { return RedBlackNode; });\nfunction RedBlackTree() {\n this._ = null; // root node\n}\n\nfunction RedBlackNode(node) {\n node.U = // parent node\n node.C = // color - true for red, false for black\n node.L = // left node\n node.R = // right node\n node.P = // previous node\n node.N = null; // next node\n}\n\nRedBlackTree.prototype = {\n constructor: RedBlackTree,\n\n insert: function(after, node) {\n var parent, grandpa, uncle;\n\n if (after) {\n node.P = after;\n node.N = after.N;\n if (after.N) after.N.P = node;\n after.N = node;\n if (after.R) {\n after = after.R;\n while (after.L) after = after.L;\n after.L = node;\n } else {\n after.R = node;\n }\n parent = after;\n } else if (this._) {\n after = RedBlackFirst(this._);\n node.P = null;\n node.N = after;\n after.P = after.L = node;\n parent = after;\n } else {\n node.P = node.N = null;\n this._ = node;\n parent = null;\n }\n node.L = node.R = null;\n node.U = parent;\n node.C = true;\n\n after = node;\n while (parent && parent.C) {\n grandpa = parent.U;\n if (parent === grandpa.L) {\n uncle = grandpa.R;\n if (uncle && uncle.C) {\n parent.C = uncle.C = false;\n grandpa.C = true;\n after = grandpa;\n } else {\n if (after === parent.R) {\n RedBlackRotateLeft(this, parent);\n after = parent;\n parent = after.U;\n }\n parent.C = false;\n grandpa.C = true;\n RedBlackRotateRight(this, grandpa);\n }\n } else {\n uncle = grandpa.L;\n if (uncle && uncle.C) {\n parent.C = uncle.C = false;\n grandpa.C = true;\n after = grandpa;\n } else {\n if (after === parent.L) {\n RedBlackRotateRight(this, parent);\n after = parent;\n parent = after.U;\n }\n parent.C = false;\n grandpa.C = true;\n RedBlackRotateLeft(this, grandpa);\n }\n }\n parent = after.U;\n }\n this._.C = false;\n },\n\n remove: function(node) {\n if (node.N) node.N.P = node.P;\n if (node.P) node.P.N = node.N;\n node.N = node.P = null;\n\n var parent = node.U,\n sibling,\n left = node.L,\n right = node.R,\n next,\n red;\n\n if (!left) next = right;\n else if (!right) next = left;\n else next = RedBlackFirst(right);\n\n if (parent) {\n if (parent.L === node) parent.L = next;\n else parent.R = next;\n } else {\n this._ = next;\n }\n\n if (left && right) {\n red = next.C;\n next.C = node.C;\n next.L = left;\n left.U = next;\n if (next !== right) {\n parent = next.U;\n next.U = node.U;\n node = next.R;\n parent.L = node;\n next.R = right;\n right.U = next;\n } else {\n next.U = parent;\n parent = next;\n node = next.R;\n }\n } else {\n red = node.C;\n node = next;\n }\n\n if (node) node.U = parent;\n if (red) return;\n if (node && node.C) { node.C = false; return; }\n\n do {\n if (node === this._) break;\n if (node === parent.L) {\n sibling = parent.R;\n if (sibling.C) {\n sibling.C = false;\n parent.C = true;\n RedBlackRotateLeft(this, parent);\n sibling = parent.R;\n }\n if ((sibling.L && sibling.L.C)\n || (sibling.R && sibling.R.C)) {\n if (!sibling.R || !sibling.R.C) {\n sibling.L.C = false;\n sibling.C = true;\n RedBlackRotateRight(this, sibling);\n sibling = parent.R;\n }\n sibling.C = parent.C;\n parent.C = sibling.R.C = false;\n RedBlackRotateLeft(this, parent);\n node = this._;\n break;\n }\n } else {\n sibling = parent.L;\n if (sibling.C) {\n sibling.C = false;\n parent.C = true;\n RedBlackRotateRight(this, parent);\n sibling = parent.L;\n }\n if ((sibling.L && sibling.L.C)\n || (sibling.R && sibling.R.C)) {\n if (!sibling.L || !sibling.L.C) {\n sibling.R.C = false;\n sibling.C = true;\n RedBlackRotateLeft(this, sibling);\n sibling = parent.L;\n }\n sibling.C = parent.C;\n parent.C = sibling.L.C = false;\n RedBlackRotateRight(this, parent);\n node = this._;\n break;\n }\n }\n sibling.C = true;\n node = parent;\n parent = parent.U;\n } while (!node.C);\n\n if (node) node.C = false;\n }\n};\n\nfunction RedBlackRotateLeft(tree, node) {\n var p = node,\n q = node.R,\n parent = p.U;\n\n if (parent) {\n if (parent.L === p) parent.L = q;\n else parent.R = q;\n } else {\n tree._ = q;\n }\n\n q.U = parent;\n p.U = q;\n p.R = q.L;\n if (p.R) p.R.U = p;\n q.L = p;\n}\n\nfunction RedBlackRotateRight(tree, node) {\n var p = node,\n q = node.L,\n parent = p.U;\n\n if (parent) {\n if (parent.L === p) parent.L = q;\n else parent.R = q;\n } else {\n tree._ = q;\n }\n\n q.U = parent;\n p.U = q;\n p.L = q.R;\n if (p.L) p.L.U = p;\n q.R = p;\n}\n\nfunction RedBlackFirst(node) {\n while (node.L) node = node.L;\n return node;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (RedBlackTree);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/RedBlackTree.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/constant.js": +/*!***********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/constant.js ***! + \***********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/point.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/point.js ***! + \********************************************************************/ +/*! exports provided: x, y */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"x\", function() { return x; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"y\", function() { return y; });\nfunction x(d) {\n return d[0];\n}\n\nfunction y(d) {\n return d[1];\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-voronoi/src/voronoi.js": +/*!**********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-voronoi/src/voronoi.js ***! + \**********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/constant.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/point.js\");\n/* harmony import */ var _Diagram__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./Diagram */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/src/Diagram.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var x = _point__WEBPACK_IMPORTED_MODULE_1__[\"x\"],\n y = _point__WEBPACK_IMPORTED_MODULE_1__[\"y\"],\n extent = null;\n\n function voronoi(data) {\n return new _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"default\"](data.map(function(d, i) {\n var s = [Math.round(x(d, i, data) / _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) * _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"], Math.round(y(d, i, data) / _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]) * _Diagram__WEBPACK_IMPORTED_MODULE_2__[\"epsilon\"]];\n s.index = i;\n s.data = d;\n return s;\n }), extent);\n }\n\n voronoi.polygons = function(data) {\n return voronoi(data).polygons();\n };\n\n voronoi.links = function(data) {\n return voronoi(data).links();\n };\n\n voronoi.triangles = function(data) {\n return voronoi(data).triangles();\n };\n\n voronoi.x = function(_) {\n return arguments.length ? (x = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), voronoi) : x;\n };\n\n voronoi.y = function(_) {\n return arguments.length ? (y = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+_), voronoi) : y;\n };\n\n voronoi.extent = function(_) {\n return arguments.length ? (extent = _ == null ? null : [[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]], voronoi) : extent && [[extent[0][0], extent[0][1]], [extent[1][0], extent[1][1]]];\n };\n\n voronoi.size = function(_) {\n return arguments.length ? (extent = _ == null ? null : [[0, 0], [+_[0], +_[1]]], voronoi) : extent && [extent[1][0] - extent[0][0], extent[1][1] - extent[0][1]];\n };\n\n return voronoi;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-voronoi/src/voronoi.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/index.js": +/*!*************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/index.js ***! + \*************************************************************/ +/*! exports provided: zoom, zoomTransform, zoomIdentity */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _src_zoom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/zoom */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoom\", function() { return _src_zoom__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _src_transform__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/transform */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/transform.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomTransform\", function() { return _src_transform__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomIdentity\", function() { return _src_transform__WEBPACK_IMPORTED_MODULE_1__[\"identity\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js ***! + \***************************************************************************************/ +/*! exports provided: Color, darker, brighter, default, rgbConvert, rgb, Rgb, hslConvert, hsl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Color\", function() { return Color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"darker\", function() { return darker; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"brighter\", function() { return brighter; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return color; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbConvert\", function() { return rgbConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Rgb\", function() { return Rgb; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslConvert\", function() { return hslConvert; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return hsl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js\");\n\n\nfunction Color() {}\n\nvar darker = 0.7;\nvar brighter = 1 / darker;\n\nvar reI = \"\\\\s*([+-]?\\\\d+)\\\\s*\",\n reN = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)\\\\s*\",\n reP = \"\\\\s*([+-]?\\\\d*\\\\.?\\\\d+(?:[eE][+-]?\\\\d+)?)%\\\\s*\",\n reHex3 = /^#([0-9a-f]{3})$/,\n reHex6 = /^#([0-9a-f]{6})$/,\n reRgbInteger = new RegExp(\"^rgb\\\\(\" + [reI, reI, reI] + \"\\\\)$\"),\n reRgbPercent = new RegExp(\"^rgb\\\\(\" + [reP, reP, reP] + \"\\\\)$\"),\n reRgbaInteger = new RegExp(\"^rgba\\\\(\" + [reI, reI, reI, reN] + \"\\\\)$\"),\n reRgbaPercent = new RegExp(\"^rgba\\\\(\" + [reP, reP, reP, reN] + \"\\\\)$\"),\n reHslPercent = new RegExp(\"^hsl\\\\(\" + [reN, reP, reP] + \"\\\\)$\"),\n reHslaPercent = new RegExp(\"^hsla\\\\(\" + [reN, reP, reP, reN] + \"\\\\)$\");\n\nvar named = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32\n};\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Color, color, {\n displayable: function() {\n return this.rgb().displayable();\n },\n hex: function() {\n return this.rgb().hex();\n },\n toString: function() {\n return this.rgb() + \"\";\n }\n});\n\nfunction color(format) {\n var m;\n format = (format + \"\").trim().toLowerCase();\n return (m = reHex3.exec(format)) ? (m = parseInt(m[1], 16), new Rgb((m >> 8 & 0xf) | (m >> 4 & 0x0f0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1)) // #f00\n : (m = reHex6.exec(format)) ? rgbn(parseInt(m[1], 16)) // #ff0000\n : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)\n : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)\n : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)\n : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)\n : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)\n : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)\n : named.hasOwnProperty(format) ? rgbn(named[format])\n : format === \"transparent\" ? new Rgb(NaN, NaN, NaN, 0)\n : null;\n}\n\nfunction rgbn(n) {\n return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);\n}\n\nfunction rgba(r, g, b, a) {\n if (a <= 0) r = g = b = NaN;\n return new Rgb(r, g, b, a);\n}\n\nfunction rgbConvert(o) {\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Rgb;\n o = o.rgb();\n return new Rgb(o.r, o.g, o.b, o.opacity);\n}\n\nfunction rgb(r, g, b, opacity) {\n return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);\n}\n\nfunction Rgb(r, g, b, opacity) {\n this.r = +r;\n this.g = +g;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Rgb, rgb, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);\n },\n rgb: function() {\n return this;\n },\n displayable: function() {\n return (0 <= this.r && this.r <= 255)\n && (0 <= this.g && this.g <= 255)\n && (0 <= this.b && this.b <= 255)\n && (0 <= this.opacity && this.opacity <= 1);\n },\n hex: function() {\n return \"#\" + hex(this.r) + hex(this.g) + hex(this.b);\n },\n toString: function() {\n var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));\n return (a === 1 ? \"rgb(\" : \"rgba(\")\n + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + \", \"\n + Math.max(0, Math.min(255, Math.round(this.b) || 0))\n + (a === 1 ? \")\" : \", \" + a + \")\");\n }\n}));\n\nfunction hex(value) {\n value = Math.max(0, Math.min(255, Math.round(value) || 0));\n return (value < 16 ? \"0\" : \"\") + value.toString(16);\n}\n\nfunction hsla(h, s, l, a) {\n if (a <= 0) h = s = l = NaN;\n else if (l <= 0 || l >= 1) h = s = NaN;\n else if (s <= 0) h = NaN;\n return new Hsl(h, s, l, a);\n}\n\nfunction hslConvert(o) {\n if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof Color)) o = color(o);\n if (!o) return new Hsl;\n if (o instanceof Hsl) return o;\n o = o.rgb();\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n min = Math.min(r, g, b),\n max = Math.max(r, g, b),\n h = NaN,\n s = max - min,\n l = (max + min) / 2;\n if (s) {\n if (r === max) h = (g - b) / s + (g < b) * 6;\n else if (g === max) h = (b - r) / s + 2;\n else h = (r - g) / s + 4;\n s /= l < 0.5 ? max + min : 2 - max - min;\n h *= 60;\n } else {\n s = l > 0 && l < 1 ? 0 : h;\n }\n return new Hsl(h, s, l, o.opacity);\n}\n\nfunction hsl(h, s, l, opacity) {\n return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hsl(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hsl, hsl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(Color, {\n brighter: function(k) {\n k = k == null ? brighter : Math.pow(brighter, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? darker : Math.pow(darker, k);\n return new Hsl(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = this.h % 360 + (this.h < 0) * 360,\n s = isNaN(h) || isNaN(this.s) ? 0 : this.s,\n l = this.l,\n m2 = l + (l < 0.5 ? l : 1 - l) * s,\n m1 = 2 * l - m2;\n return new Rgb(\n hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),\n hsl2rgb(h, m1, m2),\n hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),\n this.opacity\n );\n },\n displayable: function() {\n return (0 <= this.s && this.s <= 1 || isNaN(this.s))\n && (0 <= this.l && this.l <= 1)\n && (0 <= this.opacity && this.opacity <= 1);\n }\n}));\n\n/* From FvD 13.37, CSS Color Module Level 3 */\nfunction hsl2rgb(h, m1, m2) {\n return (h < 60 ? m1 + (m2 - m1) * h / 60\n : h < 180 ? m2\n : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60\n : m1) * 255;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/cubehelix.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/cubehelix.js ***! + \*******************************************************************************************/ +/*! exports provided: default, Cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return cubehelix; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Cubehelix\", function() { return Cubehelix; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/math.js\");\n\n\n\n\nvar A = -0.14861,\n B = +1.78277,\n C = -0.29227,\n D = -0.90649,\n E = +1.97294,\n ED = E * D,\n EB = E * B,\n BC_DA = B * C - D * A;\n\nfunction cubehelixConvert(o) {\n if (o instanceof Cubehelix) return new Cubehelix(o.h, o.s, o.l, o.opacity);\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = o.r / 255,\n g = o.g / 255,\n b = o.b / 255,\n l = (BC_DA * b + ED * r - EB * g) / (BC_DA + ED - EB),\n bl = b - l,\n k = (E * (g - l) - C * bl) / D,\n s = Math.sqrt(k * k + bl * bl) / (E * l * (1 - l)), // NaN if l=0 or l=1\n h = s ? Math.atan2(k, bl) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"] - 120 : NaN;\n return new Cubehelix(h < 0 ? h + 360 : h, s, l, o.opacity);\n}\n\nfunction cubehelix(h, s, l, opacity) {\n return arguments.length === 1 ? cubehelixConvert(h) : new Cubehelix(h, s, l, opacity == null ? 1 : opacity);\n}\n\nfunction Cubehelix(h, s, l, opacity) {\n this.h = +h;\n this.s = +s;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Cubehelix, cubehelix, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"brighter\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n darker: function(k) {\n k = k == null ? _color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"] : Math.pow(_color__WEBPACK_IMPORTED_MODULE_1__[\"darker\"], k);\n return new Cubehelix(this.h, this.s, this.l * k, this.opacity);\n },\n rgb: function() {\n var h = isNaN(this.h) ? 0 : (this.h + 120) * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"],\n l = +this.l,\n a = isNaN(this.s) ? 0 : this.s * l * (1 - l),\n cosh = Math.cos(h),\n sinh = Math.sin(h);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n 255 * (l + a * (A * cosh + B * sinh)),\n 255 * (l + a * (C * cosh + D * sinh)),\n 255 * (l + a * (E * cosh)),\n this.opacity\n );\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js ***! + \****************************************************************************************/ +/*! exports provided: default, extend */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"extend\", function() { return extend; });\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(constructor, factory, prototype) {\n constructor.prototype = factory.prototype = prototype;\n prototype.constructor = constructor;\n});\n\nfunction extend(parent, definition) {\n var prototype = Object.create(parent.prototype);\n for (var key in definition) prototype[key] = definition[key];\n return prototype;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: color, rgb, hsl, lab, hcl, lch, gray, cubehelix */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return _color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"lch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return _lab__WEBPACK_IMPORTED_MODULE_1__[\"gray\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/lab.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/lab.js ***! + \*************************************************************************************/ +/*! exports provided: gray, default, Lab, lch, hcl, Hcl */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gray\", function() { return gray; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Lab\", function() { return Lab; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"lch\", function() { return lch; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return hcl; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Hcl\", function() { return Hcl; });\n/* harmony import */ var _define__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./define */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/define.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/color.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./math */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/math.js\");\n\n\n\n\n// https://beta.observablehq.com/@mbostock/lab-and-rgb\nvar K = 18,\n Xn = 0.96422,\n Yn = 1,\n Zn = 0.82521,\n t0 = 4 / 29,\n t1 = 6 / 29,\n t2 = 3 * t1 * t1,\n t3 = t1 * t1 * t1;\n\nfunction labConvert(o) {\n if (o instanceof Lab) return new Lab(o.l, o.a, o.b, o.opacity);\n if (o instanceof Hcl) {\n if (isNaN(o.h)) return new Lab(o.l, 0, 0, o.opacity);\n var h = o.h * _math__WEBPACK_IMPORTED_MODULE_2__[\"deg2rad\"];\n return new Lab(o.l, Math.cos(h) * o.c, Math.sin(h) * o.c, o.opacity);\n }\n if (!(o instanceof _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"])) o = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"rgbConvert\"])(o);\n var r = rgb2lrgb(o.r),\n g = rgb2lrgb(o.g),\n b = rgb2lrgb(o.b),\n y = xyz2lab((0.2225045 * r + 0.7168786 * g + 0.0606169 * b) / Yn), x, z;\n if (r === g && g === b) x = z = y; else {\n x = xyz2lab((0.4360747 * r + 0.3850649 * g + 0.1430804 * b) / Xn);\n z = xyz2lab((0.0139322 * r + 0.0971045 * g + 0.7141733 * b) / Zn);\n }\n return new Lab(116 * y - 16, 500 * (x - y), 200 * (y - z), o.opacity);\n}\n\nfunction gray(l, opacity) {\n return new Lab(l, 0, 0, opacity == null ? 1 : opacity);\n}\n\nfunction lab(l, a, b, opacity) {\n return arguments.length === 1 ? labConvert(l) : new Lab(l, a, b, opacity == null ? 1 : opacity);\n}\n\nfunction Lab(l, a, b, opacity) {\n this.l = +l;\n this.a = +a;\n this.b = +b;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Lab, lab, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Lab(this.l + K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n darker: function(k) {\n return new Lab(this.l - K * (k == null ? 1 : k), this.a, this.b, this.opacity);\n },\n rgb: function() {\n var y = (this.l + 16) / 116,\n x = isNaN(this.a) ? y : y + this.a / 500,\n z = isNaN(this.b) ? y : y - this.b / 200;\n x = Xn * lab2xyz(x);\n y = Yn * lab2xyz(y);\n z = Zn * lab2xyz(z);\n return new _color__WEBPACK_IMPORTED_MODULE_1__[\"Rgb\"](\n lrgb2rgb( 3.1338561 * x - 1.6168667 * y - 0.4906146 * z),\n lrgb2rgb(-0.9787684 * x + 1.9161415 * y + 0.0334540 * z),\n lrgb2rgb( 0.0719453 * x - 0.2289914 * y + 1.4052427 * z),\n this.opacity\n );\n }\n}));\n\nfunction xyz2lab(t) {\n return t > t3 ? Math.pow(t, 1 / 3) : t / t2 + t0;\n}\n\nfunction lab2xyz(t) {\n return t > t1 ? t * t * t : t2 * (t - t0);\n}\n\nfunction lrgb2rgb(x) {\n return 255 * (x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055);\n}\n\nfunction rgb2lrgb(x) {\n return (x /= 255) <= 0.04045 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4);\n}\n\nfunction hclConvert(o) {\n if (o instanceof Hcl) return new Hcl(o.h, o.c, o.l, o.opacity);\n if (!(o instanceof Lab)) o = labConvert(o);\n if (o.a === 0 && o.b === 0) return new Hcl(NaN, 0, o.l, o.opacity);\n var h = Math.atan2(o.b, o.a) * _math__WEBPACK_IMPORTED_MODULE_2__[\"rad2deg\"];\n return new Hcl(h < 0 ? h + 360 : h, Math.sqrt(o.a * o.a + o.b * o.b), o.l, o.opacity);\n}\n\nfunction lch(l, c, h, opacity) {\n return arguments.length === 1 ? hclConvert(l) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction hcl(h, c, l, opacity) {\n return arguments.length === 1 ? hclConvert(h) : new Hcl(h, c, l, opacity == null ? 1 : opacity);\n}\n\nfunction Hcl(h, c, l, opacity) {\n this.h = +h;\n this.c = +c;\n this.l = +l;\n this.opacity = +opacity;\n}\n\nObject(_define__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(Hcl, hcl, Object(_define__WEBPACK_IMPORTED_MODULE_0__[\"extend\"])(_color__WEBPACK_IMPORTED_MODULE_1__[\"Color\"], {\n brighter: function(k) {\n return new Hcl(this.h, this.c, this.l + K * (k == null ? 1 : k), this.opacity);\n },\n darker: function(k) {\n return new Hcl(this.h, this.c, this.l - K * (k == null ? 1 : k), this.opacity);\n },\n rgb: function() {\n return labConvert(this).rgb();\n }\n}));\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/math.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/math.js ***! + \**************************************************************************************/ +/*! exports provided: deg2rad, rad2deg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"deg2rad\", function() { return deg2rad; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rad2deg\", function() { return rad2deg; });\nvar deg2rad = Math.PI / 180;\nvar rad2deg = 180 / Math.PI;\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/math.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/dispatch.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/dispatch.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar noop = {value: function() {}};\n\nfunction dispatch() {\n for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {\n if (!(t = arguments[i] + \"\") || (t in _)) throw new Error(\"illegal type: \" + t);\n _[t] = [];\n }\n return new Dispatch(_);\n}\n\nfunction Dispatch(_) {\n this._ = _;\n}\n\nfunction parseTypenames(typenames, types) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n if (t && !types.hasOwnProperty(t)) throw new Error(\"unknown type: \" + t);\n return {type: t, name: name};\n });\n}\n\nDispatch.prototype = dispatch.prototype = {\n constructor: Dispatch,\n on: function(typename, callback) {\n var _ = this._,\n T = parseTypenames(typename + \"\", _),\n t,\n i = -1,\n n = T.length;\n\n // If no callback was specified, return the callback of the given type and name.\n if (arguments.length < 2) {\n while (++i < n) if ((t = (typename = T[i]).type) && (t = get(_[t], typename.name))) return t;\n return;\n }\n\n // If a type was specified, set the callback for the given type and name.\n // Otherwise, if a null callback was specified, remove callbacks of the given name.\n if (callback != null && typeof callback !== \"function\") throw new Error(\"invalid callback: \" + callback);\n while (++i < n) {\n if (t = (typename = T[i]).type) _[t] = set(_[t], typename.name, callback);\n else if (callback == null) for (t in _) _[t] = set(_[t], typename.name, null);\n }\n\n return this;\n },\n copy: function() {\n var copy = {}, _ = this._;\n for (var t in _) copy[t] = _[t].slice();\n return new Dispatch(copy);\n },\n call: function(type, that) {\n if ((n = arguments.length - 2) > 0) for (var args = new Array(n), i = 0, n, t; i < n; ++i) args[i] = arguments[i + 2];\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n },\n apply: function(type, that, args) {\n if (!this._.hasOwnProperty(type)) throw new Error(\"unknown type: \" + type);\n for (var t = this._[type], i = 0, n = t.length; i < n; ++i) t[i].value.apply(that, args);\n }\n};\n\nfunction get(type, name) {\n for (var i = 0, n = type.length, c; i < n; ++i) {\n if ((c = type[i]).name === name) {\n return c.value;\n }\n }\n}\n\nfunction set(type, name, callback) {\n for (var i = 0, n = type.length; i < n; ++i) {\n if (type[i].name === name) {\n type[i] = noop, type = type.slice(0, i).concat(type.slice(i + 1));\n break;\n }\n }\n if (callback != null) type.push({name: name, value: callback});\n return type;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (dispatch);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js ***! + \******************************************************************************************/ +/*! exports provided: dispatch */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/dispatch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return _dispatch__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/constant.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/constant.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/drag.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/drag.js ***! + \*************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/nodrag.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/noevent.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./event */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/event.js\");\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].button;\n}\n\nfunction defaultContainer() {\n return this.parentNode;\n}\n\nfunction defaultSubject(d) {\n return d == null ? {x: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].x, y: d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].y} : d;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n container = defaultContainer,\n subject = defaultSubject,\n touchable = defaultTouchable,\n gestures = {},\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"drag\", \"end\"),\n active = 0,\n mousedownx,\n mousedowny,\n mousemoving,\n touchending,\n clickDistance2 = 0;\n\n function drag(selection) {\n selection\n .on(\"mousedown.drag\", mousedowned)\n .filter(touchable)\n .on(\"touchstart.drag\", touchstarted)\n .on(\"touchmove.drag\", touchmoved)\n .on(\"touchend.drag touchcancel.drag\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var gesture = beforestart(\"mouse\", container.apply(this, arguments), d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"mouse\"], this, arguments);\n if (!gesture) return;\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag\", mousemoved, true).on(\"mouseup.drag\", mouseupped, true);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n mousemoving = false;\n mousedownx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX;\n mousedowny = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY;\n gesture(\"start\");\n }\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n if (!mousemoving) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientX - mousedownx, dy = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].clientY - mousedowny;\n mousemoving = dx * dx + dy * dy > clickDistance2;\n }\n gestures.mouse(\"drag\");\n }\n\n function mouseupped() {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view).on(\"mousemove.drag mouseup.drag\", null);\n Object(_nodrag__WEBPACK_IMPORTED_MODULE_2__[\"yesdrag\"])(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].view, mousemoving);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gestures.mouse(\"end\");\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n c = container.apply(this, arguments),\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = beforestart(touches[i].identifier, c, d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"touch\"], this, arguments)) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"start\");\n }\n }\n }\n\n function touchmoved() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"default\"])();\n gesture(\"drag\");\n }\n }\n }\n\n function touchended() {\n var touches = d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].changedTouches,\n n = touches.length, i, gesture;\n\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!\n for (i = 0; i < n; ++i) {\n if (gesture = gestures[touches[i].identifier]) {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_3__[\"nopropagation\"])();\n gesture(\"end\");\n }\n }\n }\n\n function beforestart(id, container, point, that, args) {\n var p = point(container, id), s, dx, dy,\n sublisteners = listeners.copy();\n\n if (!Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, \"beforestart\", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {\n if ((d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"event\"].subject = s = subject.apply(that, args)) == null) return false;\n dx = s.x - p[0] || 0;\n dy = s.y - p[1] || 0;\n return true;\n })) return;\n\n return function gesture(type) {\n var p0 = p, n;\n switch (type) {\n case \"start\": gestures[id] = gesture, n = active++; break;\n case \"end\": delete gestures[id], --active; // nobreak\n case \"drag\": p = point(container, id), n = active; break;\n }\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_5__[\"default\"](drag, type, s, id, n, p[0] + dx, p[1] + dy, p[0] - p0[0], p[1] - p0[1], sublisteners), sublisteners.apply, sublisteners, [type, that, args]);\n };\n }\n\n drag.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : filter;\n };\n\n drag.container = function(_) {\n return arguments.length ? (container = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : container;\n };\n\n drag.subject = function(_) {\n return arguments.length ? (subject = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(_), drag) : subject;\n };\n\n drag.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(!!_), drag) : touchable;\n };\n\n drag.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? drag : value;\n };\n\n drag.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);\n };\n\n return drag;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/drag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/event.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/event.js ***! + \**************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return DragEvent; });\nfunction DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {\n this.target = target;\n this.type = type;\n this.subject = subject;\n this.identifier = id;\n this.active = active;\n this.x = x;\n this.y = y;\n this.dx = dx;\n this.dy = dy;\n this._ = dispatch;\n}\n\nDragEvent.prototype.on = function() {\n var value = this._.on.apply(this._, arguments);\n return value === this._ ? this : value;\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/index.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/index.js ***! + \**************************************************************************************/ +/*! exports provided: drag, dragDisable, dragEnable */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _drag__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./drag */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/drag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return _drag__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _nodrag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./nodrag */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/nodrag.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return _nodrag__WEBPACK_IMPORTED_MODULE_1__[\"yesdrag\"]; });\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/nodrag.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/nodrag.js ***! + \***************************************************************************************/ +/*! exports provided: default, yesdrag */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"yesdrag\", function() { return yesdrag; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/noevent.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(view) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n } else {\n root.__noselect = root.style.MozUserSelect;\n root.style.MozUserSelect = \"none\";\n }\n});\n\nfunction yesdrag(view, noclick) {\n var root = view.document.documentElement,\n selection = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"select\"])(view).on(\"dragstart.drag\", null);\n if (noclick) {\n selection.on(\"click.drag\", _noevent__WEBPACK_IMPORTED_MODULE_1__[\"default\"], true);\n setTimeout(function() { selection.on(\"click.drag\", null); }, 0);\n }\n if (\"onselectstart\" in root) {\n selection.on(\"selectstart.drag\", null);\n } else {\n root.style.MozUserSelect = root.__noselect;\n delete root.__noselect;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/nodrag.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/noevent.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/noevent.js ***! + \****************************************************************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/back.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/back.js ***! + \*************************************************************************************/ +/*! exports provided: backIn, backOut, backInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backIn\", function() { return backIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backOut\", function() { return backOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"backInOut\", function() { return backInOut; });\nvar overshoot = 1.70158;\n\nvar backIn = (function custom(s) {\n s = +s;\n\n function backIn(t) {\n return t * t * ((s + 1) * t - s);\n }\n\n backIn.overshoot = custom;\n\n return backIn;\n})(overshoot);\n\nvar backOut = (function custom(s) {\n s = +s;\n\n function backOut(t) {\n return --t * t * ((s + 1) * t + s) + 1;\n }\n\n backOut.overshoot = custom;\n\n return backOut;\n})(overshoot);\n\nvar backInOut = (function custom(s) {\n s = +s;\n\n function backInOut(t) {\n return ((t *= 2) < 1 ? t * t * ((s + 1) * t - s) : (t -= 2) * t * ((s + 1) * t + s) + 2) / 2;\n }\n\n backInOut.overshoot = custom;\n\n return backInOut;\n})(overshoot);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/back.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/bounce.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/bounce.js ***! + \***************************************************************************************/ +/*! exports provided: bounceIn, bounceOut, bounceInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceIn\", function() { return bounceIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceOut\", function() { return bounceOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"bounceInOut\", function() { return bounceInOut; });\nvar b1 = 4 / 11,\n b2 = 6 / 11,\n b3 = 8 / 11,\n b4 = 3 / 4,\n b5 = 9 / 11,\n b6 = 10 / 11,\n b7 = 15 / 16,\n b8 = 21 / 22,\n b9 = 63 / 64,\n b0 = 1 / b1 / b1;\n\nfunction bounceIn(t) {\n return 1 - bounceOut(1 - t);\n}\n\nfunction bounceOut(t) {\n return (t = +t) < b1 ? b0 * t * t : t < b3 ? b0 * (t -= b2) * t + b4 : t < b6 ? b0 * (t -= b5) * t + b7 : b0 * (t -= b8) * t + b9;\n}\n\nfunction bounceInOut(t) {\n return ((t *= 2) <= 1 ? 1 - bounceOut(1 - t) : bounceOut(t - 1) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/bounce.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/circle.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/circle.js ***! + \***************************************************************************************/ +/*! exports provided: circleIn, circleOut, circleInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleIn\", function() { return circleIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleOut\", function() { return circleOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"circleInOut\", function() { return circleInOut; });\nfunction circleIn(t) {\n return 1 - Math.sqrt(1 - t * t);\n}\n\nfunction circleOut(t) {\n return Math.sqrt(1 - --t * t);\n}\n\nfunction circleInOut(t) {\n return ((t *= 2) <= 1 ? 1 - Math.sqrt(1 - t * t) : Math.sqrt(1 - (t -= 2) * t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/circle.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/cubic.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/cubic.js ***! + \**************************************************************************************/ +/*! exports provided: cubicIn, cubicOut, cubicInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicIn\", function() { return cubicIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicOut\", function() { return cubicOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubicInOut\", function() { return cubicInOut; });\nfunction cubicIn(t) {\n return t * t * t;\n}\n\nfunction cubicOut(t) {\n return --t * t * t + 1;\n}\n\nfunction cubicInOut(t) {\n return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/cubic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/elastic.js": +/*!****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/elastic.js ***! + \****************************************************************************************/ +/*! exports provided: elasticIn, elasticOut, elasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticIn\", function() { return elasticIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticOut\", function() { return elasticOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"elasticInOut\", function() { return elasticInOut; });\nvar tau = 2 * Math.PI,\n amplitude = 1,\n period = 0.3;\n\nvar elasticIn = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticIn(t) {\n return a * Math.pow(2, 10 * --t) * Math.sin((s - t) / p);\n }\n\n elasticIn.amplitude = function(a) { return custom(a, p * tau); };\n elasticIn.period = function(p) { return custom(a, p); };\n\n return elasticIn;\n})(amplitude, period);\n\nvar elasticOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticOut(t) {\n return 1 - a * Math.pow(2, -10 * (t = +t)) * Math.sin((t + s) / p);\n }\n\n elasticOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticOut.period = function(p) { return custom(a, p); };\n\n return elasticOut;\n})(amplitude, period);\n\nvar elasticInOut = (function custom(a, p) {\n var s = Math.asin(1 / (a = Math.max(1, a))) * (p /= tau);\n\n function elasticInOut(t) {\n return ((t = t * 2 - 1) < 0\n ? a * Math.pow(2, 10 * t) * Math.sin((s - t) / p)\n : 2 - a * Math.pow(2, -10 * t) * Math.sin((s + t) / p)) / 2;\n }\n\n elasticInOut.amplitude = function(a) { return custom(a, p * tau); };\n elasticInOut.period = function(p) { return custom(a, p); };\n\n return elasticInOut;\n})(amplitude, period);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/elastic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/exp.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/exp.js ***! + \************************************************************************************/ +/*! exports provided: expIn, expOut, expInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expIn\", function() { return expIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expOut\", function() { return expOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"expInOut\", function() { return expInOut; });\nfunction expIn(t) {\n return Math.pow(2, 10 * t - 10);\n}\n\nfunction expOut(t) {\n return 1 - Math.pow(2, -10 * t);\n}\n\nfunction expInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(2, 10 * t - 10) : 2 - Math.pow(2, 10 - 10 * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/exp.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/index.js": +/*!**************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/index.js ***! + \**************************************************************************************/ +/*! exports provided: easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _linear__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./linear */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/linear.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return _linear__WEBPACK_IMPORTED_MODULE_0__[\"linear\"]; });\n\n/* harmony import */ var _quad__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quad */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/quad.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return _quad__WEBPACK_IMPORTED_MODULE_1__[\"quadInOut\"]; });\n\n/* harmony import */ var _cubic__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./cubic */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/cubic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return _cubic__WEBPACK_IMPORTED_MODULE_2__[\"cubicInOut\"]; });\n\n/* harmony import */ var _poly__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./poly */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/poly.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return _poly__WEBPACK_IMPORTED_MODULE_3__[\"polyInOut\"]; });\n\n/* harmony import */ var _sin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./sin */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/sin.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return _sin__WEBPACK_IMPORTED_MODULE_4__[\"sinInOut\"]; });\n\n/* harmony import */ var _exp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exp */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/exp.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return _exp__WEBPACK_IMPORTED_MODULE_5__[\"expInOut\"]; });\n\n/* harmony import */ var _circle__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./circle */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/circle.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return _circle__WEBPACK_IMPORTED_MODULE_6__[\"circleInOut\"]; });\n\n/* harmony import */ var _bounce__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./bounce */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/bounce.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return _bounce__WEBPACK_IMPORTED_MODULE_7__[\"bounceInOut\"]; });\n\n/* harmony import */ var _back__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./back */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/back.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return _back__WEBPACK_IMPORTED_MODULE_8__[\"backInOut\"]; });\n\n/* harmony import */ var _elastic__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./elastic */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/elastic.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return _elastic__WEBPACK_IMPORTED_MODULE_9__[\"elasticInOut\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/linear.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/linear.js ***! + \***************************************************************************************/ +/*! exports provided: linear */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"linear\", function() { return linear; });\nfunction linear(t) {\n return +t;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/linear.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/poly.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/poly.js ***! + \*************************************************************************************/ +/*! exports provided: polyIn, polyOut, polyInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyIn\", function() { return polyIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyOut\", function() { return polyOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"polyInOut\", function() { return polyInOut; });\nvar exponent = 3;\n\nvar polyIn = (function custom(e) {\n e = +e;\n\n function polyIn(t) {\n return Math.pow(t, e);\n }\n\n polyIn.exponent = custom;\n\n return polyIn;\n})(exponent);\n\nvar polyOut = (function custom(e) {\n e = +e;\n\n function polyOut(t) {\n return 1 - Math.pow(1 - t, e);\n }\n\n polyOut.exponent = custom;\n\n return polyOut;\n})(exponent);\n\nvar polyInOut = (function custom(e) {\n e = +e;\n\n function polyInOut(t) {\n return ((t *= 2) <= 1 ? Math.pow(t, e) : 2 - Math.pow(2 - t, e)) / 2;\n }\n\n polyInOut.exponent = custom;\n\n return polyInOut;\n})(exponent);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/poly.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/quad.js": +/*!*************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/quad.js ***! + \*************************************************************************************/ +/*! exports provided: quadIn, quadOut, quadInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadIn\", function() { return quadIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadOut\", function() { return quadOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"quadInOut\", function() { return quadInOut; });\nfunction quadIn(t) {\n return t * t;\n}\n\nfunction quadOut(t) {\n return t * (2 - t);\n}\n\nfunction quadInOut(t) {\n return ((t *= 2) <= 1 ? t * t : --t * (2 - t) + 1) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/quad.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/sin.js": +/*!************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/sin.js ***! + \************************************************************************************/ +/*! exports provided: sinIn, sinOut, sinInOut */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinIn\", function() { return sinIn; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinOut\", function() { return sinOut; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"sinInOut\", function() { return sinInOut; });\nvar pi = Math.PI,\n halfPi = pi / 2;\n\nfunction sinIn(t) {\n return 1 - Math.cos(t * halfPi);\n}\n\nfunction sinOut(t) {\n return Math.sin(t * halfPi);\n}\n\nfunction sinInOut(t) {\n return (1 - Math.cos(pi * t)) / 2;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/sin.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/array.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/array.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var nb = b ? b.length : 0,\n na = a ? Math.min(nb, a.length) : 0,\n x = new Array(na),\n c = new Array(nb),\n i;\n\n for (i = 0; i < na; ++i) x[i] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[i], b[i]);\n for (; i < nb; ++i) c[i] = b[i];\n\n return function(t) {\n for (i = 0; i < na; ++i) c[i] = x[i](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/array.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js ***! + \*********************************************************************************************/ +/*! exports provided: basis, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"basis\", function() { return basis; });\nfunction basis(t1, v0, v1, v2, v3) {\n var t2 = t1 * t1, t3 = t2 * t1;\n return ((1 - 3 * t1 + 3 * t2 - t3) * v0\n + (4 - 6 * t2 + 3 * t3) * v1\n + (1 + 3 * t1 + 3 * t2 - 3 * t3) * v2\n + t3 * v3) / 6;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length - 1;\n return function(t) {\n var i = t <= 0 ? (t = 0) : t >= 1 ? (t = 1, n - 1) : Math.floor(t * n),\n v1 = values[i],\n v2 = values[i + 1],\n v0 = i > 0 ? values[i - 1] : 2 * v1 - v2,\n v3 = i < n - 1 ? values[i + 2] : 2 * v2 - v1;\n return basis((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basisClosed.js": +/*!***************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basisClosed.js ***! + \***************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(values) {\n var n = values.length;\n return function(t) {\n var i = Math.floor(((t %= 1) < 0 ? ++t : t) * n),\n v0 = values[(i + n - 1) % n],\n v1 = values[i % n],\n v2 = values[(i + 1) % n],\n v3 = values[(i + 2) % n];\n return Object(_basis__WEBPACK_IMPORTED_MODULE_0__[\"basis\"])((t - i / n) * n, v0, v1, v2, v3);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basisClosed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js ***! + \*********************************************************************************************/ +/*! exports provided: hue, gamma, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hue\", function() { return hue; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"gamma\", function() { return gamma; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return nogamma; });\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/constant.js\");\n\n\nfunction linear(a, d) {\n return function(t) {\n return a + t * d;\n };\n}\n\nfunction exponential(a, b, y) {\n return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {\n return Math.pow(a + t * b, y);\n };\n}\n\nfunction hue(a, b) {\n var d = b - a;\n return d ? linear(a, d > 180 || d < -180 ? d - 360 * Math.round(d / 360) : d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\nfunction gamma(y) {\n return (y = +y) === 1 ? nogamma : function(a, b) {\n return b - a ? exponential(a, b, y) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n };\n}\n\nfunction nogamma(a, b) {\n var d = b - a;\n return d ? linear(a, d) : Object(_constant__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(isNaN(a) ? b : a);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/constant.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/constant.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/cubehelix.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/cubehelix.js ***! + \*************************************************************************************************/ +/*! exports provided: default, cubehelixLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"cubehelixLong\", function() { return cubehelixLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction cubehelix(hue) {\n return (function cubehelixGamma(y) {\n y = +y;\n\n function cubehelix(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"cubehelix\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(Math.pow(t, y));\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n cubehelix.gamma = cubehelixGamma;\n\n return cubehelix;\n })(1);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar cubehelixLong = cubehelix(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/cubehelix.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/date.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/date.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var d = new Date;\n return a = +a, b -= a, function(t) {\n return d.setTime(a + b * t), d;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/date.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/discrete.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/discrete.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(range) {\n var n = range.length;\n return function(t) {\n return range[Math.max(0, Math.min(n - 1, Math.floor(t * n)))];\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/discrete.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hcl.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hcl.js ***! + \*******************************************************************************************/ +/*! exports provided: default, hclLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hclLong\", function() { return hclLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hcl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hcl\"])(end)).h),\n c = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.c, end.c),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.c = c(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hclLong = hcl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hcl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hsl.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hsl.js ***! + \*******************************************************************************************/ +/*! exports provided: default, hslLong */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"hslLong\", function() { return hslLong; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction hsl(hue) {\n return function(start, end) {\n var h = hue((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(start)).h, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"hsl\"])(end)).h),\n s = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.s, end.s),\n l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.l, end.l),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.h = h(t);\n start.s = s(t);\n start.l = l(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"hue\"]));\nvar hslLong = hsl(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hsl.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hue.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hue.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = Object(_color__WEBPACK_IMPORTED_MODULE_0__[\"hue\"])(+a, +b);\n return function(t) {\n var x = i(t);\n return x - 360 * Math.floor(x / 360);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hue.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js ***! + \*********************************************************************************************/ +/*! exports provided: interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateDiscrete, interpolateHue, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, piecewise, quantize */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return _value__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/array.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return _array__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return _basis__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return _basisClosed__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/date.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return _date__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _discrete__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./discrete */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/discrete.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDiscrete\", function() { return _discrete__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _hue__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./hue */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hue.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHue\", function() { return _hue__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return _number__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/object.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return _object__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _round__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./round */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/round.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return _round__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/string.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return _string__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _transform_index__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./transform/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return _transform_index__WEBPACK_IMPORTED_MODULE_11__[\"interpolateTransformSvg\"]; });\n\n/* harmony import */ var _zoom__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./zoom */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/zoom.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return _zoom__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return _rgb__WEBPACK_IMPORTED_MODULE_13__[\"rgbBasisClosed\"]; });\n\n/* harmony import */ var _hsl__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./hsl */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hsl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return _hsl__WEBPACK_IMPORTED_MODULE_14__[\"hslLong\"]; });\n\n/* harmony import */ var _lab__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./lab */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/lab.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return _lab__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _hcl__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./hcl */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/hcl.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return _hcl__WEBPACK_IMPORTED_MODULE_16__[\"hclLong\"]; });\n\n/* harmony import */ var _cubehelix__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./cubehelix */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/cubehelix.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"default\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return _cubehelix__WEBPACK_IMPORTED_MODULE_17__[\"cubehelixLong\"]; });\n\n/* harmony import */ var _piecewise__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./piecewise */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/piecewise.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"piecewise\", function() { return _piecewise__WEBPACK_IMPORTED_MODULE_18__[\"default\"]; });\n\n/* harmony import */ var _quantize__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./quantize */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/quantize.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return _quantize__WEBPACK_IMPORTED_MODULE_19__[\"default\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/lab.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/lab.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return lab; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n\nfunction lab(start, end) {\n var l = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(start)).l, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"lab\"])(end)).l),\n a = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.a, end.a),\n b = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.l = l(t);\n start.a = a(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/lab.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return a + b * t;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/object.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/object.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _value__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./value */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var i = {},\n c = {},\n k;\n\n if (a === null || typeof a !== \"object\") a = {};\n if (b === null || typeof b !== \"object\") b = {};\n\n for (k in b) {\n if (k in a) {\n i[k] = Object(_value__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a[k], b[k]);\n } else {\n c[k] = b[k];\n }\n }\n\n return function(t) {\n for (k in i) c[k] = i[k](t);\n return c;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/object.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/piecewise.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/piecewise.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return piecewise; });\nfunction piecewise(interpolate, values) {\n var i = 0, n = values.length - 1, v = values[0], I = new Array(n < 0 ? 0 : n);\n while (i < n) I[i] = interpolate(v, v = values[++i]);\n return function(t) {\n var i = Math.max(0, Math.min(n - 1, Math.floor(t *= n)));\n return I[i](t - i);\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/piecewise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/quantize.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/quantize.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(interpolator, n) {\n var samples = new Array(n);\n for (var i = 0; i < n; ++i) samples[i] = interpolator(i / (n - 1));\n return samples;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/quantize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/rgb.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/rgb.js ***! + \*******************************************************************************************/ +/*! exports provided: default, rgbBasis, rgbBasisClosed */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasis\", function() { return rgbBasis; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"rgbBasisClosed\", function() { return rgbBasisClosed; });\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _basis__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./basis */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basis.js\");\n/* harmony import */ var _basisClosed__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./basisClosed */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/basisClosed.js\");\n/* harmony import */ var _color__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/color.js\");\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ((function rgbGamma(y) {\n var color = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"gamma\"])(y);\n\n function rgb(start, end) {\n var r = color((start = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(start)).r, (end = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(end)).r),\n g = color(start.g, end.g),\n b = color(start.b, end.b),\n opacity = Object(_color__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(start.opacity, end.opacity);\n return function(t) {\n start.r = r(t);\n start.g = g(t);\n start.b = b(t);\n start.opacity = opacity(t);\n return start + \"\";\n };\n }\n\n rgb.gamma = rgbGamma;\n\n return rgb;\n})(1));\n\nfunction rgbSpline(spline) {\n return function(colors) {\n var n = colors.length,\n r = new Array(n),\n g = new Array(n),\n b = new Array(n),\n i, color;\n for (i = 0; i < n; ++i) {\n color = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"rgb\"])(colors[i]);\n r[i] = color.r || 0;\n g[i] = color.g || 0;\n b[i] = color.b || 0;\n }\n r = spline(r);\n g = spline(g);\n b = spline(b);\n color.opacity = 1;\n return function(t) {\n color.r = r(t);\n color.g = g(t);\n color.b = b(t);\n return color + \"\";\n };\n };\n}\n\nvar rgbBasis = rgbSpline(_basis__WEBPACK_IMPORTED_MODULE_1__[\"default\"]);\nvar rgbBasisClosed = rgbSpline(_basisClosed__WEBPACK_IMPORTED_MODULE_2__[\"default\"]);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/rgb.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/round.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/round.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n return a = +a, b -= a, function(t) {\n return Math.round(a + b * t);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/round.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/string.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/string.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js\");\n\n\nvar reA = /[-+]?(?:\\d+\\.?\\d*|\\.?\\d+)(?:[eE][-+]?\\d+)?/g,\n reB = new RegExp(reA.source, \"g\");\n\nfunction zero(b) {\n return function() {\n return b;\n };\n}\n\nfunction one(b) {\n return function(t) {\n return b(t) + \"\";\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b\n am, // current match in a\n bm, // current match in b\n bs, // string preceding current number in b, if any\n i = -1, // index in s\n s = [], // string constants and placeholders\n q = []; // number interpolators\n\n // Coerce inputs to strings.\n a = a + \"\", b = b + \"\";\n\n // Interpolate pairs of numbers in a & b.\n while ((am = reA.exec(a))\n && (bm = reB.exec(b))) {\n if ((bs = bm.index) > bi) { // a string precedes the next number in b\n bs = b.slice(bi, bs);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match\n if (s[i]) s[i] += bm; // coalesce with previous string\n else s[++i] = bm;\n } else { // interpolate non-matching numbers\n s[++i] = null;\n q.push({i: i, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(am, bm)});\n }\n bi = reB.lastIndex;\n }\n\n // Add remains of b.\n if (bi < b.length) {\n bs = b.slice(bi);\n if (s[i]) s[i] += bs; // coalesce with previous string\n else s[++i] = bs;\n }\n\n // Special optimization for only a single match.\n // Otherwise, interpolate each of the numbers and rejoin the string.\n return s.length < 2 ? (q[0]\n ? one(q[0].x)\n : zero(b))\n : (b = q.length, function(t) {\n for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/string.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/decompose.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/decompose.js ***! + \***********************************************************************************************************/ +/*! exports provided: identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\nvar degrees = 180 / Math.PI;\n\nvar identity = {\n translateX: 0,\n translateY: 0,\n rotate: 0,\n skewX: 0,\n scaleX: 1,\n scaleY: 1\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b, c, d, e, f) {\n var scaleX, scaleY, skewX;\n if (scaleX = Math.sqrt(a * a + b * b)) a /= scaleX, b /= scaleX;\n if (skewX = a * c + b * d) c -= a * skewX, d -= b * skewX;\n if (scaleY = Math.sqrt(c * c + d * d)) c /= scaleY, d /= scaleY, skewX /= scaleY;\n if (a * d < b * c) a = -a, b = -b, skewX = -skewX, scaleX = -scaleX;\n return {\n translateX: e,\n translateY: f,\n rotate: Math.atan2(b, a) * degrees,\n skewX: Math.atan(skewX) * degrees,\n scaleX: scaleX,\n scaleY: scaleY\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/decompose.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/index.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/index.js ***! + \*******************************************************************************************************/ +/*! exports provided: interpolateTransformCss, interpolateTransformSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return interpolateTransformCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return interpolateTransformSvg; });\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../number */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _parse__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./parse */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/parse.js\");\n\n\n\nfunction interpolateTransform(parse, pxComma, pxParen, degParen) {\n\n function pop(s) {\n return s.length ? s.pop() + \" \" : \"\";\n }\n\n function translate(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(\"translate(\", null, pxComma, null, pxParen);\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb || yb) {\n s.push(\"translate(\" + xb + pxComma + yb + pxParen);\n }\n }\n\n function rotate(a, b, s, q) {\n if (a !== b) {\n if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path\n q.push({i: s.push(pop(s) + \"rotate(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"rotate(\" + b + degParen);\n }\n }\n\n function skewX(a, b, s, q) {\n if (a !== b) {\n q.push({i: s.push(pop(s) + \"skewX(\", null, degParen) - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(a, b)});\n } else if (b) {\n s.push(pop(s) + \"skewX(\" + b + degParen);\n }\n }\n\n function scale(xa, ya, xb, yb, s, q) {\n if (xa !== xb || ya !== yb) {\n var i = s.push(pop(s) + \"scale(\", null, \",\", null, \")\");\n q.push({i: i - 4, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(xa, xb)}, {i: i - 2, x: Object(_number__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(ya, yb)});\n } else if (xb !== 1 || yb !== 1) {\n s.push(pop(s) + \"scale(\" + xb + \",\" + yb + \")\");\n }\n }\n\n return function(a, b) {\n var s = [], // string constants and placeholders\n q = []; // number interpolators\n a = parse(a), b = parse(b);\n translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);\n rotate(a.rotate, b.rotate, s, q);\n skewX(a.skewX, b.skewX, s, q);\n scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);\n a = b = null; // gc\n return function(t) {\n var i = -1, n = q.length, o;\n while (++i < n) s[(o = q[i]).i] = o.x(t);\n return s.join(\"\");\n };\n };\n}\n\nvar interpolateTransformCss = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseCss\"], \"px, \", \"px)\", \"deg)\");\nvar interpolateTransformSvg = interpolateTransform(_parse__WEBPACK_IMPORTED_MODULE_1__[\"parseSvg\"], \", \", \")\", \")\");\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/parse.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/parse.js ***! + \*******************************************************************************************************/ +/*! exports provided: parseCss, parseSvg */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseCss\", function() { return parseCss; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"parseSvg\", function() { return parseSvg; });\n/* harmony import */ var _decompose__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./decompose */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/decompose.js\");\n\n\nvar cssNode,\n cssRoot,\n cssView,\n svgNode;\n\nfunction parseCss(value) {\n if (value === \"none\") return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!cssNode) cssNode = document.createElement(\"DIV\"), cssRoot = document.documentElement, cssView = document.defaultView;\n cssNode.style.transform = value;\n value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue(\"transform\");\n cssRoot.removeChild(cssNode);\n value = value.slice(7, -1).split(\",\");\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);\n}\n\nfunction parseSvg(value) {\n if (value == null) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n if (!svgNode) svgNode = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n svgNode.setAttribute(\"transform\", value);\n if (!(value = svgNode.transform.baseVal.consolidate())) return _decompose__WEBPACK_IMPORTED_MODULE_0__[\"identity\"];\n value = value.matrix;\n return Object(_decompose__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(value.a, value.b, value.c, value.d, value.e, value.f);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/transform/parse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var _rgb__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./rgb */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/rgb.js\");\n/* harmony import */ var _array__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./array */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/array.js\");\n/* harmony import */ var _date__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./date */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/date.js\");\n/* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./number */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/number.js\");\n/* harmony import */ var _object__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./object */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/object.js\");\n/* harmony import */ var _string__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./string */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/string.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/constant.js\");\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var t = typeof b, c;\n return b == null || t === \"boolean\" ? Object(_constant__WEBPACK_IMPORTED_MODULE_7__[\"default\"])(b)\n : (t === \"number\" ? _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"]\n : t === \"string\" ? ((c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]) : _string__WEBPACK_IMPORTED_MODULE_6__[\"default\"])\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? _rgb__WEBPACK_IMPORTED_MODULE_1__[\"default\"]\n : b instanceof Date ? _date__WEBPACK_IMPORTED_MODULE_3__[\"default\"]\n : Array.isArray(b) ? _array__WEBPACK_IMPORTED_MODULE_2__[\"default\"]\n : typeof b.valueOf !== \"function\" && typeof b.toString !== \"function\" || isNaN(b) ? _object__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n : _number__WEBPACK_IMPORTED_MODULE_4__[\"default\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/value.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/zoom.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/zoom.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar rho = Math.SQRT2,\n rho2 = 2,\n rho4 = 4,\n epsilon2 = 1e-12;\n\nfunction cosh(x) {\n return ((x = Math.exp(x)) + 1 / x) / 2;\n}\n\nfunction sinh(x) {\n return ((x = Math.exp(x)) - 1 / x) / 2;\n}\n\nfunction tanh(x) {\n return ((x = Math.exp(2 * x)) - 1) / (x + 1);\n}\n\n// p0 = [ux0, uy0, w0]\n// p1 = [ux1, uy1, w1]\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(p0, p1) {\n var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],\n ux1 = p1[0], uy1 = p1[1], w1 = p1[2],\n dx = ux1 - ux0,\n dy = uy1 - uy0,\n d2 = dx * dx + dy * dy,\n i,\n S;\n\n // Special case for u0 ≅ u1.\n if (d2 < epsilon2) {\n S = Math.log(w1 / w0) / rho;\n i = function(t) {\n return [\n ux0 + t * dx,\n uy0 + t * dy,\n w0 * Math.exp(rho * t * S)\n ];\n }\n }\n\n // General case.\n else {\n var d1 = Math.sqrt(d2),\n b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),\n b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),\n r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),\n r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);\n S = (r1 - r0) / rho;\n i = function(t) {\n var s = t * S,\n coshr0 = cosh(r0),\n u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));\n return [\n ux0 + u * dx,\n uy0 + u * dy,\n w0 * coshr0 / cosh(rho * s + r0)\n ];\n }\n }\n\n i.duration = S * 1000;\n\n return i;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/constant.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/constant.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/create.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/create.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/select.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return Object(_select__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name).call(document.documentElement));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/create.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js\");\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js\");\n\n\n\nfunction creatorInherit(name) {\n return function() {\n var document = this.ownerDocument,\n uri = this.namespaceURI;\n return uri === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"] && document.documentElement.namespaceURI === _namespaces__WEBPACK_IMPORTED_MODULE_1__[\"xhtml\"]\n ? document.createElement(name)\n : document.createElementNS(uri, name);\n };\n}\n\nfunction creatorFixed(fullname) {\n return function() {\n return this.ownerDocument.createElementNS(fullname.space, fullname.local);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return (fullname.local\n ? creatorFixed\n : creatorInherit)(fullname);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js ***! + \*******************************************************************************************/ +/*! exports provided: create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _create__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./create */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/create.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return _create__WEBPACK_IMPORTED_MODULE_0__[\"default\"]; });\n\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./creator */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return _creator__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _local__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./local */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/local.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return _local__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./matcher */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/matcher.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return _matcher__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n/* harmony import */ var _mouse__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./mouse */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/mouse.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return _mouse__WEBPACK_IMPORTED_MODULE_4__[\"default\"]; });\n\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./namespace */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return _namespace__WEBPACK_IMPORTED_MODULE_5__[\"default\"]; });\n\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return _namespaces__WEBPACK_IMPORTED_MODULE_6__[\"default\"]; });\n\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return _point__WEBPACK_IMPORTED_MODULE_7__[\"default\"]; });\n\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/select.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return _select__WEBPACK_IMPORTED_MODULE_8__[\"default\"]; });\n\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return _selectAll__WEBPACK_IMPORTED_MODULE_9__[\"default\"]; });\n\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return _selection_index__WEBPACK_IMPORTED_MODULE_10__[\"default\"]; });\n\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selector */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return _selector__WEBPACK_IMPORTED_MODULE_11__[\"default\"]; });\n\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectorAll.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return _selectorAll__WEBPACK_IMPORTED_MODULE_12__[\"default\"]; });\n\n/* harmony import */ var _selection_style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./selection/style */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/style.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return _selection_style__WEBPACK_IMPORTED_MODULE_13__[\"styleValue\"]; });\n\n/* harmony import */ var _touch__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./touch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touch.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return _touch__WEBPACK_IMPORTED_MODULE_14__[\"default\"]; });\n\n/* harmony import */ var _touches__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./touches */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touches.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return _touches__WEBPACK_IMPORTED_MODULE_15__[\"default\"]; });\n\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./window */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return _window__WEBPACK_IMPORTED_MODULE_16__[\"default\"]; });\n\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return _selection_on__WEBPACK_IMPORTED_MODULE_17__[\"customEvent\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/local.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/local.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return local; });\nvar nextId = 0;\n\nfunction local() {\n return new Local;\n}\n\nfunction Local() {\n this._ = \"@\" + (++nextId).toString(36);\n}\n\nLocal.prototype = local.prototype = {\n constructor: Local,\n get: function(node) {\n var id = this._;\n while (!(id in node)) if (!(node = node.parentNode)) return;\n return node[id];\n },\n set: function(node, value) {\n return node[this._] = value;\n },\n remove: function(node) {\n return this._ in node && delete node[this._];\n },\n toString: function() {\n return this._;\n }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/local.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/matcher.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/matcher.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nvar matcher = function(selector) {\n return function() {\n return this.matches(selector);\n };\n};\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!element.matches) {\n var vendorMatches = element.webkitMatchesSelector\n || element.msMatchesSelector\n || element.mozMatchesSelector\n || element.oMatchesSelector;\n matcher = function(selector) {\n return function() {\n return vendorMatches.call(this, selector);\n };\n };\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (matcher);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/matcher.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/mouse.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/mouse.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n var event = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])();\n if (event.changedTouches) event = event.changedTouches[0];\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, event);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/mouse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespaces__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./namespaces */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var prefix = name += \"\", i = prefix.indexOf(\":\");\n if (i >= 0 && (prefix = name.slice(0, i)) !== \"xmlns\") name = name.slice(i + 1);\n return _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"].hasOwnProperty(prefix) ? {space: _namespaces__WEBPACK_IMPORTED_MODULE_0__[\"default\"][prefix], local: name} : name;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js ***! + \************************************************************************************************/ +/*! exports provided: xhtml, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"xhtml\", function() { return xhtml; });\nvar xhtml = \"http://www.w3.org/1999/xhtml\";\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n svg: \"http://www.w3.org/2000/svg\",\n xhtml: xhtml,\n xlink: \"http://www.w3.org/1999/xlink\",\n xml: \"http://www.w3.org/XML/1998/namespace\",\n xmlns: \"http://www.w3.org/2000/xmlns/\"\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespaces.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, event) {\n var svg = node.ownerSVGElement || node;\n\n if (svg.createSVGPoint) {\n var point = svg.createSVGPoint();\n point.x = event.clientX, point.y = event.clientY;\n point = point.matrixTransform(node.getScreenCTM().inverse());\n return [point.x, point.y];\n }\n\n var rect = node.getBoundingClientRect();\n return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/select.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/select.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[document.querySelector(selector)]], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([[selector]], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectAll.js": +/*!***********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectAll.js ***! + \***********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return typeof selector === \"string\"\n ? new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([document.querySelectorAll(selector)], [document.documentElement])\n : new _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"]([selector == null ? [] : selector], _selection_index__WEBPACK_IMPORTED_MODULE_0__[\"root\"]);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/append.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/append.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n return this.select(function() {\n return this.appendChild(create.apply(this, arguments));\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/append.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/attr.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/attr.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _namespace__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../namespace */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/namespace.js\");\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, value) {\n return function() {\n this.setAttribute(name, value);\n };\n}\n\nfunction attrConstantNS(fullname, value) {\n return function() {\n this.setAttributeNS(fullname.space, fullname.local, value);\n };\n}\n\nfunction attrFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttribute(name);\n else this.setAttribute(name, v);\n };\n}\n\nfunction attrFunctionNS(fullname, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.removeAttributeNS(fullname.space, fullname.local);\n else this.setAttributeNS(fullname.space, fullname.local, v);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(_namespace__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name);\n\n if (arguments.length < 2) {\n var node = this.node();\n return fullname.local\n ? node.getAttributeNS(fullname.space, fullname.local)\n : node.getAttribute(fullname);\n }\n\n return this.each((value == null\n ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)\n : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/call.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/call.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var callback = arguments[0];\n arguments[0] = this;\n callback.apply(null, arguments);\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/call.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/classed.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/classed.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction classArray(string) {\n return string.trim().split(/^|\\s+/);\n}\n\nfunction classList(node) {\n return node.classList || new ClassList(node);\n}\n\nfunction ClassList(node) {\n this._node = node;\n this._names = classArray(node.getAttribute(\"class\") || \"\");\n}\n\nClassList.prototype = {\n add: function(name) {\n var i = this._names.indexOf(name);\n if (i < 0) {\n this._names.push(name);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n remove: function(name) {\n var i = this._names.indexOf(name);\n if (i >= 0) {\n this._names.splice(i, 1);\n this._node.setAttribute(\"class\", this._names.join(\" \"));\n }\n },\n contains: function(name) {\n return this._names.indexOf(name) >= 0;\n }\n};\n\nfunction classedAdd(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.add(names[i]);\n}\n\nfunction classedRemove(node, names) {\n var list = classList(node), i = -1, n = names.length;\n while (++i < n) list.remove(names[i]);\n}\n\nfunction classedTrue(names) {\n return function() {\n classedAdd(this, names);\n };\n}\n\nfunction classedFalse(names) {\n return function() {\n classedRemove(this, names);\n };\n}\n\nfunction classedFunction(names, value) {\n return function() {\n (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var names = classArray(name + \"\");\n\n if (arguments.length < 2) {\n var list = classList(this.node()), i = -1, n = names.length;\n while (++i < n) if (!list.contains(names[i])) return false;\n return true;\n }\n\n return this.each((typeof value === \"function\"\n ? classedFunction : value\n ? classedTrue\n : classedFalse)(names, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/classed.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/clone.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/clone.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction selection_cloneShallow() {\n return this.parentNode.insertBefore(this.cloneNode(false), this.nextSibling);\n}\n\nfunction selection_cloneDeep() {\n return this.parentNode.insertBefore(this.cloneNode(true), this.nextSibling);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(deep) {\n return this.select(deep ? selection_cloneDeep : selection_cloneShallow);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/clone.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/data.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/data.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../constant */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/constant.js\");\n\n\n\n\nvar keyPrefix = \"$\"; // Protect against keys like “__proto__”.\n\nfunction bindIndex(parent, group, enter, update, exit, data) {\n var i = 0,\n node,\n groupLength = group.length,\n dataLength = data.length;\n\n // Put any non-null nodes that fit into update.\n // Put any null nodes into enter.\n // Put any remaining data into enter.\n for (; i < dataLength; ++i) {\n if (node = group[i]) {\n node.__data__ = data[i];\n update[i] = node;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Put any non-null nodes that don’t fit into exit.\n for (; i < groupLength; ++i) {\n if (node = group[i]) {\n exit[i] = node;\n }\n }\n}\n\nfunction bindKey(parent, group, enter, update, exit, data, key) {\n var i,\n node,\n nodeByKeyValue = {},\n groupLength = group.length,\n dataLength = data.length,\n keyValues = new Array(groupLength),\n keyValue;\n\n // Compute the key for each node.\n // If multiple nodes have the same key, the duplicates are added to exit.\n for (i = 0; i < groupLength; ++i) {\n if (node = group[i]) {\n keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);\n if (keyValue in nodeByKeyValue) {\n exit[i] = node;\n } else {\n nodeByKeyValue[keyValue] = node;\n }\n }\n }\n\n // Compute the key for each datum.\n // If there a node associated with this key, join and add it to update.\n // If there is not (or the key is a duplicate), add it to enter.\n for (i = 0; i < dataLength; ++i) {\n keyValue = keyPrefix + key.call(parent, data[i], i, data);\n if (node = nodeByKeyValue[keyValue]) {\n update[i] = node;\n node.__data__ = data[i];\n nodeByKeyValue[keyValue] = null;\n } else {\n enter[i] = new _enter__WEBPACK_IMPORTED_MODULE_1__[\"EnterNode\"](parent, data[i]);\n }\n }\n\n // Add any remaining nodes that were not bound to data to exit.\n for (i = 0; i < groupLength; ++i) {\n if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {\n exit[i] = node;\n }\n }\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value, key) {\n if (!value) {\n data = new Array(this.size()), j = -1;\n this.each(function(d) { data[++j] = d; });\n return data;\n }\n\n var bind = key ? bindKey : bindIndex,\n parents = this._parents,\n groups = this._groups;\n\n if (typeof value !== \"function\") value = Object(_constant__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(value);\n\n for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {\n var parent = parents[j],\n group = groups[j],\n groupLength = group.length,\n data = value.call(parent, parent && parent.__data__, j, parents),\n dataLength = data.length,\n enterGroup = enter[j] = new Array(dataLength),\n updateGroup = update[j] = new Array(dataLength),\n exitGroup = exit[j] = new Array(groupLength);\n\n bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);\n\n // Now connect the enter nodes to their following update node, such that\n // appendChild can insert the materialized enter node before this node,\n // rather than at the end of the parent node.\n for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {\n if (previous = enterGroup[i0]) {\n if (i0 >= i1) i1 = i0 + 1;\n while (!(next = updateGroup[i1]) && ++i1 < dataLength);\n previous._next = next || null;\n }\n }\n }\n\n update = new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](update, parents);\n update._enter = enter;\n update._exit = exit;\n return update;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/data.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/datum.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/datum.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.property(\"__data__\", value)\n : this.node().__data__;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/datum.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/dispatch.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/dispatch.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js\");\n\n\nfunction dispatchEvent(node, type, params) {\n var window = Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node),\n event = window.CustomEvent;\n\n if (typeof event === \"function\") {\n event = new event(type, params);\n } else {\n event = window.document.createEvent(\"Event\");\n if (params) event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail;\n else event.initEvent(type, false, false);\n }\n\n node.dispatchEvent(event);\n}\n\nfunction dispatchConstant(type, params) {\n return function() {\n return dispatchEvent(this, type, params);\n };\n}\n\nfunction dispatchFunction(type, params) {\n return function() {\n return dispatchEvent(this, type, params.apply(this, arguments));\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(type, params) {\n return this.each((typeof params === \"function\"\n ? dispatchFunction\n : dispatchConstant)(type, params));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/dispatch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/each.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/each.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback) {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {\n if (node = group[i]) callback.call(node, node.__data__, i, group);\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/each.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/empty.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/empty.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return !this.node();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/empty.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/enter.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/enter.js ***! + \*****************************************************************************************************/ +/*! exports provided: default, EnterNode */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"EnterNode\", function() { return EnterNode; });\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._enter || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\nfunction EnterNode(parent, datum) {\n this.ownerDocument = parent.ownerDocument;\n this.namespaceURI = parent.namespaceURI;\n this._next = null;\n this._parent = parent;\n this.__data__ = datum;\n}\n\nEnterNode.prototype = {\n constructor: EnterNode,\n appendChild: function(child) { return this._parent.insertBefore(child, this._next); },\n insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },\n querySelector: function(selector) { return this._parent.querySelector(selector); },\n querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/enter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/exit.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/exit.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sparse__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sparse */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sparse.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Selection\"](this._exit || this._groups.map(_sparse__WEBPACK_IMPORTED_MODULE_0__[\"default\"]), this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/exit.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/filter.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/filter.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _matcher__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../matcher */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/matcher.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(_matcher__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/html.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/html.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction htmlRemove() {\n this.innerHTML = \"\";\n}\n\nfunction htmlConstant(value) {\n return function() {\n this.innerHTML = value;\n };\n}\n\nfunction htmlFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.innerHTML = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? htmlRemove : (typeof value === \"function\"\n ? htmlFunction\n : htmlConstant)(value))\n : this.node().innerHTML;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/html.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js ***! + \*****************************************************************************************************/ +/*! exports provided: root, Selection, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"root\", function() { return root; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Selection\", function() { return Selection; });\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/selectAll.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/filter.js\");\n/* harmony import */ var _data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./data */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/data.js\");\n/* harmony import */ var _enter__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./enter */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/enter.js\");\n/* harmony import */ var _exit__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./exit */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/exit.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/merge.js\");\n/* harmony import */ var _order__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./order */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/order.js\");\n/* harmony import */ var _sort__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sort */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sort.js\");\n/* harmony import */ var _call__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./call */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/call.js\");\n/* harmony import */ var _nodes__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./nodes */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/nodes.js\");\n/* harmony import */ var _node__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./node */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/node.js\");\n/* harmony import */ var _size__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./size */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/size.js\");\n/* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./empty */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/empty.js\");\n/* harmony import */ var _each__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./each */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/each.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/attr.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/style.js\");\n/* harmony import */ var _property__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./property */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/property.js\");\n/* harmony import */ var _classed__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! ./classed */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/classed.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/text.js\");\n/* harmony import */ var _html__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! ./html */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/html.js\");\n/* harmony import */ var _raise__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! ./raise */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/raise.js\");\n/* harmony import */ var _lower__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! ./lower */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/lower.js\");\n/* harmony import */ var _append__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! ./append */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/append.js\");\n/* harmony import */ var _insert__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! ./insert */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/insert.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/remove.js\");\n/* harmony import */ var _clone__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! ./clone */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/clone.js\");\n/* harmony import */ var _datum__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! ./datum */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/datum.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js\");\n/* harmony import */ var _dispatch__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! ./dispatch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/dispatch.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar root = [null];\n\nfunction Selection(groups, parents) {\n this._groups = groups;\n this._parents = parents;\n}\n\nfunction selection() {\n return new Selection([[document.documentElement]], root);\n}\n\nSelection.prototype = selection.prototype = {\n constructor: Selection,\n select: _select__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n data: _data__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n enter: _enter__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n exit: _exit__WEBPACK_IMPORTED_MODULE_5__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n order: _order__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n sort: _sort__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n call: _call__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n nodes: _nodes__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n node: _node__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n size: _size__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n empty: _empty__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n each: _each__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n property: _property__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n classed: _classed__WEBPACK_IMPORTED_MODULE_18__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_19__[\"default\"],\n html: _html__WEBPACK_IMPORTED_MODULE_20__[\"default\"],\n raise: _raise__WEBPACK_IMPORTED_MODULE_21__[\"default\"],\n lower: _lower__WEBPACK_IMPORTED_MODULE_22__[\"default\"],\n append: _append__WEBPACK_IMPORTED_MODULE_23__[\"default\"],\n insert: _insert__WEBPACK_IMPORTED_MODULE_24__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_25__[\"default\"],\n clone: _clone__WEBPACK_IMPORTED_MODULE_26__[\"default\"],\n datum: _datum__WEBPACK_IMPORTED_MODULE_27__[\"default\"],\n on: _on__WEBPACK_IMPORTED_MODULE_28__[\"default\"],\n dispatch: _dispatch__WEBPACK_IMPORTED_MODULE_29__[\"default\"]\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (selection);\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/insert.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/insert.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _creator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../creator */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/creator.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js\");\n\n\n\nfunction constantNull() {\n return null;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, before) {\n var create = typeof name === \"function\" ? name : Object(_creator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(name),\n select = before == null ? constantNull : typeof before === \"function\" ? before : Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(before);\n return this.select(function() {\n return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/insert.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/lower.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/lower.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction lower() {\n if (this.previousSibling) this.parentNode.insertBefore(this, this.parentNode.firstChild);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(lower);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/lower.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/merge.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/merge.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selection) {\n\n for (var groups0 = this._groups, groups1 = selection._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](merges, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/node.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/node.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {\n for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {\n var node = group[i];\n if (node) return node;\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/node.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/nodes.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/nodes.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var nodes = new Array(this.size()), i = -1;\n this.each(function() { nodes[++i] = this; });\n return nodes;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/nodes.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js": +/*!**************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js ***! + \**************************************************************************************************/ +/*! exports provided: event, default, customEvent */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return event; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return customEvent; });\nvar filterEvents = {};\n\nvar event = null;\n\nif (typeof document !== \"undefined\") {\n var element = document.documentElement;\n if (!(\"onmouseenter\" in element)) {\n filterEvents = {mouseenter: \"mouseover\", mouseleave: \"mouseout\"};\n }\n}\n\nfunction filterContextListener(listener, index, group) {\n listener = contextListener(listener, index, group);\n return function(event) {\n var related = event.relatedTarget;\n if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {\n listener.call(this, event);\n }\n };\n}\n\nfunction contextListener(listener, index, group) {\n return function(event1) {\n var event0 = event; // Events can be reentrant (e.g., focus).\n event = event1;\n try {\n listener.call(this, this.__data__, index, group);\n } finally {\n event = event0;\n }\n };\n}\n\nfunction parseTypenames(typenames) {\n return typenames.trim().split(/^|\\s+/).map(function(t) {\n var name = \"\", i = t.indexOf(\".\");\n if (i >= 0) name = t.slice(i + 1), t = t.slice(0, i);\n return {type: t, name: name};\n });\n}\n\nfunction onRemove(typename) {\n return function() {\n var on = this.__on;\n if (!on) return;\n for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {\n if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n } else {\n on[++i] = o;\n }\n }\n if (++i) on.length = i;\n else delete this.__on;\n };\n}\n\nfunction onAdd(typename, value, capture) {\n var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;\n return function(d, i, group) {\n var on = this.__on, o, listener = wrap(value, i, group);\n if (on) for (var j = 0, m = on.length; j < m; ++j) {\n if ((o = on[j]).type === typename.type && o.name === typename.name) {\n this.removeEventListener(o.type, o.listener, o.capture);\n this.addEventListener(o.type, o.listener = listener, o.capture = capture);\n o.value = value;\n return;\n }\n }\n this.addEventListener(typename.type, listener, capture);\n o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};\n if (!on) this.__on = [o];\n else on.push(o);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(typename, value, capture) {\n var typenames = parseTypenames(typename + \"\"), i, n = typenames.length, t;\n\n if (arguments.length < 2) {\n var on = this.node().__on;\n if (on) for (var j = 0, m = on.length, o; j < m; ++j) {\n for (i = 0, o = on[j]; i < n; ++i) {\n if ((t = typenames[i]).type === o.type && t.name === o.name) {\n return o.value;\n }\n }\n }\n return;\n }\n\n on = value ? onAdd : onRemove;\n if (capture == null) capture = false;\n for (i = 0; i < n; ++i) this.each(on(typenames[i], value, capture));\n return this;\n});\n\nfunction customEvent(event1, listener, that, args) {\n var event0 = event;\n event1.sourceEvent = event;\n event = event1;\n try {\n return listener.apply(that, args);\n } finally {\n event = event0;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/order.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/order.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n\n for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {\n for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {\n if (node = group[i]) {\n if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);\n next = node;\n }\n }\n }\n\n return this;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/property.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/property.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction propertyRemove(name) {\n return function() {\n delete this[name];\n };\n}\n\nfunction propertyConstant(name, value) {\n return function() {\n this[name] = value;\n };\n}\n\nfunction propertyFunction(name, value) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) delete this[name];\n else this[name] = v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n return arguments.length > 1\n ? this.each((value == null\n ? propertyRemove : typeof value === \"function\"\n ? propertyFunction\n : propertyConstant)(name, value))\n : this.node()[name];\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/property.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/raise.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/raise.js ***! + \*****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction raise() {\n if (this.nextSibling) this.parentNode.appendChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(raise);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/raise.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/remove.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/remove.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction remove() {\n var parent = this.parentNode;\n if (parent) parent.removeChild(this);\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.each(remove);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/select.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/select.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selector */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selector__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/selectAll.js": +/*!*********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/selectAll.js ***! + \*********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n/* harmony import */ var _selectorAll__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../selectorAll */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectorAll.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n if (typeof select !== \"function\") select = Object(_selectorAll__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n subgroups.push(select.call(node, node.__data__, i, group));\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](subgroups, parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/size.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/size.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var size = 0;\n this.each(function() { ++size; });\n return size;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/size.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sort.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sort.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(compare) {\n if (!compare) compare = ascending;\n\n function compareNode(a, b) {\n return a && b ? compare(a.__data__, b.__data__) : !a - !b;\n }\n\n for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n sortgroup[i] = node;\n }\n }\n sortgroup.sort(compareNode);\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Selection\"](sortgroups, this._parents).order();\n});\n\nfunction ascending(a, b) {\n return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sparse.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sparse.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(update) {\n return new Array(update.length);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/sparse.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/style.js": +/*!*****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/style.js ***! + \*****************************************************************************************************/ +/*! exports provided: default, styleValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"styleValue\", function() { return styleValue; });\n/* harmony import */ var _window__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../window */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js\");\n\n\nfunction styleRemove(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, value, priority) {\n return function() {\n this.style.setProperty(name, value, priority);\n };\n}\n\nfunction styleFunction(name, value, priority) {\n return function() {\n var v = value.apply(this, arguments);\n if (v == null) this.style.removeProperty(name);\n else this.style.setProperty(name, v, priority);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n return arguments.length > 1\n ? this.each((value == null\n ? styleRemove : typeof value === \"function\"\n ? styleFunction\n : styleConstant)(name, value, priority == null ? \"\" : priority))\n : styleValue(this.node(), name);\n});\n\nfunction styleValue(node, name) {\n return node.style.getPropertyValue(name)\n || Object(_window__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(node).getComputedStyle(node, null).getPropertyValue(name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/text.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/text.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction textRemove() {\n this.textContent = \"\";\n}\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var v = value.apply(this, arguments);\n this.textContent = v == null ? \"\" : v;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return arguments.length\n ? this.each(value == null\n ? textRemove : (typeof value === \"function\"\n ? textFunction\n : textConstant)(value))\n : this.node().textContent;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js": +/*!**********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js ***! + \**********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction none() {}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? none : function() {\n return this.querySelector(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selector.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectorAll.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectorAll.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction empty() {\n return [];\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(selector) {\n return selector == null ? empty : function() {\n return this.querySelectorAll(selector);\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selectorAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js": +/*!*************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js ***! + \*************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_on__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/on */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/selection/on.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var current = _selection_on__WEBPACK_IMPORTED_MODULE_0__[\"event\"], source;\n while (source = current.sourceEvent) current = source;\n return current;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touch.js": +/*!*******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touch.js ***! + \*******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches, identifier) {\n if (arguments.length < 3) identifier = touches, touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().changedTouches;\n\n for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {\n if ((touch = touches[i]).identifier === identifier) {\n return Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touch);\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touch.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touches.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touches.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _sourceEvent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sourceEvent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/sourceEvent.js\");\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/point.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, touches) {\n if (touches == null) touches = Object(_sourceEvent__WEBPACK_IMPORTED_MODULE_0__[\"default\"])().touches;\n\n for (var i = 0, n = touches ? touches.length : 0, points = new Array(n); i < n; ++i) {\n points[i] = Object(_point__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, touches[i]);\n }\n\n return points;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/touches.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js ***! + \********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node) {\n return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node\n || (node.document && node) // node is a Window\n || node.defaultView; // node is a Document\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/window.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/index.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/index.js ***! + \***************************************************************************************/ +/*! exports provided: now, timer, timerFlush, timeout, interval */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return _timer__WEBPACK_IMPORTED_MODULE_0__[\"timerFlush\"]; });\n\n/* harmony import */ var _timeout__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./timeout */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timeout.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return _timeout__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _interval__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./interval */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/interval.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return _interval__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/interval.js": +/*!******************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/interval.js ***! + \******************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"], total = delay;\n if (delay == null) return t.restart(callback, delay, time), t;\n delay = +delay, time = time == null ? Object(_timer__WEBPACK_IMPORTED_MODULE_0__[\"now\"])() : +time;\n t.restart(function tick(elapsed) {\n elapsed += total;\n t.restart(tick, total += delay, time);\n callback(elapsed);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/interval.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timeout.js": +/*!*****************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timeout.js ***! + \*****************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _timer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./timer */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(callback, delay, time) {\n var t = new _timer__WEBPACK_IMPORTED_MODULE_0__[\"Timer\"];\n delay = delay == null ? 0 : +delay;\n t.restart(function(elapsed) {\n t.stop();\n callback(elapsed + delay);\n }, delay, time);\n return t;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timeout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js": +/*!***************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js ***! + \***************************************************************************************/ +/*! exports provided: now, Timer, timer, timerFlush */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return now; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Timer\", function() { return Timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return timer; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return timerFlush; });\nvar frame = 0, // is an animation frame pending?\n timeout = 0, // is a timeout pending?\n interval = 0, // are any timers active?\n pokeDelay = 1000, // how frequently we check for clock skew\n taskHead,\n taskTail,\n clockLast = 0,\n clockNow = 0,\n clockSkew = 0,\n clock = typeof performance === \"object\" && performance.now ? performance : Date,\n setFrame = typeof window === \"object\" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };\n\nfunction now() {\n return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);\n}\n\nfunction clearNow() {\n clockNow = 0;\n}\n\nfunction Timer() {\n this._call =\n this._time =\n this._next = null;\n}\n\nTimer.prototype = timer.prototype = {\n constructor: Timer,\n restart: function(callback, delay, time) {\n if (typeof callback !== \"function\") throw new TypeError(\"callback is not a function\");\n time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);\n if (!this._next && taskTail !== this) {\n if (taskTail) taskTail._next = this;\n else taskHead = this;\n taskTail = this;\n }\n this._call = callback;\n this._time = time;\n sleep();\n },\n stop: function() {\n if (this._call) {\n this._call = null;\n this._time = Infinity;\n sleep();\n }\n }\n};\n\nfunction timer(callback, delay, time) {\n var t = new Timer;\n t.restart(callback, delay, time);\n return t;\n}\n\nfunction timerFlush() {\n now(); // Get the current time, if not already set.\n ++frame; // Pretend we’ve set an alarm, if we haven’t already.\n var t = taskHead, e;\n while (t) {\n if ((e = clockNow - t._time) >= 0) t._call.call(null, e);\n t = t._next;\n }\n --frame;\n}\n\nfunction wake() {\n clockNow = (clockLast = clock.now()) + clockSkew;\n frame = timeout = 0;\n try {\n timerFlush();\n } finally {\n frame = 0;\n nap();\n clockNow = 0;\n }\n}\n\nfunction poke() {\n var now = clock.now(), delay = now - clockLast;\n if (delay > pokeDelay) clockSkew -= delay, clockLast = now;\n}\n\nfunction nap() {\n var t0, t1 = taskHead, t2, time = Infinity;\n while (t1) {\n if (t1._call) {\n if (time > t1._time) time = t1._time;\n t0 = t1, t1 = t1._next;\n } else {\n t2 = t1._next, t1._next = null;\n t1 = t0 ? t0._next = t2 : taskHead = t2;\n }\n }\n taskTail = t0;\n sleep(time);\n}\n\nfunction sleep(time) {\n if (frame) return; // Soonest alarm already set, or will be.\n if (timeout) timeout = clearTimeout(timeout);\n var delay = time - clockNow; // Strictly less than if we recomputed clockNow.\n if (delay > 24) {\n if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew);\n if (interval) interval = clearInterval(interval);\n } else {\n if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay);\n frame = 1, setFrame(wake);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/timer.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/active.js": +/*!*********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/active.js ***! + \*********************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\nvar root = [null];\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n i;\n\n if (schedules) {\n name = name == null ? null : name + \"\";\n for (i in schedules) {\n if ((schedule = schedules[i]).state > _transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"SCHEDULED\"] && schedule.name === name) {\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]([[node]], root, name, +i);\n }\n }\n }\n\n return null;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/active.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/index.js": +/*!********************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/index.js ***! + \********************************************************************************************/ +/*! exports provided: transition, active, interrupt */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _selection_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./selection/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/index.js\");\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./transition/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return _transition_index__WEBPACK_IMPORTED_MODULE_1__[\"default\"]; });\n\n/* harmony import */ var _active__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./active */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/active.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return _active__WEBPACK_IMPORTED_MODULE_2__[\"default\"]; });\n\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/interrupt.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return _interrupt__WEBPACK_IMPORTED_MODULE_3__[\"default\"]; });\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/interrupt.js": +/*!************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/interrupt.js ***! + \************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name) {\n var schedules = node.__transition,\n schedule,\n active,\n empty = true,\n i;\n\n if (!schedules) return;\n\n name = name == null ? null : name + \"\";\n\n for (i in schedules) {\n if ((schedule = schedules[i]).name !== name) { empty = false; continue; }\n active = schedule.state > _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"STARTING\"] && schedule.state < _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDING\"];\n schedule.state = _transition_schedule__WEBPACK_IMPORTED_MODULE_0__[\"ENDED\"];\n schedule.timer.stop();\n if (active) schedule.on.call(\"interrupt\", node, node.__data__, schedule.index, schedule.group);\n delete schedules[i];\n }\n\n if (empty) delete node.__transition;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/index.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/index.js ***! + \******************************************************************************************************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interrupt */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/interrupt.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/transition.js\");\n\n\n\n\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.interrupt = _interrupt__WEBPACK_IMPORTED_MODULE_1__[\"default\"];\nd3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.transition = _transition__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/interrupt.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/interrupt.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _interrupt__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../interrupt */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/interrupt.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n return this.each(function() {\n Object(_interrupt__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(this, name);\n });\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/interrupt.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/transition.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/transition.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _transition_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../transition/index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _transition_schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../transition/schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-ease */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-ease/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/index.js\");\n\n\n\n\n\nvar defaultTiming = {\n time: null, // Set on use.\n delay: 0,\n duration: 250,\n ease: d3_ease__WEBPACK_IMPORTED_MODULE_2__[\"easeCubicInOut\"]\n};\n\nfunction inherit(node, id) {\n var timing;\n while (!(timing = node.__transition) || !(timing = timing[id])) {\n if (!(node = node.parentNode)) {\n return defaultTiming.time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), defaultTiming;\n }\n }\n return timing;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name) {\n var id,\n timing;\n\n if (name instanceof _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"]) {\n id = name._id, name = name._name;\n } else {\n id = Object(_transition_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])(), (timing = defaultTiming).time = Object(d3_timer__WEBPACK_IMPORTED_MODULE_3__[\"now\"])(), name = name == null ? null : name + \"\";\n }\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n Object(_transition_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id, i, group, timing || inherit(node, id));\n }\n }\n }\n\n return new _transition_index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/selection/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attr.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attr.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction attrRemove(name) {\n return function() {\n this.removeAttribute(name);\n };\n}\n\nfunction attrRemoveNS(fullname) {\n return function() {\n this.removeAttributeNS(fullname.space, fullname.local);\n };\n}\n\nfunction attrConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrConstantNS(fullname, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction attrFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttribute(name);\n value0 = this.getAttribute(name);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction attrFunctionNS(fullname, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0, value1 = value(this);\n if (value1 == null) return void this.removeAttributeNS(fullname.space, fullname.local);\n value0 = this.getAttributeNS(fullname.space, fullname.local);\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"namespace\"])(name), i = fullname === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformSvg\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return this.attrTween(name, typeof value === \"function\"\n ? (fullname.local ? attrFunctionNS : attrFunction)(fullname, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"attr.\" + name, value))\n : value == null ? (fullname.local ? attrRemoveNS : attrRemove)(fullname)\n : (fullname.local ? attrConstantNS : attrConstant)(fullname, i, value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attr.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attrTween.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attrTween.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n\n\nfunction attrTweenNS(fullname, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttributeNS(fullname.space, fullname.local, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\nfunction attrTween(name, value) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.setAttribute(name, i(t));\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var key = \"attr.\" + name;\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n var fullname = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"namespace\"])(name);\n return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attrTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/delay.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/delay.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction delayFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = +value.apply(this, arguments);\n };\n}\n\nfunction delayConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"])(this, id).delay = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? delayFunction\n : delayConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).delay;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/delay.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/duration.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/duration.js ***! + \**********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction durationFunction(id, value) {\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = +value.apply(this, arguments);\n };\n}\n\nfunction durationConstant(id, value) {\n return value = +value, function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).duration = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each((typeof value === \"function\"\n ? durationFunction\n : durationConstant)(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).duration;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/duration.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/ease.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/ease.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction easeConstant(id, value) {\n if (typeof value !== \"function\") throw new Error;\n return function() {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id).ease = value;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n var id = this._id;\n\n return arguments.length\n ? this.each(easeConstant(id, value))\n : Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).ease;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/ease.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/filter.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/filter.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(match) {\n if (typeof match !== \"function\") match = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"matcher\"])(match);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {\n if ((node = group[i]) && match.call(node, node.__data__, i, group)) {\n subgroup.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/filter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js ***! + \*******************************************************************************************************/ +/*! exports provided: Transition, default, newId */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transition\", function() { return Transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transition; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"newId\", function() { return newId; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _attr__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attr */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attr.js\");\n/* harmony import */ var _attrTween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./attrTween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/attrTween.js\");\n/* harmony import */ var _delay__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./delay */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/delay.js\");\n/* harmony import */ var _duration__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./duration */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/duration.js\");\n/* harmony import */ var _ease__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./ease */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/ease.js\");\n/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./filter */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/filter.js\");\n/* harmony import */ var _merge__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./merge */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/merge.js\");\n/* harmony import */ var _on__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./on */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/on.js\");\n/* harmony import */ var _remove__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./remove */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/remove.js\");\n/* harmony import */ var _select__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./select */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/select.js\");\n/* harmony import */ var _selectAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./selectAll */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selectAll.js\");\n/* harmony import */ var _selection__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selection.js\");\n/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./style */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/style.js\");\n/* harmony import */ var _styleTween__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./styleTween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/styleTween.js\");\n/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./text */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/text.js\");\n/* harmony import */ var _transition__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./transition */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/transition.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar id = 0;\n\nfunction Transition(groups, parents, name, id) {\n this._groups = groups;\n this._parents = parents;\n this._name = name;\n this._id = id;\n}\n\nfunction transition(name) {\n return Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"])().transition(name);\n}\n\nfunction newId() {\n return ++id;\n}\n\nvar selection_prototype = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype;\n\nTransition.prototype = transition.prototype = {\n constructor: Transition,\n select: _select__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n selectAll: _selectAll__WEBPACK_IMPORTED_MODULE_11__[\"default\"],\n filter: _filter__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n merge: _merge__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n selection: _selection__WEBPACK_IMPORTED_MODULE_12__[\"default\"],\n transition: _transition__WEBPACK_IMPORTED_MODULE_16__[\"default\"],\n call: selection_prototype.call,\n nodes: selection_prototype.nodes,\n node: selection_prototype.node,\n size: selection_prototype.size,\n empty: selection_prototype.empty,\n each: selection_prototype.each,\n on: _on__WEBPACK_IMPORTED_MODULE_8__[\"default\"],\n attr: _attr__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n attrTween: _attrTween__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n style: _style__WEBPACK_IMPORTED_MODULE_13__[\"default\"],\n styleTween: _styleTween__WEBPACK_IMPORTED_MODULE_14__[\"default\"],\n text: _text__WEBPACK_IMPORTED_MODULE_15__[\"default\"],\n remove: _remove__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n tween: _tween__WEBPACK_IMPORTED_MODULE_17__[\"default\"],\n delay: _delay__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n duration: _duration__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n ease: _ease__WEBPACK_IMPORTED_MODULE_5__[\"default\"]\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/interpolate.js": +/*!*************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/interpolate.js ***! + \*************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-color/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n var c;\n return (typeof b === \"number\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateNumber\"]\n : b instanceof d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"] ? d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"]\n : (c = Object(d3_color__WEBPACK_IMPORTED_MODULE_0__[\"color\"])(b)) ? (b = c, d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateRgb\"])\n : d3_interpolate__WEBPACK_IMPORTED_MODULE_1__[\"interpolateString\"])(a, b);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/interpolate.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/merge.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/merge.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(transition) {\n if (transition._id !== this._id) throw new Error;\n\n for (var groups0 = this._groups, groups1 = transition._groups, m0 = groups0.length, m1 = groups1.length, m = Math.min(m0, m1), merges = new Array(m0), j = 0; j < m; ++j) {\n for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {\n if (node = group0[i] || group1[i]) {\n merge[i] = node;\n }\n }\n }\n\n for (; j < m0; ++j) {\n merges[j] = groups0[j];\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](merges, this._parents, this._name, this._id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/merge.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/on.js": +/*!****************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/on.js ***! + \****************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction start(name) {\n return (name + \"\").trim().split(/^|\\s+/).every(function(t) {\n var i = t.indexOf(\".\");\n if (i >= 0) t = t.slice(0, i);\n return !t || t === \"start\";\n });\n}\n\nfunction onFunction(id, name, listener) {\n var on0, on1, sit = start(name) ? _schedule__WEBPACK_IMPORTED_MODULE_0__[\"init\"] : _schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"];\n return function() {\n var schedule = sit(this, id),\n on = schedule.on;\n\n // If this node shared a dispatch with the previous node,\n // just assign the updated shared dispatch and we’re done!\n // Otherwise, copy-on-write.\n if (on !== on0) (on1 = (on0 = on).copy()).on(name, listener);\n\n schedule.on = on1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, listener) {\n var id = this._id;\n\n return arguments.length < 2\n ? Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).on.on(name)\n : this.each(onFunction(id, name, listener));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/on.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/remove.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/remove.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction removeFunction(id) {\n return function() {\n var parent = this.parentNode;\n for (var i in this.__transition) if (+i !== id) return;\n if (parent) parent.removeChild(this);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return this.on(\"end.remove\", removeFunction(this._id));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/remove.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js": +/*!**********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js ***! + \**********************************************************************************************************/ +/*! exports provided: CREATED, SCHEDULED, STARTING, STARTED, RUNNING, ENDING, ENDED, default, init, set, get */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"CREATED\", function() { return CREATED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SCHEDULED\", function() { return SCHEDULED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTING\", function() { return STARTING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"STARTED\", function() { return STARTED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RUNNING\", function() { return RUNNING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDING\", function() { return ENDING; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"ENDED\", function() { return ENDED; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"init\", function() { return init; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return set; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"get\", function() { return get; });\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-timer/src/index.js\");\n\n\n\nvar emptyOn = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"end\", \"interrupt\");\nvar emptyTween = [];\n\nvar CREATED = 0;\nvar SCHEDULED = 1;\nvar STARTING = 2;\nvar STARTED = 3;\nvar RUNNING = 4;\nvar ENDING = 5;\nvar ENDED = 6;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(node, name, id, index, group, timing) {\n var schedules = node.__transition;\n if (!schedules) node.__transition = {};\n else if (id in schedules) return;\n create(node, id, {\n name: name,\n index: index, // For context during callback.\n group: group, // For context during callback.\n on: emptyOn,\n tween: emptyTween,\n time: timing.time,\n delay: timing.delay,\n duration: timing.duration,\n ease: timing.ease,\n timer: null,\n state: CREATED\n });\n});\n\nfunction init(node, id) {\n var schedule = get(node, id);\n if (schedule.state > CREATED) throw new Error(\"too late; already scheduled\");\n return schedule;\n}\n\nfunction set(node, id) {\n var schedule = get(node, id);\n if (schedule.state > STARTING) throw new Error(\"too late; already started\");\n return schedule;\n}\n\nfunction get(node, id) {\n var schedule = node.__transition;\n if (!schedule || !(schedule = schedule[id])) throw new Error(\"transition not found\");\n return schedule;\n}\n\nfunction create(node, id, self) {\n var schedules = node.__transition,\n tween;\n\n // Initialize the self timer when the transition is created.\n // Note the actual delay is not known until the first callback!\n schedules[id] = self;\n self.timer = Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timer\"])(schedule, 0, self.time);\n\n function schedule(elapsed) {\n self.state = SCHEDULED;\n self.timer.restart(start, self.delay, self.time);\n\n // If the elapsed delay is less than our first sleep, start immediately.\n if (self.delay <= elapsed) start(elapsed - self.delay);\n }\n\n function start(elapsed) {\n var i, j, n, o;\n\n // If the state is not SCHEDULED, then we previously errored on start.\n if (self.state !== SCHEDULED) return stop();\n\n for (i in schedules) {\n o = schedules[i];\n if (o.name !== self.name) continue;\n\n // While this element already has a starting transition during this frame,\n // defer starting an interrupting transition until that transition has a\n // chance to tick (and possibly end); see d3/d3-transition#54!\n if (o.state === STARTED) return Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(start);\n\n // Interrupt the active transition, if any.\n // Dispatch the interrupt event.\n if (o.state === RUNNING) {\n o.state = ENDED;\n o.timer.stop();\n o.on.call(\"interrupt\", node, node.__data__, o.index, o.group);\n delete schedules[i];\n }\n\n // Cancel any pre-empted transitions. No interrupt event is dispatched\n // because the cancelled transitions never started. Note that this also\n // removes this transition from the pending list!\n else if (+i < id) {\n o.state = ENDED;\n o.timer.stop();\n delete schedules[i];\n }\n }\n\n // Defer the first tick to end of the current frame; see d3/d3#1576.\n // Note the transition may be canceled after start and before the first tick!\n // Note this must be scheduled before the start event; see d3/d3-transition#16!\n // Assuming this is successful, subsequent callbacks go straight to tick.\n Object(d3_timer__WEBPACK_IMPORTED_MODULE_1__[\"timeout\"])(function() {\n if (self.state === STARTED) {\n self.state = RUNNING;\n self.timer.restart(tick, self.delay, self.time);\n tick(elapsed);\n }\n });\n\n // Dispatch the start event.\n // Note this must be done before the tween are initialized.\n self.state = STARTING;\n self.on.call(\"start\", node, node.__data__, self.index, self.group);\n if (self.state !== STARTING) return; // interrupted\n self.state = STARTED;\n\n // Initialize the tween, deleting null tween.\n tween = new Array(n = self.tween.length);\n for (i = 0, j = -1; i < n; ++i) {\n if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {\n tween[++j] = o;\n }\n }\n tween.length = j + 1;\n }\n\n function tick(elapsed) {\n var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),\n i = -1,\n n = tween.length;\n\n while (++i < n) {\n tween[i].call(null, t);\n }\n\n // Dispatch the end event.\n if (self.state === ENDING) {\n self.on.call(\"end\", node, node.__data__, self.index, self.group);\n stop();\n }\n }\n\n function stop() {\n self.state = ENDED;\n self.timer.stop();\n delete schedules[id];\n for (var i in schedules) return; // eslint-disable-line no-unused-vars\n delete node.__transition;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/select.js": +/*!********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/select.js ***! + \********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selector\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {\n if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {\n if (\"__data__\" in node) subnode.__data__ = node.__data__;\n subgroup[i] = subnode;\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(subgroup[i], name, id, i, subgroup, Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id));\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, this._parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/select.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selectAll.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selectAll.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(select) {\n var name = this._name,\n id = this._id;\n\n if (typeof select !== \"function\") select = Object(d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selectorAll\"])(select);\n\n for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n for (var children = select.call(node, node.__data__, i, group), child, inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"get\"])(node, id), k = 0, l = children.length; k < l; ++k) {\n if (child = children[k]) {\n Object(_schedule__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(child, name, id, k, children, inherit);\n }\n }\n subgroups.push(children);\n parents.push(node);\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_1__[\"Transition\"](subgroups, parents, name, id);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selectAll.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selection.js": +/*!***********************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selection.js ***! + \***********************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n\n\nvar Selection = d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"selection\"].prototype.constructor;\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n return new Selection(this._groups, this._parents);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/selection.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/style.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/style.js ***! + \*******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js\");\n/* harmony import */ var _interpolate__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/interpolate.js\");\n\n\n\n\n\nfunction styleRemove(name, interpolate) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\nfunction styleRemoveEnd(name) {\n return function() {\n this.style.removeProperty(name);\n };\n}\n\nfunction styleConstant(name, interpolate, value1) {\n var value00,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name);\n return value0 === value1 ? null\n : value0 === value00 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value1);\n };\n}\n\nfunction styleFunction(name, interpolate, value) {\n var value00,\n value10,\n interpolate0;\n return function() {\n var value0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name),\n value1 = value(this);\n if (value1 == null) value1 = (this.style.removeProperty(name), Object(d3_selection__WEBPACK_IMPORTED_MODULE_1__[\"style\"])(this, name));\n return value0 === value1 ? null\n : value0 === value00 && value1 === value10 ? interpolate0\n : interpolate0 = interpolate(value00 = value0, value10 = value1);\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var i = (name += \"\") === \"transform\" ? d3_interpolate__WEBPACK_IMPORTED_MODULE_0__[\"interpolateTransformCss\"] : _interpolate__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\n return value == null ? this\n .styleTween(name, styleRemove(name, i))\n .on(\"end.style.\" + name, styleRemoveEnd(name))\n : this.styleTween(name, typeof value === \"function\"\n ? styleFunction(name, i, Object(_tween__WEBPACK_IMPORTED_MODULE_2__[\"tweenValue\"])(this, \"style.\" + name, value))\n : styleConstant(name, i, value + \"\"), priority);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/style.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/styleTween.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/styleTween.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\nfunction styleTween(name, value, priority) {\n function tween() {\n var node = this, i = value.apply(node, arguments);\n return i && function(t) {\n node.style.setProperty(name, i(t), priority);\n };\n }\n tween._value = value;\n return tween;\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value, priority) {\n var key = \"style.\" + (name += \"\");\n if (arguments.length < 2) return (key = this.tween(key)) && key._value;\n if (value == null) return this.tween(key, null);\n if (typeof value !== \"function\") throw new Error;\n return this.tween(key, styleTween(name, value, priority == null ? \"\" : priority));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/styleTween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/text.js": +/*!******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/text.js ***! + \******************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _tween__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./tween */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js\");\n\n\nfunction textConstant(value) {\n return function() {\n this.textContent = value;\n };\n}\n\nfunction textFunction(value) {\n return function() {\n var value1 = value(this);\n this.textContent = value1 == null ? \"\" : value1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(value) {\n return this.tween(\"text\", typeof value === \"function\"\n ? textFunction(Object(_tween__WEBPACK_IMPORTED_MODULE_0__[\"tweenValue\"])(this, \"text\", value))\n : textConstant(value == null ? \"\" : value + \"\"));\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/text.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/transition.js": +/*!************************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/transition.js ***! + \************************************************************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./index */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/index.js\");\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var name = this._name,\n id0 = this._id,\n id1 = Object(_index__WEBPACK_IMPORTED_MODULE_0__[\"newId\"])();\n\n for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {\n for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {\n if (node = group[i]) {\n var inherit = Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"get\"])(node, id0);\n Object(_schedule__WEBPACK_IMPORTED_MODULE_1__[\"default\"])(node, name, id1, i, group, {\n time: inherit.time + inherit.delay + inherit.duration,\n delay: 0,\n duration: inherit.duration,\n ease: inherit.ease\n });\n }\n }\n }\n\n return new _index__WEBPACK_IMPORTED_MODULE_0__[\"Transition\"](groups, this._parents, name, id1);\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/transition.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js": +/*!*******************************************************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js ***! + \*******************************************************************************************************/ +/*! exports provided: default, tweenValue */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"tweenValue\", function() { return tweenValue; });\n/* harmony import */ var _schedule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./schedule */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/schedule.js\");\n\n\nfunction tweenRemove(id, name) {\n var tween0, tween1;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = tween0 = tween;\n for (var i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1 = tween1.slice();\n tween1.splice(i, 1);\n break;\n }\n }\n }\n\n schedule.tween = tween1;\n };\n}\n\nfunction tweenFunction(id, name, value) {\n var tween0, tween1;\n if (typeof value !== \"function\") throw new Error;\n return function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id),\n tween = schedule.tween;\n\n // If this node shared tween with the previous node,\n // just assign the updated shared tween and we’re done!\n // Otherwise, copy-on-write.\n if (tween !== tween0) {\n tween1 = (tween0 = tween).slice();\n for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {\n if (tween1[i].name === name) {\n tween1[i] = t;\n break;\n }\n }\n if (i === n) tween1.push(t);\n }\n\n schedule.tween = tween1;\n };\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(name, value) {\n var id = this._id;\n\n name += \"\";\n\n if (arguments.length < 2) {\n var tween = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(this.node(), id).tween;\n for (var i = 0, n = tween.length, t; i < n; ++i) {\n if ((t = tween[i]).name === name) {\n return t.value;\n }\n }\n return null;\n }\n\n return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));\n});\n\nfunction tweenValue(transition, name, value) {\n var id = transition._id;\n\n transition.each(function() {\n var schedule = Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"set\"])(this, id);\n (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);\n });\n\n return function(node) {\n return Object(_schedule__WEBPACK_IMPORTED_MODULE_0__[\"get\"])(node, id).value[name];\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/transition/tween.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/src/constant.js": +/*!********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/src/constant.js ***! + \********************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(x) {\n return function() {\n return x;\n };\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/src/constant.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/src/event.js": +/*!*****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/src/event.js ***! + \*****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return ZoomEvent; });\nfunction ZoomEvent(target, type, transform) {\n this.target = target;\n this.type = type;\n this.transform = transform;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/src/event.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/src/noevent.js": +/*!*******************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/src/noevent.js ***! + \*******************************************************************/ +/*! exports provided: nopropagation, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"nopropagation\", function() { return nopropagation; });\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n\n\nfunction nopropagation() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].preventDefault();\n d3_selection__WEBPACK_IMPORTED_MODULE_0__[\"event\"].stopImmediatePropagation();\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/src/noevent.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/src/transform.js": +/*!*********************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/src/transform.js ***! + \*********************************************************************/ +/*! exports provided: Transform, identity, default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"Transform\", function() { return Transform; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"identity\", function() { return identity; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return transform; });\nfunction Transform(k, x, y) {\n this.k = k;\n this.x = x;\n this.y = y;\n}\n\nTransform.prototype = {\n constructor: Transform,\n scale: function(k) {\n return k === 1 ? this : new Transform(this.k * k, this.x, this.y);\n },\n translate: function(x, y) {\n return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);\n },\n apply: function(point) {\n return [point[0] * this.k + this.x, point[1] * this.k + this.y];\n },\n applyX: function(x) {\n return x * this.k + this.x;\n },\n applyY: function(y) {\n return y * this.k + this.y;\n },\n invert: function(location) {\n return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];\n },\n invertX: function(x) {\n return (x - this.x) / this.k;\n },\n invertY: function(y) {\n return (y - this.y) / this.k;\n },\n rescaleX: function(x) {\n return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));\n },\n rescaleY: function(y) {\n return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));\n },\n toString: function() {\n return \"translate(\" + this.x + \",\" + this.y + \") scale(\" + this.k + \")\";\n }\n};\n\nvar identity = new Transform(1, 0, 0);\n\ntransform.prototype = Transform.prototype;\n\nfunction transform(node) {\n return node.__zoom || identity;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/src/transform.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3-zoom/src/zoom.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3-zoom/src/zoom.js ***! + \****************************************************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-dispatch/src/index.js\");\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-drag */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-drag/src/index.js\");\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-interpolate/src/index.js\");\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-selection/src/index.js\");\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-transition */ \"./node_modules/dagre-d3/node_modules/d3-zoom/node_modules/d3-transition/src/index.js\");\n/* harmony import */ var _constant__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./constant */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/constant.js\");\n/* harmony import */ var _event__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./event */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/event.js\");\n/* harmony import */ var _transform__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./transform */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/transform.js\");\n/* harmony import */ var _noevent__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./noevent */ \"./node_modules/dagre-d3/node_modules/d3-zoom/src/noevent.js\");\n\n\n\n\n\n\n\n\n\n\n// Ignore right-click, since that should open the context menu.\nfunction defaultFilter() {\n return !d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].button;\n}\n\nfunction defaultExtent() {\n var e = this, w, h;\n if (e instanceof SVGElement) {\n e = e.ownerSVGElement || e;\n w = e.width.baseVal.value;\n h = e.height.baseVal.value;\n } else {\n w = e.clientWidth;\n h = e.clientHeight;\n }\n return [[0, 0], [w, h]];\n}\n\nfunction defaultTransform() {\n return this.__zoom || _transform__WEBPACK_IMPORTED_MODULE_7__[\"identity\"];\n}\n\nfunction defaultWheelDelta() {\n return -d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].deltaY * (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].deltaMode ? 120 : 1) / 500;\n}\n\nfunction defaultTouchable() {\n return \"ontouchstart\" in this;\n}\n\nfunction defaultConstrain(transform, extent, translateExtent) {\n var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],\n dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],\n dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],\n dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];\n return transform.translate(\n dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),\n dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)\n );\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function() {\n var filter = defaultFilter,\n extent = defaultExtent,\n constrain = defaultConstrain,\n wheelDelta = defaultWheelDelta,\n touchable = defaultTouchable,\n scaleExtent = [0, Infinity],\n translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],\n duration = 250,\n interpolate = d3_interpolate__WEBPACK_IMPORTED_MODULE_2__[\"interpolateZoom\"],\n gestures = [],\n listeners = Object(d3_dispatch__WEBPACK_IMPORTED_MODULE_0__[\"dispatch\"])(\"start\", \"zoom\", \"end\"),\n touchstarting,\n touchending,\n touchDelay = 500,\n wheelDelay = 150,\n clickDistance2 = 0;\n\n function zoom(selection) {\n selection\n .property(\"__zoom\", defaultTransform)\n .on(\"wheel.zoom\", wheeled)\n .on(\"mousedown.zoom\", mousedowned)\n .on(\"dblclick.zoom\", dblclicked)\n .filter(touchable)\n .on(\"touchstart.zoom\", touchstarted)\n .on(\"touchmove.zoom\", touchmoved)\n .on(\"touchend.zoom touchcancel.zoom\", touchended)\n .style(\"touch-action\", \"none\")\n .style(\"-webkit-tap-highlight-color\", \"rgba(0,0,0,0)\");\n }\n\n zoom.transform = function(collection, transform) {\n var selection = collection.selection ? collection.selection() : collection;\n selection.property(\"__zoom\", defaultTransform);\n if (collection !== selection) {\n schedule(collection, transform);\n } else {\n selection.interrupt().each(function() {\n gesture(this, arguments)\n .start()\n .zoom(null, typeof transform === \"function\" ? transform.apply(this, arguments) : transform)\n .end();\n });\n }\n };\n\n zoom.scaleBy = function(selection, k) {\n zoom.scaleTo(selection, function() {\n var k0 = this.__zoom.k,\n k1 = typeof k === \"function\" ? k.apply(this, arguments) : k;\n return k0 * k1;\n });\n };\n\n zoom.scaleTo = function(selection, k) {\n zoom.transform(selection, function() {\n var e = extent.apply(this, arguments),\n t0 = this.__zoom,\n p0 = centroid(e),\n p1 = t0.invert(p0),\n k1 = typeof k === \"function\" ? k.apply(this, arguments) : k;\n return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);\n });\n };\n\n zoom.translateBy = function(selection, x, y) {\n zoom.transform(selection, function() {\n return constrain(this.__zoom.translate(\n typeof x === \"function\" ? x.apply(this, arguments) : x,\n typeof y === \"function\" ? y.apply(this, arguments) : y\n ), extent.apply(this, arguments), translateExtent);\n });\n };\n\n zoom.translateTo = function(selection, x, y) {\n zoom.transform(selection, function() {\n var e = extent.apply(this, arguments),\n t = this.__zoom,\n p = centroid(e);\n return constrain(_transform__WEBPACK_IMPORTED_MODULE_7__[\"identity\"].translate(p[0], p[1]).scale(t.k).translate(\n typeof x === \"function\" ? -x.apply(this, arguments) : -x,\n typeof y === \"function\" ? -y.apply(this, arguments) : -y\n ), e, translateExtent);\n });\n };\n\n function scale(transform, k) {\n k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));\n return k === transform.k ? transform : new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](k, transform.x, transform.y);\n }\n\n function translate(transform, p0, p1) {\n var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;\n return x === transform.x && y === transform.y ? transform : new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](transform.k, x, y);\n }\n\n function centroid(extent) {\n return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];\n }\n\n function schedule(transition, transform, center) {\n transition\n .on(\"start.zoom\", function() { gesture(this, arguments).start(); })\n .on(\"interrupt.zoom end.zoom\", function() { gesture(this, arguments).end(); })\n .tween(\"zoom\", function() {\n var that = this,\n args = arguments,\n g = gesture(that, args),\n e = extent.apply(that, args),\n p = center || centroid(e),\n w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),\n a = that.__zoom,\n b = typeof transform === \"function\" ? transform.apply(that, args) : transform,\n i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));\n return function(t) {\n if (t === 1) t = b; // Avoid rounding error on end.\n else { var l = i(t), k = w / l[2]; t = new _transform__WEBPACK_IMPORTED_MODULE_7__[\"Transform\"](k, p[0] - l[0] * k, p[1] - l[1] * k); }\n g.zoom(null, t);\n };\n });\n }\n\n function gesture(that, args) {\n for (var i = 0, n = gestures.length, g; i < n; ++i) {\n if ((g = gestures[i]).that === that) {\n return g;\n }\n }\n return new Gesture(that, args);\n }\n\n function Gesture(that, args) {\n this.that = that;\n this.args = args;\n this.index = -1;\n this.active = 0;\n this.extent = extent.apply(that, args);\n }\n\n Gesture.prototype = {\n start: function() {\n if (++this.active === 1) {\n this.index = gestures.push(this) - 1;\n this.emit(\"start\");\n }\n return this;\n },\n zoom: function(key, transform) {\n if (this.mouse && key !== \"mouse\") this.mouse[1] = transform.invert(this.mouse[0]);\n if (this.touch0 && key !== \"touch\") this.touch0[1] = transform.invert(this.touch0[0]);\n if (this.touch1 && key !== \"touch\") this.touch1[1] = transform.invert(this.touch1[0]);\n this.that.__zoom = transform;\n this.emit(\"zoom\");\n return this;\n },\n end: function() {\n if (--this.active === 0) {\n gestures.splice(this.index, 1);\n this.index = -1;\n this.emit(\"end\");\n }\n return this;\n },\n emit: function(type) {\n Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"customEvent\"])(new _event__WEBPACK_IMPORTED_MODULE_6__[\"default\"](zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);\n }\n };\n\n function wheeled() {\n if (!filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n t = this.__zoom,\n k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this);\n\n // If the mouse is in the same location as before, reuse it.\n // If there were recent wheel events, reset the wheel idle timeout.\n if (g.wheel) {\n if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {\n g.mouse[1] = t.invert(g.mouse[0] = p);\n }\n clearTimeout(g.wheel);\n }\n\n // If this wheel event won’t trigger a transform change, ignore it.\n else if (t.k === k) return;\n\n // Otherwise, capture the mouse point and location at the start.\n else {\n g.mouse = [p, t.invert(p)];\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n }\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n g.wheel = setTimeout(wheelidled, wheelDelay);\n g.zoom(\"mouse\", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));\n\n function wheelidled() {\n g.wheel = null;\n g.end();\n }\n }\n\n function mousedowned() {\n if (touchending || !filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n v = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view).on(\"mousemove.zoom\", mousemoved, true).on(\"mouseup.zoom\", mouseupped, true),\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this),\n x0 = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientX,\n y0 = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientY;\n\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragDisable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n g.mouse = [p, this.__zoom.invert(p)];\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n\n function mousemoved() {\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (!g.moved) {\n var dx = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientX - x0, dy = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].clientY - y0;\n g.moved = dx * dx + dy * dy > clickDistance2;\n }\n g.zoom(\"mouse\", constrain(translate(g.that.__zoom, g.mouse[0] = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(g.that), g.mouse[1]), g.extent, translateExtent));\n }\n\n function mouseupped() {\n v.on(\"mousemove.zoom mouseup.zoom\", null);\n Object(d3_drag__WEBPACK_IMPORTED_MODULE_1__[\"dragEnable\"])(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].view, g.moved);\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n g.end();\n }\n }\n\n function dblclicked() {\n if (!filter.apply(this, arguments)) return;\n var t0 = this.__zoom,\n p0 = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"mouse\"])(this),\n p1 = t0.invert(p0),\n k1 = t0.k * (d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].shiftKey ? 0.5 : 2),\n t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (duration > 0) Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).transition().duration(duration).call(schedule, t1, p0);\n else Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).call(zoom.transform, t1);\n }\n\n function touchstarted() {\n if (!filter.apply(this, arguments)) return;\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n started,\n n = touches.length, i, t, p;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n for (i = 0; i < n; ++i) {\n t = touches[i], p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"touch\"])(this, touches, t.identifier);\n p = [p, this.__zoom.invert(p), t.identifier];\n if (!g.touch0) g.touch0 = p, started = true;\n else if (!g.touch1) g.touch1 = p;\n }\n\n // If this is a dbltap, reroute to the (optional) dblclick.zoom handler.\n if (touchstarting) {\n touchstarting = clearTimeout(touchstarting);\n if (!g.touch1) {\n g.end();\n p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"select\"])(this).on(\"dblclick.zoom\");\n if (p) p.apply(this, arguments);\n return;\n }\n }\n\n if (started) {\n touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay);\n Object(d3_transition__WEBPACK_IMPORTED_MODULE_4__[\"interrupt\"])(this);\n g.start();\n }\n }\n\n function touchmoved() {\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n n = touches.length, i, t, p, l;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"default\"])();\n if (touchstarting) touchstarting = clearTimeout(touchstarting);\n for (i = 0; i < n; ++i) {\n t = touches[i], p = Object(d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"touch\"])(this, touches, t.identifier);\n if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;\n else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;\n }\n t = g.that.__zoom;\n if (g.touch1) {\n var p0 = g.touch0[0], l0 = g.touch0[1],\n p1 = g.touch1[0], l1 = g.touch1[1],\n dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,\n dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;\n t = scale(t, Math.sqrt(dp / dl));\n p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];\n l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];\n }\n else if (g.touch0) p = g.touch0[0], l = g.touch0[1];\n else return;\n g.zoom(\"touch\", constrain(translate(t, p, l), g.extent, translateExtent));\n }\n\n function touchended() {\n var g = gesture(this, arguments),\n touches = d3_selection__WEBPACK_IMPORTED_MODULE_3__[\"event\"].changedTouches,\n n = touches.length, i, t;\n\n Object(_noevent__WEBPACK_IMPORTED_MODULE_8__[\"nopropagation\"])();\n if (touchending) clearTimeout(touchending);\n touchending = setTimeout(function() { touchending = null; }, touchDelay);\n for (i = 0; i < n; ++i) {\n t = touches[i];\n if (g.touch0 && g.touch0[2] === t.identifier) delete g.touch0;\n else if (g.touch1 && g.touch1[2] === t.identifier) delete g.touch1;\n }\n if (g.touch1 && !g.touch0) g.touch0 = g.touch1, delete g.touch1;\n if (g.touch0) g.touch0[1] = this.__zoom.invert(g.touch0[0]);\n else g.end();\n }\n\n zoom.wheelDelta = function(_) {\n return arguments.length ? (wheelDelta = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(+_), zoom) : wheelDelta;\n };\n\n zoom.filter = function(_) {\n return arguments.length ? (filter = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), zoom) : filter;\n };\n\n zoom.touchable = function(_) {\n return arguments.length ? (touchable = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])(!!_), zoom) : touchable;\n };\n\n zoom.extent = function(_) {\n return arguments.length ? (extent = typeof _ === \"function\" ? _ : Object(_constant__WEBPACK_IMPORTED_MODULE_5__[\"default\"])([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;\n };\n\n zoom.scaleExtent = function(_) {\n return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];\n };\n\n zoom.translateExtent = function(_) {\n return arguments.length ? (translateExtent[0][0] = +_[0][0], translateExtent[1][0] = +_[1][0], translateExtent[0][1] = +_[0][1], translateExtent[1][1] = +_[1][1], zoom) : [[translateExtent[0][0], translateExtent[0][1]], [translateExtent[1][0], translateExtent[1][1]]];\n };\n\n zoom.constrain = function(_) {\n return arguments.length ? (constrain = _, zoom) : constrain;\n };\n\n zoom.duration = function(_) {\n return arguments.length ? (duration = +_, zoom) : duration;\n };\n\n zoom.interpolate = function(_) {\n return arguments.length ? (interpolate = _, zoom) : interpolate;\n };\n\n zoom.on = function() {\n var value = listeners.on.apply(listeners, arguments);\n return value === listeners ? zoom : value;\n };\n\n zoom.clickDistance = function(_) {\n return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);\n };\n\n return zoom;\n});\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3-zoom/src/zoom.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3/build/package.js": +/*!****************************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3/build/package.js ***! + \****************************************************************/ +/*! exports provided: name, version, description, keywords, homepage, license, author, main, unpkg, jsdelivr, module, repository, scripts, devDependencies, dependencies */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"name\", function() { return name; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"version\", function() { return version; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"description\", function() { return description; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"keywords\", function() { return keywords; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"homepage\", function() { return homepage; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"license\", function() { return license; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"author\", function() { return author; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"main\", function() { return main; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"unpkg\", function() { return unpkg; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"jsdelivr\", function() { return jsdelivr; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"module\", function() { return module; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"repository\", function() { return repository; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"scripts\", function() { return scripts; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"devDependencies\", function() { return devDependencies; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"dependencies\", function() { return dependencies; });\nvar name = \"d3\";\nvar version = \"4.13.0\";\nvar description = \"Data-Driven Documents\";\nvar keywords = [\"dom\",\"visualization\",\"svg\",\"animation\",\"canvas\"];\nvar homepage = \"https://d3js.org\";\nvar license = \"BSD-3-Clause\";\nvar author = {\"name\":\"Mike Bostock\",\"url\":\"https://bost.ocks.org/mike\"};\nvar main = \"build/d3.node.js\";\nvar unpkg = \"build/d3.min.js\";\nvar jsdelivr = \"build/d3.min.js\";\nvar module = \"index\";\nvar repository = {\"type\":\"git\",\"url\":\"https://github.com/d3/d3.git\"};\nvar scripts = {\"pretest\":\"rimraf build && mkdir build && json2module package.json > build/package.js && node rollup.node\",\"test\":\"tape 'test/**/*-test.js'\",\"prepublishOnly\":\"npm run test && rollup -c --banner \\\"$(preamble)\\\" && uglifyjs -b beautify=false,preamble=\\\"'$(preamble)'\\\" build/d3.js -c negate_iife=false -m -o build/d3.min.js\",\"postpublish\":\"git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3/build/d3.js d3.v4.js && cp ../d3/build/d3.min.js d3.v4.min.js && git add d3.v4.js d3.v4.min.js && git commit -m \\\"d3 ${npm_package_version}\\\" && git push && cd - && cd ../d3-bower && git pull && cp ../d3/LICENSE ../d3/README.md ../d3/build/d3.js ../d3/build/d3.min.js . && git add -- LICENSE README.md d3.js d3.min.js && git commit -m \\\"${npm_package_version}\\\" && git tag -am \\\"${npm_package_version}\\\" v${npm_package_version} && git push && git push --tags && cd - && zip -j build/d3.zip -- LICENSE README.md API.md CHANGES.md build/d3.js build/d3.min.js\"};\nvar devDependencies = {\"json2module\":\"0.0\",\"package-preamble\":\"0.1\",\"rimraf\":\"2\",\"rollup\":\"0.53\",\"rollup-plugin-ascii\":\"0.0\",\"rollup-plugin-node-resolve\":\"3\",\"tape\":\"4\",\"uglify-js\":\"3.2\"};\nvar dependencies = {\"d3-array\":\"1.2.1\",\"d3-axis\":\"1.0.8\",\"d3-brush\":\"1.0.4\",\"d3-chord\":\"1.0.4\",\"d3-collection\":\"1.0.4\",\"d3-color\":\"1.0.3\",\"d3-dispatch\":\"1.0.3\",\"d3-drag\":\"1.2.1\",\"d3-dsv\":\"1.0.8\",\"d3-ease\":\"1.0.3\",\"d3-force\":\"1.1.0\",\"d3-format\":\"1.2.2\",\"d3-geo\":\"1.9.1\",\"d3-hierarchy\":\"1.1.5\",\"d3-interpolate\":\"1.1.6\",\"d3-path\":\"1.0.5\",\"d3-polygon\":\"1.0.3\",\"d3-quadtree\":\"1.0.3\",\"d3-queue\":\"3.0.7\",\"d3-random\":\"1.1.0\",\"d3-request\":\"1.0.6\",\"d3-scale\":\"1.0.7\",\"d3-selection\":\"1.3.0\",\"d3-shape\":\"1.2.0\",\"d3-time\":\"1.0.8\",\"d3-time-format\":\"2.1.1\",\"d3-timer\":\"1.0.7\",\"d3-transition\":\"1.1.1\",\"d3-voronoi\":\"1.1.2\",\"d3-zoom\":\"1.7.1\"};\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3/build/package.js?"); + +/***/ }), + +/***/ "./node_modules/dagre-d3/node_modules/d3/index.js": +/*!********************************************************!*\ + !*** ./node_modules/dagre-d3/node_modules/d3/index.js ***! + \********************************************************/ +/*! exports provided: version, bisect, bisectRight, bisectLeft, ascending, bisector, cross, descending, deviation, extent, histogram, thresholdFreedmanDiaconis, thresholdScott, thresholdSturges, max, mean, median, merge, min, pairs, permute, quantile, range, scan, shuffle, sum, ticks, tickIncrement, tickStep, transpose, variance, zip, axisTop, axisRight, axisBottom, axisLeft, brush, brushX, brushY, brushSelection, chord, ribbon, nest, set, map, keys, values, entries, color, rgb, hsl, lab, hcl, cubehelix, dispatch, drag, dragDisable, dragEnable, dsvFormat, csvParse, csvParseRows, csvFormat, csvFormatRows, tsvParse, tsvParseRows, tsvFormat, tsvFormatRows, easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut, easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly, easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn, easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut, easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut, easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn, easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut, forceCenter, forceCollide, forceLink, forceManyBody, forceRadial, forceSimulation, forceX, forceY, formatDefaultLocale, format, formatPrefix, formatLocale, formatSpecifier, precisionFixed, precisionPrefix, precisionRound, geoArea, geoBounds, geoCentroid, geoCircle, geoClipAntimeridian, geoClipCircle, geoClipExtent, geoClipRectangle, geoContains, geoDistance, geoGraticule, geoGraticule10, geoInterpolate, geoLength, geoPath, geoAlbers, geoAlbersUsa, geoAzimuthalEqualArea, geoAzimuthalEqualAreaRaw, geoAzimuthalEquidistant, geoAzimuthalEquidistantRaw, geoConicConformal, geoConicConformalRaw, geoConicEqualArea, geoConicEqualAreaRaw, geoConicEquidistant, geoConicEquidistantRaw, geoEquirectangular, geoEquirectangularRaw, geoGnomonic, geoGnomonicRaw, geoIdentity, geoProjection, geoProjectionMutator, geoMercator, geoMercatorRaw, geoNaturalEarth1, geoNaturalEarth1Raw, geoOrthographic, geoOrthographicRaw, geoStereographic, geoStereographicRaw, geoTransverseMercator, geoTransverseMercatorRaw, geoRotation, geoStream, geoTransform, cluster, hierarchy, pack, packSiblings, packEnclose, partition, stratify, tree, treemap, treemapBinary, treemapDice, treemapSlice, treemapSliceDice, treemapSquarify, treemapResquarify, interpolate, interpolateArray, interpolateBasis, interpolateBasisClosed, interpolateDate, interpolateNumber, interpolateObject, interpolateRound, interpolateString, interpolateTransformCss, interpolateTransformSvg, interpolateZoom, interpolateRgb, interpolateRgbBasis, interpolateRgbBasisClosed, interpolateHsl, interpolateHslLong, interpolateLab, interpolateHcl, interpolateHclLong, interpolateCubehelix, interpolateCubehelixLong, quantize, path, polygonArea, polygonCentroid, polygonHull, polygonContains, polygonLength, quadtree, queue, randomUniform, randomNormal, randomLogNormal, randomBates, randomIrwinHall, randomExponential, request, html, json, text, xml, csv, tsv, scaleBand, scalePoint, scaleIdentity, scaleLinear, scaleLog, scaleOrdinal, scaleImplicit, scalePow, scaleSqrt, scaleQuantile, scaleQuantize, scaleThreshold, scaleTime, scaleUtc, schemeCategory10, schemeCategory20b, schemeCategory20c, schemeCategory20, interpolateCubehelixDefault, interpolateRainbow, interpolateWarm, interpolateCool, interpolateViridis, interpolateMagma, interpolateInferno, interpolatePlasma, scaleSequential, create, creator, local, matcher, mouse, namespace, namespaces, clientPoint, select, selectAll, selection, selector, selectorAll, style, touch, touches, window, event, customEvent, arc, area, line, pie, areaRadial, radialArea, lineRadial, radialLine, pointRadial, linkHorizontal, linkVertical, linkRadial, symbol, symbols, symbolCircle, symbolCross, symbolDiamond, symbolSquare, symbolStar, symbolTriangle, symbolWye, curveBasisClosed, curveBasisOpen, curveBasis, curveBundle, curveCardinalClosed, curveCardinalOpen, curveCardinal, curveCatmullRomClosed, curveCatmullRomOpen, curveCatmullRom, curveLinearClosed, curveLinear, curveMonotoneX, curveMonotoneY, curveNatural, curveStep, curveStepAfter, curveStepBefore, stack, stackOffsetExpand, stackOffsetDiverging, stackOffsetNone, stackOffsetSilhouette, stackOffsetWiggle, stackOrderAscending, stackOrderDescending, stackOrderInsideOut, stackOrderNone, stackOrderReverse, timeInterval, timeMillisecond, timeMilliseconds, utcMillisecond, utcMilliseconds, timeSecond, timeSeconds, utcSecond, utcSeconds, timeMinute, timeMinutes, timeHour, timeHours, timeDay, timeDays, timeWeek, timeWeeks, timeSunday, timeSundays, timeMonday, timeMondays, timeTuesday, timeTuesdays, timeWednesday, timeWednesdays, timeThursday, timeThursdays, timeFriday, timeFridays, timeSaturday, timeSaturdays, timeMonth, timeMonths, timeYear, timeYears, utcMinute, utcMinutes, utcHour, utcHours, utcDay, utcDays, utcWeek, utcWeeks, utcSunday, utcSundays, utcMonday, utcMondays, utcTuesday, utcTuesdays, utcWednesday, utcWednesdays, utcThursday, utcThursdays, utcFriday, utcFridays, utcSaturday, utcSaturdays, utcMonth, utcMonths, utcYear, utcYears, timeFormatDefaultLocale, timeFormat, timeParse, utcFormat, utcParse, timeFormatLocale, isoFormat, isoParse, now, timer, timerFlush, timeout, interval, transition, active, interrupt, voronoi, zoom, zoomTransform, zoomIdentity */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _build_package__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./build/package */ \"./node_modules/dagre-d3/node_modules/d3/build/package.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"version\", function() { return _build_package__WEBPACK_IMPORTED_MODULE_0__[\"version\"]; });\n\n/* harmony import */ var d3_array__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! d3-array */ \"./node_modules/dagre-d3/node_modules/d3-array/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisect\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisect\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectRight\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisectRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisectLeft\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisectLeft\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ascending\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"ascending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"bisector\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"bisector\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cross\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"cross\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"descending\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"descending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"deviation\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"deviation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"extent\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"extent\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"histogram\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"histogram\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdFreedmanDiaconis\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdFreedmanDiaconis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdScott\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdScott\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"thresholdSturges\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"thresholdSturges\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"max\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"max\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mean\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"mean\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"median\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"median\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"merge\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"merge\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"min\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"min\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pairs\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"pairs\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"permute\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"permute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantile\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"quantile\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"range\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"range\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scan\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"scan\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"shuffle\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"shuffle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"sum\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"sum\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ticks\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"ticks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickIncrement\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"tickIncrement\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tickStep\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"tickStep\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transpose\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"transpose\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"variance\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"variance\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zip\", function() { return d3_array__WEBPACK_IMPORTED_MODULE_1__[\"zip\"]; });\n\n/* harmony import */ var d3_axis__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! d3-axis */ \"./node_modules/dagre-d3/node_modules/d3-axis/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisTop\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisTop\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisRight\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisRight\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisBottom\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisBottom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"axisLeft\", function() { return d3_axis__WEBPACK_IMPORTED_MODULE_2__[\"axisLeft\"]; });\n\n/* harmony import */ var d3_brush__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! d3-brush */ \"./node_modules/dagre-d3/node_modules/d3-brush/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brush\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brush\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushX\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushY\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"brushSelection\", function() { return d3_brush__WEBPACK_IMPORTED_MODULE_3__[\"brushSelection\"]; });\n\n/* harmony import */ var d3_chord__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! d3-chord */ \"./node_modules/dagre-d3/node_modules/d3-chord/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"chord\", function() { return d3_chord__WEBPACK_IMPORTED_MODULE_4__[\"chord\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"ribbon\", function() { return d3_chord__WEBPACK_IMPORTED_MODULE_4__[\"ribbon\"]; });\n\n/* harmony import */ var d3_collection__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! d3-collection */ \"./node_modules/dagre-d3/node_modules/d3-collection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"nest\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"nest\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"set\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"set\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"map\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"map\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"keys\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"keys\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"values\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"values\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"entries\", function() { return d3_collection__WEBPACK_IMPORTED_MODULE_5__[\"entries\"]; });\n\n/* harmony import */ var d3_color__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! d3-color */ \"./node_modules/dagre-d3/node_modules/d3-color/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"color\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"color\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"rgb\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"rgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hsl\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"hsl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lab\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"lab\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hcl\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"hcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cubehelix\", function() { return d3_color__WEBPACK_IMPORTED_MODULE_6__[\"cubehelix\"]; });\n\n/* harmony import */ var d3_dispatch__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! d3-dispatch */ \"./node_modules/dagre-d3/node_modules/d3-dispatch/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dispatch\", function() { return d3_dispatch__WEBPACK_IMPORTED_MODULE_7__[\"dispatch\"]; });\n\n/* harmony import */ var d3_drag__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! d3-drag */ \"./node_modules/dagre-d3/node_modules/d3-drag/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"drag\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_8__[\"drag\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragDisable\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_8__[\"dragDisable\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dragEnable\", function() { return d3_drag__WEBPACK_IMPORTED_MODULE_8__[\"dragEnable\"]; });\n\n/* harmony import */ var d3_dsv__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! d3-dsv */ \"./node_modules/dagre-d3/node_modules/d3-dsv/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"dsvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"dsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParse\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"csvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvParseRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"csvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"csvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csvFormatRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"csvFormatRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParse\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"tsvParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvParseRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"tsvParseRows\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormat\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"tsvFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsvFormatRows\", function() { return d3_dsv__WEBPACK_IMPORTED_MODULE_9__[\"tsvFormatRows\"]; });\n\n/* harmony import */ var d3_ease__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! d3-ease */ \"./node_modules/dagre-d3/node_modules/d3-ease/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeLinear\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuad\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeQuad\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeQuadIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeQuadOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeQuadInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeQuadInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubic\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCubic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCubicIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCubicOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCubicInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCubicInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePoly\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easePoly\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easePolyIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easePolyOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easePolyInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easePolyInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSin\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeSin\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeSinIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeSinOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeSinInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeSinInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExp\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeExp\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeExpIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeExpOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeExpInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeExpInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircle\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCircleIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCircleOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeCircleInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeCircleInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounce\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBounce\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBounceIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBounceOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBounceInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBounceInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBack\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBackIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBackOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeBackInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeBackInOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElastic\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeElastic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticIn\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeElasticIn\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeElasticOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"easeElasticInOut\", function() { return d3_ease__WEBPACK_IMPORTED_MODULE_10__[\"easeElasticInOut\"]; });\n\n/* harmony import */ var d3_force__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! d3-force */ \"./node_modules/dagre-d3/node_modules/d3-force/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCenter\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceCenter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceCollide\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceCollide\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceLink\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceLink\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceManyBody\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceManyBody\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceRadial\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceSimulation\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceSimulation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceX\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"forceY\", function() { return d3_force__WEBPACK_IMPORTED_MODULE_11__[\"forceY\"]; });\n\n/* harmony import */ var d3_format__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! d3-format */ \"./node_modules/dagre-d3/node_modules/d3-format/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatDefaultLocale\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"formatDefaultLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"format\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"format\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatPrefix\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"formatPrefix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatLocale\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"formatLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"formatSpecifier\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"formatSpecifier\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionFixed\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"precisionFixed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionPrefix\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"precisionPrefix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"precisionRound\", function() { return d3_format__WEBPACK_IMPORTED_MODULE_12__[\"precisionRound\"]; });\n\n/* harmony import */ var d3_geo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! d3-geo */ \"./node_modules/dagre-d3/node_modules/d3-geo/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoBounds\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoBounds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCentroid\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoCentroid\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoCircle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipAntimeridian\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoClipAntimeridian\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipCircle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoClipCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipExtent\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoClipExtent\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoClipRectangle\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoClipRectangle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoContains\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoContains\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoDistance\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoDistance\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoGraticule\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGraticule10\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoGraticule10\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoInterpolate\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoInterpolate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoLength\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoLength\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoPath\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoPath\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbers\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAlbers\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAlbersUsa\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAlbersUsa\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAzimuthalEqualArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEqualAreaRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAzimuthalEqualAreaRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistant\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAzimuthalEquidistant\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoAzimuthalEquidistantRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoAzimuthalEquidistantRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformal\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicConformal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicConformalRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicConformalRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualArea\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicEqualArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEqualAreaRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicEqualAreaRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistant\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicEquidistant\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoConicEquidistantRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoConicEquidistantRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangular\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoEquirectangular\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoEquirectangularRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoEquirectangularRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoGnomonic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoGnomonicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoGnomonicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoIdentity\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoIdentity\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjection\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoProjection\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoProjectionMutator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoProjectionMutator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoMercator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoMercatorRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoMercatorRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoNaturalEarth1\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoNaturalEarth1Raw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoNaturalEarth1Raw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoOrthographic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoOrthographicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoOrthographicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographic\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoStereographic\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStereographicRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoStereographicRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercator\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoTransverseMercator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransverseMercatorRaw\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoTransverseMercatorRaw\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoRotation\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoRotation\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoStream\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoStream\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"geoTransform\", function() { return d3_geo__WEBPACK_IMPORTED_MODULE_13__[\"geoTransform\"]; });\n\n/* harmony import */ var d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! d3-hierarchy */ \"./node_modules/dagre-d3/node_modules/d3-hierarchy/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"cluster\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"cluster\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"hierarchy\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"hierarchy\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pack\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"pack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packSiblings\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"packSiblings\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"packEnclose\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"packEnclose\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"partition\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"partition\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stratify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"stratify\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tree\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"tree\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemap\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemap\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapBinary\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapBinary\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapDice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapDice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSlice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapSlice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSliceDice\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapSliceDice\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapSquarify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapSquarify\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"treemapResquarify\", function() { return d3_hierarchy__WEBPACK_IMPORTED_MODULE_14__[\"treemapResquarify\"]; });\n\n/* harmony import */ var d3_interpolate__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! d3-interpolate */ \"./node_modules/dagre-d3/node_modules/d3-interpolate/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolate\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateArray\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateArray\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasis\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateBasisClosed\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateDate\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateDate\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateNumber\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateNumber\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateObject\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateObject\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRound\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateRound\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateString\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateString\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformCss\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateTransformCss\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateTransformSvg\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateTransformSvg\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateZoom\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateZoom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgb\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateRgb\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasis\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateRgbBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRgbBasisClosed\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateRgbBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHsl\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateHsl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHslLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateHslLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateLab\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateLab\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHcl\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateHcl\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateHclLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateHclLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelix\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateCubehelix\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixLong\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"interpolateCubehelixLong\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quantize\", function() { return d3_interpolate__WEBPACK_IMPORTED_MODULE_15__[\"quantize\"]; });\n\n/* harmony import */ var d3_path__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! d3-path */ \"./node_modules/dagre-d3/node_modules/d3-path/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"path\", function() { return d3_path__WEBPACK_IMPORTED_MODULE_16__[\"path\"]; });\n\n/* harmony import */ var d3_polygon__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! d3-polygon */ \"./node_modules/dagre-d3/node_modules/d3-polygon/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonArea\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_17__[\"polygonArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonCentroid\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_17__[\"polygonCentroid\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonHull\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_17__[\"polygonHull\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonContains\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_17__[\"polygonContains\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"polygonLength\", function() { return d3_polygon__WEBPACK_IMPORTED_MODULE_17__[\"polygonLength\"]; });\n\n/* harmony import */ var d3_quadtree__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(/*! d3-quadtree */ \"./node_modules/dagre-d3/node_modules/d3-quadtree/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"quadtree\", function() { return d3_quadtree__WEBPACK_IMPORTED_MODULE_18__[\"quadtree\"]; });\n\n/* harmony import */ var d3_queue__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(/*! d3-queue */ \"./node_modules/d3-queue/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"queue\", function() { return d3_queue__WEBPACK_IMPORTED_MODULE_19__[\"queue\"]; });\n\n/* harmony import */ var d3_random__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(/*! d3-random */ \"./node_modules/dagre-d3/node_modules/d3-random/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomUniform\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomUniform\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomNormal\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomNormal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomLogNormal\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomLogNormal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomBates\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomBates\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomIrwinHall\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomIrwinHall\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"randomExponential\", function() { return d3_random__WEBPACK_IMPORTED_MODULE_20__[\"randomExponential\"]; });\n\n/* harmony import */ var d3_request__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(/*! d3-request */ \"./node_modules/d3-request/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"request\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"request\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"html\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"html\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"json\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"json\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"text\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"text\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"xml\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"xml\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"csv\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"csv\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"tsv\", function() { return d3_request__WEBPACK_IMPORTED_MODULE_21__[\"tsv\"]; });\n\n/* harmony import */ var d3_scale__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(/*! d3-scale */ \"./node_modules/dagre-d3/node_modules/d3-scale/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleBand\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleBand\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePoint\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scalePoint\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleIdentity\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleIdentity\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLinear\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleLog\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleLog\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleOrdinal\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleOrdinal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleImplicit\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleImplicit\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scalePow\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scalePow\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSqrt\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleSqrt\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantile\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleQuantile\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleQuantize\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleQuantize\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleThreshold\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleThreshold\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleTime\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleTime\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleUtc\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleUtc\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory10\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"schemeCategory10\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20b\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"schemeCategory20b\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20c\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"schemeCategory20c\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"schemeCategory20\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"schemeCategory20\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCubehelixDefault\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateCubehelixDefault\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateRainbow\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateRainbow\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateWarm\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateWarm\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateCool\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateCool\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateViridis\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateViridis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateMagma\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateMagma\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolateInferno\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolateInferno\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interpolatePlasma\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"interpolatePlasma\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"scaleSequential\", function() { return d3_scale__WEBPACK_IMPORTED_MODULE_22__[\"scaleSequential\"]; });\n\n/* harmony import */ var d3_selection__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(/*! d3-selection */ \"./node_modules/dagre-d3/node_modules/d3-selection/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"create\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"create\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"creator\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"creator\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"local\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"local\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"matcher\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"matcher\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"mouse\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"mouse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespace\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"namespace\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"namespaces\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"namespaces\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"clientPoint\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"clientPoint\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"select\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"select\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectAll\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"selectAll\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selection\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"selection\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selector\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"selector\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"selectorAll\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"selectorAll\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"style\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"style\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touch\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"touch\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"touches\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"touches\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"window\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"window\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"event\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"event\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"customEvent\", function() { return d3_selection__WEBPACK_IMPORTED_MODULE_23__[\"customEvent\"]; });\n\n/* harmony import */ var d3_shape__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(/*! d3-shape */ \"./node_modules/dagre-d3/node_modules/d3-shape/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"arc\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"arc\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"area\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"area\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"line\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"line\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pie\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"pie\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"areaRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"areaRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialArea\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"radialArea\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"lineRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"lineRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"radialLine\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"radialLine\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"pointRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"pointRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkHorizontal\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"linkHorizontal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkVertical\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"linkVertical\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"linkRadial\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"linkRadial\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbol\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbol\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbols\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbols\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCircle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolCircle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolCross\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolCross\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolDiamond\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolDiamond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolSquare\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolSquare\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolStar\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolStar\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolTriangle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolTriangle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"symbolWye\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"symbolWye\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveBasisClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasisOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveBasisOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBasis\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveBasis\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveBundle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveBundle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCardinalClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinalOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCardinalOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCardinal\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCardinal\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCatmullRomClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRomOpen\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCatmullRomOpen\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveCatmullRom\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveCatmullRom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinearClosed\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveLinearClosed\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveLinear\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveLinear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneX\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveMonotoneX\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveMonotoneY\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveMonotoneY\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveNatural\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveNatural\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStep\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveStep\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepAfter\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveStepAfter\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"curveStepBefore\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"curveStepBefore\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stack\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stack\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetExpand\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOffsetExpand\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetDiverging\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOffsetDiverging\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetNone\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOffsetNone\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetSilhouette\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOffsetSilhouette\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOffsetWiggle\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOffsetWiggle\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderAscending\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOrderAscending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderDescending\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOrderDescending\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderInsideOut\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOrderInsideOut\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderNone\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOrderNone\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"stackOrderReverse\", function() { return d3_shape__WEBPACK_IMPORTED_MODULE_24__[\"stackOrderReverse\"]; });\n\n/* harmony import */ var d3_time__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(/*! d3-time */ \"./node_modules/dagre-d3/node_modules/d3-time/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeInterval\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeInterval\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMillisecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMillisecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMilliseconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMilliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMillisecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMillisecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMilliseconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMilliseconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSeconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSeconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSecond\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSecond\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSeconds\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSeconds\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinute\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMinute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMinutes\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMinutes\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHour\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeHour\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeHours\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeHours\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDay\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeDay\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeDays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeDays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeek\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeWeek\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWeeks\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeWeeks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSunday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSundays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMondays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeTuesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeWednesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeThursdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFriday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFridays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeSaturdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeSaturdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonth\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMonth\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeMonths\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeMonths\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYear\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeYear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeYears\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"timeYears\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinute\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMinute\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMinutes\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMinutes\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHour\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcHour\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcHours\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcHours\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDay\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcDay\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcDays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcDays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeek\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcWeek\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWeeks\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcWeeks\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSunday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSunday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSundays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSundays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMonday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMondays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMondays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcTuesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcTuesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcTuesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcWednesday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcWednesdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcWednesdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcThursday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcThursdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcThursdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFriday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcFriday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFridays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcFridays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturday\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSaturday\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcSaturdays\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcSaturdays\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonth\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMonth\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcMonths\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcMonths\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYear\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcYear\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcYears\", function() { return d3_time__WEBPACK_IMPORTED_MODULE_25__[\"utcYears\"]; });\n\n/* harmony import */ var d3_time_format__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(/*! d3-time-format */ \"./node_modules/dagre-d3/node_modules/d3-time-format/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatDefaultLocale\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"timeFormatDefaultLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"timeFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"timeParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"utcFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"utcParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"utcParse\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeFormatLocale\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"timeFormatLocale\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoFormat\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"isoFormat\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"isoParse\", function() { return d3_time_format__WEBPACK_IMPORTED_MODULE_26__[\"isoParse\"]; });\n\n/* harmony import */ var d3_timer__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(/*! d3-timer */ \"./node_modules/dagre-d3/node_modules/d3-timer/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"now\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_27__[\"now\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timer\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_27__[\"timer\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timerFlush\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_27__[\"timerFlush\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"timeout\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_27__[\"timeout\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interval\", function() { return d3_timer__WEBPACK_IMPORTED_MODULE_27__[\"interval\"]; });\n\n/* harmony import */ var d3_transition__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(/*! d3-transition */ \"./node_modules/dagre-d3/node_modules/d3-transition/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"transition\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_28__[\"transition\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"active\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_28__[\"active\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"interrupt\", function() { return d3_transition__WEBPACK_IMPORTED_MODULE_28__[\"interrupt\"]; });\n\n/* harmony import */ var d3_voronoi__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(/*! d3-voronoi */ \"./node_modules/dagre-d3/node_modules/d3-voronoi/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"voronoi\", function() { return d3_voronoi__WEBPACK_IMPORTED_MODULE_29__[\"voronoi\"]; });\n\n/* harmony import */ var d3_zoom__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(/*! d3-zoom */ \"./node_modules/dagre-d3/node_modules/d3-zoom/index.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoom\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_30__[\"zoom\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomTransform\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_30__[\"zoomTransform\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"zoomIdentity\", function() { return d3_zoom__WEBPACK_IMPORTED_MODULE_30__[\"zoomIdentity\"]; });\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre-d3/node_modules/d3/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/index.js": +/*!*************************************!*\ + !*** ./node_modules/dagre/index.js ***! + \*************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/*\nCopyright (c) 2012-2014 Chris Pettitt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\n\nmodule.exports = {\n graphlib: __webpack_require__(/*! ./lib/graphlib */ \"./node_modules/dagre/lib/graphlib.js\"),\n\n layout: __webpack_require__(/*! ./lib/layout */ \"./node_modules/dagre/lib/layout.js\"),\n debug: __webpack_require__(/*! ./lib/debug */ \"./node_modules/dagre/lib/debug.js\"),\n util: {\n time: __webpack_require__(/*! ./lib/util */ \"./node_modules/dagre/lib/util.js\").time,\n notime: __webpack_require__(/*! ./lib/util */ \"./node_modules/dagre/lib/util.js\").notime\n },\n version: __webpack_require__(/*! ./lib/version */ \"./node_modules/dagre/lib/version.js\")\n};\n\n\n//# sourceURL=webpack:///./node_modules/dagre/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/acyclic.js": +/*!*******************************************!*\ + !*** ./node_modules/dagre/lib/acyclic.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n greedyFAS = __webpack_require__(/*! ./greedy-fas */ \"./node_modules/dagre/lib/greedy-fas.js\");\n\nmodule.exports = {\n run: run,\n undo: undo\n};\n\nfunction run(g) {\n var fas = (g.graph().acyclicer === \"greedy\"\n ? greedyFAS(g, weightFn(g))\n : dfsFAS(g));\n _.forEach(fas, function(e) {\n var label = g.edge(e);\n g.removeEdge(e);\n label.forwardName = e.name;\n label.reversed = true;\n g.setEdge(e.w, e.v, label, _.uniqueId(\"rev\"));\n });\n\n function weightFn(g) {\n return function(e) {\n return g.edge(e).weight;\n };\n }\n}\n\nfunction dfsFAS(g) {\n var fas = [],\n stack = {},\n visited = {};\n\n function dfs(v) {\n if (_.has(visited, v)) {\n return;\n }\n visited[v] = true;\n stack[v] = true;\n _.forEach(g.outEdges(v), function(e) {\n if (_.has(stack, e.w)) {\n fas.push(e);\n } else {\n dfs(e.w);\n }\n });\n delete stack[v];\n }\n\n _.forEach(g.nodes(), dfs);\n return fas;\n}\n\nfunction undo(g) {\n _.forEach(g.edges(), function(e) {\n var label = g.edge(e);\n if (label.reversed) {\n g.removeEdge(e);\n\n var forwardName = label.forwardName;\n delete label.reversed;\n delete label.forwardName;\n g.setEdge(e.w, e.v, label, forwardName);\n }\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/acyclic.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/add-border-segments.js": +/*!*******************************************************!*\ + !*** ./node_modules/dagre/lib/add-border-segments.js ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\");\n\nmodule.exports = addBorderSegments;\n\nfunction addBorderSegments(g) {\n function dfs(v) {\n var children = g.children(v),\n node = g.node(v);\n if (children.length) {\n _.forEach(children, dfs);\n }\n\n if (_.has(node, \"minRank\")) {\n node.borderLeft = [];\n node.borderRight = [];\n for (var rank = node.minRank, maxRank = node.maxRank + 1;\n rank < maxRank;\n ++rank) {\n addBorderNode(g, \"borderLeft\", \"_bl\", v, node, rank);\n addBorderNode(g, \"borderRight\", \"_br\", v, node, rank);\n }\n }\n }\n\n _.forEach(g.children(), dfs);\n}\n\nfunction addBorderNode(g, prop, prefix, sg, sgNode, rank) {\n var label = { width: 0, height: 0, rank: rank, borderType: prop },\n prev = sgNode[prop][rank - 1],\n curr = util.addDummyNode(g, \"border\", label, prefix);\n sgNode[prop][rank] = curr;\n g.setParent(curr, sg);\n if (prev) {\n g.setEdge(prev, curr, { weight: 1 });\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/add-border-segments.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/coordinate-system.js": +/*!*****************************************************!*\ + !*** ./node_modules/dagre/lib/coordinate-system.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = {\n adjust: adjust,\n undo: undo\n};\n\nfunction adjust(g) {\n var rankDir = g.graph().rankdir.toLowerCase();\n if (rankDir === \"lr\" || rankDir === \"rl\") {\n swapWidthHeight(g);\n }\n}\n\nfunction undo(g) {\n var rankDir = g.graph().rankdir.toLowerCase();\n if (rankDir === \"bt\" || rankDir === \"rl\") {\n reverseY(g);\n }\n\n if (rankDir === \"lr\" || rankDir === \"rl\") {\n swapXY(g);\n swapWidthHeight(g);\n }\n}\n\nfunction swapWidthHeight(g) {\n _.forEach(g.nodes(), function(v) { swapWidthHeightOne(g.node(v)); });\n _.forEach(g.edges(), function(e) { swapWidthHeightOne(g.edge(e)); });\n}\n\nfunction swapWidthHeightOne(attrs) {\n var w = attrs.width;\n attrs.width = attrs.height;\n attrs.height = w;\n}\n\nfunction reverseY(g) {\n _.forEach(g.nodes(), function(v) { reverseYOne(g.node(v)); });\n\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n _.forEach(edge.points, reverseYOne);\n if (_.has(edge, \"y\")) {\n reverseYOne(edge);\n }\n });\n}\n\nfunction reverseYOne(attrs) {\n attrs.y = -attrs.y;\n}\n\nfunction swapXY(g) {\n _.forEach(g.nodes(), function(v) { swapXYOne(g.node(v)); });\n\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n _.forEach(edge.points, swapXYOne);\n if (_.has(edge, \"x\")) {\n swapXYOne(edge);\n }\n });\n}\n\nfunction swapXYOne(attrs) {\n var x = attrs.x;\n attrs.x = attrs.y;\n attrs.y = x;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/coordinate-system.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/data/list.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre/lib/data/list.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("/*\n * Simple doubly linked list implementation derived from Cormen, et al.,\n * \"Introduction to Algorithms\".\n */\n\nmodule.exports = List;\n\nfunction List() {\n var sentinel = {};\n sentinel._next = sentinel._prev = sentinel;\n this._sentinel = sentinel;\n}\n\nList.prototype.dequeue = function() {\n var sentinel = this._sentinel,\n entry = sentinel._prev;\n if (entry !== sentinel) {\n unlink(entry);\n return entry;\n }\n};\n\nList.prototype.enqueue = function(entry) {\n var sentinel = this._sentinel;\n if (entry._prev && entry._next) {\n unlink(entry);\n }\n entry._next = sentinel._next;\n sentinel._next._prev = entry;\n sentinel._next = entry;\n entry._prev = sentinel;\n};\n\nList.prototype.toString = function() {\n var strs = [],\n sentinel = this._sentinel,\n curr = sentinel._prev;\n while (curr !== sentinel) {\n strs.push(JSON.stringify(curr, filterOutLinks));\n curr = curr._prev;\n }\n return \"[\" + strs.join(\", \") + \"]\";\n};\n\nfunction unlink(entry) {\n entry._prev._next = entry._next;\n entry._next._prev = entry._prev;\n delete entry._next;\n delete entry._prev;\n}\n\nfunction filterOutLinks(k, v) {\n if (k !== \"_next\" && k !== \"_prev\") {\n return v;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/data/list.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/debug.js": +/*!*****************************************!*\ + !*** ./node_modules/dagre/lib/debug.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\"),\n Graph = __webpack_require__(/*! ./graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph;\n\nmodule.exports = {\n debugOrdering: debugOrdering\n};\n\n/* istanbul ignore next */\nfunction debugOrdering(g) {\n var layerMatrix = util.buildLayerMatrix(g);\n\n var h = new Graph({ compound: true, multigraph: true }).setGraph({});\n\n _.forEach(g.nodes(), function(v) {\n h.setNode(v, { label: v });\n h.setParent(v, \"layer\" + g.node(v).rank);\n });\n\n _.forEach(g.edges(), function(e) {\n h.setEdge(e.v, e.w, {}, e.name);\n });\n\n _.forEach(layerMatrix, function(layer, i) {\n var layerV = \"layer\" + i;\n h.setNode(layerV, { rank: \"same\" });\n _.reduce(layer, function(u, v) {\n h.setEdge(u, v, { style: \"invis\" });\n return v;\n });\n });\n\n return h;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/debug.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/graphlib.js": +/*!********************************************!*\ + !*** ./node_modules/dagre/lib/graphlib.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar graphlib;\n\nif (true) {\n try {\n graphlib = __webpack_require__(/*! graphlib */ \"./node_modules/graphlib/index.js\");\n } catch (e) {}\n}\n\nif (!graphlib) {\n graphlib = window.graphlib;\n}\n\nmodule.exports = graphlib;\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/graphlib.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/greedy-fas.js": +/*!**********************************************!*\ + !*** ./node_modules/dagre/lib/greedy-fas.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ./graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph,\n List = __webpack_require__(/*! ./data/list */ \"./node_modules/dagre/lib/data/list.js\");\n\n/*\n * A greedy heuristic for finding a feedback arc set for a graph. A feedback\n * arc set is a set of edges that can be removed to make a graph acyclic.\n * The algorithm comes from: P. Eades, X. Lin, and W. F. Smyth, \"A fast and\n * effective heuristic for the feedback arc set problem.\" This implementation\n * adjusts that from the paper to allow for weighted edges.\n */\nmodule.exports = greedyFAS;\n\nvar DEFAULT_WEIGHT_FN = _.constant(1);\n\nfunction greedyFAS(g, weightFn) {\n if (g.nodeCount() <= 1) {\n return [];\n }\n var state = buildState(g, weightFn || DEFAULT_WEIGHT_FN);\n var results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx);\n\n // Expand multi-edges\n return _.flatten(_.map(results, function(e) {\n return g.outEdges(e.v, e.w);\n }), true);\n}\n\nfunction doGreedyFAS(g, buckets, zeroIdx) {\n var results = [],\n sources = buckets[buckets.length - 1],\n sinks = buckets[0];\n\n var entry;\n while (g.nodeCount()) {\n while ((entry = sinks.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }\n while ((entry = sources.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }\n if (g.nodeCount()) {\n for (var i = buckets.length - 2; i > 0; --i) {\n entry = buckets[i].dequeue();\n if (entry) {\n results = results.concat(removeNode(g, buckets, zeroIdx, entry, true));\n break;\n }\n }\n }\n }\n\n return results;\n}\n\nfunction removeNode(g, buckets, zeroIdx, entry, collectPredecessors) {\n var results = collectPredecessors ? [] : undefined;\n\n _.forEach(g.inEdges(entry.v), function(edge) {\n var weight = g.edge(edge),\n uEntry = g.node(edge.v);\n\n if (collectPredecessors) {\n results.push({ v: edge.v, w: edge.w });\n }\n\n uEntry.out -= weight;\n assignBucket(buckets, zeroIdx, uEntry);\n });\n\n _.forEach(g.outEdges(entry.v), function(edge) {\n var weight = g.edge(edge),\n w = edge.w,\n wEntry = g.node(w);\n wEntry[\"in\"] -= weight;\n assignBucket(buckets, zeroIdx, wEntry);\n });\n\n g.removeNode(entry.v);\n\n return results;\n}\n\nfunction buildState(g, weightFn) {\n var fasGraph = new Graph(),\n maxIn = 0,\n maxOut = 0;\n\n _.forEach(g.nodes(), function(v) {\n fasGraph.setNode(v, { v: v, \"in\": 0, out: 0 });\n });\n\n // Aggregate weights on nodes, but also sum the weights across multi-edges\n // into a single edge for the fasGraph.\n _.forEach(g.edges(), function(e) {\n var prevWeight = fasGraph.edge(e.v, e.w) || 0,\n weight = weightFn(e),\n edgeWeight = prevWeight + weight;\n fasGraph.setEdge(e.v, e.w, edgeWeight);\n maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight);\n maxIn = Math.max(maxIn, fasGraph.node(e.w)[\"in\"] += weight);\n });\n\n var buckets = _.range(maxOut + maxIn + 3).map(function() { return new List(); });\n var zeroIdx = maxIn + 1;\n\n _.forEach(fasGraph.nodes(), function(v) {\n assignBucket(buckets, zeroIdx, fasGraph.node(v));\n });\n\n return { graph: fasGraph, buckets: buckets, zeroIdx: zeroIdx };\n}\n\nfunction assignBucket(buckets, zeroIdx, entry) {\n if (!entry.out) {\n buckets[0].enqueue(entry);\n } else if (!entry[\"in\"]) {\n buckets[buckets.length - 1].enqueue(entry);\n } else {\n buckets[entry.out - entry[\"in\"] + zeroIdx].enqueue(entry);\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/greedy-fas.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/layout.js": +/*!******************************************!*\ + !*** ./node_modules/dagre/lib/layout.js ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n acyclic = __webpack_require__(/*! ./acyclic */ \"./node_modules/dagre/lib/acyclic.js\"),\n normalize = __webpack_require__(/*! ./normalize */ \"./node_modules/dagre/lib/normalize.js\"),\n rank = __webpack_require__(/*! ./rank */ \"./node_modules/dagre/lib/rank/index.js\"),\n normalizeRanks = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\").normalizeRanks,\n parentDummyChains = __webpack_require__(/*! ./parent-dummy-chains */ \"./node_modules/dagre/lib/parent-dummy-chains.js\"),\n removeEmptyRanks = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\").removeEmptyRanks,\n nestingGraph = __webpack_require__(/*! ./nesting-graph */ \"./node_modules/dagre/lib/nesting-graph.js\"),\n addBorderSegments = __webpack_require__(/*! ./add-border-segments */ \"./node_modules/dagre/lib/add-border-segments.js\"),\n coordinateSystem = __webpack_require__(/*! ./coordinate-system */ \"./node_modules/dagre/lib/coordinate-system.js\"),\n order = __webpack_require__(/*! ./order */ \"./node_modules/dagre/lib/order/index.js\"),\n position = __webpack_require__(/*! ./position */ \"./node_modules/dagre/lib/position/index.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\"),\n Graph = __webpack_require__(/*! ./graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph;\n\nmodule.exports = layout;\n\nfunction layout(g, opts) {\n var time = opts && opts.debugTiming ? util.time : util.notime;\n time(\"layout\", function() {\n var layoutGraph = time(\" buildLayoutGraph\",\n function() { return buildLayoutGraph(g); });\n time(\" runLayout\", function() { runLayout(layoutGraph, time); });\n time(\" updateInputGraph\", function() { updateInputGraph(g, layoutGraph); });\n });\n}\n\nfunction runLayout(g, time) {\n time(\" makeSpaceForEdgeLabels\", function() { makeSpaceForEdgeLabels(g); });\n time(\" removeSelfEdges\", function() { removeSelfEdges(g); });\n time(\" acyclic\", function() { acyclic.run(g); });\n time(\" nestingGraph.run\", function() { nestingGraph.run(g); });\n time(\" rank\", function() { rank(util.asNonCompoundGraph(g)); });\n time(\" injectEdgeLabelProxies\", function() { injectEdgeLabelProxies(g); });\n time(\" removeEmptyRanks\", function() { removeEmptyRanks(g); });\n time(\" nestingGraph.cleanup\", function() { nestingGraph.cleanup(g); });\n time(\" normalizeRanks\", function() { normalizeRanks(g); });\n time(\" assignRankMinMax\", function() { assignRankMinMax(g); });\n time(\" removeEdgeLabelProxies\", function() { removeEdgeLabelProxies(g); });\n time(\" normalize.run\", function() { normalize.run(g); });\n time(\" parentDummyChains\", function() { parentDummyChains(g); });\n time(\" addBorderSegments\", function() { addBorderSegments(g); });\n time(\" order\", function() { order(g); });\n time(\" insertSelfEdges\", function() { insertSelfEdges(g); });\n time(\" adjustCoordinateSystem\", function() { coordinateSystem.adjust(g); });\n time(\" position\", function() { position(g); });\n time(\" positionSelfEdges\", function() { positionSelfEdges(g); });\n time(\" removeBorderNodes\", function() { removeBorderNodes(g); });\n time(\" normalize.undo\", function() { normalize.undo(g); });\n time(\" fixupEdgeLabelCoords\", function() { fixupEdgeLabelCoords(g); });\n time(\" undoCoordinateSystem\", function() { coordinateSystem.undo(g); });\n time(\" translateGraph\", function() { translateGraph(g); });\n time(\" assignNodeIntersects\", function() { assignNodeIntersects(g); });\n time(\" reversePoints\", function() { reversePointsForReversedEdges(g); });\n time(\" acyclic.undo\", function() { acyclic.undo(g); });\n}\n\n/*\n * Copies final layout information from the layout graph back to the input\n * graph. This process only copies whitelisted attributes from the layout graph\n * to the input graph, so it serves as a good place to determine what\n * attributes can influence layout.\n */\nfunction updateInputGraph(inputGraph, layoutGraph) {\n _.forEach(inputGraph.nodes(), function(v) {\n var inputLabel = inputGraph.node(v),\n layoutLabel = layoutGraph.node(v);\n\n if (inputLabel) {\n inputLabel.x = layoutLabel.x;\n inputLabel.y = layoutLabel.y;\n\n if (layoutGraph.children(v).length) {\n inputLabel.width = layoutLabel.width;\n inputLabel.height = layoutLabel.height;\n }\n }\n });\n\n _.forEach(inputGraph.edges(), function(e) {\n var inputLabel = inputGraph.edge(e),\n layoutLabel = layoutGraph.edge(e);\n\n inputLabel.points = layoutLabel.points;\n if (_.has(layoutLabel, \"x\")) {\n inputLabel.x = layoutLabel.x;\n inputLabel.y = layoutLabel.y;\n }\n });\n\n inputGraph.graph().width = layoutGraph.graph().width;\n inputGraph.graph().height = layoutGraph.graph().height;\n}\n\nvar graphNumAttrs = [\"nodesep\", \"edgesep\", \"ranksep\", \"marginx\", \"marginy\"],\n graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: \"tb\" },\n graphAttrs = [\"acyclicer\", \"ranker\", \"rankdir\", \"align\"],\n nodeNumAttrs = [\"width\", \"height\"],\n nodeDefaults = { width: 0, height: 0 },\n edgeNumAttrs = [\"minlen\", \"weight\", \"width\", \"height\", \"labeloffset\"],\n edgeDefaults = {\n minlen: 1, weight: 1, width: 0, height: 0,\n labeloffset: 10, labelpos: \"r\"\n },\n edgeAttrs = [\"labelpos\"];\n\n/*\n * Constructs a new graph from the input graph, which can be used for layout.\n * This process copies only whitelisted attributes from the input graph to the\n * layout graph. Thus this function serves as a good place to determine what\n * attributes can influence layout.\n */\nfunction buildLayoutGraph(inputGraph) {\n var g = new Graph({ multigraph: true, compound: true }),\n graph = canonicalize(inputGraph.graph());\n\n g.setGraph(_.merge({},\n graphDefaults,\n selectNumberAttrs(graph, graphNumAttrs),\n _.pick(graph, graphAttrs)));\n\n _.forEach(inputGraph.nodes(), function(v) {\n var node = canonicalize(inputGraph.node(v));\n g.setNode(v, _.defaults(selectNumberAttrs(node, nodeNumAttrs), nodeDefaults));\n g.setParent(v, inputGraph.parent(v));\n });\n\n _.forEach(inputGraph.edges(), function(e) {\n var edge = canonicalize(inputGraph.edge(e));\n g.setEdge(e, _.merge({},\n edgeDefaults,\n selectNumberAttrs(edge, edgeNumAttrs),\n _.pick(edge, edgeAttrs)));\n });\n\n return g;\n}\n\n/*\n * This idea comes from the Gansner paper: to account for edge labels in our\n * layout we split each rank in half by doubling minlen and halving ranksep.\n * Then we can place labels at these mid-points between nodes.\n *\n * We also add some minimal padding to the width to push the label for the edge\n * away from the edge itself a bit.\n */\nfunction makeSpaceForEdgeLabels(g) {\n var graph = g.graph();\n graph.ranksep /= 2;\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n edge.minlen *= 2;\n if (edge.labelpos.toLowerCase() !== \"c\") {\n if (graph.rankdir === \"TB\" || graph.rankdir === \"BT\") {\n edge.width += edge.labeloffset;\n } else {\n edge.height += edge.labeloffset;\n }\n }\n });\n}\n\n/*\n * Creates temporary dummy nodes that capture the rank in which each edge's\n * label is going to, if it has one of non-zero width and height. We do this\n * so that we can safely remove empty ranks while preserving balance for the\n * label's position.\n */\nfunction injectEdgeLabelProxies(g) {\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n if (edge.width && edge.height) {\n var v = g.node(e.v),\n w = g.node(e.w),\n label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e };\n util.addDummyNode(g, \"edge-proxy\", label, \"_ep\");\n }\n });\n}\n\nfunction assignRankMinMax(g) {\n var maxRank = 0;\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v);\n if (node.borderTop) {\n node.minRank = g.node(node.borderTop).rank;\n node.maxRank = g.node(node.borderBottom).rank;\n maxRank = _.max(maxRank, node.maxRank);\n }\n });\n g.graph().maxRank = maxRank;\n}\n\nfunction removeEdgeLabelProxies(g) {\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v);\n if (node.dummy === \"edge-proxy\") {\n g.edge(node.e).labelRank = node.rank;\n g.removeNode(v);\n }\n });\n}\n\nfunction translateGraph(g) {\n var minX = Number.POSITIVE_INFINITY,\n maxX = 0,\n minY = Number.POSITIVE_INFINITY,\n maxY = 0,\n graphLabel = g.graph(),\n marginX = graphLabel.marginx || 0,\n marginY = graphLabel.marginy || 0;\n\n function getExtremes(attrs) {\n var x = attrs.x,\n y = attrs.y,\n w = attrs.width,\n h = attrs.height;\n minX = Math.min(minX, x - w / 2);\n maxX = Math.max(maxX, x + w / 2);\n minY = Math.min(minY, y - h / 2);\n maxY = Math.max(maxY, y + h / 2);\n }\n\n _.forEach(g.nodes(), function(v) { getExtremes(g.node(v)); });\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n if (_.has(edge, \"x\")) {\n getExtremes(edge);\n }\n });\n\n minX -= marginX;\n minY -= marginY;\n\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v);\n node.x -= minX;\n node.y -= minY;\n });\n\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n _.forEach(edge.points, function(p) {\n p.x -= minX;\n p.y -= minY;\n });\n if (_.has(edge, \"x\")) { edge.x -= minX; }\n if (_.has(edge, \"y\")) { edge.y -= minY; }\n });\n\n graphLabel.width = maxX - minX + marginX;\n graphLabel.height = maxY - minY + marginY;\n}\n\nfunction assignNodeIntersects(g) {\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e),\n nodeV = g.node(e.v),\n nodeW = g.node(e.w),\n p1, p2;\n if (!edge.points) {\n edge.points = [];\n p1 = nodeW;\n p2 = nodeV;\n } else {\n p1 = edge.points[0];\n p2 = edge.points[edge.points.length - 1];\n }\n edge.points.unshift(util.intersectRect(nodeV, p1));\n edge.points.push(util.intersectRect(nodeW, p2));\n });\n}\n\nfunction fixupEdgeLabelCoords(g) {\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n if (_.has(edge, \"x\")) {\n if (edge.labelpos === \"l\" || edge.labelpos === \"r\") {\n edge.width -= edge.labeloffset;\n }\n switch (edge.labelpos) {\n case \"l\": edge.x -= edge.width / 2 + edge.labeloffset; break;\n case \"r\": edge.x += edge.width / 2 + edge.labeloffset; break;\n }\n }\n });\n}\n\nfunction reversePointsForReversedEdges(g) {\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n if (edge.reversed) {\n edge.points.reverse();\n }\n });\n}\n\nfunction removeBorderNodes(g) {\n _.forEach(g.nodes(), function(v) {\n if (g.children(v).length) {\n var node = g.node(v),\n t = g.node(node.borderTop),\n b = g.node(node.borderBottom),\n l = g.node(_.last(node.borderLeft)),\n r = g.node(_.last(node.borderRight));\n\n node.width = Math.abs(r.x - l.x);\n node.height = Math.abs(b.y - t.y);\n node.x = l.x + node.width / 2;\n node.y = t.y + node.height / 2;\n }\n });\n\n _.forEach(g.nodes(), function(v) {\n if (g.node(v).dummy === \"border\") {\n g.removeNode(v);\n }\n });\n}\n\nfunction removeSelfEdges(g) {\n _.forEach(g.edges(), function(e) {\n if (e.v === e.w) {\n var node = g.node(e.v);\n if (!node.selfEdges) {\n node.selfEdges = [];\n }\n node.selfEdges.push({ e: e, label: g.edge(e) });\n g.removeEdge(e);\n }\n });\n}\n\nfunction insertSelfEdges(g) {\n var layers = util.buildLayerMatrix(g);\n _.forEach(layers, function(layer) {\n var orderShift = 0;\n _.forEach(layer, function(v, i) {\n var node = g.node(v);\n node.order = i + orderShift;\n _.forEach(node.selfEdges, function(selfEdge) {\n util.addDummyNode(g, \"selfedge\", {\n width: selfEdge.label.width,\n height: selfEdge.label.height,\n rank: node.rank,\n order: i + (++orderShift),\n e: selfEdge.e,\n label: selfEdge.label\n }, \"_se\");\n });\n delete node.selfEdges;\n });\n });\n}\n\nfunction positionSelfEdges(g) {\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v);\n if (node.dummy === \"selfedge\") {\n var selfNode = g.node(node.e.v),\n x = selfNode.x + selfNode.width / 2,\n y = selfNode.y,\n dx = node.x - x,\n dy = selfNode.height / 2;\n g.setEdge(node.e, node.label);\n g.removeNode(v);\n node.label.points = [\n { x: x + 2 * dx / 3, y: y - dy },\n { x: x + 5 * dx / 6, y: y - dy },\n { x: x + dx , y: y },\n { x: x + 5 * dx / 6, y: y + dy },\n { x: x + 2 * dx / 3, y: y + dy }\n ];\n node.label.x = node.x;\n node.label.y = node.y;\n }\n });\n}\n\nfunction selectNumberAttrs(obj, attrs) {\n return _.mapValues(_.pick(obj, attrs), Number);\n}\n\nfunction canonicalize(attrs) {\n var newAttrs = {};\n _.forEach(attrs, function(v, k) {\n newAttrs[k.toLowerCase()] = v;\n });\n return newAttrs;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/layout.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/lodash.js": +/*!******************************************!*\ + !*** ./node_modules/dagre/lib/lodash.js ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar lodash;\n\nif (true) {\n try {\n lodash = __webpack_require__(/*! lodash */ \"./node_modules/lodash/lodash.js\");\n } catch (e) {}\n}\n\nif (!lodash) {\n lodash = window._;\n}\n\nmodule.exports = lodash;\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/lodash.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/nesting-graph.js": +/*!*************************************************!*\ + !*** ./node_modules/dagre/lib/nesting-graph.js ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\");\n\nmodule.exports = {\n run: run,\n cleanup: cleanup\n};\n\n/*\n * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs,\n * adds appropriate edges to ensure that all cluster nodes are placed between\n * these boundries, and ensures that the graph is connected.\n *\n * In addition we ensure, through the use of the minlen property, that nodes\n * and subgraph border nodes to not end up on the same rank.\n *\n * Preconditions:\n *\n * 1. Input graph is a DAG\n * 2. Nodes in the input graph has a minlen attribute\n *\n * Postconditions:\n *\n * 1. Input graph is connected.\n * 2. Dummy nodes are added for the tops and bottoms of subgraphs.\n * 3. The minlen attribute for nodes is adjusted to ensure nodes do not\n * get placed on the same rank as subgraph border nodes.\n *\n * The nesting graph idea comes from Sander, \"Layout of Compound Directed\n * Graphs.\"\n */\nfunction run(g) {\n var root = util.addDummyNode(g, \"root\", {}, \"_root\");\n var depths = treeDepths(g);\n var height = _.max(_.values(depths)) - 1; // Note: depths is an Object not an array\n var nodeSep = 2 * height + 1;\n\n g.graph().nestingRoot = root;\n\n // Multiply minlen by nodeSep to align nodes on non-border ranks.\n _.forEach(g.edges(), function(e) { g.edge(e).minlen *= nodeSep; });\n\n // Calculate a weight that is sufficient to keep subgraphs vertically compact\n var weight = sumWeights(g) + 1;\n\n // Create border nodes and link them up\n _.forEach(g.children(), function(child) {\n dfs(g, root, nodeSep, weight, height, depths, child);\n });\n\n // Save the multiplier for node layers for later removal of empty border\n // layers.\n g.graph().nodeRankFactor = nodeSep;\n}\n\nfunction dfs(g, root, nodeSep, weight, height, depths, v) {\n var children = g.children(v);\n if (!children.length) {\n if (v !== root) {\n g.setEdge(root, v, { weight: 0, minlen: nodeSep });\n }\n return;\n }\n\n var top = util.addBorderNode(g, \"_bt\"),\n bottom = util.addBorderNode(g, \"_bb\"),\n label = g.node(v);\n\n g.setParent(top, v);\n label.borderTop = top;\n g.setParent(bottom, v);\n label.borderBottom = bottom;\n\n _.forEach(children, function(child) {\n dfs(g, root, nodeSep, weight, height, depths, child);\n\n var childNode = g.node(child),\n childTop = childNode.borderTop ? childNode.borderTop : child,\n childBottom = childNode.borderBottom ? childNode.borderBottom : child,\n thisWeight = childNode.borderTop ? weight : 2 * weight,\n minlen = childTop !== childBottom ? 1 : height - depths[v] + 1;\n\n g.setEdge(top, childTop, {\n weight: thisWeight,\n minlen: minlen,\n nestingEdge: true\n });\n\n g.setEdge(childBottom, bottom, {\n weight: thisWeight,\n minlen: minlen,\n nestingEdge: true\n });\n });\n\n if (!g.parent(v)) {\n g.setEdge(root, top, { weight: 0, minlen: height + depths[v] });\n }\n}\n\nfunction treeDepths(g) {\n var depths = {};\n function dfs(v, depth) {\n var children = g.children(v);\n if (children && children.length) {\n _.forEach(children, function(child) {\n dfs(child, depth + 1);\n });\n }\n depths[v] = depth;\n }\n _.forEach(g.children(), function(v) { dfs(v, 1); });\n return depths;\n}\n\nfunction sumWeights(g) {\n return _.reduce(g.edges(), function(acc, e) {\n return acc + g.edge(e).weight;\n }, 0);\n}\n\nfunction cleanup(g) {\n var graphLabel = g.graph();\n g.removeNode(graphLabel.nestingRoot);\n delete graphLabel.nestingRoot;\n _.forEach(g.edges(), function(e) {\n var edge = g.edge(e);\n if (edge.nestingEdge) {\n g.removeEdge(e);\n }\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/nesting-graph.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/normalize.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre/lib/normalize.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/util.js\");\n\nmodule.exports = {\n run: run,\n undo: undo\n};\n\n/*\n * Breaks any long edges in the graph into short segments that span 1 layer\n * each. This operation is undoable with the denormalize function.\n *\n * Pre-conditions:\n *\n * 1. The input graph is a DAG.\n * 2. Each node in the graph has a \"rank\" property.\n *\n * Post-condition:\n *\n * 1. All edges in the graph have a length of 1.\n * 2. Dummy nodes are added where edges have been split into segments.\n * 3. The graph is augmented with a \"dummyChains\" attribute which contains\n * the first dummy in each chain of dummy nodes produced.\n */\nfunction run(g) {\n g.graph().dummyChains = [];\n _.forEach(g.edges(), function(edge) { normalizeEdge(g, edge); });\n}\n\nfunction normalizeEdge(g, e) {\n var v = e.v,\n vRank = g.node(v).rank,\n w = e.w,\n wRank = g.node(w).rank,\n name = e.name,\n edgeLabel = g.edge(e),\n labelRank = edgeLabel.labelRank;\n\n if (wRank === vRank + 1) return;\n\n g.removeEdge(e);\n\n var dummy, attrs, i;\n for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) {\n edgeLabel.points = [];\n attrs = {\n width: 0, height: 0,\n edgeLabel: edgeLabel, edgeObj: e,\n rank: vRank\n };\n dummy = util.addDummyNode(g, \"edge\", attrs, \"_d\");\n if (vRank === labelRank) {\n attrs.width = edgeLabel.width;\n attrs.height = edgeLabel.height;\n attrs.dummy = \"edge-label\";\n attrs.labelpos = edgeLabel.labelpos;\n }\n g.setEdge(v, dummy, { weight: edgeLabel.weight }, name);\n if (i === 0) {\n g.graph().dummyChains.push(dummy);\n }\n v = dummy;\n }\n\n g.setEdge(v, w, { weight: edgeLabel.weight }, name);\n}\n\nfunction undo(g) {\n _.forEach(g.graph().dummyChains, function(v) {\n var node = g.node(v),\n origLabel = node.edgeLabel,\n w;\n g.setEdge(node.edgeObj, origLabel);\n while (node.dummy) {\n w = g.successors(v)[0];\n g.removeNode(v);\n origLabel.points.push({ x: node.x, y: node.y });\n if (node.dummy === \"edge-label\") {\n origLabel.x = node.x;\n origLabel.y = node.y;\n origLabel.width = node.width;\n origLabel.height = node.height;\n }\n v = w;\n node = g.node(v);\n }\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/normalize.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/add-subgraph-constraints.js": +/*!******************************************************************!*\ + !*** ./node_modules/dagre/lib/order/add-subgraph-constraints.js ***! + \******************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = addSubgraphConstraints;\n\nfunction addSubgraphConstraints(g, cg, vs) {\n var prev = {},\n rootPrev;\n\n _.forEach(vs, function(v) {\n var child = g.parent(v),\n parent,\n prevChild;\n while (child) {\n parent = g.parent(child);\n if (parent) {\n prevChild = prev[parent];\n prev[parent] = child;\n } else {\n prevChild = rootPrev;\n rootPrev = child;\n }\n if (prevChild && prevChild !== child) {\n cg.setEdge(prevChild, child);\n return;\n }\n child = parent;\n }\n });\n\n /*\n function dfs(v) {\n var children = v ? g.children(v) : g.children();\n if (children.length) {\n var min = Number.POSITIVE_INFINITY,\n subgraphs = [];\n _.each(children, function(child) {\n var childMin = dfs(child);\n if (g.children(child).length) {\n subgraphs.push({ v: child, order: childMin });\n }\n min = Math.min(min, childMin);\n });\n _.reduce(_.sortBy(subgraphs, \"order\"), function(prev, curr) {\n cg.setEdge(prev.v, curr.v);\n return curr;\n });\n return min;\n }\n return g.node(v).order;\n }\n dfs(undefined);\n */\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/add-subgraph-constraints.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/barycenter.js": +/*!****************************************************!*\ + !*** ./node_modules/dagre/lib/order/barycenter.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = barycenter;\n\nfunction barycenter(g, movable) {\n return _.map(movable, function(v) {\n var inV = g.inEdges(v);\n if (!inV.length) {\n return { v: v };\n } else {\n var result = _.reduce(inV, function(acc, e) {\n var edge = g.edge(e),\n nodeU = g.node(e.v);\n return {\n sum: acc.sum + (edge.weight * nodeU.order),\n weight: acc.weight + edge.weight\n };\n }, { sum: 0, weight: 0 });\n\n return {\n v: v,\n barycenter: result.sum / result.weight,\n weight: result.weight\n };\n }\n });\n}\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/barycenter.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/build-layer-graph.js": +/*!***********************************************************!*\ + !*** ./node_modules/dagre/lib/order/build-layer-graph.js ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph;\n\nmodule.exports = buildLayerGraph;\n\n/*\n * Constructs a graph that can be used to sort a layer of nodes. The graph will\n * contain all base and subgraph nodes from the request layer in their original\n * hierarchy and any edges that are incident on these nodes and are of the type\n * requested by the \"relationship\" parameter.\n *\n * Nodes from the requested rank that do not have parents are assigned a root\n * node in the output graph, which is set in the root graph attribute. This\n * makes it easy to walk the hierarchy of movable nodes during ordering.\n *\n * Pre-conditions:\n *\n * 1. Input graph is a DAG\n * 2. Base nodes in the input graph have a rank attribute\n * 3. Subgraph nodes in the input graph has minRank and maxRank attributes\n * 4. Edges have an assigned weight\n *\n * Post-conditions:\n *\n * 1. Output graph has all nodes in the movable rank with preserved\n * hierarchy.\n * 2. Root nodes in the movable layer are made children of the node\n * indicated by the root attribute of the graph.\n * 3. Non-movable nodes incident on movable nodes, selected by the\n * relationship parameter, are included in the graph (without hierarchy).\n * 4. Edges incident on movable nodes, selected by the relationship\n * parameter, are added to the output graph.\n * 5. The weights for copied edges are aggregated as need, since the output\n * graph is not a multi-graph.\n */\nfunction buildLayerGraph(g, rank, relationship) {\n var root = createRootNode(g),\n result = new Graph({ compound: true }).setGraph({ root: root })\n .setDefaultNodeLabel(function(v) { return g.node(v); });\n\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v),\n parent = g.parent(v);\n\n if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) {\n result.setNode(v);\n result.setParent(v, parent || root);\n\n // This assumes we have only short edges!\n _.forEach(g[relationship](v), function(e) {\n var u = e.v === v ? e.w : e.v,\n edge = result.edge(u, v),\n weight = !_.isUndefined(edge) ? edge.weight : 0;\n result.setEdge(u, v, { weight: g.edge(e).weight + weight });\n });\n\n if (_.has(node, \"minRank\")) {\n result.setNode(v, {\n borderLeft: node.borderLeft[rank],\n borderRight: node.borderRight[rank]\n });\n }\n }\n });\n\n return result;\n}\n\nfunction createRootNode(g) {\n var v;\n while (g.hasNode((v = _.uniqueId(\"_root\"))));\n return v;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/build-layer-graph.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/cross-count.js": +/*!*****************************************************!*\ + !*** ./node_modules/dagre/lib/order/cross-count.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = crossCount;\n\n/*\n * A function that takes a layering (an array of layers, each with an array of\n * ordererd nodes) and a graph and returns a weighted crossing count.\n *\n * Pre-conditions:\n *\n * 1. Input graph must be simple (not a multigraph), directed, and include\n * only simple edges.\n * 2. Edges in the input graph must have assigned weights.\n *\n * Post-conditions:\n *\n * 1. The graph and layering matrix are left unchanged.\n *\n * This algorithm is derived from Barth, et al., \"Bilayer Cross Counting.\"\n */\nfunction crossCount(g, layering) {\n var cc = 0;\n for (var i = 1; i < layering.length; ++i) {\n cc += twoLayerCrossCount(g, layering[i-1], layering[i]);\n }\n return cc;\n}\n\nfunction twoLayerCrossCount(g, northLayer, southLayer) {\n // Sort all of the edges between the north and south layers by their position\n // in the north layer and then the south. Map these edges to the position of\n // their head in the south layer.\n var southPos = _.zipObject(southLayer,\n _.map(southLayer, function (v, i) { return i; }));\n var southEntries = _.flatten(_.map(northLayer, function(v) {\n return _.chain(g.outEdges(v))\n .map(function(e) {\n return { pos: southPos[e.w], weight: g.edge(e).weight };\n })\n .sortBy(\"pos\")\n .value();\n }), true);\n\n // Build the accumulator tree\n var firstIndex = 1;\n while (firstIndex < southLayer.length) firstIndex <<= 1;\n var treeSize = 2 * firstIndex - 1;\n firstIndex -= 1;\n var tree = _.map(new Array(treeSize), function() { return 0; });\n\n // Calculate the weighted crossings\n var cc = 0;\n _.forEach(southEntries.forEach(function(entry) {\n var index = entry.pos + firstIndex;\n tree[index] += entry.weight;\n var weightSum = 0;\n while (index > 0) {\n if (index % 2) {\n weightSum += tree[index + 1];\n }\n index = (index - 1) >> 1;\n tree[index] += entry.weight;\n }\n cc += entry.weight * weightSum;\n }));\n\n return cc;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/cross-count.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/index.js": +/*!***********************************************!*\ + !*** ./node_modules/dagre/lib/order/index.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n initOrder = __webpack_require__(/*! ./init-order */ \"./node_modules/dagre/lib/order/init-order.js\"),\n crossCount = __webpack_require__(/*! ./cross-count */ \"./node_modules/dagre/lib/order/cross-count.js\"),\n sortSubgraph = __webpack_require__(/*! ./sort-subgraph */ \"./node_modules/dagre/lib/order/sort-subgraph.js\"),\n buildLayerGraph = __webpack_require__(/*! ./build-layer-graph */ \"./node_modules/dagre/lib/order/build-layer-graph.js\"),\n addSubgraphConstraints = __webpack_require__(/*! ./add-subgraph-constraints */ \"./node_modules/dagre/lib/order/add-subgraph-constraints.js\"),\n Graph = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph,\n util = __webpack_require__(/*! ../util */ \"./node_modules/dagre/lib/util.js\");\n\nmodule.exports = order;\n\n/*\n * Applies heuristics to minimize edge crossings in the graph and sets the best\n * order solution as an order attribute on each node.\n *\n * Pre-conditions:\n *\n * 1. Graph must be DAG\n * 2. Graph nodes must be objects with a \"rank\" attribute\n * 3. Graph edges must have the \"weight\" attribute\n *\n * Post-conditions:\n *\n * 1. Graph nodes will have an \"order\" attribute based on the results of the\n * algorithm.\n */\nfunction order(g) {\n var maxRank = util.maxRank(g),\n downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), \"inEdges\"),\n upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), \"outEdges\");\n\n var layering = initOrder(g);\n assignOrder(g, layering);\n\n var bestCC = Number.POSITIVE_INFINITY,\n best;\n\n for (var i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) {\n sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2);\n\n layering = util.buildLayerMatrix(g);\n var cc = crossCount(g, layering);\n if (cc < bestCC) {\n lastBest = 0;\n best = _.cloneDeep(layering);\n bestCC = cc;\n }\n }\n\n assignOrder(g, best);\n}\n\nfunction buildLayerGraphs(g, ranks, relationship) {\n return _.map(ranks, function(rank) {\n return buildLayerGraph(g, rank, relationship);\n });\n}\n\nfunction sweepLayerGraphs(layerGraphs, biasRight) {\n var cg = new Graph();\n _.forEach(layerGraphs, function(lg) {\n var root = lg.graph().root;\n var sorted = sortSubgraph(lg, root, cg, biasRight);\n _.forEach(sorted.vs, function(v, i) {\n lg.node(v).order = i;\n });\n addSubgraphConstraints(lg, cg, sorted.vs);\n });\n}\n\nfunction assignOrder(g, layering) {\n _.forEach(layering, function(layer) {\n _.forEach(layer, function(v, i) {\n g.node(v).order = i;\n });\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/init-order.js": +/*!****************************************************!*\ + !*** ./node_modules/dagre/lib/order/init-order.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = initOrder;\n\n/*\n * Assigns an initial order value for each node by performing a DFS search\n * starting from nodes in the first rank. Nodes are assigned an order in their\n * rank as they are first visited.\n *\n * This approach comes from Gansner, et al., \"A Technique for Drawing Directed\n * Graphs.\"\n *\n * Returns a layering matrix with an array per layer and each layer sorted by\n * the order of its nodes.\n */\nfunction initOrder(g) {\n var visited = {},\n simpleNodes = _.filter(g.nodes(), function(v) {\n return !g.children(v).length;\n }),\n maxRank = _.max(_.map(simpleNodes, function(v) { return g.node(v).rank; })),\n layers = _.map(_.range(maxRank + 1), function() { return []; });\n\n function dfs(v) {\n if (_.has(visited, v)) return;\n visited[v] = true;\n var node = g.node(v);\n layers[node.rank].push(v);\n _.forEach(g.successors(v), dfs);\n }\n\n var orderedVs = _.sortBy(simpleNodes, function(v) { return g.node(v).rank; });\n _.forEach(orderedVs, dfs);\n\n return layers;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/init-order.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/resolve-conflicts.js": +/*!***********************************************************!*\ + !*** ./node_modules/dagre/lib/order/resolve-conflicts.js ***! + \***********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = resolveConflicts;\n\n/*\n * Given a list of entries of the form {v, barycenter, weight} and a\n * constraint graph this function will resolve any conflicts between the\n * constraint graph and the barycenters for the entries. If the barycenters for\n * an entry would violate a constraint in the constraint graph then we coalesce\n * the nodes in the conflict into a new node that respects the contraint and\n * aggregates barycenter and weight information.\n *\n * This implementation is based on the description in Forster, \"A Fast and\n * Simple Hueristic for Constrained Two-Level Crossing Reduction,\" thought it\n * differs in some specific details.\n *\n * Pre-conditions:\n *\n * 1. Each entry has the form {v, barycenter, weight}, or if the node has\n * no barycenter, then {v}.\n *\n * Returns:\n *\n * A new list of entries of the form {vs, i, barycenter, weight}. The list\n * `vs` may either be a singleton or it may be an aggregation of nodes\n * ordered such that they do not violate constraints from the constraint\n * graph. The property `i` is the lowest original index of any of the\n * elements in `vs`.\n */\nfunction resolveConflicts(entries, cg) {\n var mappedEntries = {};\n _.forEach(entries, function(entry, i) {\n var tmp = mappedEntries[entry.v] = {\n indegree: 0,\n \"in\": [],\n out: [],\n vs: [entry.v],\n i: i\n };\n if (!_.isUndefined(entry.barycenter)) {\n tmp.barycenter = entry.barycenter;\n tmp.weight = entry.weight;\n }\n });\n\n _.forEach(cg.edges(), function(e) {\n var entryV = mappedEntries[e.v],\n entryW = mappedEntries[e.w];\n if (!_.isUndefined(entryV) && !_.isUndefined(entryW)) {\n entryW.indegree++;\n entryV.out.push(mappedEntries[e.w]);\n }\n });\n\n var sourceSet = _.filter(mappedEntries, function(entry) {\n return !entry.indegree;\n });\n\n return doResolveConflicts(sourceSet);\n}\n\nfunction doResolveConflicts(sourceSet) {\n var entries = [];\n\n function handleIn(vEntry) {\n return function(uEntry) {\n if (uEntry.merged) {\n return;\n }\n if (_.isUndefined(uEntry.barycenter) ||\n _.isUndefined(vEntry.barycenter) ||\n uEntry.barycenter >= vEntry.barycenter) {\n mergeEntries(vEntry, uEntry);\n }\n };\n }\n\n function handleOut(vEntry) {\n return function(wEntry) {\n wEntry[\"in\"].push(vEntry);\n if (--wEntry.indegree === 0) {\n sourceSet.push(wEntry);\n }\n };\n }\n\n while (sourceSet.length) {\n var entry = sourceSet.pop();\n entries.push(entry);\n _.forEach(entry[\"in\"].reverse(), handleIn(entry));\n _.forEach(entry.out, handleOut(entry));\n }\n\n return _.chain(entries)\n .filter(function(entry) { return !entry.merged; })\n .map(function(entry) {\n return _.pick(entry, [\"vs\", \"i\", \"barycenter\", \"weight\"]);\n })\n .value();\n}\n\nfunction mergeEntries(target, source) {\n var sum = 0,\n weight = 0;\n\n if (target.weight) {\n sum += target.barycenter * target.weight;\n weight += target.weight;\n }\n\n if (source.weight) {\n sum += source.barycenter * source.weight;\n weight += source.weight;\n }\n\n target.vs = source.vs.concat(target.vs);\n target.barycenter = sum / weight;\n target.weight = weight;\n target.i = Math.min(source.i, target.i);\n source.merged = true;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/resolve-conflicts.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/sort-subgraph.js": +/*!*******************************************************!*\ + !*** ./node_modules/dagre/lib/order/sort-subgraph.js ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n barycenter = __webpack_require__(/*! ./barycenter */ \"./node_modules/dagre/lib/order/barycenter.js\"),\n resolveConflicts = __webpack_require__(/*! ./resolve-conflicts */ \"./node_modules/dagre/lib/order/resolve-conflicts.js\"),\n sort = __webpack_require__(/*! ./sort */ \"./node_modules/dagre/lib/order/sort.js\");\n\nmodule.exports = sortSubgraph;\n\nfunction sortSubgraph(g, v, cg, biasRight) {\n var movable = g.children(v),\n node = g.node(v),\n bl = node ? node.borderLeft : undefined,\n br = node ? node.borderRight: undefined,\n subgraphs = {};\n\n if (bl) {\n movable = _.filter(movable, function(w) {\n return w !== bl && w !== br;\n });\n }\n\n var barycenters = barycenter(g, movable);\n _.forEach(barycenters, function(entry) {\n if (g.children(entry.v).length) {\n var subgraphResult = sortSubgraph(g, entry.v, cg, biasRight);\n subgraphs[entry.v] = subgraphResult;\n if (_.has(subgraphResult, \"barycenter\")) {\n mergeBarycenters(entry, subgraphResult);\n }\n }\n });\n\n var entries = resolveConflicts(barycenters, cg);\n expandSubgraphs(entries, subgraphs);\n\n var result = sort(entries, biasRight);\n\n if (bl) {\n result.vs = _.flatten([bl, result.vs, br], true);\n if (g.predecessors(bl).length) {\n var blPred = g.node(g.predecessors(bl)[0]),\n brPred = g.node(g.predecessors(br)[0]);\n if (!_.has(result, \"barycenter\")) {\n result.barycenter = 0;\n result.weight = 0;\n }\n result.barycenter = (result.barycenter * result.weight +\n blPred.order + brPred.order) / (result.weight + 2);\n result.weight += 2;\n }\n }\n\n return result;\n}\n\nfunction expandSubgraphs(entries, subgraphs) {\n _.forEach(entries, function(entry) {\n entry.vs = _.flatten(entry.vs.map(function(v) {\n if (subgraphs[v]) {\n return subgraphs[v].vs;\n }\n return v;\n }), true);\n });\n}\n\nfunction mergeBarycenters(target, other) {\n if (!_.isUndefined(target.barycenter)) {\n target.barycenter = (target.barycenter * target.weight +\n other.barycenter * other.weight) /\n (target.weight + other.weight);\n target.weight += other.weight;\n } else {\n target.barycenter = other.barycenter;\n target.weight = other.weight;\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/sort-subgraph.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/order/sort.js": +/*!**********************************************!*\ + !*** ./node_modules/dagre/lib/order/sort.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ../util */ \"./node_modules/dagre/lib/util.js\");\n\nmodule.exports = sort;\n\nfunction sort(entries, biasRight) {\n var parts = util.partition(entries, function(entry) {\n return _.has(entry, \"barycenter\");\n });\n var sortable = parts.lhs,\n unsortable = _.sortBy(parts.rhs, function(entry) { return -entry.i; }),\n vs = [],\n sum = 0,\n weight = 0,\n vsIndex = 0;\n\n sortable.sort(compareWithBias(!!biasRight));\n\n vsIndex = consumeUnsortable(vs, unsortable, vsIndex);\n\n _.forEach(sortable, function (entry) {\n vsIndex += entry.vs.length;\n vs.push(entry.vs);\n sum += entry.barycenter * entry.weight;\n weight += entry.weight;\n vsIndex = consumeUnsortable(vs, unsortable, vsIndex);\n });\n\n var result = { vs: _.flatten(vs, true) };\n if (weight) {\n result.barycenter = sum / weight;\n result.weight = weight;\n }\n return result;\n}\n\nfunction consumeUnsortable(vs, unsortable, index) {\n var last;\n while (unsortable.length && (last = _.last(unsortable)).i <= index) {\n unsortable.pop();\n vs.push(last.vs);\n index++;\n }\n return index;\n}\n\nfunction compareWithBias(bias) {\n return function(entryV, entryW) {\n if (entryV.barycenter < entryW.barycenter) {\n return -1;\n } else if (entryV.barycenter > entryW.barycenter) {\n return 1;\n }\n\n return !bias ? entryV.i - entryW.i : entryW.i - entryV.i;\n };\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/order/sort.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/parent-dummy-chains.js": +/*!*******************************************************!*\ + !*** ./node_modules/dagre/lib/parent-dummy-chains.js ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = parentDummyChains;\n\nfunction parentDummyChains(g) {\n var postorderNums = postorder(g);\n\n _.forEach(g.graph().dummyChains, function(v) {\n var node = g.node(v),\n edgeObj = node.edgeObj,\n pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w),\n path = pathData.path,\n lca = pathData.lca,\n pathIdx = 0,\n pathV = path[pathIdx],\n ascending = true;\n\n while (v !== edgeObj.w) {\n node = g.node(v);\n\n if (ascending) {\n while ((pathV = path[pathIdx]) !== lca &&\n g.node(pathV).maxRank < node.rank) {\n pathIdx++;\n }\n\n if (pathV === lca) {\n ascending = false;\n }\n }\n\n if (!ascending) {\n while (pathIdx < path.length - 1 &&\n g.node(pathV = path[pathIdx + 1]).minRank <= node.rank) {\n pathIdx++;\n }\n pathV = path[pathIdx];\n }\n\n g.setParent(v, pathV);\n v = g.successors(v)[0];\n }\n });\n}\n\n// Find a path from v to w through the lowest common ancestor (LCA). Return the\n// full path and the LCA.\nfunction findPath(g, postorderNums, v, w) {\n var vPath = [],\n wPath = [],\n low = Math.min(postorderNums[v].low, postorderNums[w].low),\n lim = Math.max(postorderNums[v].lim, postorderNums[w].lim),\n parent,\n lca;\n\n // Traverse up from v to find the LCA\n parent = v;\n do {\n parent = g.parent(parent);\n vPath.push(parent);\n } while (parent &&\n (postorderNums[parent].low > low || lim > postorderNums[parent].lim));\n lca = parent;\n\n // Traverse from w to LCA\n parent = w;\n while ((parent = g.parent(parent)) !== lca) {\n wPath.push(parent);\n }\n\n return { path: vPath.concat(wPath.reverse()), lca: lca };\n}\n\nfunction postorder(g) {\n var result = {},\n lim = 0;\n\n function dfs(v) {\n var low = lim;\n _.forEach(g.children(v), dfs);\n result[v] = { low: low, lim: lim++ };\n }\n _.forEach(g.children(), dfs);\n\n return result;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/parent-dummy-chains.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/position/bk.js": +/*!***********************************************!*\ + !*** ./node_modules/dagre/lib/position/bk.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph,\n util = __webpack_require__(/*! ../util */ \"./node_modules/dagre/lib/util.js\");\n\n/*\n * This module provides coordinate assignment based on Brandes and Köpf, \"Fast\n * and Simple Horizontal Coordinate Assignment.\"\n */\n\nmodule.exports = {\n positionX: positionX,\n findType1Conflicts: findType1Conflicts,\n findType2Conflicts: findType2Conflicts,\n addConflict: addConflict,\n hasConflict: hasConflict,\n verticalAlignment: verticalAlignment,\n horizontalCompaction: horizontalCompaction,\n alignCoordinates: alignCoordinates,\n findSmallestWidthAlignment: findSmallestWidthAlignment,\n balance: balance\n};\n\n/*\n * Marks all edges in the graph with a type-1 conflict with the \"type1Conflict\"\n * property. A type-1 conflict is one where a non-inner segment crosses an\n * inner segment. An inner segment is an edge with both incident nodes marked\n * with the \"dummy\" property.\n *\n * This algorithm scans layer by layer, starting with the second, for type-1\n * conflicts between the current layer and the previous layer. For each layer\n * it scans the nodes from left to right until it reaches one that is incident\n * on an inner segment. It then scans predecessors to determine if they have\n * edges that cross that inner segment. At the end a final scan is done for all\n * nodes on the current rank to see if they cross the last visited inner\n * segment.\n *\n * This algorithm (safely) assumes that a dummy node will only be incident on a\n * single node in the layers being scanned.\n */\nfunction findType1Conflicts(g, layering) {\n var conflicts = {};\n\n function visitLayer(prevLayer, layer) {\n var\n // last visited node in the previous layer that is incident on an inner\n // segment.\n k0 = 0,\n // Tracks the last node in this layer scanned for crossings with a type-1\n // segment.\n scanPos = 0,\n prevLayerLength = prevLayer.length,\n lastNode = _.last(layer);\n\n _.forEach(layer, function(v, i) {\n var w = findOtherInnerSegmentNode(g, v),\n k1 = w ? g.node(w).order : prevLayerLength;\n\n if (w || v === lastNode) {\n _.forEach(layer.slice(scanPos, i +1), function(scanNode) {\n _.forEach(g.predecessors(scanNode), function(u) {\n var uLabel = g.node(u),\n uPos = uLabel.order;\n if ((uPos < k0 || k1 < uPos) &&\n !(uLabel.dummy && g.node(scanNode).dummy)) {\n addConflict(conflicts, u, scanNode);\n }\n });\n });\n scanPos = i + 1;\n k0 = k1;\n }\n });\n\n return layer;\n }\n\n _.reduce(layering, visitLayer);\n return conflicts;\n}\n\nfunction findType2Conflicts(g, layering) {\n var conflicts = {};\n\n function scan(south, southPos, southEnd, prevNorthBorder, nextNorthBorder) {\n var v;\n _.forEach(_.range(southPos, southEnd), function(i) {\n v = south[i];\n if (g.node(v).dummy) {\n _.forEach(g.predecessors(v), function(u) {\n var uNode = g.node(u);\n if (uNode.dummy &&\n (uNode.order < prevNorthBorder || uNode.order > nextNorthBorder)) {\n addConflict(conflicts, u, v);\n }\n });\n }\n });\n }\n\n\n function visitLayer(north, south) {\n var prevNorthPos = -1,\n nextNorthPos,\n southPos = 0;\n\n _.forEach(south, function(v, southLookahead) {\n if (g.node(v).dummy === \"border\") {\n var predecessors = g.predecessors(v);\n if (predecessors.length) {\n nextNorthPos = g.node(predecessors[0]).order;\n scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos);\n southPos = southLookahead;\n prevNorthPos = nextNorthPos;\n }\n }\n scan(south, southPos, south.length, nextNorthPos, north.length);\n });\n\n return south;\n }\n\n _.reduce(layering, visitLayer);\n return conflicts;\n}\n\nfunction findOtherInnerSegmentNode(g, v) {\n if (g.node(v).dummy) {\n return _.find(g.predecessors(v), function(u) {\n return g.node(u).dummy;\n });\n }\n}\n\nfunction addConflict(conflicts, v, w) {\n if (v > w) {\n var tmp = v;\n v = w;\n w = tmp;\n }\n\n var conflictsV = conflicts[v];\n if (!conflictsV) {\n conflicts[v] = conflictsV = {};\n }\n conflictsV[w] = true;\n}\n\nfunction hasConflict(conflicts, v, w) {\n if (v > w) {\n var tmp = v;\n v = w;\n w = tmp;\n }\n return _.has(conflicts[v], w);\n}\n\n/*\n * Try to align nodes into vertical \"blocks\" where possible. This algorithm\n * attempts to align a node with one of its median neighbors. If the edge\n * connecting a neighbor is a type-1 conflict then we ignore that possibility.\n * If a previous node has already formed a block with a node after the node\n * we're trying to form a block with, we also ignore that possibility - our\n * blocks would be split in that scenario.\n */\nfunction verticalAlignment(g, layering, conflicts, neighborFn) {\n var root = {},\n align = {},\n pos = {};\n\n // We cache the position here based on the layering because the graph and\n // layering may be out of sync. The layering matrix is manipulated to\n // generate different extreme alignments.\n _.forEach(layering, function(layer) {\n _.forEach(layer, function(v, order) {\n root[v] = v;\n align[v] = v;\n pos[v] = order;\n });\n });\n\n _.forEach(layering, function(layer) {\n var prevIdx = -1;\n _.forEach(layer, function(v) {\n var ws = neighborFn(v);\n if (ws.length) {\n ws = _.sortBy(ws, function(w) { return pos[w]; });\n var mp = (ws.length - 1) / 2;\n for (var i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) {\n var w = ws[i];\n if (align[v] === v &&\n prevIdx < pos[w] &&\n !hasConflict(conflicts, v, w)) {\n align[w] = v;\n align[v] = root[v] = root[w];\n prevIdx = pos[w];\n }\n }\n }\n });\n });\n\n return { root: root, align: align };\n}\n\nfunction horizontalCompaction(g, layering, root, align, reverseSep) {\n // This portion of the algorithm differs from BK due to a number of problems.\n // Instead of their algorithm we construct a new block graph and do two\n // sweeps. The first sweep places blocks with the smallest possible\n // coordinates. The second sweep removes unused space by moving blocks to the\n // greatest coordinates without violating separation.\n var xs = {},\n blockG = buildBlockGraph(g, layering, root, reverseSep),\n borderType = reverseSep ? \"borderLeft\" : \"borderRight\";\n\n function iterate(setXsFunc, nextNodesFunc) {\n var stack = blockG.nodes();\n var elem = stack.pop();\n var visited = {};\n while (elem) {\n if (visited[elem]) {\n setXsFunc(elem);\n } else {\n visited[elem] = true;\n stack.push(elem);\n stack = stack.concat(nextNodesFunc(elem));\n }\n\n elem = stack.pop();\n }\n }\n\n // First pass, assign smallest coordinates\n function pass1(elem) {\n xs[elem] = blockG.inEdges(elem).reduce(function(acc, e) {\n return Math.max(acc, xs[e.v] + blockG.edge(e));\n }, 0);\n }\n\n // Second pass, assign greatest coordinates\n function pass2(elem) {\n var min = blockG.outEdges(elem).reduce(function(acc, e) {\n return Math.min(acc, xs[e.w] - blockG.edge(e));\n }, Number.POSITIVE_INFINITY);\n\n var node = g.node(elem);\n if (min !== Number.POSITIVE_INFINITY && node.borderType !== borderType) {\n xs[elem] = Math.max(xs[elem], min);\n }\n }\n\n iterate(pass1, _.bind(blockG.predecessors, blockG));\n iterate(pass2, _.bind(blockG.successors, blockG));\n\n // Assign x coordinates to all nodes\n _.forEach(align, function(v) {\n xs[v] = xs[root[v]];\n });\n\n return xs;\n}\n\n\nfunction buildBlockGraph(g, layering, root, reverseSep) {\n var blockGraph = new Graph(),\n graphLabel = g.graph(),\n sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep);\n\n _.forEach(layering, function(layer) {\n var u;\n _.forEach(layer, function(v) {\n var vRoot = root[v];\n blockGraph.setNode(vRoot);\n if (u) {\n var uRoot = root[u],\n prevMax = blockGraph.edge(uRoot, vRoot);\n blockGraph.setEdge(uRoot, vRoot, Math.max(sepFn(g, v, u), prevMax || 0));\n }\n u = v;\n });\n });\n\n return blockGraph;\n}\n\n/*\n * Returns the alignment that has the smallest width of the given alignments.\n */\nfunction findSmallestWidthAlignment(g, xss) {\n return _.minBy(_.values(xss), function (xs) {\n var max = Number.NEGATIVE_INFINITY;\n var min = Number.POSITIVE_INFINITY;\n\n _.forIn(xs, function (x, v) {\n var halfWidth = width(g, v) / 2;\n\n max = Math.max(x + halfWidth, max);\n min = Math.min(x - halfWidth, min);\n });\n\n return max - min;\n });\n}\n\n/*\n * Align the coordinates of each of the layout alignments such that\n * left-biased alignments have their minimum coordinate at the same point as\n * the minimum coordinate of the smallest width alignment and right-biased\n * alignments have their maximum coordinate at the same point as the maximum\n * coordinate of the smallest width alignment.\n */\nfunction alignCoordinates(xss, alignTo) {\n var alignToVals = _.values(alignTo),\n alignToMin = _.min(alignToVals),\n alignToMax = _.max(alignToVals);\n\n _.forEach([\"u\", \"d\"], function(vert) {\n _.forEach([\"l\", \"r\"], function(horiz) {\n var alignment = vert + horiz,\n xs = xss[alignment],\n delta;\n if (xs === alignTo) return;\n\n var xsVals = _.values(xs);\n delta = horiz === \"l\" ? alignToMin - _.min(xsVals) : alignToMax - _.max(xsVals);\n\n if (delta) {\n xss[alignment] = _.mapValues(xs, function(x) { return x + delta; });\n }\n });\n });\n}\n\nfunction balance(xss, align) {\n return _.mapValues(xss.ul, function(ignore, v) {\n if (align) {\n return xss[align.toLowerCase()][v];\n } else {\n var xs = _.sortBy(_.map(xss, v));\n return (xs[1] + xs[2]) / 2;\n }\n });\n}\n\nfunction positionX(g) {\n var layering = util.buildLayerMatrix(g),\n conflicts = _.merge(findType1Conflicts(g, layering),\n findType2Conflicts(g, layering));\n\n var xss = {},\n adjustedLayering;\n _.forEach([\"u\", \"d\"], function(vert) {\n adjustedLayering = vert === \"u\" ? layering : _.values(layering).reverse();\n _.forEach([\"l\", \"r\"], function(horiz) {\n if (horiz === \"r\") {\n adjustedLayering = _.map(adjustedLayering, function(inner) {\n return _.values(inner).reverse();\n });\n }\n\n var neighborFn = _.bind(vert === \"u\" ? g.predecessors : g.successors, g);\n var align = verticalAlignment(g, adjustedLayering, conflicts, neighborFn);\n var xs = horizontalCompaction(g, adjustedLayering,\n align.root, align.align,\n horiz === \"r\");\n if (horiz === \"r\") {\n xs = _.mapValues(xs, function(x) { return -x; });\n }\n xss[vert + horiz] = xs;\n });\n });\n\n var smallestWidth = findSmallestWidthAlignment(g, xss);\n alignCoordinates(xss, smallestWidth);\n return balance(xss, g.graph().align);\n}\n\nfunction sep(nodeSep, edgeSep, reverseSep) {\n return function(g, v, w) {\n var vLabel = g.node(v),\n wLabel = g.node(w),\n sum = 0,\n delta;\n\n sum += vLabel.width / 2;\n if (_.has(vLabel, \"labelpos\")) {\n switch (vLabel.labelpos.toLowerCase()) {\n case \"l\": delta = -vLabel.width / 2; break;\n case \"r\": delta = vLabel.width / 2; break;\n }\n }\n if (delta) {\n sum += reverseSep ? delta : -delta;\n }\n delta = 0;\n\n sum += (vLabel.dummy ? edgeSep : nodeSep) / 2;\n sum += (wLabel.dummy ? edgeSep : nodeSep) / 2;\n\n sum += wLabel.width / 2;\n if (_.has(wLabel, \"labelpos\")) {\n switch (wLabel.labelpos.toLowerCase()) {\n case \"l\": delta = wLabel.width / 2; break;\n case \"r\": delta = -wLabel.width / 2; break;\n }\n }\n if (delta) {\n sum += reverseSep ? delta : -delta;\n }\n delta = 0;\n\n return sum;\n };\n}\n\nfunction width(g, v) {\n return g.node(v).width;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/position/bk.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/position/index.js": +/*!**************************************************!*\ + !*** ./node_modules/dagre/lib/position/index.js ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n util = __webpack_require__(/*! ../util */ \"./node_modules/dagre/lib/util.js\"),\n positionX = __webpack_require__(/*! ./bk */ \"./node_modules/dagre/lib/position/bk.js\").positionX;\n\nmodule.exports = position;\n\nfunction position(g) {\n g = util.asNonCompoundGraph(g);\n\n positionY(g);\n _.forEach(positionX(g), function(x, v) {\n g.node(v).x = x;\n });\n}\n\nfunction positionY(g) {\n var layering = util.buildLayerMatrix(g),\n rankSep = g.graph().ranksep,\n prevY = 0;\n _.forEach(layering, function(layer) {\n var maxHeight = _.max(_.map(layer, function(v) { return g.node(v).height; }));\n _.forEach(layer, function(v) {\n g.node(v).y = prevY + maxHeight / 2;\n });\n prevY += maxHeight + rankSep;\n });\n}\n\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/position/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/rank/feasible-tree.js": +/*!******************************************************!*\ + !*** ./node_modules/dagre/lib/rank/feasible-tree.js ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph,\n slack = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/rank/util.js\").slack;\n\nmodule.exports = feasibleTree;\n\n/*\n * Constructs a spanning tree with tight edges and adjusted the input node's\n * ranks to achieve this. A tight edge is one that is has a length that matches\n * its \"minlen\" attribute.\n *\n * The basic structure for this function is derived from Gansner, et al., \"A\n * Technique for Drawing Directed Graphs.\"\n *\n * Pre-conditions:\n *\n * 1. Graph must be a DAG.\n * 2. Graph must be connected.\n * 3. Graph must have at least one node.\n * 5. Graph nodes must have been previously assigned a \"rank\" property that\n * respects the \"minlen\" property of incident edges.\n * 6. Graph edges must have a \"minlen\" property.\n *\n * Post-conditions:\n *\n * - Graph nodes will have their rank adjusted to ensure that all edges are\n * tight.\n *\n * Returns a tree (undirected graph) that is constructed using only \"tight\"\n * edges.\n */\nfunction feasibleTree(g) {\n var t = new Graph({ directed: false });\n\n // Choose arbitrary node from which to start our tree\n var start = g.nodes()[0],\n size = g.nodeCount();\n t.setNode(start, {});\n\n var edge, delta;\n while (tightTree(t, g) < size) {\n edge = findMinSlackEdge(t, g);\n delta = t.hasNode(edge.v) ? slack(g, edge) : -slack(g, edge);\n shiftRanks(t, g, delta);\n }\n\n return t;\n}\n\n/*\n * Finds a maximal tree of tight edges and returns the number of nodes in the\n * tree.\n */\nfunction tightTree(t, g) {\n function dfs(v) {\n _.forEach(g.nodeEdges(v), function(e) {\n var edgeV = e.v,\n w = (v === edgeV) ? e.w : edgeV;\n if (!t.hasNode(w) && !slack(g, e)) {\n t.setNode(w, {});\n t.setEdge(v, w, {});\n dfs(w);\n }\n });\n }\n\n _.forEach(t.nodes(), dfs);\n return t.nodeCount();\n}\n\n/*\n * Finds the edge with the smallest slack that is incident on tree and returns\n * it.\n */\nfunction findMinSlackEdge(t, g) {\n return _.minBy(g.edges(), function(e) {\n if (t.hasNode(e.v) !== t.hasNode(e.w)) {\n return slack(g, e);\n }\n });\n}\n\nfunction shiftRanks(t, g, delta) {\n _.forEach(t.nodes(), function(v) {\n g.node(v).rank += delta;\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/rank/feasible-tree.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/rank/index.js": +/*!**********************************************!*\ + !*** ./node_modules/dagre/lib/rank/index.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar rankUtil = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/rank/util.js\"),\n longestPath = rankUtil.longestPath,\n feasibleTree = __webpack_require__(/*! ./feasible-tree */ \"./node_modules/dagre/lib/rank/feasible-tree.js\"),\n networkSimplex = __webpack_require__(/*! ./network-simplex */ \"./node_modules/dagre/lib/rank/network-simplex.js\");\n\nmodule.exports = rank;\n\n/*\n * Assigns a rank to each node in the input graph that respects the \"minlen\"\n * constraint specified on edges between nodes.\n *\n * This basic structure is derived from Gansner, et al., \"A Technique for\n * Drawing Directed Graphs.\"\n *\n * Pre-conditions:\n *\n * 1. Graph must be a connected DAG\n * 2. Graph nodes must be objects\n * 3. Graph edges must have \"weight\" and \"minlen\" attributes\n *\n * Post-conditions:\n *\n * 1. Graph nodes will have a \"rank\" attribute based on the results of the\n * algorithm. Ranks can start at any index (including negative), we'll\n * fix them up later.\n */\nfunction rank(g) {\n switch(g.graph().ranker) {\n case \"network-simplex\": networkSimplexRanker(g); break;\n case \"tight-tree\": tightTreeRanker(g); break;\n case \"longest-path\": longestPathRanker(g); break;\n default: networkSimplexRanker(g);\n }\n}\n\n// A fast and simple ranker, but results are far from optimal.\nvar longestPathRanker = longestPath;\n\nfunction tightTreeRanker(g) {\n longestPath(g);\n feasibleTree(g);\n}\n\nfunction networkSimplexRanker(g) {\n networkSimplex(g);\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/rank/index.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/rank/network-simplex.js": +/*!********************************************************!*\ + !*** ./node_modules/dagre/lib/rank/network-simplex.js ***! + \********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n feasibleTree = __webpack_require__(/*! ./feasible-tree */ \"./node_modules/dagre/lib/rank/feasible-tree.js\"),\n slack = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/rank/util.js\").slack,\n initRank = __webpack_require__(/*! ./util */ \"./node_modules/dagre/lib/rank/util.js\").longestPath,\n preorder = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").alg.preorder,\n postorder = __webpack_require__(/*! ../graphlib */ \"./node_modules/dagre/lib/graphlib.js\").alg.postorder,\n simplify = __webpack_require__(/*! ../util */ \"./node_modules/dagre/lib/util.js\").simplify;\n\nmodule.exports = networkSimplex;\n\n// Expose some internals for testing purposes\nnetworkSimplex.initLowLimValues = initLowLimValues;\nnetworkSimplex.initCutValues = initCutValues;\nnetworkSimplex.calcCutValue = calcCutValue;\nnetworkSimplex.leaveEdge = leaveEdge;\nnetworkSimplex.enterEdge = enterEdge;\nnetworkSimplex.exchangeEdges = exchangeEdges;\n\n/*\n * The network simplex algorithm assigns ranks to each node in the input graph\n * and iteratively improves the ranking to reduce the length of edges.\n *\n * Preconditions:\n *\n * 1. The input graph must be a DAG.\n * 2. All nodes in the graph must have an object value.\n * 3. All edges in the graph must have \"minlen\" and \"weight\" attributes.\n *\n * Postconditions:\n *\n * 1. All nodes in the graph will have an assigned \"rank\" attribute that has\n * been optimized by the network simplex algorithm. Ranks start at 0.\n *\n *\n * A rough sketch of the algorithm is as follows:\n *\n * 1. Assign initial ranks to each node. We use the longest path algorithm,\n * which assigns ranks to the lowest position possible. In general this\n * leads to very wide bottom ranks and unnecessarily long edges.\n * 2. Construct a feasible tight tree. A tight tree is one such that all\n * edges in the tree have no slack (difference between length of edge\n * and minlen for the edge). This by itself greatly improves the assigned\n * rankings by shorting edges.\n * 3. Iteratively find edges that have negative cut values. Generally a\n * negative cut value indicates that the edge could be removed and a new\n * tree edge could be added to produce a more compact graph.\n *\n * Much of the algorithms here are derived from Gansner, et al., \"A Technique\n * for Drawing Directed Graphs.\" The structure of the file roughly follows the\n * structure of the overall algorithm.\n */\nfunction networkSimplex(g) {\n g = simplify(g);\n initRank(g);\n var t = feasibleTree(g);\n initLowLimValues(t);\n initCutValues(t, g);\n\n var e, f;\n while ((e = leaveEdge(t))) {\n f = enterEdge(t, g, e);\n exchangeEdges(t, g, e, f);\n }\n}\n\n/*\n * Initializes cut values for all edges in the tree.\n */\nfunction initCutValues(t, g) {\n var vs = postorder(t, t.nodes());\n vs = vs.slice(0, vs.length - 1);\n _.forEach(vs, function(v) {\n assignCutValue(t, g, v);\n });\n}\n\nfunction assignCutValue(t, g, child) {\n var childLab = t.node(child),\n parent = childLab.parent;\n t.edge(child, parent).cutvalue = calcCutValue(t, g, child);\n}\n\n/*\n * Given the tight tree, its graph, and a child in the graph calculate and\n * return the cut value for the edge between the child and its parent.\n */\nfunction calcCutValue(t, g, child) {\n var childLab = t.node(child),\n parent = childLab.parent,\n // True if the child is on the tail end of the edge in the directed graph\n childIsTail = true,\n // The graph's view of the tree edge we're inspecting\n graphEdge = g.edge(child, parent),\n // The accumulated cut value for the edge between this node and its parent\n cutValue = 0;\n\n if (!graphEdge) {\n childIsTail = false;\n graphEdge = g.edge(parent, child);\n }\n\n cutValue = graphEdge.weight;\n\n _.forEach(g.nodeEdges(child), function(e) {\n var isOutEdge = e.v === child,\n other = isOutEdge ? e.w : e.v;\n\n if (other !== parent) {\n var pointsToHead = isOutEdge === childIsTail,\n otherWeight = g.edge(e).weight;\n\n cutValue += pointsToHead ? otherWeight : -otherWeight;\n if (isTreeEdge(t, child, other)) {\n var otherCutValue = t.edge(child, other).cutvalue;\n cutValue += pointsToHead ? -otherCutValue : otherCutValue;\n }\n }\n });\n\n return cutValue;\n}\n\nfunction initLowLimValues(tree, root) {\n if (arguments.length < 2) {\n root = tree.nodes()[0];\n }\n dfsAssignLowLim(tree, {}, 1, root);\n}\n\nfunction dfsAssignLowLim(tree, visited, nextLim, v, parent) {\n var low = nextLim,\n label = tree.node(v);\n\n visited[v] = true;\n _.forEach(tree.neighbors(v), function(w) {\n if (!_.has(visited, w)) {\n nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v);\n }\n });\n\n label.low = low;\n label.lim = nextLim++;\n if (parent) {\n label.parent = parent;\n } else {\n // TODO should be able to remove this when we incrementally update low lim\n delete label.parent;\n }\n\n return nextLim;\n}\n\nfunction leaveEdge(tree) {\n return _.find(tree.edges(), function(e) {\n return tree.edge(e).cutvalue < 0;\n });\n}\n\nfunction enterEdge(t, g, edge) {\n var v = edge.v,\n w = edge.w;\n\n // For the rest of this function we assume that v is the tail and w is the\n // head, so if we don't have this edge in the graph we should flip it to\n // match the correct orientation.\n if (!g.hasEdge(v, w)) {\n v = edge.w;\n w = edge.v;\n }\n\n var vLabel = t.node(v),\n wLabel = t.node(w),\n tailLabel = vLabel,\n flip = false;\n\n // If the root is in the tail of the edge then we need to flip the logic that\n // checks for the head and tail nodes in the candidates function below.\n if (vLabel.lim > wLabel.lim) {\n tailLabel = wLabel;\n flip = true;\n }\n\n var candidates = _.filter(g.edges(), function(edge) {\n return flip === isDescendant(t, t.node(edge.v), tailLabel) &&\n flip !== isDescendant(t, t.node(edge.w), tailLabel);\n });\n\n return _.minBy(candidates, function(edge) { return slack(g, edge); });\n}\n\nfunction exchangeEdges(t, g, e, f) {\n var v = e.v,\n w = e.w;\n t.removeEdge(v, w);\n t.setEdge(f.v, f.w, {});\n initLowLimValues(t);\n initCutValues(t, g);\n updateRanks(t, g);\n}\n\nfunction updateRanks(t, g) {\n var root = _.find(t.nodes(), function(v) { return !g.node(v).parent; }),\n vs = preorder(t, root);\n vs = vs.slice(1);\n _.forEach(vs, function(v) {\n var parent = t.node(v).parent,\n edge = g.edge(v, parent),\n flipped = false;\n\n if (!edge) {\n edge = g.edge(parent, v);\n flipped = true;\n }\n\n g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen);\n });\n}\n\n/*\n * Returns true if the edge is in the tree.\n */\nfunction isTreeEdge(tree, u, v) {\n return tree.hasEdge(u, v);\n}\n\n/*\n * Returns true if the specified node is descendant of the root node per the\n * assigned low and lim attributes in the tree.\n */\nfunction isDescendant(tree, vLabel, rootLabel) {\n return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/rank/network-simplex.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/rank/util.js": +/*!*********************************************!*\ + !*** ./node_modules/dagre/lib/rank/util.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ../lodash */ \"./node_modules/dagre/lib/lodash.js\");\n\nmodule.exports = {\n longestPath: longestPath,\n slack: slack\n};\n\n/*\n * Initializes ranks for the input graph using the longest path algorithm. This\n * algorithm scales well and is fast in practice, it yields rather poor\n * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom\n * ranks wide and leaving edges longer than necessary. However, due to its\n * speed, this algorithm is good for getting an initial ranking that can be fed\n * into other algorithms.\n *\n * This algorithm does not normalize layers because it will be used by other\n * algorithms in most cases. If using this algorithm directly, be sure to\n * run normalize at the end.\n *\n * Pre-conditions:\n *\n * 1. Input graph is a DAG.\n * 2. Input graph node labels can be assigned properties.\n *\n * Post-conditions:\n *\n * 1. Each node will be assign an (unnormalized) \"rank\" property.\n */\nfunction longestPath(g) {\n var visited = {};\n\n function dfs(v) {\n var label = g.node(v);\n if (_.has(visited, v)) {\n return label.rank;\n }\n visited[v] = true;\n\n var rank = _.minBy(_.map(g.outEdges(v), function(e) {\n return dfs(e.w) - g.edge(e).minlen;\n }));\n\n if (rank === Number.POSITIVE_INFINITY || // return value of _.map([]) for Lodash 3\n rank === undefined || // return value of _.map([]) for Lodash 4\n rank === null) { // return value of _.map([null])\n rank = 0;\n }\n\n return (label.rank = rank);\n }\n\n _.forEach(g.sources(), dfs);\n}\n\n/*\n * Returns the amount of slack for the given edge. The slack is defined as the\n * difference between the length of the edge and its minimum length.\n */\nfunction slack(g, e) {\n return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen;\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/rank/util.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/util.js": +/*!****************************************!*\ + !*** ./node_modules/dagre/lib/util.js ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/dagre/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ./graphlib */ \"./node_modules/dagre/lib/graphlib.js\").Graph;\n\nmodule.exports = {\n addDummyNode: addDummyNode,\n simplify: simplify,\n asNonCompoundGraph: asNonCompoundGraph,\n successorWeights: successorWeights,\n predecessorWeights: predecessorWeights,\n intersectRect: intersectRect,\n buildLayerMatrix: buildLayerMatrix,\n normalizeRanks: normalizeRanks,\n removeEmptyRanks: removeEmptyRanks,\n addBorderNode: addBorderNode,\n maxRank: maxRank,\n partition: partition,\n time: time,\n notime: notime\n};\n\n/*\n * Adds a dummy node to the graph and return v.\n */\nfunction addDummyNode(g, type, attrs, name) {\n var v;\n do {\n v = _.uniqueId(name);\n } while (g.hasNode(v));\n\n attrs.dummy = type;\n g.setNode(v, attrs);\n return v;\n}\n\n/*\n * Returns a new graph with only simple edges. Handles aggregation of data\n * associated with multi-edges.\n */\nfunction simplify(g) {\n var simplified = new Graph().setGraph(g.graph());\n _.forEach(g.nodes(), function(v) { simplified.setNode(v, g.node(v)); });\n _.forEach(g.edges(), function(e) {\n var simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 },\n label = g.edge(e);\n simplified.setEdge(e.v, e.w, {\n weight: simpleLabel.weight + label.weight,\n minlen: Math.max(simpleLabel.minlen, label.minlen)\n });\n });\n return simplified;\n}\n\nfunction asNonCompoundGraph(g) {\n var simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph());\n _.forEach(g.nodes(), function(v) {\n if (!g.children(v).length) {\n simplified.setNode(v, g.node(v));\n }\n });\n _.forEach(g.edges(), function(e) {\n simplified.setEdge(e, g.edge(e));\n });\n return simplified;\n}\n\nfunction successorWeights(g) {\n var weightMap = _.map(g.nodes(), function(v) {\n var sucs = {};\n _.forEach(g.outEdges(v), function(e) {\n sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight;\n });\n return sucs;\n });\n return _.zipObject(g.nodes(), weightMap);\n}\n\nfunction predecessorWeights(g) {\n var weightMap = _.map(g.nodes(), function(v) {\n var preds = {};\n _.forEach(g.inEdges(v), function(e) {\n preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight;\n });\n return preds;\n });\n return _.zipObject(g.nodes(), weightMap);\n}\n\n/*\n * Finds where a line starting at point ({x, y}) would intersect a rectangle\n * ({x, y, width, height}) if it were pointing at the rectangle's center.\n */\nfunction intersectRect(rect, point) {\n var x = rect.x;\n var y = rect.y;\n\n // Rectangle intersection algorithm from:\n // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes\n var dx = point.x - x;\n var dy = point.y - y;\n var w = rect.width / 2;\n var h = rect.height / 2;\n\n if (!dx && !dy) {\n throw new Error(\"Not possible to find intersection inside of the rectangle\");\n }\n\n var sx, sy;\n if (Math.abs(dy) * w > Math.abs(dx) * h) {\n // Intersection is top or bottom of rect.\n if (dy < 0) {\n h = -h;\n }\n sx = h * dx / dy;\n sy = h;\n } else {\n // Intersection is left or right of rect.\n if (dx < 0) {\n w = -w;\n }\n sx = w;\n sy = w * dy / dx;\n }\n\n return { x: x + sx, y: y + sy };\n}\n\n/*\n * Given a DAG with each node assigned \"rank\" and \"order\" properties, this\n * function will produce a matrix with the ids of each node.\n */\nfunction buildLayerMatrix(g) {\n var layering = _.map(_.range(maxRank(g) + 1), function() { return []; });\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v),\n rank = node.rank;\n if (!_.isUndefined(rank)) {\n layering[rank][node.order] = v;\n }\n });\n return layering;\n}\n\n/*\n * Adjusts the ranks for all nodes in the graph such that all nodes v have\n * rank(v) >= 0 and at least one node w has rank(w) = 0.\n */\nfunction normalizeRanks(g) {\n var min = _.minBy(_.map(g.nodes(), function(v) { return g.node(v).rank; }));\n _.forEach(g.nodes(), function(v) {\n var node = g.node(v);\n if (_.has(node, \"rank\")) {\n node.rank -= min;\n }\n });\n}\n\nfunction removeEmptyRanks(g) {\n // Ranks may not start at 0, so we need to offset them\n var offset = _.minBy(_.map(g.nodes(), function(v) { return g.node(v).rank; }));\n\n var layers = [];\n _.forEach(g.nodes(), function(v) {\n var rank = g.node(v).rank - offset;\n if (!layers[rank]) {\n layers[rank] = [];\n }\n layers[rank].push(v);\n });\n\n var delta = 0,\n nodeRankFactor = g.graph().nodeRankFactor;\n _.forEach(layers, function(vs, i) {\n if (_.isUndefined(vs) && i % nodeRankFactor !== 0) {\n --delta;\n } else if (delta) {\n _.forEach(vs, function(v) { g.node(v).rank += delta; });\n }\n });\n}\n\nfunction addBorderNode(g, prefix, rank, order) {\n var node = {\n width: 0,\n height: 0\n };\n if (arguments.length >= 4) {\n node.rank = rank;\n node.order = order;\n }\n return addDummyNode(g, \"border\", node, prefix);\n}\n\nfunction maxRank(g) {\n return _.max(_.map(g.nodes(), function(v) {\n var rank = g.node(v).rank;\n if (!_.isUndefined(rank)) {\n return rank;\n }\n }));\n}\n\n/*\n * Partition a collection into two groups: `lhs` and `rhs`. If the supplied\n * function returns true for an entry it goes into `lhs`. Otherwise it goes\n * into `rhs.\n */\nfunction partition(collection, fn) {\n var result = { lhs: [], rhs: [] };\n _.forEach(collection, function(value) {\n if (fn(value)) {\n result.lhs.push(value);\n } else {\n result.rhs.push(value);\n }\n });\n return result;\n}\n\n/*\n * Returns a new function that wraps `fn` with a timer. The wrapper logs the\n * time it takes to execute the function.\n */\nfunction time(name, fn) {\n var start = _.now();\n try {\n return fn();\n } finally {\n console.log(name + \" time: \" + (_.now() - start) + \"ms\");\n }\n}\n\nfunction notime(name, fn) {\n return fn();\n}\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/util.js?"); + +/***/ }), + +/***/ "./node_modules/dagre/lib/version.js": +/*!*******************************************!*\ + !*** ./node_modules/dagre/lib/version.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = \"0.8.2\";\n\n\n//# sourceURL=webpack:///./node_modules/dagre/lib/version.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/ExecutionEnvironment.js": +/*!*******************************************************!*\ + !*** ./node_modules/fbjs/lib/ExecutionEnvironment.js ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\n\n\nvar canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);\n\n/**\n * Simple, lightweight module assisting with the detection and context of\n * Worker. Helps avoid circular dependencies and allows code to reason about\n * whether or not they are in a Worker, even if they never include the main\n * `ReactWorker` dependency.\n */\nvar ExecutionEnvironment = {\n\n canUseDOM: canUseDOM,\n\n canUseWorkers: typeof Worker !== 'undefined',\n\n canUseEventListeners: canUseDOM && !!(window.addEventListener || window.attachEvent),\n\n canUseViewport: canUseDOM && !!window.screen,\n\n isInWorker: !canUseDOM // For now, this is true - might change in the future.\n\n};\n\nmodule.exports = ExecutionEnvironment;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/ExecutionEnvironment.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/camelize.js": +/*!*******************************************!*\ + !*** ./node_modules/fbjs/lib/camelize.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\nvar _hyphenPattern = /-(.)/g;\n\n/**\n * Camelcases a hyphenated string, for example:\n *\n * > camelize('background-color')\n * < \"backgroundColor\"\n *\n * @param {string} string\n * @return {string}\n */\nfunction camelize(string) {\n return string.replace(_hyphenPattern, function (_, character) {\n return character.toUpperCase();\n });\n}\n\nmodule.exports = camelize;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/camelize.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/camelizeStyleName.js": +/*!****************************************************!*\ + !*** ./node_modules/fbjs/lib/camelizeStyleName.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\n\n\nvar camelize = __webpack_require__(/*! ./camelize */ \"./node_modules/fbjs/lib/camelize.js\");\n\nvar msPattern = /^-ms-/;\n\n/**\n * Camelcases a hyphenated CSS property name, for example:\n *\n * > camelizeStyleName('background-color')\n * < \"backgroundColor\"\n * > camelizeStyleName('-moz-transition')\n * < \"MozTransition\"\n * > camelizeStyleName('-ms-transition')\n * < \"msTransition\"\n *\n * As Andi Smith suggests\n * (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix\n * is converted to lowercase `ms`.\n *\n * @param {string} string\n * @return {string}\n */\nfunction camelizeStyleName(string) {\n return camelize(string.replace(msPattern, 'ms-'));\n}\n\nmodule.exports = camelizeStyleName;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/camelizeStyleName.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/containsNode.js": +/*!***********************************************!*\ + !*** ./node_modules/fbjs/lib/containsNode.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * \n */\n\nvar isTextNode = __webpack_require__(/*! ./isTextNode */ \"./node_modules/fbjs/lib/isTextNode.js\");\n\n/*eslint-disable no-bitwise */\n\n/**\n * Checks if a given DOM node contains or is another DOM node.\n */\nfunction containsNode(outerNode, innerNode) {\n if (!outerNode || !innerNode) {\n return false;\n } else if (outerNode === innerNode) {\n return true;\n } else if (isTextNode(outerNode)) {\n return false;\n } else if (isTextNode(innerNode)) {\n return containsNode(outerNode, innerNode.parentNode);\n } else if ('contains' in outerNode) {\n return outerNode.contains(innerNode);\n } else if (outerNode.compareDocumentPosition) {\n return !!(outerNode.compareDocumentPosition(innerNode) & 16);\n } else {\n return false;\n }\n}\n\nmodule.exports = containsNode;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/containsNode.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/emptyFunction.js": +/*!************************************************!*\ + !*** ./node_modules/fbjs/lib/emptyFunction.js ***! + \************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * \n */\n\nfunction makeEmptyFunction(arg) {\n return function () {\n return arg;\n };\n}\n\n/**\n * This function accepts and discards inputs; it has no side effects. This is\n * primarily useful idiomatically for overridable function endpoints which\n * always need to be callable, since JS lacks a null-call idiom ala Cocoa.\n */\nvar emptyFunction = function emptyFunction() {};\n\nemptyFunction.thatReturns = makeEmptyFunction;\nemptyFunction.thatReturnsFalse = makeEmptyFunction(false);\nemptyFunction.thatReturnsTrue = makeEmptyFunction(true);\nemptyFunction.thatReturnsNull = makeEmptyFunction(null);\nemptyFunction.thatReturnsThis = function () {\n return this;\n};\nemptyFunction.thatReturnsArgument = function (arg) {\n return arg;\n};\n\nmodule.exports = emptyFunction;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/emptyFunction.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/emptyObject.js": +/*!**********************************************!*\ + !*** ./node_modules/fbjs/lib/emptyObject.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\n\n\nvar emptyObject = {};\n\nif (true) {\n Object.freeze(emptyObject);\n}\n\nmodule.exports = emptyObject;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/emptyObject.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/getActiveElement.js": +/*!***************************************************!*\ + !*** ./node_modules/fbjs/lib/getActiveElement.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\n/* eslint-disable fb-www/typeof-undefined */\n\n/**\n * Same as document.activeElement but wraps in a try-catch block. In IE it is\n * not safe to call document.activeElement if there is nothing focused.\n *\n * The activeElement will be null only if the document or document body is not\n * yet defined.\n *\n * @param {?DOMDocument} doc Defaults to current document.\n * @return {?DOMElement}\n */\nfunction getActiveElement(doc) /*?DOMElement*/{\n doc = doc || (typeof document !== 'undefined' ? document : undefined);\n if (typeof doc === 'undefined') {\n return null;\n }\n try {\n return doc.activeElement || doc.body;\n } catch (e) {\n return doc.body;\n }\n}\n\nmodule.exports = getActiveElement;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/getActiveElement.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/hyphenate.js": +/*!********************************************!*\ + !*** ./node_modules/fbjs/lib/hyphenate.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\nvar _uppercasePattern = /([A-Z])/g;\n\n/**\n * Hyphenates a camelcased string, for example:\n *\n * > hyphenate('backgroundColor')\n * < \"background-color\"\n *\n * For CSS style names, use `hyphenateStyleName` instead which works properly\n * with all vendor prefixes, including `ms`.\n *\n * @param {string} string\n * @return {string}\n */\nfunction hyphenate(string) {\n return string.replace(_uppercasePattern, '-$1').toLowerCase();\n}\n\nmodule.exports = hyphenate;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/hyphenate.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/hyphenateStyleName.js": +/*!*****************************************************!*\ + !*** ./node_modules/fbjs/lib/hyphenateStyleName.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\n\n\nvar hyphenate = __webpack_require__(/*! ./hyphenate */ \"./node_modules/fbjs/lib/hyphenate.js\");\n\nvar msPattern = /^ms-/;\n\n/**\n * Hyphenates a camelcased CSS property name, for example:\n *\n * > hyphenateStyleName('backgroundColor')\n * < \"background-color\"\n * > hyphenateStyleName('MozTransition')\n * < \"-moz-transition\"\n * > hyphenateStyleName('msTransition')\n * < \"-ms-transition\"\n *\n * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix\n * is converted to `-ms-`.\n *\n * @param {string} string\n * @return {string}\n */\nfunction hyphenateStyleName(string) {\n return hyphenate(string).replace(msPattern, '-ms-');\n}\n\nmodule.exports = hyphenateStyleName;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/hyphenateStyleName.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/invariant.js": +/*!********************************************!*\ + !*** ./node_modules/fbjs/lib/invariant.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\n\n\n/**\n * Use invariant() to assert state which your program assumes to be true.\n *\n * Provide sprintf-style format (only %s is supported) and arguments\n * to provide information about what broke and what you were\n * expecting.\n *\n * The invariant message will be stripped in production, but the invariant\n * will remain to ensure logic does not differ in production.\n */\n\nvar validateFormat = function validateFormat(format) {};\n\nif (true) {\n validateFormat = function validateFormat(format) {\n if (format === undefined) {\n throw new Error('invariant requires an error message argument');\n }\n };\n}\n\nfunction invariant(condition, format, a, b, c, d, e, f) {\n validateFormat(format);\n\n if (!condition) {\n var error;\n if (format === undefined) {\n error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');\n } else {\n var args = [a, b, c, d, e, f];\n var argIndex = 0;\n error = new Error(format.replace(/%s/g, function () {\n return args[argIndex++];\n }));\n error.name = 'Invariant Violation';\n }\n\n error.framesToPop = 1; // we don't care about invariant's own frame\n throw error;\n }\n}\n\nmodule.exports = invariant;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/invariant.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/isNode.js": +/*!*****************************************!*\ + !*** ./node_modules/fbjs/lib/isNode.js ***! + \*****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\n/**\n * @param {*} object The object to check.\n * @return {boolean} Whether or not the object is a DOM node.\n */\nfunction isNode(object) {\n var doc = object ? object.ownerDocument || object : document;\n var defaultView = doc.defaultView || window;\n return !!(object && (typeof defaultView.Node === 'function' ? object instanceof defaultView.Node : typeof object === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'));\n}\n\nmodule.exports = isNode;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/isNode.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/isTextNode.js": +/*!*********************************************!*\ + !*** ./node_modules/fbjs/lib/isTextNode.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\n/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n */\n\nvar isNode = __webpack_require__(/*! ./isNode */ \"./node_modules/fbjs/lib/isNode.js\");\n\n/**\n * @param {*} object The object to check.\n * @return {boolean} Whether or not the object is a DOM text node.\n */\nfunction isTextNode(object) {\n return isNode(object) && object.nodeType == 3;\n}\n\nmodule.exports = isTextNode;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/isTextNode.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/memoizeStringOnly.js": +/*!****************************************************!*\ + !*** ./node_modules/fbjs/lib/memoizeStringOnly.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * \n * @typechecks static-only\n */\n\n\n\n/**\n * Memoizes the return value of a function that accepts one string argument.\n */\n\nfunction memoizeStringOnly(callback) {\n var cache = {};\n return function (string) {\n if (!cache.hasOwnProperty(string)) {\n cache[string] = callback.call(this, string);\n }\n return cache[string];\n };\n}\n\nmodule.exports = memoizeStringOnly;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/memoizeStringOnly.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/shallowEqual.js": +/*!***********************************************!*\ + !*** ./node_modules/fbjs/lib/shallowEqual.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n * @typechecks\n * \n */\n\n/*eslint-disable no-self-compare */\n\n\n\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\n\n/**\n * inlined Object.is polyfill to avoid requiring consumers ship their own\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\n */\nfunction is(x, y) {\n // SameValue algorithm\n if (x === y) {\n // Steps 1-5, 7-10\n // Steps 6.b-6.e: +0 != -0\n // Added the nonzero y check to make Flow happy, but it is redundant\n return x !== 0 || y !== 0 || 1 / x === 1 / y;\n } else {\n // Step 6.a: NaN == NaN\n return x !== x && y !== y;\n }\n}\n\n/**\n * Performs equality by iterating through keys on an object and returning false\n * when any key has values which are not strictly equal between the arguments.\n * Returns true when the values of all keys are strictly equal.\n */\nfunction shallowEqual(objA, objB) {\n if (is(objA, objB)) {\n return true;\n }\n\n if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {\n return false;\n }\n\n var keysA = Object.keys(objA);\n var keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n // Test for A's keys different from B.\n for (var i = 0; i < keysA.length; i++) {\n if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n\n return true;\n}\n\nmodule.exports = shallowEqual;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/shallowEqual.js?"); + +/***/ }), + +/***/ "./node_modules/fbjs/lib/warning.js": +/*!******************************************!*\ + !*** ./node_modules/fbjs/lib/warning.js ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("/**\n * Copyright (c) 2014-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n *\n */\n\n\n\nvar emptyFunction = __webpack_require__(/*! ./emptyFunction */ \"./node_modules/fbjs/lib/emptyFunction.js\");\n\n/**\n * Similar to invariant but only logs a warning if the condition is not met.\n * This can be used to log issues in development environments in critical\n * paths. Removing the logging code for production environments will keep the\n * same logic and follow the same code paths.\n */\n\nvar warning = emptyFunction;\n\nif (true) {\n var printWarning = function printWarning(format) {\n for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n args[_key - 1] = arguments[_key];\n }\n\n var argIndex = 0;\n var message = 'Warning: ' + format.replace(/%s/g, function () {\n return args[argIndex++];\n });\n if (typeof console !== 'undefined') {\n console.error(message);\n }\n try {\n // --- Welcome to debugging React ---\n // This error was thrown as a convenience so that you can use this stack\n // to find the callsite that caused this warning to fire.\n throw new Error(message);\n } catch (x) {}\n };\n\n warning = function warning(condition, format) {\n if (format === undefined) {\n throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');\n }\n\n if (format.indexOf('Failed Composite propType: ') === 0) {\n return; // Ignore CompositeComponent proptype check.\n }\n\n if (!condition) {\n for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {\n args[_key2 - 2] = arguments[_key2];\n }\n\n printWarning.apply(undefined, [format].concat(args));\n }\n };\n}\n\nmodule.exports = warning;\n\n//# sourceURL=webpack:///./node_modules/fbjs/lib/warning.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/index.js": +/*!****************************************!*\ + !*** ./node_modules/graphlib/index.js ***! + \****************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/**\n * Copyright (c) 2014, Chris Pettitt\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice, this\n * list of conditions and the following disclaimer.\n *\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n * this list of conditions and the following disclaimer in the documentation\n * and/or other materials provided with the distribution.\n *\n * 3. Neither the name of the copyright holder nor the names of its contributors\n * may be used to endorse or promote products derived from this software without\n * specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\nvar lib = __webpack_require__(/*! ./lib */ \"./node_modules/graphlib/lib/index.js\");\n\nmodule.exports = {\n Graph: lib.Graph,\n json: __webpack_require__(/*! ./lib/json */ \"./node_modules/graphlib/lib/json.js\"),\n alg: __webpack_require__(/*! ./lib/alg */ \"./node_modules/graphlib/lib/alg/index.js\"),\n version: lib.version\n};\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/index.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/components.js": +/*!*****************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/components.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = components;\n\nfunction components(g) {\n var visited = {},\n cmpts = [],\n cmpt;\n\n function dfs(v) {\n if (_.has(visited, v)) return;\n visited[v] = true;\n cmpt.push(v);\n _.each(g.successors(v), dfs);\n _.each(g.predecessors(v), dfs);\n }\n\n _.each(g.nodes(), function(v) {\n cmpt = [];\n dfs(v);\n if (cmpt.length) {\n cmpts.push(cmpt);\n }\n });\n\n return cmpts;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/components.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/dfs.js": +/*!**********************************************!*\ + !*** ./node_modules/graphlib/lib/alg/dfs.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = dfs;\n\n/*\n * A helper that preforms a pre- or post-order traversal on the input graph\n * and returns the nodes in the order they were visited. If the graph is\n * undirected then this algorithm will navigate using neighbors. If the graph\n * is directed then this algorithm will navigate using successors.\n *\n * Order must be one of \"pre\" or \"post\".\n */\nfunction dfs(g, vs, order) {\n if (!_.isArray(vs)) {\n vs = [vs];\n }\n\n var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g);\n\n var acc = [],\n visited = {};\n _.each(vs, function(v) {\n if (!g.hasNode(v)) {\n throw new Error(\"Graph does not have node: \" + v);\n }\n\n doDfs(g, v, order === \"post\", visited, navigation, acc);\n });\n return acc;\n}\n\nfunction doDfs(g, v, postorder, visited, navigation, acc) {\n if (!_.has(visited, v)) {\n visited[v] = true;\n\n if (!postorder) { acc.push(v); }\n _.each(navigation(v), function(w) {\n doDfs(g, w, postorder, visited, navigation, acc);\n });\n if (postorder) { acc.push(v); }\n }\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/dfs.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/dijkstra-all.js": +/*!*******************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/dijkstra-all.js ***! + \*******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var dijkstra = __webpack_require__(/*! ./dijkstra */ \"./node_modules/graphlib/lib/alg/dijkstra.js\"),\n _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = dijkstraAll;\n\nfunction dijkstraAll(g, weightFunc, edgeFunc) {\n return _.transform(g.nodes(), function(acc, v) {\n acc[v] = dijkstra(g, v, weightFunc, edgeFunc);\n }, {});\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/dijkstra-all.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/dijkstra.js": +/*!***************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/dijkstra.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\"),\n PriorityQueue = __webpack_require__(/*! ../data/priority-queue */ \"./node_modules/graphlib/lib/data/priority-queue.js\");\n\nmodule.exports = dijkstra;\n\nvar DEFAULT_WEIGHT_FUNC = _.constant(1);\n\nfunction dijkstra(g, source, weightFn, edgeFn) {\n return runDijkstra(g, String(source),\n weightFn || DEFAULT_WEIGHT_FUNC,\n edgeFn || function(v) { return g.outEdges(v); });\n}\n\nfunction runDijkstra(g, source, weightFn, edgeFn) {\n var results = {},\n pq = new PriorityQueue(),\n v, vEntry;\n\n var updateNeighbors = function(edge) {\n var w = edge.v !== v ? edge.v : edge.w,\n wEntry = results[w],\n weight = weightFn(edge),\n distance = vEntry.distance + weight;\n\n if (weight < 0) {\n throw new Error(\"dijkstra does not allow negative edge weights. \" +\n \"Bad edge: \" + edge + \" Weight: \" + weight);\n }\n\n if (distance < wEntry.distance) {\n wEntry.distance = distance;\n wEntry.predecessor = v;\n pq.decrease(w, distance);\n }\n };\n\n g.nodes().forEach(function(v) {\n var distance = v === source ? 0 : Number.POSITIVE_INFINITY;\n results[v] = { distance: distance };\n pq.add(v, distance);\n });\n\n while (pq.size() > 0) {\n v = pq.removeMin();\n vEntry = results[v];\n if (vEntry.distance === Number.POSITIVE_INFINITY) {\n break;\n }\n\n edgeFn(v).forEach(updateNeighbors);\n }\n\n return results;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/dijkstra.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/find-cycles.js": +/*!******************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/find-cycles.js ***! + \******************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\"),\n tarjan = __webpack_require__(/*! ./tarjan */ \"./node_modules/graphlib/lib/alg/tarjan.js\");\n\nmodule.exports = findCycles;\n\nfunction findCycles(g) {\n return _.filter(tarjan(g), function(cmpt) {\n return cmpt.length > 1 || (cmpt.length === 1 && g.hasEdge(cmpt[0], cmpt[0]));\n });\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/find-cycles.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/floyd-warshall.js": +/*!*********************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/floyd-warshall.js ***! + \*********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = floydWarshall;\n\nvar DEFAULT_WEIGHT_FUNC = _.constant(1);\n\nfunction floydWarshall(g, weightFn, edgeFn) {\n return runFloydWarshall(g,\n weightFn || DEFAULT_WEIGHT_FUNC,\n edgeFn || function(v) { return g.outEdges(v); });\n}\n\nfunction runFloydWarshall(g, weightFn, edgeFn) {\n var results = {},\n nodes = g.nodes();\n\n nodes.forEach(function(v) {\n results[v] = {};\n results[v][v] = { distance: 0 };\n nodes.forEach(function(w) {\n if (v !== w) {\n results[v][w] = { distance: Number.POSITIVE_INFINITY };\n }\n });\n edgeFn(v).forEach(function(edge) {\n var w = edge.v === v ? edge.w : edge.v,\n d = weightFn(edge);\n results[v][w] = { distance: d, predecessor: v };\n });\n });\n\n nodes.forEach(function(k) {\n var rowK = results[k];\n nodes.forEach(function(i) {\n var rowI = results[i];\n nodes.forEach(function(j) {\n var ik = rowI[k];\n var kj = rowK[j];\n var ij = rowI[j];\n var altDistance = ik.distance + kj.distance;\n if (altDistance < ij.distance) {\n ij.distance = altDistance;\n ij.predecessor = kj.predecessor;\n }\n });\n });\n });\n\n return results;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/floyd-warshall.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/index.js": +/*!************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/index.js ***! + \************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("module.exports = {\n components: __webpack_require__(/*! ./components */ \"./node_modules/graphlib/lib/alg/components.js\"),\n dijkstra: __webpack_require__(/*! ./dijkstra */ \"./node_modules/graphlib/lib/alg/dijkstra.js\"),\n dijkstraAll: __webpack_require__(/*! ./dijkstra-all */ \"./node_modules/graphlib/lib/alg/dijkstra-all.js\"),\n findCycles: __webpack_require__(/*! ./find-cycles */ \"./node_modules/graphlib/lib/alg/find-cycles.js\"),\n floydWarshall: __webpack_require__(/*! ./floyd-warshall */ \"./node_modules/graphlib/lib/alg/floyd-warshall.js\"),\n isAcyclic: __webpack_require__(/*! ./is-acyclic */ \"./node_modules/graphlib/lib/alg/is-acyclic.js\"),\n postorder: __webpack_require__(/*! ./postorder */ \"./node_modules/graphlib/lib/alg/postorder.js\"),\n preorder: __webpack_require__(/*! ./preorder */ \"./node_modules/graphlib/lib/alg/preorder.js\"),\n prim: __webpack_require__(/*! ./prim */ \"./node_modules/graphlib/lib/alg/prim.js\"),\n tarjan: __webpack_require__(/*! ./tarjan */ \"./node_modules/graphlib/lib/alg/tarjan.js\"),\n topsort: __webpack_require__(/*! ./topsort */ \"./node_modules/graphlib/lib/alg/topsort.js\")\n};\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/index.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/is-acyclic.js": +/*!*****************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/is-acyclic.js ***! + \*****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var topsort = __webpack_require__(/*! ./topsort */ \"./node_modules/graphlib/lib/alg/topsort.js\");\n\nmodule.exports = isAcyclic;\n\nfunction isAcyclic(g) {\n try {\n topsort(g);\n } catch (e) {\n if (e instanceof topsort.CycleException) {\n return false;\n }\n throw e;\n }\n return true;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/is-acyclic.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/postorder.js": +/*!****************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/postorder.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var dfs = __webpack_require__(/*! ./dfs */ \"./node_modules/graphlib/lib/alg/dfs.js\");\n\nmodule.exports = postorder;\n\nfunction postorder(g, vs) {\n return dfs(g, vs, \"post\");\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/postorder.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/preorder.js": +/*!***************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/preorder.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var dfs = __webpack_require__(/*! ./dfs */ \"./node_modules/graphlib/lib/alg/dfs.js\");\n\nmodule.exports = preorder;\n\nfunction preorder(g, vs) {\n return dfs(g, vs, \"pre\");\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/preorder.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/prim.js": +/*!***********************************************!*\ + !*** ./node_modules/graphlib/lib/alg/prim.js ***! + \***********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ../graph */ \"./node_modules/graphlib/lib/graph.js\"),\n PriorityQueue = __webpack_require__(/*! ../data/priority-queue */ \"./node_modules/graphlib/lib/data/priority-queue.js\");\n\nmodule.exports = prim;\n\nfunction prim(g, weightFunc) {\n var result = new Graph(),\n parents = {},\n pq = new PriorityQueue(),\n v;\n\n function updateNeighbors(edge) {\n var w = edge.v === v ? edge.w : edge.v,\n pri = pq.priority(w);\n if (pri !== undefined) {\n var edgeWeight = weightFunc(edge);\n if (edgeWeight < pri) {\n parents[w] = v;\n pq.decrease(w, edgeWeight);\n }\n }\n }\n\n if (g.nodeCount() === 0) {\n return result;\n }\n\n _.each(g.nodes(), function(v) {\n pq.add(v, Number.POSITIVE_INFINITY);\n result.setNode(v);\n });\n\n // Start from an arbitrary node\n pq.decrease(g.nodes()[0], 0);\n\n var init = false;\n while (pq.size() > 0) {\n v = pq.removeMin();\n if (_.has(parents, v)) {\n result.setEdge(v, parents[v]);\n } else if (init) {\n throw new Error(\"Input graph is not connected: \" + g);\n } else {\n init = true;\n }\n\n g.nodeEdges(v).forEach(updateNeighbors);\n }\n\n return result;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/prim.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/tarjan.js": +/*!*************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/tarjan.js ***! + \*************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = tarjan;\n\nfunction tarjan(g) {\n var index = 0,\n stack = [],\n visited = {}, // node id -> { onStack, lowlink, index }\n results = [];\n\n function dfs(v) {\n var entry = visited[v] = {\n onStack: true,\n lowlink: index,\n index: index++\n };\n stack.push(v);\n\n g.successors(v).forEach(function(w) {\n if (!_.has(visited, w)) {\n dfs(w);\n entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink);\n } else if (visited[w].onStack) {\n entry.lowlink = Math.min(entry.lowlink, visited[w].index);\n }\n });\n\n if (entry.lowlink === entry.index) {\n var cmpt = [],\n w;\n do {\n w = stack.pop();\n visited[w].onStack = false;\n cmpt.push(w);\n } while (v !== w);\n results.push(cmpt);\n }\n }\n\n g.nodes().forEach(function(v) {\n if (!_.has(visited, v)) {\n dfs(v);\n }\n });\n\n return results;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/tarjan.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/alg/topsort.js": +/*!**************************************************!*\ + !*** ./node_modules/graphlib/lib/alg/topsort.js ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = topsort;\ntopsort.CycleException = CycleException;\n\nfunction topsort(g) {\n var visited = {},\n stack = {},\n results = [];\n\n function visit(node) {\n if (_.has(stack, node)) {\n throw new CycleException();\n }\n\n if (!_.has(visited, node)) {\n stack[node] = true;\n visited[node] = true;\n _.each(g.predecessors(node), visit);\n delete stack[node];\n results.push(node);\n }\n }\n\n _.each(g.sinks(), visit);\n\n if (_.size(visited) !== g.nodeCount()) {\n throw new CycleException();\n }\n\n return results;\n}\n\nfunction CycleException() {}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/alg/topsort.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/data/priority-queue.js": +/*!**********************************************************!*\ + !*** ./node_modules/graphlib/lib/data/priority-queue.js ***! + \**********************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ../lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = PriorityQueue;\n\n/**\n * A min-priority queue data structure. This algorithm is derived from Cormen,\n * et al., \"Introduction to Algorithms\". The basic idea of a min-priority\n * queue is that you can efficiently (in O(1) time) get the smallest key in\n * the queue. Adding and removing elements takes O(log n) time. A key can\n * have its priority decreased in O(log n) time.\n */\nfunction PriorityQueue() {\n this._arr = [];\n this._keyIndices = {};\n}\n\n/**\n * Returns the number of elements in the queue. Takes `O(1)` time.\n */\nPriorityQueue.prototype.size = function() {\n return this._arr.length;\n};\n\n/**\n * Returns the keys that are in the queue. Takes `O(n)` time.\n */\nPriorityQueue.prototype.keys = function() {\n return this._arr.map(function(x) { return x.key; });\n};\n\n/**\n * Returns `true` if **key** is in the queue and `false` if not.\n */\nPriorityQueue.prototype.has = function(key) {\n return _.has(this._keyIndices, key);\n};\n\n/**\n * Returns the priority for **key**. If **key** is not present in the queue\n * then this function returns `undefined`. Takes `O(1)` time.\n *\n * @param {Object} key\n */\nPriorityQueue.prototype.priority = function(key) {\n var index = this._keyIndices[key];\n if (index !== undefined) {\n return this._arr[index].priority;\n }\n};\n\n/**\n * Returns the key for the minimum element in this queue. If the queue is\n * empty this function throws an Error. Takes `O(1)` time.\n */\nPriorityQueue.prototype.min = function() {\n if (this.size() === 0) {\n throw new Error(\"Queue underflow\");\n }\n return this._arr[0].key;\n};\n\n/**\n * Inserts a new key into the priority queue. If the key already exists in\n * the queue this function returns `false`; otherwise it will return `true`.\n * Takes `O(n)` time.\n *\n * @param {Object} key the key to add\n * @param {Number} priority the initial priority for the key\n */\nPriorityQueue.prototype.add = function(key, priority) {\n var keyIndices = this._keyIndices;\n key = String(key);\n if (!_.has(keyIndices, key)) {\n var arr = this._arr;\n var index = arr.length;\n keyIndices[key] = index;\n arr.push({key: key, priority: priority});\n this._decrease(index);\n return true;\n }\n return false;\n};\n\n/**\n * Removes and returns the smallest key in the queue. Takes `O(log n)` time.\n */\nPriorityQueue.prototype.removeMin = function() {\n this._swap(0, this._arr.length - 1);\n var min = this._arr.pop();\n delete this._keyIndices[min.key];\n this._heapify(0);\n return min.key;\n};\n\n/**\n * Decreases the priority for **key** to **priority**. If the new priority is\n * greater than the previous priority, this function will throw an Error.\n *\n * @param {Object} key the key for which to raise priority\n * @param {Number} priority the new priority for the key\n */\nPriorityQueue.prototype.decrease = function(key, priority) {\n var index = this._keyIndices[key];\n if (priority > this._arr[index].priority) {\n throw new Error(\"New priority is greater than current priority. \" +\n \"Key: \" + key + \" Old: \" + this._arr[index].priority + \" New: \" + priority);\n }\n this._arr[index].priority = priority;\n this._decrease(index);\n};\n\nPriorityQueue.prototype._heapify = function(i) {\n var arr = this._arr;\n var l = 2 * i,\n r = l + 1,\n largest = i;\n if (l < arr.length) {\n largest = arr[l].priority < arr[largest].priority ? l : largest;\n if (r < arr.length) {\n largest = arr[r].priority < arr[largest].priority ? r : largest;\n }\n if (largest !== i) {\n this._swap(i, largest);\n this._heapify(largest);\n }\n }\n};\n\nPriorityQueue.prototype._decrease = function(index) {\n var arr = this._arr;\n var priority = arr[index].priority;\n var parent;\n while (index !== 0) {\n parent = index >> 1;\n if (arr[parent].priority < priority) {\n break;\n }\n this._swap(index, parent);\n index = parent;\n }\n};\n\nPriorityQueue.prototype._swap = function(i, j) {\n var arr = this._arr;\n var keyIndices = this._keyIndices;\n var origArrI = arr[i];\n var origArrJ = arr[j];\n arr[i] = origArrJ;\n arr[j] = origArrI;\n keyIndices[origArrJ.key] = i;\n keyIndices[origArrI.key] = j;\n};\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/data/priority-queue.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/graph.js": +/*!********************************************!*\ + !*** ./node_modules/graphlib/lib/graph.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +eval("\n\nvar _ = __webpack_require__(/*! ./lodash */ \"./node_modules/graphlib/lib/lodash.js\");\n\nmodule.exports = Graph;\n\nvar DEFAULT_EDGE_NAME = \"\\x00\",\n GRAPH_NODE = \"\\x00\",\n EDGE_KEY_DELIM = \"\\x01\";\n\n// Implementation notes:\n//\n// * Node id query functions should return string ids for the nodes\n// * Edge id query functions should return an \"edgeObj\", edge object, that is\n// composed of enough information to uniquely identify an edge: {v, w, name}.\n// * Internally we use an \"edgeId\", a stringified form of the edgeObj, to\n// reference edges. This is because we need a performant way to look these\n// edges up and, object properties, which have string keys, are the closest\n// we're going to get to a performant hashtable in JavaScript.\n\nfunction Graph(opts) {\n this._isDirected = _.has(opts, \"directed\") ? opts.directed : true;\n this._isMultigraph = _.has(opts, \"multigraph\") ? opts.multigraph : false;\n this._isCompound = _.has(opts, \"compound\") ? opts.compound : false;\n\n // Label for the graph itself\n this._label = undefined;\n\n // Defaults to be set when creating a new node\n this._defaultNodeLabelFn = _.constant(undefined);\n\n // Defaults to be set when creating a new edge\n this._defaultEdgeLabelFn = _.constant(undefined);\n\n // v -> label\n this._nodes = {};\n\n if (this._isCompound) {\n // v -> parent\n this._parent = {};\n\n // v -> children\n this._children = {};\n this._children[GRAPH_NODE] = {};\n }\n\n // v -> edgeObj\n this._in = {};\n\n // u -> v -> Number\n this._preds = {};\n\n // v -> edgeObj\n this._out = {};\n\n // v -> w -> Number\n this._sucs = {};\n\n // e -> edgeObj\n this._edgeObjs = {};\n\n // e -> label\n this._edgeLabels = {};\n}\n\n/* Number of nodes in the graph. Should only be changed by the implementation. */\nGraph.prototype._nodeCount = 0;\n\n/* Number of edges in the graph. Should only be changed by the implementation. */\nGraph.prototype._edgeCount = 0;\n\n\n/* === Graph functions ========= */\n\nGraph.prototype.isDirected = function() {\n return this._isDirected;\n};\n\nGraph.prototype.isMultigraph = function() {\n return this._isMultigraph;\n};\n\nGraph.prototype.isCompound = function() {\n return this._isCompound;\n};\n\nGraph.prototype.setGraph = function(label) {\n this._label = label;\n return this;\n};\n\nGraph.prototype.graph = function() {\n return this._label;\n};\n\n\n/* === Node functions ========== */\n\nGraph.prototype.setDefaultNodeLabel = function(newDefault) {\n if (!_.isFunction(newDefault)) {\n newDefault = _.constant(newDefault);\n }\n this._defaultNodeLabelFn = newDefault;\n return this;\n};\n\nGraph.prototype.nodeCount = function() {\n return this._nodeCount;\n};\n\nGraph.prototype.nodes = function() {\n return _.keys(this._nodes);\n};\n\nGraph.prototype.sources = function() {\n var self = this;\n return _.filter(this.nodes(), function(v) {\n return _.isEmpty(self._in[v]);\n });\n};\n\nGraph.prototype.sinks = function() {\n var self = this;\n return _.filter(this.nodes(), function(v) {\n return _.isEmpty(self._out[v]);\n });\n};\n\nGraph.prototype.setNodes = function(vs, value) {\n var args = arguments;\n var self = this;\n _.each(vs, function(v) {\n if (args.length > 1) {\n self.setNode(v, value);\n } else {\n self.setNode(v);\n }\n });\n return this;\n};\n\nGraph.prototype.setNode = function(v, value) {\n if (_.has(this._nodes, v)) {\n if (arguments.length > 1) {\n this._nodes[v] = value;\n }\n return this;\n }\n\n this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v);\n if (this._isCompound) {\n this._parent[v] = GRAPH_NODE;\n this._children[v] = {};\n this._children[GRAPH_NODE][v] = true;\n }\n this._in[v] = {};\n this._preds[v] = {};\n this._out[v] = {};\n this._sucs[v] = {};\n ++this._nodeCount;\n return this;\n};\n\nGraph.prototype.node = function(v) {\n return this._nodes[v];\n};\n\nGraph.prototype.hasNode = function(v) {\n return _.has(this._nodes, v);\n};\n\nGraph.prototype.removeNode = function(v) {\n var self = this;\n if (_.has(this._nodes, v)) {\n var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); };\n delete this._nodes[v];\n if (this._isCompound) {\n this._removeFromParentsChildList(v);\n delete this._parent[v];\n _.each(this.children(v), function(child) {\n self.setParent(child);\n });\n delete this._children[v];\n }\n _.each(_.keys(this._in[v]), removeEdge);\n delete this._in[v];\n delete this._preds[v];\n _.each(_.keys(this._out[v]), removeEdge);\n delete this._out[v];\n delete this._sucs[v];\n --this._nodeCount;\n }\n return this;\n};\n\nGraph.prototype.setParent = function(v, parent) {\n if (!this._isCompound) {\n throw new Error(\"Cannot set parent in a non-compound graph\");\n }\n\n if (_.isUndefined(parent)) {\n parent = GRAPH_NODE;\n } else {\n // Coerce parent to string\n parent += \"\";\n for (var ancestor = parent;\n !_.isUndefined(ancestor);\n ancestor = this.parent(ancestor)) {\n if (ancestor === v) {\n throw new Error(\"Setting \" + parent+ \" as parent of \" + v +\n \" would create a cycle\");\n }\n }\n\n this.setNode(parent);\n }\n\n this.setNode(v);\n this._removeFromParentsChildList(v);\n this._parent[v] = parent;\n this._children[parent][v] = true;\n return this;\n};\n\nGraph.prototype._removeFromParentsChildList = function(v) {\n delete this._children[this._parent[v]][v];\n};\n\nGraph.prototype.parent = function(v) {\n if (this._isCompound) {\n var parent = this._parent[v];\n if (parent !== GRAPH_NODE) {\n return parent;\n }\n }\n};\n\nGraph.prototype.children = function(v) {\n if (_.isUndefined(v)) {\n v = GRAPH_NODE;\n }\n\n if (this._isCompound) {\n var children = this._children[v];\n if (children) {\n return _.keys(children);\n }\n } else if (v === GRAPH_NODE) {\n return this.nodes();\n } else if (this.hasNode(v)) {\n return [];\n }\n};\n\nGraph.prototype.predecessors = function(v) {\n var predsV = this._preds[v];\n if (predsV) {\n return _.keys(predsV);\n }\n};\n\nGraph.prototype.successors = function(v) {\n var sucsV = this._sucs[v];\n if (sucsV) {\n return _.keys(sucsV);\n }\n};\n\nGraph.prototype.neighbors = function(v) {\n var preds = this.predecessors(v);\n if (preds) {\n return _.union(preds, this.successors(v));\n }\n};\n\nGraph.prototype.isLeaf = function (v) {\n var neighbors;\n if (this.isDirected()) {\n neighbors = this.successors(v);\n } else {\n neighbors = this.neighbors(v);\n }\n return neighbors.length === 0;\n};\n\nGraph.prototype.filterNodes = function(filter) {\n var copy = new this.constructor({\n directed: this._isDirected,\n multigraph: this._isMultigraph,\n compound: this._isCompound\n });\n\n copy.setGraph(this.graph());\n\n var self = this;\n _.each(this._nodes, function(value, v) {\n if (filter(v)) {\n copy.setNode(v, value);\n }\n });\n\n _.each(this._edgeObjs, function(e) {\n if (copy.hasNode(e.v) && copy.hasNode(e.w)) {\n copy.setEdge(e, self.edge(e));\n }\n });\n\n var parents = {};\n function findParent(v) {\n var parent = self.parent(v);\n if (parent === undefined || copy.hasNode(parent)) {\n parents[v] = parent;\n return parent;\n } else if (parent in parents) {\n return parents[parent];\n } else {\n return findParent(parent);\n }\n }\n\n if (this._isCompound) {\n _.each(copy.nodes(), function(v) {\n copy.setParent(v, findParent(v));\n });\n }\n\n return copy;\n};\n\n/* === Edge functions ========== */\n\nGraph.prototype.setDefaultEdgeLabel = function(newDefault) {\n if (!_.isFunction(newDefault)) {\n newDefault = _.constant(newDefault);\n }\n this._defaultEdgeLabelFn = newDefault;\n return this;\n};\n\nGraph.prototype.edgeCount = function() {\n return this._edgeCount;\n};\n\nGraph.prototype.edges = function() {\n return _.values(this._edgeObjs);\n};\n\nGraph.prototype.setPath = function(vs, value) {\n var self = this,\n args = arguments;\n _.reduce(vs, function(v, w) {\n if (args.length > 1) {\n self.setEdge(v, w, value);\n } else {\n self.setEdge(v, w);\n }\n return w;\n });\n return this;\n};\n\n/*\n * setEdge(v, w, [value, [name]])\n * setEdge({ v, w, [name] }, [value])\n */\nGraph.prototype.setEdge = function() {\n var v, w, name, value,\n valueSpecified = false,\n arg0 = arguments[0];\n\n if (typeof arg0 === \"object\" && arg0 !== null && \"v\" in arg0) {\n v = arg0.v;\n w = arg0.w;\n name = arg0.name;\n if (arguments.length === 2) {\n value = arguments[1];\n valueSpecified = true;\n }\n } else {\n v = arg0;\n w = arguments[1];\n name = arguments[3];\n if (arguments.length > 2) {\n value = arguments[2];\n valueSpecified = true;\n }\n }\n\n v = \"\" + v;\n w = \"\" + w;\n if (!_.isUndefined(name)) {\n name = \"\" + name;\n }\n\n var e = edgeArgsToId(this._isDirected, v, w, name);\n if (_.has(this._edgeLabels, e)) {\n if (valueSpecified) {\n this._edgeLabels[e] = value;\n }\n return this;\n }\n\n if (!_.isUndefined(name) && !this._isMultigraph) {\n throw new Error(\"Cannot set a named edge when isMultigraph = false\");\n }\n\n // It didn't exist, so we need to create it.\n // First ensure the nodes exist.\n this.setNode(v);\n this.setNode(w);\n\n this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name);\n\n var edgeObj = edgeArgsToObj(this._isDirected, v, w, name);\n // Ensure we add undirected edges in a consistent way.\n v = edgeObj.v;\n w = edgeObj.w;\n\n Object.freeze(edgeObj);\n this._edgeObjs[e] = edgeObj;\n incrementOrInitEntry(this._preds[w], v);\n incrementOrInitEntry(this._sucs[v], w);\n this._in[w][e] = edgeObj;\n this._out[v][e] = edgeObj;\n this._edgeCount++;\n return this;\n};\n\nGraph.prototype.edge = function(v, w, name) {\n var e = (arguments.length === 1\n ? edgeObjToId(this._isDirected, arguments[0])\n : edgeArgsToId(this._isDirected, v, w, name));\n return this._edgeLabels[e];\n};\n\nGraph.prototype.hasEdge = function(v, w, name) {\n var e = (arguments.length === 1\n ? edgeObjToId(this._isDirected, arguments[0])\n : edgeArgsToId(this._isDirected, v, w, name));\n return _.has(this._edgeLabels, e);\n};\n\nGraph.prototype.removeEdge = function(v, w, name) {\n var e = (arguments.length === 1\n ? edgeObjToId(this._isDirected, arguments[0])\n : edgeArgsToId(this._isDirected, v, w, name)),\n edge = this._edgeObjs[e];\n if (edge) {\n v = edge.v;\n w = edge.w;\n delete this._edgeLabels[e];\n delete this._edgeObjs[e];\n decrementOrRemoveEntry(this._preds[w], v);\n decrementOrRemoveEntry(this._sucs[v], w);\n delete this._in[w][e];\n delete this._out[v][e];\n this._edgeCount--;\n }\n return this;\n};\n\nGraph.prototype.inEdges = function(v, u) {\n var inV = this._in[v];\n if (inV) {\n var edges = _.values(inV);\n if (!u) {\n return edges;\n }\n return _.filter(edges, function(edge) { return edge.v === u; });\n }\n};\n\nGraph.prototype.outEdges = function(v, w) {\n var outV = this._out[v];\n if (outV) {\n var edges = _.values(outV);\n if (!w) {\n return edges;\n }\n return _.filter(edges, function(edge) { return edge.w === w; });\n }\n};\n\nGraph.prototype.nodeEdges = function(v, w) {\n var inEdges = this.inEdges(v, w);\n if (inEdges) {\n return inEdges.concat(this.outEdges(v, w));\n }\n};\n\nfunction incrementOrInitEntry(map, k) {\n if (map[k]) {\n map[k]++;\n } else {\n map[k] = 1;\n }\n}\n\nfunction decrementOrRemoveEntry(map, k) {\n if (!--map[k]) { delete map[k]; }\n}\n\nfunction edgeArgsToId(isDirected, v_, w_, name) {\n var v = \"\" + v_;\n var w = \"\" + w_;\n if (!isDirected && v > w) {\n var tmp = v;\n v = w;\n w = tmp;\n }\n return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM +\n (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name);\n}\n\nfunction edgeArgsToObj(isDirected, v_, w_, name) {\n var v = \"\" + v_;\n var w = \"\" + w_;\n if (!isDirected && v > w) {\n var tmp = v;\n v = w;\n w = tmp;\n }\n var edgeObj = { v: v, w: w };\n if (name) {\n edgeObj.name = name;\n }\n return edgeObj;\n}\n\nfunction edgeObjToId(isDirected, edgeObj) {\n return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name);\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/graph.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/index.js": +/*!********************************************!*\ + !*** ./node_modules/graphlib/lib/index.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// Includes only the \"core\" of graphlib\nmodule.exports = {\n Graph: __webpack_require__(/*! ./graph */ \"./node_modules/graphlib/lib/graph.js\"),\n version: __webpack_require__(/*! ./version */ \"./node_modules/graphlib/lib/version.js\")\n};\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/index.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/json.js": +/*!*******************************************!*\ + !*** ./node_modules/graphlib/lib/json.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("var _ = __webpack_require__(/*! ./lodash */ \"./node_modules/graphlib/lib/lodash.js\"),\n Graph = __webpack_require__(/*! ./graph */ \"./node_modules/graphlib/lib/graph.js\");\n\nmodule.exports = {\n write: write,\n read: read\n};\n\nfunction write(g) {\n var json = {\n options: {\n directed: g.isDirected(),\n multigraph: g.isMultigraph(),\n compound: g.isCompound()\n },\n nodes: writeNodes(g),\n edges: writeEdges(g)\n };\n if (!_.isUndefined(g.graph())) {\n json.value = _.clone(g.graph());\n }\n return json;\n}\n\nfunction writeNodes(g) {\n return _.map(g.nodes(), function(v) {\n var nodeValue = g.node(v),\n parent = g.parent(v),\n node = { v: v };\n if (!_.isUndefined(nodeValue)) {\n node.value = nodeValue;\n }\n if (!_.isUndefined(parent)) {\n node.parent = parent;\n }\n return node;\n });\n}\n\nfunction writeEdges(g) {\n return _.map(g.edges(), function(e) {\n var edgeValue = g.edge(e),\n edge = { v: e.v, w: e.w };\n if (!_.isUndefined(e.name)) {\n edge.name = e.name;\n }\n if (!_.isUndefined(edgeValue)) {\n edge.value = edgeValue;\n }\n return edge;\n });\n}\n\nfunction read(json) {\n var g = new Graph(json.options).setGraph(json.value);\n _.each(json.nodes, function(entry) {\n g.setNode(entry.v, entry.value);\n if (entry.parent) {\n g.setParent(entry.v, entry.parent);\n }\n });\n _.each(json.edges, function(entry) {\n g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value);\n });\n return g;\n}\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/json.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/lodash.js": +/*!*********************************************!*\ + !*** ./node_modules/graphlib/lib/lodash.js ***! + \*********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* global window */\n\nvar lodash;\n\nif (true) {\n try {\n lodash = __webpack_require__(/*! lodash */ \"./node_modules/graphlib/node_modules/lodash/lodash.js\");\n } catch (e) {}\n}\n\nif (!lodash) {\n lodash = window._;\n}\n\nmodule.exports = lodash;\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/lodash.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/lib/version.js": +/*!**********************************************!*\ + !*** ./node_modules/graphlib/lib/version.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = '2.1.5';\n\n\n//# sourceURL=webpack:///./node_modules/graphlib/lib/version.js?"); + +/***/ }), + +/***/ "./node_modules/graphlib/node_modules/lodash/lodash.js": +/*!*************************************************************!*\ + !*** ./node_modules/graphlib/node_modules/lodash/lodash.js ***! + \*************************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("/* WEBPACK VAR INJECTION */(function(global, module) {var __WEBPACK_AMD_DEFINE_RESULT__;/**\n * @license\n * Lodash \n * Copyright JS Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n;(function() {\n\n /** Used as a safe reference for `undefined` in pre-ES5 environments. */\n var undefined;\n\n /** Used as the semantic version number. */\n var VERSION = '4.17.11';\n\n /** Used as the size to enable large array optimizations. */\n var LARGE_ARRAY_SIZE = 200;\n\n /** Error message constants. */\n var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',\n FUNC_ERROR_TEXT = 'Expected a function';\n\n /** Used to stand-in for `undefined` hash values. */\n var HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n /** Used as the maximum memoize cache size. */\n var MAX_MEMOIZE_SIZE = 500;\n\n /** Used as the internal argument placeholder. */\n var PLACEHOLDER = '__lodash_placeholder__';\n\n /** Used to compose bitmasks for cloning. */\n var CLONE_DEEP_FLAG = 1,\n CLONE_FLAT_FLAG = 2,\n CLONE_SYMBOLS_FLAG = 4;\n\n /** Used to compose bitmasks for value comparisons. */\n var COMPARE_PARTIAL_FLAG = 1,\n COMPARE_UNORDERED_FLAG = 2;\n\n /** Used to compose bitmasks for function metadata. */\n var WRAP_BIND_FLAG = 1,\n WRAP_BIND_KEY_FLAG = 2,\n WRAP_CURRY_BOUND_FLAG = 4,\n WRAP_CURRY_FLAG = 8,\n WRAP_CURRY_RIGHT_FLAG = 16,\n WRAP_PARTIAL_FLAG = 32,\n WRAP_PARTIAL_RIGHT_FLAG = 64,\n WRAP_ARY_FLAG = 128,\n WRAP_REARG_FLAG = 256,\n WRAP_FLIP_FLAG = 512;\n\n /** Used as default options for `_.truncate`. */\n var DEFAULT_TRUNC_LENGTH = 30,\n DEFAULT_TRUNC_OMISSION = '...';\n\n /** Used to detect hot functions by number of calls within a span of milliseconds. */\n var HOT_COUNT = 800,\n HOT_SPAN = 16;\n\n /** Used to indicate the type of lazy iteratees. */\n var LAZY_FILTER_FLAG = 1,\n LAZY_MAP_FLAG = 2,\n LAZY_WHILE_FLAG = 3;\n\n /** Used as references for various `Number` constants. */\n var INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991,\n MAX_INTEGER = 1.7976931348623157e+308,\n NAN = 0 / 0;\n\n /** Used as references for the maximum length and index of an array. */\n var MAX_ARRAY_LENGTH = 4294967295,\n MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,\n HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;\n\n /** Used to associate wrap methods with their bit flags. */\n var wrapFlags = [\n ['ary', WRAP_ARY_FLAG],\n ['bind', WRAP_BIND_FLAG],\n ['bindKey', WRAP_BIND_KEY_FLAG],\n ['curry', WRAP_CURRY_FLAG],\n ['curryRight', WRAP_CURRY_RIGHT_FLAG],\n ['flip', WRAP_FLIP_FLAG],\n ['partial', WRAP_PARTIAL_FLAG],\n ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],\n ['rearg', WRAP_REARG_FLAG]\n ];\n\n /** `Object#toString` result references. */\n var argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n asyncTag = '[object AsyncFunction]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n domExcTag = '[object DOMException]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n nullTag = '[object Null]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n proxyTag = '[object Proxy]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n undefinedTag = '[object Undefined]',\n weakMapTag = '[object WeakMap]',\n weakSetTag = '[object WeakSet]';\n\n var arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n /** Used to match empty string literals in compiled template source. */\n var reEmptyStringLeading = /\\b__p \\+= '';/g,\n reEmptyStringMiddle = /\\b(__p \\+=) '' \\+/g,\n reEmptyStringTrailing = /(__e\\(.*?\\)|\\b__t\\)) \\+\\n'';/g;\n\n /** Used to match HTML entities and HTML characters. */\n var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,\n reUnescapedHtml = /[&<>\"']/g,\n reHasEscapedHtml = RegExp(reEscapedHtml.source),\n reHasUnescapedHtml = RegExp(reUnescapedHtml.source);\n\n /** Used to match template delimiters. */\n var reEscape = /<%-([\\s\\S]+?)%>/g,\n reEvaluate = /<%([\\s\\S]+?)%>/g,\n reInterpolate = /<%=([\\s\\S]+?)%>/g;\n\n /** Used to match property names within property paths. */\n var reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n reIsPlainProp = /^\\w*$/,\n rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n\n /**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\n var reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g,\n reHasRegExpChar = RegExp(reRegExpChar.source);\n\n /** Used to match leading and trailing whitespace. */\n var reTrim = /^\\s+|\\s+$/g,\n reTrimStart = /^\\s+/,\n reTrimEnd = /\\s+$/;\n\n /** Used to match wrap detail comments. */\n var reWrapComment = /\\{(?:\\n\\/\\* \\[wrapped with .+\\] \\*\\/)?\\n?/,\n reWrapDetails = /\\{\\n\\/\\* \\[wrapped with (.+)\\] \\*/,\n reSplitDetails = /,? & /;\n\n /** Used to match words composed of alphanumeric characters. */\n var reAsciiWord = /[^\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\x7f]+/g;\n\n /** Used to match backslashes in property paths. */\n var reEscapeChar = /\\\\(\\\\)?/g;\n\n /**\n * Used to match\n * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).\n */\n var reEsTemplate = /\\$\\{([^\\\\}]*(?:\\\\.[^\\\\}]*)*)\\}/g;\n\n /** Used to match `RegExp` flags from their coerced string values. */\n var reFlags = /\\w*$/;\n\n /** Used to detect bad signed hexadecimal string values. */\n var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n /** Used to detect binary string values. */\n var reIsBinary = /^0b[01]+$/i;\n\n /** Used to detect host constructors (Safari). */\n var reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n /** Used to detect octal string values. */\n var reIsOctal = /^0o[0-7]+$/i;\n\n /** Used to detect unsigned integer values. */\n var reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n /** Used to match Latin Unicode letters (excluding mathematical operators). */\n var reLatin = /[\\xc0-\\xd6\\xd8-\\xf6\\xf8-\\xff\\u0100-\\u017f]/g;\n\n /** Used to ensure capturing order of template delimiters. */\n var reNoMatch = /($^)/;\n\n /** Used to match unescaped characters in compiled string literals. */\n var reUnescapedString = /['\\n\\r\\u2028\\u2029\\\\]/g;\n\n /** Used to compose unicode character classes. */\n var rsAstralRange = '\\\\ud800-\\\\udfff',\n rsComboMarksRange = '\\\\u0300-\\\\u036f',\n reComboHalfMarksRange = '\\\\ufe20-\\\\ufe2f',\n rsComboSymbolsRange = '\\\\u20d0-\\\\u20ff',\n rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,\n rsDingbatRange = '\\\\u2700-\\\\u27bf',\n rsLowerRange = 'a-z\\\\xdf-\\\\xf6\\\\xf8-\\\\xff',\n rsMathOpRange = '\\\\xac\\\\xb1\\\\xd7\\\\xf7',\n rsNonCharRange = '\\\\x00-\\\\x2f\\\\x3a-\\\\x40\\\\x5b-\\\\x60\\\\x7b-\\\\xbf',\n rsPunctuationRange = '\\\\u2000-\\\\u206f',\n rsSpaceRange = ' \\\\t\\\\x0b\\\\f\\\\xa0\\\\ufeff\\\\n\\\\r\\\\u2028\\\\u2029\\\\u1680\\\\u180e\\\\u2000\\\\u2001\\\\u2002\\\\u2003\\\\u2004\\\\u2005\\\\u2006\\\\u2007\\\\u2008\\\\u2009\\\\u200a\\\\u202f\\\\u205f\\\\u3000',\n rsUpperRange = 'A-Z\\\\xc0-\\\\xd6\\\\xd8-\\\\xde',\n rsVarRange = '\\\\ufe0e\\\\ufe0f',\n rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;\n\n /** Used to compose unicode capture groups. */\n var rsApos = \"['\\u2019]\",\n rsAstral = '[' + rsAstralRange + ']',\n rsBreak = '[' + rsBreakRange + ']',\n rsCombo = '[' + rsComboRange + ']',\n rsDigits = '\\\\d+',\n rsDingbat = '[' + rsDingbatRange + ']',\n rsLower = '[' + rsLowerRange + ']',\n rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',\n rsFitz = '\\\\ud83c[\\\\udffb-\\\\udfff]',\n rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',\n rsNonAstral = '[^' + rsAstralRange + ']',\n rsRegional = '(?:\\\\ud83c[\\\\udde6-\\\\uddff]){2}',\n rsSurrPair = '[\\\\ud800-\\\\udbff][\\\\udc00-\\\\udfff]',\n rsUpper = '[' + rsUpperRange + ']',\n rsZWJ = '\\\\u200d';\n\n /** Used to compose unicode regexes. */\n var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',\n rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',\n rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',\n rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',\n reOptMod = rsModifier + '?',\n rsOptVar = '[' + rsVarRange + ']?',\n rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',\n rsOrdLower = '\\\\d*(?:1st|2nd|3rd|(?![123])\\\\dth)(?=\\\\b|[A-Z_])',\n rsOrdUpper = '\\\\d*(?:1ST|2ND|3RD|(?![123])\\\\dTH)(?=\\\\b|[a-z_])',\n rsSeq = rsOptVar + reOptMod + rsOptJoin,\n rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,\n rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';\n\n /** Used to match apostrophes. */\n var reApos = RegExp(rsApos, 'g');\n\n /**\n * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\n * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\n */\n var reComboMark = RegExp(rsCombo, 'g');\n\n /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */\n var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');\n\n /** Used to match complex or compound words. */\n var reUnicodeWord = RegExp([\n rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',\n rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',\n rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,\n rsUpper + '+' + rsOptContrUpper,\n rsOrdUpper,\n rsOrdLower,\n rsDigits,\n rsEmoji\n ].join('|'), 'g');\n\n /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */\n var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');\n\n /** Used to detect strings that need a more robust regexp to match words. */\n var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;\n\n /** Used to assign default `context` object properties. */\n var contextProps = [\n 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',\n 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',\n 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array',\n 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',\n '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'\n ];\n\n /** Used to make template sourceURLs easier to identify. */\n var templateCounter = -1;\n\n /** Used to identify `toStringTag` values of typed arrays. */\n var typedArrayTags = {};\n typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\n typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\n typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\n typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\n typedArrayTags[uint32Tag] = true;\n typedArrayTags[argsTag] = typedArrayTags[arrayTag] =\n typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\n typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\n typedArrayTags[errorTag] = typedArrayTags[funcTag] =\n typedArrayTags[mapTag] = typedArrayTags[numberTag] =\n typedArrayTags[objectTag] = typedArrayTags[regexpTag] =\n typedArrayTags[setTag] = typedArrayTags[stringTag] =\n typedArrayTags[weakMapTag] = false;\n\n /** Used to identify `toStringTag` values supported by `_.clone`. */\n var cloneableTags = {};\n cloneableTags[argsTag] = cloneableTags[arrayTag] =\n cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\n cloneableTags[boolTag] = cloneableTags[dateTag] =\n cloneableTags[float32Tag] = cloneableTags[float64Tag] =\n cloneableTags[int8Tag] = cloneableTags[int16Tag] =\n cloneableTags[int32Tag] = cloneableTags[mapTag] =\n cloneableTags[numberTag] = cloneableTags[objectTag] =\n cloneableTags[regexpTag] = cloneableTags[setTag] =\n cloneableTags[stringTag] = cloneableTags[symbolTag] =\n cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\n cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\n cloneableTags[errorTag] = cloneableTags[funcTag] =\n cloneableTags[weakMapTag] = false;\n\n /** Used to map Latin Unicode letters to basic Latin letters. */\n var deburredLetters = {\n // Latin-1 Supplement block.\n '\\xc0': 'A', '\\xc1': 'A', '\\xc2': 'A', '\\xc3': 'A', '\\xc4': 'A', '\\xc5': 'A',\n '\\xe0': 'a', '\\xe1': 'a', '\\xe2': 'a', '\\xe3': 'a', '\\xe4': 'a', '\\xe5': 'a',\n '\\xc7': 'C', '\\xe7': 'c',\n '\\xd0': 'D', '\\xf0': 'd',\n '\\xc8': 'E', '\\xc9': 'E', '\\xca': 'E', '\\xcb': 'E',\n '\\xe8': 'e', '\\xe9': 'e', '\\xea': 'e', '\\xeb': 'e',\n '\\xcc': 'I', '\\xcd': 'I', '\\xce': 'I', '\\xcf': 'I',\n '\\xec': 'i', '\\xed': 'i', '\\xee': 'i', '\\xef': 'i',\n '\\xd1': 'N', '\\xf1': 'n',\n '\\xd2': 'O', '\\xd3': 'O', '\\xd4': 'O', '\\xd5': 'O', '\\xd6': 'O', '\\xd8': 'O',\n '\\xf2': 'o', '\\xf3': 'o', '\\xf4': 'o', '\\xf5': 'o', '\\xf6': 'o', '\\xf8': 'o',\n '\\xd9': 'U', '\\xda': 'U', '\\xdb': 'U', '\\xdc': 'U',\n '\\xf9': 'u', '\\xfa': 'u', '\\xfb': 'u', '\\xfc': 'u',\n '\\xdd': 'Y', '\\xfd': 'y', '\\xff': 'y',\n '\\xc6': 'Ae', '\\xe6': 'ae',\n '\\xde': 'Th', '\\xfe': 'th',\n '\\xdf': 'ss',\n // Latin Extended-A block.\n '\\u0100': 'A', '\\u0102': 'A', '\\u0104': 'A',\n '\\u0101': 'a', '\\u0103': 'a', '\\u0105': 'a',\n '\\u0106': 'C', '\\u0108': 'C', '\\u010a': 'C', '\\u010c': 'C',\n '\\u0107': 'c', '\\u0109': 'c', '\\u010b': 'c', '\\u010d': 'c',\n '\\u010e': 'D', '\\u0110': 'D', '\\u010f': 'd', '\\u0111': 'd',\n '\\u0112': 'E', '\\u0114': 'E', '\\u0116': 'E', '\\u0118': 'E', '\\u011a': 'E',\n '\\u0113': 'e', '\\u0115': 'e', '\\u0117': 'e', '\\u0119': 'e', '\\u011b': 'e',\n '\\u011c': 'G', '\\u011e': 'G', '\\u0120': 'G', '\\u0122': 'G',\n '\\u011d': 'g', '\\u011f': 'g', '\\u0121': 'g', '\\u0123': 'g',\n '\\u0124': 'H', '\\u0126': 'H', '\\u0125': 'h', '\\u0127': 'h',\n '\\u0128': 'I', '\\u012a': 'I', '\\u012c': 'I', '\\u012e': 'I', '\\u0130': 'I',\n '\\u0129': 'i', '\\u012b': 'i', '\\u012d': 'i', '\\u012f': 'i', '\\u0131': 'i',\n '\\u0134': 'J', '\\u0135': 'j',\n '\\u0136': 'K', '\\u0137': 'k', '\\u0138': 'k',\n '\\u0139': 'L', '\\u013b': 'L', '\\u013d': 'L', '\\u013f': 'L', '\\u0141': 'L',\n '\\u013a': 'l', '\\u013c': 'l', '\\u013e': 'l', '\\u0140': 'l', '\\u0142': 'l',\n '\\u0143': 'N', '\\u0145': 'N', '\\u0147': 'N', '\\u014a': 'N',\n '\\u0144': 'n', '\\u0146': 'n', '\\u0148': 'n', '\\u014b': 'n',\n '\\u014c': 'O', '\\u014e': 'O', '\\u0150': 'O',\n '\\u014d': 'o', '\\u014f': 'o', '\\u0151': 'o',\n '\\u0154': 'R', '\\u0156': 'R', '\\u0158': 'R',\n '\\u0155': 'r', '\\u0157': 'r', '\\u0159': 'r',\n '\\u015a': 'S', '\\u015c': 'S', '\\u015e': 'S', '\\u0160': 'S',\n '\\u015b': 's', '\\u015d': 's', '\\u015f': 's', '\\u0161': 's',\n '\\u0162': 'T', '\\u0164': 'T', '\\u0166': 'T',\n '\\u0163': 't', '\\u0165': 't', '\\u0167': 't',\n '\\u0168': 'U', '\\u016a': 'U', '\\u016c': 'U', '\\u016e': 'U', '\\u0170': 'U', '\\u0172': 'U',\n '\\u0169': 'u', '\\u016b': 'u', '\\u016d': 'u', '\\u016f': 'u', '\\u0171': 'u', '\\u0173': 'u',\n '\\u0174': 'W', '\\u0175': 'w',\n '\\u0176': 'Y', '\\u0177': 'y', '\\u0178': 'Y',\n '\\u0179': 'Z', '\\u017b': 'Z', '\\u017d': 'Z',\n '\\u017a': 'z', '\\u017c': 'z', '\\u017e': 'z',\n '\\u0132': 'IJ', '\\u0133': 'ij',\n '\\u0152': 'Oe', '\\u0153': 'oe',\n '\\u0149': \"'n\", '\\u017f': 's'\n };\n\n /** Used to map characters to HTML entities. */\n var htmlEscapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": '''\n };\n\n /** Used to map HTML entities to characters. */\n var htmlUnescapes = {\n '&': '&',\n '<': '<',\n '>': '>',\n '"': '\"',\n ''': \"'\"\n };\n\n /** Used to escape characters for inclusion in compiled string literals. */\n var stringEscapes = {\n '\\\\': '\\\\',\n \"'\": \"'\",\n '\\n': 'n',\n '\\r': 'r',\n '\\u2028': 'u2028',\n '\\u2029': 'u2029'\n };\n\n /** Built-in method references without a dependency on `root`. */\n var freeParseFloat = parseFloat,\n freeParseInt = parseInt;\n\n /** Detect free variable `global` from Node.js. */\n var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n /** Detect free variable `self`. */\n var freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n /** Used as a reference to the global object. */\n var root = freeGlobal || freeSelf || Function('return this')();\n\n /** Detect free variable `exports`. */\n var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n /** Detect free variable `module`. */\n var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n /** Detect the popular CommonJS extension `module.exports`. */\n var moduleExports = freeModule && freeModule.exports === freeExports;\n\n /** Detect free variable `process` from Node.js. */\n var freeProcess = moduleExports && freeGlobal.process;\n\n /** Used to access faster Node.js helpers. */\n var nodeUtil = (function() {\n try {\n // Use `util.types` for Node.js 10+.\n var types = freeModule && freeModule.require && freeModule.require('util').types;\n\n if (types) {\n return types;\n }\n\n // Legacy `process.binding('util')` for Node.js < 10.\n return freeProcess && freeProcess.binding && freeProcess.binding('util');\n } catch (e) {}\n }());\n\n /* Node.js helper references. */\n var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,\n nodeIsDate = nodeUtil && nodeUtil.isDate,\n nodeIsMap = nodeUtil && nodeUtil.isMap,\n nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,\n nodeIsSet = nodeUtil && nodeUtil.isSet,\n nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * A faster alternative to `Function#apply`, this function invokes `func`\n * with the `this` binding of `thisArg` and the arguments of `args`.\n *\n * @private\n * @param {Function} func The function to invoke.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} args The arguments to invoke `func` with.\n * @returns {*} Returns the result of `func`.\n */\n function apply(func, thisArg, args) {\n switch (args.length) {\n case 0: return func.call(thisArg);\n case 1: return func.call(thisArg, args[0]);\n case 2: return func.call(thisArg, args[0], args[1]);\n case 3: return func.call(thisArg, args[0], args[1], args[2]);\n }\n return func.apply(thisArg, args);\n }\n\n /**\n * A specialized version of `baseAggregator` for arrays.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function arrayAggregator(array, setter, iteratee, accumulator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n var value = array[index];\n setter(accumulator, value, iteratee(value), array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEach(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (iteratee(array[index], index, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.forEachRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\n function arrayEachRight(array, iteratee) {\n var length = array == null ? 0 : array.length;\n\n while (length--) {\n if (iteratee(array[length], length, array) === false) {\n break;\n }\n }\n return array;\n }\n\n /**\n * A specialized version of `_.every` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n */\n function arrayEvery(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (!predicate(array[index], index, array)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * A specialized version of `_.filter` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function arrayFilter(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.includes` for arrays without support for\n * specifying an index to search from.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludes(array, value) {\n var length = array == null ? 0 : array.length;\n return !!length && baseIndexOf(array, value, 0) > -1;\n }\n\n /**\n * This function is like `arrayIncludes` except that it accepts a comparator.\n *\n * @private\n * @param {Array} [array] The array to inspect.\n * @param {*} target The value to search for.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {boolean} Returns `true` if `target` is found, else `false`.\n */\n function arrayIncludesWith(array, value, comparator) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (comparator(value, array[index])) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `_.map` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function arrayMap(array, iteratee) {\n var index = -1,\n length = array == null ? 0 : array.length,\n result = Array(length);\n\n while (++index < length) {\n result[index] = iteratee(array[index], index, array);\n }\n return result;\n }\n\n /**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\n function arrayPush(array, values) {\n var index = -1,\n length = values.length,\n offset = array.length;\n\n while (++index < length) {\n array[offset + index] = values[index];\n }\n return array;\n }\n\n /**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduce(array, iteratee, accumulator, initAccum) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n if (initAccum && length) {\n accumulator = array[++index];\n }\n while (++index < length) {\n accumulator = iteratee(accumulator, array[index], index, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.reduceRight` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the last element of `array` as\n * the initial value.\n * @returns {*} Returns the accumulated value.\n */\n function arrayReduceRight(array, iteratee, accumulator, initAccum) {\n var length = array == null ? 0 : array.length;\n if (initAccum && length) {\n accumulator = array[--length];\n }\n while (length--) {\n accumulator = iteratee(accumulator, array[length], length, array);\n }\n return accumulator;\n }\n\n /**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function arraySome(array, predicate) {\n var index = -1,\n length = array == null ? 0 : array.length;\n\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Gets the size of an ASCII `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n var asciiSize = baseProperty('length');\n\n /**\n * Converts an ASCII `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function asciiToArray(string) {\n return string.split('');\n }\n\n /**\n * Splits an ASCII `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function asciiWords(string) {\n return string.match(reAsciiWord) || [];\n }\n\n /**\n * The base implementation of methods like `_.findKey` and `_.findLastKey`,\n * without support for iteratee shorthands, which iterates over `collection`\n * using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the found element or its key, else `undefined`.\n */\n function baseFindKey(collection, predicate, eachFunc) {\n var result;\n eachFunc(collection, function(value, key, collection) {\n if (predicate(value, key, collection)) {\n result = key;\n return false;\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.findIndex` and `_.findLastIndex` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} predicate The function invoked per iteration.\n * @param {number} fromIndex The index to search from.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseFindIndex(array, predicate, fromIndex, fromRight) {\n var length = array.length,\n index = fromIndex + (fromRight ? 1 : -1);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (predicate(array[index], index, array)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOf(array, value, fromIndex) {\n return value === value\n ? strictIndexOf(array, value, fromIndex)\n : baseFindIndex(array, baseIsNaN, fromIndex);\n }\n\n /**\n * This function is like `baseIndexOf` except that it accepts a comparator.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @param {Function} comparator The comparator invoked per element.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function baseIndexOfWith(array, value, fromIndex, comparator) {\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (comparator(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * The base implementation of `_.isNaN` without support for number objects.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n */\n function baseIsNaN(value) {\n return value !== value;\n }\n\n /**\n * The base implementation of `_.mean` and `_.meanBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the mean.\n */\n function baseMean(array, iteratee) {\n var length = array == null ? 0 : array.length;\n return length ? (baseSum(array, iteratee) / length) : NAN;\n }\n\n /**\n * The base implementation of `_.property` without support for deep paths.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function baseProperty(key) {\n return function(object) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.propertyOf` without support for deep paths.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyOf(object) {\n return function(key) {\n return object == null ? undefined : object[key];\n };\n }\n\n /**\n * The base implementation of `_.reduce` and `_.reduceRight`, without support\n * for iteratee shorthands, which iterates over `collection` using `eachFunc`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} accumulator The initial value.\n * @param {boolean} initAccum Specify using the first or last element of\n * `collection` as the initial value.\n * @param {Function} eachFunc The function to iterate over `collection`.\n * @returns {*} Returns the accumulated value.\n */\n function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {\n eachFunc(collection, function(value, index, collection) {\n accumulator = initAccum\n ? (initAccum = false, value)\n : iteratee(accumulator, value, index, collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.sortBy` which uses `comparer` to define the\n * sort order of `array` and replaces criteria objects with their corresponding\n * values.\n *\n * @private\n * @param {Array} array The array to sort.\n * @param {Function} comparer The function to define sort order.\n * @returns {Array} Returns `array`.\n */\n function baseSortBy(array, comparer) {\n var length = array.length;\n\n array.sort(comparer);\n while (length--) {\n array[length] = array[length].value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.sum` and `_.sumBy` without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {number} Returns the sum.\n */\n function baseSum(array, iteratee) {\n var result,\n index = -1,\n length = array.length;\n\n while (++index < length) {\n var current = iteratee(array[index]);\n if (current !== undefined) {\n result = result === undefined ? current : (result + current);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\n function baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array\n * of key-value pairs for `object` corresponding to the property names of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the key-value pairs.\n */\n function baseToPairs(object, props) {\n return arrayMap(props, function(key) {\n return [key, object[key]];\n });\n }\n\n /**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\n function baseUnary(func) {\n return function(value) {\n return func(value);\n };\n }\n\n /**\n * The base implementation of `_.values` and `_.valuesIn` which creates an\n * array of `object` property values corresponding to the property names\n * of `props`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} props The property names to get values for.\n * @returns {Object} Returns the array of property values.\n */\n function baseValues(object, props) {\n return arrayMap(props, function(key) {\n return object[key];\n });\n }\n\n /**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function cacheHas(cache, key) {\n return cache.has(key);\n }\n\n /**\n * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the first unmatched string symbol.\n */\n function charsStartIndex(strSymbols, chrSymbols) {\n var index = -1,\n length = strSymbols.length;\n\n while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol\n * that is not found in the character symbols.\n *\n * @private\n * @param {Array} strSymbols The string symbols to inspect.\n * @param {Array} chrSymbols The character symbols to find.\n * @returns {number} Returns the index of the last unmatched string symbol.\n */\n function charsEndIndex(strSymbols, chrSymbols) {\n var index = strSymbols.length;\n\n while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n return index;\n }\n\n /**\n * Gets the number of `placeholder` occurrences in `array`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} placeholder The placeholder to search for.\n * @returns {number} Returns the placeholder count.\n */\n function countHolders(array, placeholder) {\n var length = array.length,\n result = 0;\n\n while (length--) {\n if (array[length] === placeholder) {\n ++result;\n }\n }\n return result;\n }\n\n /**\n * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A\n * letters to basic Latin letters.\n *\n * @private\n * @param {string} letter The matched letter to deburr.\n * @returns {string} Returns the deburred letter.\n */\n var deburrLetter = basePropertyOf(deburredLetters);\n\n /**\n * Used by `_.escape` to convert characters to HTML entities.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n var escapeHtmlChar = basePropertyOf(htmlEscapes);\n\n /**\n * Used by `_.template` to escape characters for inclusion in compiled string literals.\n *\n * @private\n * @param {string} chr The matched character to escape.\n * @returns {string} Returns the escaped character.\n */\n function escapeStringChar(chr) {\n return '\\\\' + stringEscapes[chr];\n }\n\n /**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function getValue(object, key) {\n return object == null ? undefined : object[key];\n }\n\n /**\n * Checks if `string` contains Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a symbol is found, else `false`.\n */\n function hasUnicode(string) {\n return reHasUnicode.test(string);\n }\n\n /**\n * Checks if `string` contains a word composed of Unicode symbols.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {boolean} Returns `true` if a word is found, else `false`.\n */\n function hasUnicodeWord(string) {\n return reHasUnicodeWord.test(string);\n }\n\n /**\n * Converts `iterator` to an array.\n *\n * @private\n * @param {Object} iterator The iterator to convert.\n * @returns {Array} Returns the converted array.\n */\n function iteratorToArray(iterator) {\n var data,\n result = [];\n\n while (!(data = iterator.next()).done) {\n result.push(data.value);\n }\n return result;\n }\n\n /**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\n function mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n }\n\n /**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\n function overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n }\n\n /**\n * Replaces all `placeholder` elements in `array` with an internal placeholder\n * and returns an array of their indexes.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {*} placeholder The placeholder to replace.\n * @returns {Array} Returns the new array of placeholder indexes.\n */\n function replaceHolders(array, placeholder) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value === placeholder || value === PLACEHOLDER) {\n array[index] = PLACEHOLDER;\n result[resIndex++] = index;\n }\n }\n return result;\n }\n\n /**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\n function setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n }\n\n /**\n * Converts `set` to its value-value pairs.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the value-value pairs.\n */\n function setToPairs(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = [value, value];\n });\n return result;\n }\n\n /**\n * A specialized version of `_.indexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictIndexOf(array, value, fromIndex) {\n var index = fromIndex - 1,\n length = array.length;\n\n while (++index < length) {\n if (array[index] === value) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * A specialized version of `_.lastIndexOf` which performs strict equality\n * comparisons of values, i.e. `===`.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} fromIndex The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function strictLastIndexOf(array, value, fromIndex) {\n var index = fromIndex + 1;\n while (index--) {\n if (array[index] === value) {\n return index;\n }\n }\n return index;\n }\n\n /**\n * Gets the number of symbols in `string`.\n *\n * @private\n * @param {string} string The string to inspect.\n * @returns {number} Returns the string size.\n */\n function stringSize(string) {\n return hasUnicode(string)\n ? unicodeSize(string)\n : asciiSize(string);\n }\n\n /**\n * Converts `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function stringToArray(string) {\n return hasUnicode(string)\n ? unicodeToArray(string)\n : asciiToArray(string);\n }\n\n /**\n * Used by `_.unescape` to convert HTML entities to characters.\n *\n * @private\n * @param {string} chr The matched character to unescape.\n * @returns {string} Returns the unescaped character.\n */\n var unescapeHtmlChar = basePropertyOf(htmlUnescapes);\n\n /**\n * Gets the size of a Unicode `string`.\n *\n * @private\n * @param {string} string The string inspect.\n * @returns {number} Returns the string size.\n */\n function unicodeSize(string) {\n var result = reUnicode.lastIndex = 0;\n while (reUnicode.test(string)) {\n ++result;\n }\n return result;\n }\n\n /**\n * Converts a Unicode `string` to an array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the converted array.\n */\n function unicodeToArray(string) {\n return string.match(reUnicode) || [];\n }\n\n /**\n * Splits a Unicode `string` into an array of its words.\n *\n * @private\n * @param {string} The string to inspect.\n * @returns {Array} Returns the words of `string`.\n */\n function unicodeWords(string) {\n return string.match(reUnicodeWord) || [];\n }\n\n /*--------------------------------------------------------------------------*/\n\n /**\n * Create a new pristine `lodash` function using the `context` object.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Util\n * @param {Object} [context=root] The context object.\n * @returns {Function} Returns a new `lodash` function.\n * @example\n *\n * _.mixin({ 'foo': _.constant('foo') });\n *\n * var lodash = _.runInContext();\n * lodash.mixin({ 'bar': lodash.constant('bar') });\n *\n * _.isFunction(_.foo);\n * // => true\n * _.isFunction(_.bar);\n * // => false\n *\n * lodash.isFunction(lodash.foo);\n * // => false\n * lodash.isFunction(lodash.bar);\n * // => true\n *\n * // Create a suped-up `defer` in Node.js.\n * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;\n */\n var runInContext = (function runInContext(context) {\n context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));\n\n /** Built-in constructor references. */\n var Array = context.Array,\n Date = context.Date,\n Error = context.Error,\n Function = context.Function,\n Math = context.Math,\n Object = context.Object,\n RegExp = context.RegExp,\n String = context.String,\n TypeError = context.TypeError;\n\n /** Used for built-in method references. */\n var arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n /** Used to detect overreaching core-js shims. */\n var coreJsData = context['__core-js_shared__'];\n\n /** Used to resolve the decompiled source of functions. */\n var funcToString = funcProto.toString;\n\n /** Used to check objects for own properties. */\n var hasOwnProperty = objectProto.hasOwnProperty;\n\n /** Used to generate unique IDs. */\n var idCounter = 0;\n\n /** Used to detect methods masquerading as native. */\n var maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n }());\n\n /**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\n var nativeObjectToString = objectProto.toString;\n\n /** Used to infer the `Object` constructor. */\n var objectCtorString = funcToString.call(Object);\n\n /** Used to restore the original `_` reference in `_.noConflict`. */\n var oldDash = root._;\n\n /** Used to detect if a method is native. */\n var reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n );\n\n /** Built-in value references. */\n var Buffer = moduleExports ? context.Buffer : undefined,\n Symbol = context.Symbol,\n Uint8Array = context.Uint8Array,\n allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined,\n getPrototype = overArg(Object.getPrototypeOf, Object),\n objectCreate = Object.create,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice,\n spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined,\n symIterator = Symbol ? Symbol.iterator : undefined,\n symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n var defineProperty = (function() {\n try {\n var func = getNative(Object, 'defineProperty');\n func({}, '', {});\n return func;\n } catch (e) {}\n }());\n\n /** Mocked built-ins. */\n var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout,\n ctxNow = Date && Date.now !== root.Date.now && Date.now,\n ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout;\n\n /* Built-in method references for those with the same name as other `lodash` methods. */\n var nativeCeil = Math.ceil,\n nativeFloor = Math.floor,\n nativeGetSymbols = Object.getOwnPropertySymbols,\n nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n nativeIsFinite = context.isFinite,\n nativeJoin = arrayProto.join,\n nativeKeys = overArg(Object.keys, Object),\n nativeMax = Math.max,\n nativeMin = Math.min,\n nativeNow = Date.now,\n nativeParseInt = context.parseInt,\n nativeRandom = Math.random,\n nativeReverse = arrayProto.reverse;\n\n /* Built-in method references that are verified to be native. */\n var DataView = getNative(context, 'DataView'),\n Map = getNative(context, 'Map'),\n Promise = getNative(context, 'Promise'),\n Set = getNative(context, 'Set'),\n WeakMap = getNative(context, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n /** Used to store function metadata. */\n var metaMap = WeakMap && new WeakMap;\n\n /** Used to lookup unminified function names. */\n var realNames = {};\n\n /** Used to detect maps, sets, and weakmaps. */\n var dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n /** Used to convert symbols to primitives and strings. */\n var symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,\n symbolToString = symbolProto ? symbolProto.toString : undefined;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` object which wraps `value` to enable implicit method\n * chain sequences. Methods that operate on and return arrays, collections,\n * and functions can be chained together. Methods that retrieve a single value\n * or may return a primitive value will automatically end the chain sequence\n * and return the unwrapped value. Otherwise, the value must be unwrapped\n * with `_#value`.\n *\n * Explicit chain sequences, which must be unwrapped with `_#value`, may be\n * enabled using `_.chain`.\n *\n * The execution of chained methods is lazy, that is, it's deferred until\n * `_#value` is implicitly or explicitly called.\n *\n * Lazy evaluation allows several methods to support shortcut fusion.\n * Shortcut fusion is an optimization to merge iteratee calls; this avoids\n * the creation of intermediate arrays and can greatly reduce the number of\n * iteratee executions. Sections of a chain sequence qualify for shortcut\n * fusion if the section is applied to an array and iteratees accept only\n * one argument. The heuristic for whether a section qualifies for shortcut\n * fusion is subject to change.\n *\n * Chaining is supported in custom builds as long as the `_#value` method is\n * directly or indirectly included in the build.\n *\n * In addition to lodash methods, wrappers have `Array` and `String` methods.\n *\n * The wrapper `Array` methods are:\n * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`\n *\n * The wrapper `String` methods are:\n * `replace` and `split`\n *\n * The wrapper methods that support shortcut fusion are:\n * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,\n * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,\n * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`\n *\n * The chainable wrapper methods are:\n * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,\n * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,\n * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,\n * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,\n * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,\n * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,\n * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,\n * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,\n * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,\n * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,\n * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,\n * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,\n * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,\n * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,\n * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,\n * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,\n * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,\n * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,\n * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,\n * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,\n * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,\n * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,\n * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,\n * `zipObject`, `zipObjectDeep`, and `zipWith`\n *\n * The wrapper methods that are **not** chainable by default are:\n * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,\n * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`,\n * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`,\n * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`,\n * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`,\n * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`,\n * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`,\n * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`,\n * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`,\n * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`,\n * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`,\n * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`,\n * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`,\n * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`,\n * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`,\n * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`,\n * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`,\n * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`,\n * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`,\n * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`,\n * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`,\n * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`,\n * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`,\n * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`,\n * `upperFirst`, `value`, and `words`\n *\n * @name _\n * @constructor\n * @category Seq\n * @param {*} value The value to wrap in a `lodash` instance.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2, 3]);\n *\n * // Returns an unwrapped value.\n * wrapped.reduce(_.add);\n * // => 6\n *\n * // Returns a wrapped value.\n * var squares = wrapped.map(square);\n *\n * _.isArray(squares);\n * // => false\n *\n * _.isArray(squares.value());\n * // => true\n */\n function lodash(value) {\n if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {\n if (value instanceof LodashWrapper) {\n return value;\n }\n if (hasOwnProperty.call(value, '__wrapped__')) {\n return wrapperClone(value);\n }\n }\n return new LodashWrapper(value);\n }\n\n /**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} proto The object to inherit from.\n * @returns {Object} Returns the new object.\n */\n var baseCreate = (function() {\n function object() {}\n return function(proto) {\n if (!isObject(proto)) {\n return {};\n }\n if (objectCreate) {\n return objectCreate(proto);\n }\n object.prototype = proto;\n var result = new object;\n object.prototype = undefined;\n return result;\n };\n }());\n\n /**\n * The function whose prototype chain sequence wrappers inherit from.\n *\n * @private\n */\n function baseLodash() {\n // No operation performed.\n }\n\n /**\n * The base constructor for creating `lodash` wrapper objects.\n *\n * @private\n * @param {*} value The value to wrap.\n * @param {boolean} [chainAll] Enable explicit method chain sequences.\n */\n function LodashWrapper(value, chainAll) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__chain__ = !!chainAll;\n this.__index__ = 0;\n this.__values__ = undefined;\n }\n\n /**\n * By default, the template delimiters used by lodash are like those in\n * embedded Ruby (ERB) as well as ES2015 template strings. Change the\n * following template settings to use alternative delimiters.\n *\n * @static\n * @memberOf _\n * @type {Object}\n */\n lodash.templateSettings = {\n\n /**\n * Used to detect `data` property values to be HTML-escaped.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'escape': reEscape,\n\n /**\n * Used to detect code to be evaluated.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'evaluate': reEvaluate,\n\n /**\n * Used to detect `data` property values to inject.\n *\n * @memberOf _.templateSettings\n * @type {RegExp}\n */\n 'interpolate': reInterpolate,\n\n /**\n * Used to reference the data object in the template text.\n *\n * @memberOf _.templateSettings\n * @type {string}\n */\n 'variable': '',\n\n /**\n * Used to import variables into the compiled template.\n *\n * @memberOf _.templateSettings\n * @type {Object}\n */\n 'imports': {\n\n /**\n * A reference to the `lodash` function.\n *\n * @memberOf _.templateSettings.imports\n * @type {Function}\n */\n '_': lodash\n }\n };\n\n // Ensure wrappers are instances of `baseLodash`.\n lodash.prototype = baseLodash.prototype;\n lodash.prototype.constructor = lodash;\n\n LodashWrapper.prototype = baseCreate(baseLodash.prototype);\n LodashWrapper.prototype.constructor = LodashWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.\n *\n * @private\n * @constructor\n * @param {*} value The value to wrap.\n */\n function LazyWrapper(value) {\n this.__wrapped__ = value;\n this.__actions__ = [];\n this.__dir__ = 1;\n this.__filtered__ = false;\n this.__iteratees__ = [];\n this.__takeCount__ = MAX_ARRAY_LENGTH;\n this.__views__ = [];\n }\n\n /**\n * Creates a clone of the lazy wrapper object.\n *\n * @private\n * @name clone\n * @memberOf LazyWrapper\n * @returns {Object} Returns the cloned `LazyWrapper` object.\n */\n function lazyClone() {\n var result = new LazyWrapper(this.__wrapped__);\n result.__actions__ = copyArray(this.__actions__);\n result.__dir__ = this.__dir__;\n result.__filtered__ = this.__filtered__;\n result.__iteratees__ = copyArray(this.__iteratees__);\n result.__takeCount__ = this.__takeCount__;\n result.__views__ = copyArray(this.__views__);\n return result;\n }\n\n /**\n * Reverses the direction of lazy iteration.\n *\n * @private\n * @name reverse\n * @memberOf LazyWrapper\n * @returns {Object} Returns the new reversed `LazyWrapper` object.\n */\n function lazyReverse() {\n if (this.__filtered__) {\n var result = new LazyWrapper(this);\n result.__dir__ = -1;\n result.__filtered__ = true;\n } else {\n result = this.clone();\n result.__dir__ *= -1;\n }\n return result;\n }\n\n /**\n * Extracts the unwrapped value from its lazy wrapper.\n *\n * @private\n * @name value\n * @memberOf LazyWrapper\n * @returns {*} Returns the unwrapped value.\n */\n function lazyValue() {\n var array = this.__wrapped__.value(),\n dir = this.__dir__,\n isArr = isArray(array),\n isRight = dir < 0,\n arrLength = isArr ? array.length : 0,\n view = getView(0, arrLength, this.__views__),\n start = view.start,\n end = view.end,\n length = end - start,\n index = isRight ? end : (start - 1),\n iteratees = this.__iteratees__,\n iterLength = iteratees.length,\n resIndex = 0,\n takeCount = nativeMin(length, this.__takeCount__);\n\n if (!isArr || (!isRight && arrLength == length && takeCount == length)) {\n return baseWrapperValue(array, this.__actions__);\n }\n var result = [];\n\n outer:\n while (length-- && resIndex < takeCount) {\n index += dir;\n\n var iterIndex = -1,\n value = array[index];\n\n while (++iterIndex < iterLength) {\n var data = iteratees[iterIndex],\n iteratee = data.iteratee,\n type = data.type,\n computed = iteratee(value);\n\n if (type == LAZY_MAP_FLAG) {\n value = computed;\n } else if (!computed) {\n if (type == LAZY_FILTER_FLAG) {\n continue outer;\n } else {\n break outer;\n }\n }\n }\n result[resIndex++] = value;\n }\n return result;\n }\n\n // Ensure `LazyWrapper` is an instance of `baseLodash`.\n LazyWrapper.prototype = baseCreate(baseLodash.prototype);\n LazyWrapper.prototype.constructor = LazyWrapper;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Hash(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\n function hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function hashDelete(key) {\n var result = this.has(key) && delete this.__data__[key];\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n }\n\n /**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);\n }\n\n /**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\n function hashSet(key, value) {\n var data = this.__data__;\n this.size += this.has(key) ? 0 : 1;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n }\n\n // Add methods to `Hash`.\n Hash.prototype.clear = hashClear;\n Hash.prototype['delete'] = hashDelete;\n Hash.prototype.get = hashGet;\n Hash.prototype.has = hashHas;\n Hash.prototype.set = hashSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function ListCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\n function listCacheClear() {\n this.__data__ = [];\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n --this.size;\n return true;\n }\n\n /**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n }\n\n /**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n }\n\n /**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\n function listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n ++this.size;\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n }\n\n // Add methods to `ListCache`.\n ListCache.prototype.clear = listCacheClear;\n ListCache.prototype['delete'] = listCacheDelete;\n ListCache.prototype.get = listCacheGet;\n ListCache.prototype.has = listCacheHas;\n ListCache.prototype.set = listCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function MapCache(entries) {\n var index = -1,\n length = entries == null ? 0 : entries.length;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n }\n\n /**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\n function mapCacheClear() {\n this.size = 0;\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n }\n\n /**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function mapCacheDelete(key) {\n var result = getMapData(this, key)['delete'](key);\n this.size -= result ? 1 : 0;\n return result;\n }\n\n /**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function mapCacheGet(key) {\n return getMapData(this, key).get(key);\n }\n\n /**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function mapCacheHas(key) {\n return getMapData(this, key).has(key);\n }\n\n /**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\n function mapCacheSet(key, value) {\n var data = getMapData(this, key),\n size = data.size;\n\n data.set(key, value);\n this.size += data.size == size ? 0 : 1;\n return this;\n }\n\n // Add methods to `MapCache`.\n MapCache.prototype.clear = mapCacheClear;\n MapCache.prototype['delete'] = mapCacheDelete;\n MapCache.prototype.get = mapCacheGet;\n MapCache.prototype.has = mapCacheHas;\n MapCache.prototype.set = mapCacheSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\n function SetCache(values) {\n var index = -1,\n length = values == null ? 0 : values.length;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n }\n\n /**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\n function setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n }\n\n /**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\n function setCacheHas(value) {\n return this.__data__.has(value);\n }\n\n // Add methods to `SetCache`.\n SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\n SetCache.prototype.has = setCacheHas;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\n function Stack(entries) {\n var data = this.__data__ = new ListCache(entries);\n this.size = data.size;\n }\n\n /**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\n function stackClear() {\n this.__data__ = new ListCache;\n this.size = 0;\n }\n\n /**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\n function stackDelete(key) {\n var data = this.__data__,\n result = data['delete'](key);\n\n this.size = data.size;\n return result;\n }\n\n /**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\n function stackGet(key) {\n return this.__data__.get(key);\n }\n\n /**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\n function stackHas(key) {\n return this.__data__.has(key);\n }\n\n /**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\n function stackSet(key, value) {\n var data = this.__data__;\n if (data instanceof ListCache) {\n var pairs = data.__data__;\n if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n pairs.push([key, value]);\n this.size = ++data.size;\n return this;\n }\n data = this.__data__ = new MapCache(pairs);\n }\n data.set(key, value);\n this.size = data.size;\n return this;\n }\n\n // Add methods to `Stack`.\n Stack.prototype.clear = stackClear;\n Stack.prototype['delete'] = stackDelete;\n Stack.prototype.get = stackGet;\n Stack.prototype.has = stackHas;\n Stack.prototype.set = stackSet;\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\n function arrayLikeKeys(value, inherited) {\n var isArr = isArray(value),\n isArg = !isArr && isArguments(value),\n isBuff = !isArr && !isArg && isBuffer(value),\n isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n skipIndexes = isArr || isArg || isBuff || isType,\n result = skipIndexes ? baseTimes(value.length, String) : [],\n length = result.length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (\n // Safari 9 has enumerable `arguments.length` in strict mode.\n key == 'length' ||\n // Node.js 0.10 has enumerable non-index properties on buffers.\n (isBuff && (key == 'offset' || key == 'parent')) ||\n // PhantomJS 2 has enumerable non-index properties on typed arrays.\n (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||\n // Skip index properties.\n isIndex(key, length)\n ))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `_.sample` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @returns {*} Returns the random element.\n */\n function arraySample(array) {\n var length = array.length;\n return length ? array[baseRandom(0, length - 1)] : undefined;\n }\n\n /**\n * A specialized version of `_.sampleSize` for arrays.\n *\n * @private\n * @param {Array} array The array to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function arraySampleSize(array, n) {\n return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));\n }\n\n /**\n * A specialized version of `_.shuffle` for arrays.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function arrayShuffle(array) {\n return shuffleSelf(copyArray(array));\n }\n\n /**\n * This function is like `assignValue` except that it doesn't assign\n * `undefined` values.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignMergeValue(object, key, value) {\n if ((value !== undefined && !eq(object[key], value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function assignValue(object, key, value) {\n var objValue = object[key];\n if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n (value === undefined && !(key in object))) {\n baseAssignValue(object, key, value);\n }\n }\n\n /**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\n function assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n }\n\n /**\n * Aggregates elements of `collection` on `accumulator` with keys transformed\n * by `iteratee` and values set by `setter`.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform keys.\n * @param {Object} accumulator The initial aggregated object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseAggregator(collection, setter, iteratee, accumulator) {\n baseEach(collection, function(value, key, collection) {\n setter(accumulator, value, iteratee(value), collection);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssign(object, source) {\n return object && copyObject(source, keys(source), object);\n }\n\n /**\n * The base implementation of `_.assignIn` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\n function baseAssignIn(object, source) {\n return object && copyObject(source, keysIn(source), object);\n }\n\n /**\n * The base implementation of `assignValue` and `assignMergeValue` without\n * value checks.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\n function baseAssignValue(object, key, value) {\n if (key == '__proto__' && defineProperty) {\n defineProperty(object, key, {\n 'configurable': true,\n 'enumerable': true,\n 'value': value,\n 'writable': true\n });\n } else {\n object[key] = value;\n }\n }\n\n /**\n * The base implementation of `_.at` without support for individual paths.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {string[]} paths The property paths to pick.\n * @returns {Array} Returns the picked elements.\n */\n function baseAt(object, paths) {\n var index = -1,\n length = paths.length,\n result = Array(length),\n skip = object == null;\n\n while (++index < length) {\n result[index] = skip ? undefined : get(object, paths[index]);\n }\n return result;\n }\n\n /**\n * The base implementation of `_.clamp` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n */\n function baseClamp(number, lower, upper) {\n if (number === number) {\n if (upper !== undefined) {\n number = number <= upper ? number : upper;\n }\n if (lower !== undefined) {\n number = number >= lower ? number : lower;\n }\n }\n return number;\n }\n\n /**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Deep clone\n * 2 - Flatten inherited properties\n * 4 - Clone symbols\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\n function baseClone(value, bitmask, customizer, key, object, stack) {\n var result,\n isDeep = bitmask & CLONE_DEEP_FLAG,\n isFlat = bitmask & CLONE_FLAT_FLAG,\n isFull = bitmask & CLONE_SYMBOLS_FLAG;\n\n if (customizer) {\n result = object ? customizer(value, key, object, stack) : customizer(value);\n }\n if (result !== undefined) {\n return result;\n }\n if (!isObject(value)) {\n return value;\n }\n var isArr = isArray(value);\n if (isArr) {\n result = initCloneArray(value);\n if (!isDeep) {\n return copyArray(value, result);\n }\n } else {\n var tag = getTag(value),\n isFunc = tag == funcTag || tag == genTag;\n\n if (isBuffer(value)) {\n return cloneBuffer(value, isDeep);\n }\n if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n result = (isFlat || isFunc) ? {} : initCloneObject(value);\n if (!isDeep) {\n return isFlat\n ? copySymbolsIn(value, baseAssignIn(result, value))\n : copySymbols(value, baseAssign(result, value));\n }\n } else {\n if (!cloneableTags[tag]) {\n return object ? value : {};\n }\n result = initCloneByTag(value, tag, isDeep);\n }\n }\n // Check for circular references and return its corresponding clone.\n stack || (stack = new Stack);\n var stacked = stack.get(value);\n if (stacked) {\n return stacked;\n }\n stack.set(value, result);\n\n if (isSet(value)) {\n value.forEach(function(subValue) {\n result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));\n });\n\n return result;\n }\n\n if (isMap(value)) {\n value.forEach(function(subValue, key) {\n result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n\n return result;\n }\n\n var keysFunc = isFull\n ? (isFlat ? getAllKeysIn : getAllKeys)\n : (isFlat ? keysIn : keys);\n\n var props = isArr ? undefined : keysFunc(value);\n arrayEach(props || value, function(subValue, key) {\n if (props) {\n key = subValue;\n subValue = value[key];\n }\n // Recursively populate clone (susceptible to call stack limits).\n assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));\n });\n return result;\n }\n\n /**\n * The base implementation of `_.conforms` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property predicates to conform to.\n * @returns {Function} Returns the new spec function.\n */\n function baseConforms(source) {\n var props = keys(source);\n return function(object) {\n return baseConformsTo(object, source, props);\n };\n }\n\n /**\n * The base implementation of `_.conformsTo` which accepts `props` to check.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property predicates to conform to.\n * @returns {boolean} Returns `true` if `object` conforms, else `false`.\n */\n function baseConformsTo(object, source, props) {\n var length = props.length;\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (length--) {\n var key = props[length],\n predicate = source[key],\n value = object[key];\n\n if ((value === undefined && !(key in object)) || !predicate(value)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.delay` and `_.defer` which accepts `args`\n * to provide to `func`.\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {Array} args The arguments to provide to `func`.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n function baseDelay(func, wait, args) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return setTimeout(function() { func.apply(undefined, args); }, wait);\n }\n\n /**\n * The base implementation of methods like `_.difference` without support\n * for excluding multiple arrays or iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Array} values The values to exclude.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n */\n function baseDifference(array, values, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n isCommon = true,\n length = array.length,\n result = [],\n valuesLength = values.length;\n\n if (!length) {\n return result;\n }\n if (iteratee) {\n values = arrayMap(values, baseUnary(iteratee));\n }\n if (comparator) {\n includes = arrayIncludesWith;\n isCommon = false;\n }\n else if (values.length >= LARGE_ARRAY_SIZE) {\n includes = cacheHas;\n isCommon = false;\n values = new SetCache(values);\n }\n outer:\n while (++index < length) {\n var value = array[index],\n computed = iteratee == null ? value : iteratee(value);\n\n value = (comparator || value !== 0) ? value : 0;\n if (isCommon && computed === computed) {\n var valuesIndex = valuesLength;\n while (valuesIndex--) {\n if (values[valuesIndex] === computed) {\n continue outer;\n }\n }\n result.push(value);\n }\n else if (!includes(values, computed, comparator)) {\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.forEach` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEach = createBaseEach(baseForOwn);\n\n /**\n * The base implementation of `_.forEachRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n */\n var baseEachRight = createBaseEach(baseForOwnRight, true);\n\n /**\n * The base implementation of `_.every` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`\n */\n function baseEvery(collection, predicate) {\n var result = true;\n baseEach(collection, function(value, index, collection) {\n result = !!predicate(value, index, collection);\n return result;\n });\n return result;\n }\n\n /**\n * The base implementation of methods like `_.max` and `_.min` which accepts a\n * `comparator` to determine the extremum value.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The iteratee invoked per iteration.\n * @param {Function} comparator The comparator used to compare values.\n * @returns {*} Returns the extremum value.\n */\n function baseExtremum(array, iteratee, comparator) {\n var index = -1,\n length = array.length;\n\n while (++index < length) {\n var value = array[index],\n current = iteratee(value);\n\n if (current != null && (computed === undefined\n ? (current === current && !isSymbol(current))\n : comparator(current, computed)\n )) {\n var computed = current,\n result = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.fill` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n */\n function baseFill(array, value, start, end) {\n var length = array.length;\n\n start = toInteger(start);\n if (start < 0) {\n start = -start > length ? 0 : (length + start);\n }\n end = (end === undefined || end > length) ? length : toInteger(end);\n if (end < 0) {\n end += length;\n }\n end = start > end ? 0 : toLength(end);\n while (start < end) {\n array[start++] = value;\n }\n return array;\n }\n\n /**\n * The base implementation of `_.filter` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\n function baseFilter(collection, predicate) {\n var result = [];\n baseEach(collection, function(value, index, collection) {\n if (predicate(value, index, collection)) {\n result.push(value);\n }\n });\n return result;\n }\n\n /**\n * The base implementation of `_.flatten` with support for restricting flattening.\n *\n * @private\n * @param {Array} array The array to flatten.\n * @param {number} depth The maximum recursion depth.\n * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n * @param {Array} [result=[]] The initial result value.\n * @returns {Array} Returns the new flattened array.\n */\n function baseFlatten(array, depth, predicate, isStrict, result) {\n var index = -1,\n length = array.length;\n\n predicate || (predicate = isFlattenable);\n result || (result = []);\n\n while (++index < length) {\n var value = array[index];\n if (depth > 0 && predicate(value)) {\n if (depth > 1) {\n // Recursively flatten arrays (susceptible to call stack limits).\n baseFlatten(value, depth - 1, predicate, isStrict, result);\n } else {\n arrayPush(result, value);\n }\n } else if (!isStrict) {\n result[result.length] = value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `baseForOwn` which iterates over `object`\n * properties returned by `keysFunc` and invokes `iteratee` for each property.\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseFor = createBaseFor();\n\n /**\n * This function is like `baseFor` except that it iterates over properties\n * in the opposite order.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @returns {Object} Returns `object`.\n */\n var baseForRight = createBaseFor(true);\n\n /**\n * The base implementation of `_.forOwn` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwn(object, iteratee) {\n return object && baseFor(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.forOwnRight` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Object} Returns `object`.\n */\n function baseForOwnRight(object, iteratee) {\n return object && baseForRight(object, iteratee, keys);\n }\n\n /**\n * The base implementation of `_.functions` which creates an array of\n * `object` function property names filtered from `props`.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Array} props The property names to filter.\n * @returns {Array} Returns the function names.\n */\n function baseFunctions(object, props) {\n return arrayFilter(props, function(key) {\n return isFunction(object[key]);\n });\n }\n\n /**\n * The base implementation of `_.get` without support for default values.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @returns {*} Returns the resolved value.\n */\n function baseGet(object, path) {\n path = castPath(path, object);\n\n var index = 0,\n length = path.length;\n\n while (object != null && index < length) {\n object = object[toKey(path[index++])];\n }\n return (index && index == length) ? object : undefined;\n }\n\n /**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function baseGetAllKeys(object, keysFunc, symbolsFunc) {\n var result = keysFunc(object);\n return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n }\n\n /**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n function baseGetTag(value) {\n if (value == null) {\n return value === undefined ? undefinedTag : nullTag;\n }\n return (symToStringTag && symToStringTag in Object(value))\n ? getRawTag(value)\n : objectToString(value);\n }\n\n /**\n * The base implementation of `_.gt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n */\n function baseGt(value, other) {\n return value > other;\n }\n\n /**\n * The base implementation of `_.has` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHas(object, key) {\n return object != null && hasOwnProperty.call(object, key);\n }\n\n /**\n * The base implementation of `_.hasIn` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\n function baseHasIn(object, key) {\n return object != null && key in Object(object);\n }\n\n /**\n * The base implementation of `_.inRange` which doesn't coerce arguments.\n *\n * @private\n * @param {number} number The number to check.\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n */\n function baseInRange(number, start, end) {\n return number >= nativeMin(start, end) && number < nativeMax(start, end);\n }\n\n /**\n * The base implementation of methods like `_.intersection`, without support\n * for iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of shared values.\n */\n function baseIntersection(arrays, iteratee, comparator) {\n var includes = comparator ? arrayIncludesWith : arrayIncludes,\n length = arrays[0].length,\n othLength = arrays.length,\n othIndex = othLength,\n caches = Array(othLength),\n maxLength = Infinity,\n result = [];\n\n while (othIndex--) {\n var array = arrays[othIndex];\n if (othIndex && iteratee) {\n array = arrayMap(array, baseUnary(iteratee));\n }\n maxLength = nativeMin(array.length, maxLength);\n caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))\n ? new SetCache(othIndex && array)\n : undefined;\n }\n array = arrays[0];\n\n var index = -1,\n seen = caches[0];\n\n outer:\n while (++index < length && result.length < maxLength) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n value = (comparator || value !== 0) ? value : 0;\n if (!(seen\n ? cacheHas(seen, computed)\n : includes(result, computed, comparator)\n )) {\n othIndex = othLength;\n while (--othIndex) {\n var cache = caches[othIndex];\n if (!(cache\n ? cacheHas(cache, computed)\n : includes(arrays[othIndex], computed, comparator))\n ) {\n continue outer;\n }\n }\n if (seen) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.invert` and `_.invertBy` which inverts\n * `object` with values transformed by `iteratee` and set by `setter`.\n *\n * @private\n * @param {Object} object The object to iterate over.\n * @param {Function} setter The function to set `accumulator` values.\n * @param {Function} iteratee The iteratee to transform values.\n * @param {Object} accumulator The initial inverted object.\n * @returns {Function} Returns `accumulator`.\n */\n function baseInverter(object, setter, iteratee, accumulator) {\n baseForOwn(object, function(value, key, object) {\n setter(accumulator, iteratee(value), key, object);\n });\n return accumulator;\n }\n\n /**\n * The base implementation of `_.invoke` without support for individual\n * method arguments.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the method to invoke.\n * @param {Array} args The arguments to invoke the method with.\n * @returns {*} Returns the result of the invoked method.\n */\n function baseInvoke(object, path, args) {\n path = castPath(path, object);\n object = parent(object, path);\n var func = object == null ? object : object[toKey(last(path))];\n return func == null ? undefined : apply(func, object, args);\n }\n\n /**\n * The base implementation of `_.isArguments`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n */\n function baseIsArguments(value) {\n return isObjectLike(value) && baseGetTag(value) == argsTag;\n }\n\n /**\n * The base implementation of `_.isArrayBuffer` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.\n */\n function baseIsArrayBuffer(value) {\n return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;\n }\n\n /**\n * The base implementation of `_.isDate` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a date object, else `false`.\n */\n function baseIsDate(value) {\n return isObjectLike(value) && baseGetTag(value) == dateTag;\n }\n\n /**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\n function baseIsEqual(value, other, bitmask, customizer, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n }\n\n /**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {\n var objIsArr = isArray(object),\n othIsArr = isArray(other),\n objTag = objIsArr ? arrayTag : getTag(object),\n othTag = othIsArr ? arrayTag : getTag(other);\n\n objTag = objTag == argsTag ? objectTag : objTag;\n othTag = othTag == argsTag ? objectTag : othTag;\n\n var objIsObj = objTag == objectTag,\n othIsObj = othTag == objectTag,\n isSameTag = objTag == othTag;\n\n if (isSameTag && isBuffer(object)) {\n if (!isBuffer(other)) {\n return false;\n }\n objIsArr = true;\n objIsObj = false;\n }\n if (isSameTag && !objIsObj) {\n stack || (stack = new Stack);\n return (objIsArr || isTypedArray(object))\n ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)\n : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);\n }\n if (!(bitmask & COMPARE_PARTIAL_FLAG)) {\n var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n if (objIsWrapped || othIsWrapped) {\n var objUnwrapped = objIsWrapped ? object.value() : object,\n othUnwrapped = othIsWrapped ? other.value() : other;\n\n stack || (stack = new Stack);\n return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);\n }\n }\n if (!isSameTag) {\n return false;\n }\n stack || (stack = new Stack);\n return equalObjects(object, other, bitmask, customizer, equalFunc, stack);\n }\n\n /**\n * The base implementation of `_.isMap` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n */\n function baseIsMap(value) {\n return isObjectLike(value) && getTag(value) == mapTag;\n }\n\n /**\n * The base implementation of `_.isMatch` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Array} matchData The property names, values, and compare flags to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n */\n function baseIsMatch(object, source, matchData, customizer) {\n var index = matchData.length,\n length = index,\n noCustomizer = !customizer;\n\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (index--) {\n var data = matchData[index];\n if ((noCustomizer && data[2])\n ? data[1] !== object[data[0]]\n : !(data[0] in object)\n ) {\n return false;\n }\n }\n while (++index < length) {\n data = matchData[index];\n var key = data[0],\n objValue = object[key],\n srcValue = data[1];\n\n if (noCustomizer && data[2]) {\n if (objValue === undefined && !(key in object)) {\n return false;\n }\n } else {\n var stack = new Stack;\n if (customizer) {\n var result = customizer(objValue, srcValue, key, object, source, stack);\n }\n if (!(result === undefined\n ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)\n : result\n )) {\n return false;\n }\n }\n }\n return true;\n }\n\n /**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\n function baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = isFunction(value) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n }\n\n /**\n * The base implementation of `_.isRegExp` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.\n */\n function baseIsRegExp(value) {\n return isObjectLike(value) && baseGetTag(value) == regexpTag;\n }\n\n /**\n * The base implementation of `_.isSet` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a set, else `false`.\n */\n function baseIsSet(value) {\n return isObjectLike(value) && getTag(value) == setTag;\n }\n\n /**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\n function baseIsTypedArray(value) {\n return isObjectLike(value) &&\n isLength(value.length) && !!typedArrayTags[baseGetTag(value)];\n }\n\n /**\n * The base implementation of `_.iteratee`.\n *\n * @private\n * @param {*} [value=_.identity] The value to convert to an iteratee.\n * @returns {Function} Returns the iteratee.\n */\n function baseIteratee(value) {\n // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n if (typeof value == 'function') {\n return value;\n }\n if (value == null) {\n return identity;\n }\n if (typeof value == 'object') {\n return isArray(value)\n ? baseMatchesProperty(value[0], value[1])\n : baseMatches(value);\n }\n return property(value);\n }\n\n /**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function baseKeysIn(object) {\n if (!isObject(object)) {\n return nativeKeysIn(object);\n }\n var isProto = isPrototype(object),\n result = [];\n\n for (var key in object) {\n if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.lt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than `other`,\n * else `false`.\n */\n function baseLt(value, other) {\n return value < other;\n }\n\n /**\n * The base implementation of `_.map` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n */\n function baseMap(collection, iteratee) {\n var index = -1,\n result = isArrayLike(collection) ? Array(collection.length) : [];\n\n baseEach(collection, function(value, key, collection) {\n result[++index] = iteratee(value, key, collection);\n });\n return result;\n }\n\n /**\n * The base implementation of `_.matches` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property values to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatches(source) {\n var matchData = getMatchData(source);\n if (matchData.length == 1 && matchData[0][2]) {\n return matchesStrictComparable(matchData[0][0], matchData[0][1]);\n }\n return function(object) {\n return object === source || baseIsMatch(object, source, matchData);\n };\n }\n\n /**\n * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.\n *\n * @private\n * @param {string} path The path of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function baseMatchesProperty(path, srcValue) {\n if (isKey(path) && isStrictComparable(srcValue)) {\n return matchesStrictComparable(toKey(path), srcValue);\n }\n return function(object) {\n var objValue = get(object, path);\n return (objValue === undefined && objValue === srcValue)\n ? hasIn(object, path)\n : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);\n };\n }\n\n /**\n * The base implementation of `_.merge` without support for multiple sources.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} [customizer] The function to customize merged values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMerge(object, source, srcIndex, customizer, stack) {\n if (object === source) {\n return;\n }\n baseFor(source, function(srcValue, key) {\n if (isObject(srcValue)) {\n stack || (stack = new Stack);\n baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);\n }\n else {\n var newValue = customizer\n ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)\n : undefined;\n\n if (newValue === undefined) {\n newValue = srcValue;\n }\n assignMergeValue(object, key, newValue);\n }\n }, keysIn);\n }\n\n /**\n * A specialized version of `baseMerge` for arrays and objects which performs\n * deep merges and tracks traversed objects enabling objects with circular\n * references to be merged.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @param {string} key The key of the value to merge.\n * @param {number} srcIndex The index of `source`.\n * @param {Function} mergeFunc The function to merge values.\n * @param {Function} [customizer] The function to customize assigned values.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n */\n function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {\n var objValue = safeGet(object, key),\n srcValue = safeGet(source, key),\n stacked = stack.get(srcValue);\n\n if (stacked) {\n assignMergeValue(object, key, stacked);\n return;\n }\n var newValue = customizer\n ? customizer(objValue, srcValue, (key + ''), object, source, stack)\n : undefined;\n\n var isCommon = newValue === undefined;\n\n if (isCommon) {\n var isArr = isArray(srcValue),\n isBuff = !isArr && isBuffer(srcValue),\n isTyped = !isArr && !isBuff && isTypedArray(srcValue);\n\n newValue = srcValue;\n if (isArr || isBuff || isTyped) {\n if (isArray(objValue)) {\n newValue = objValue;\n }\n else if (isArrayLikeObject(objValue)) {\n newValue = copyArray(objValue);\n }\n else if (isBuff) {\n isCommon = false;\n newValue = cloneBuffer(srcValue, true);\n }\n else if (isTyped) {\n isCommon = false;\n newValue = cloneTypedArray(srcValue, true);\n }\n else {\n newValue = [];\n }\n }\n else if (isPlainObject(srcValue) || isArguments(srcValue)) {\n newValue = objValue;\n if (isArguments(objValue)) {\n newValue = toPlainObject(objValue);\n }\n else if (!isObject(objValue) || isFunction(objValue)) {\n newValue = initCloneObject(srcValue);\n }\n }\n else {\n isCommon = false;\n }\n }\n if (isCommon) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, newValue);\n mergeFunc(newValue, srcValue, srcIndex, customizer, stack);\n stack['delete'](srcValue);\n }\n assignMergeValue(object, key, newValue);\n }\n\n /**\n * The base implementation of `_.nth` which doesn't coerce arguments.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {number} n The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n */\n function baseNth(array, n) {\n var length = array.length;\n if (!length) {\n return;\n }\n n += n < 0 ? length : 0;\n return isIndex(n, length) ? array[n] : undefined;\n }\n\n /**\n * The base implementation of `_.orderBy` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n * @param {string[]} orders The sort orders of `iteratees`.\n * @returns {Array} Returns the new sorted array.\n */\n function baseOrderBy(collection, iteratees, orders) {\n var index = -1;\n iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee()));\n\n var result = baseMap(collection, function(value, key, collection) {\n var criteria = arrayMap(iteratees, function(iteratee) {\n return iteratee(value);\n });\n return { 'criteria': criteria, 'index': ++index, 'value': value };\n });\n\n return baseSortBy(result, function(object, other) {\n return compareMultiple(object, other, orders);\n });\n }\n\n /**\n * The base implementation of `_.pick` without support for individual\n * property identifiers.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @returns {Object} Returns the new object.\n */\n function basePick(object, paths) {\n return basePickBy(object, paths, function(value, path) {\n return hasIn(object, path);\n });\n }\n\n /**\n * The base implementation of `_.pickBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The source object.\n * @param {string[]} paths The property paths to pick.\n * @param {Function} predicate The function invoked per property.\n * @returns {Object} Returns the new object.\n */\n function basePickBy(object, paths, predicate) {\n var index = -1,\n length = paths.length,\n result = {};\n\n while (++index < length) {\n var path = paths[index],\n value = baseGet(object, path);\n\n if (predicate(value, path)) {\n baseSet(result, castPath(path, object), value);\n }\n }\n return result;\n }\n\n /**\n * A specialized version of `baseProperty` which supports deep paths.\n *\n * @private\n * @param {Array|string} path The path of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\n function basePropertyDeep(path) {\n return function(object) {\n return baseGet(object, path);\n };\n }\n\n /**\n * The base implementation of `_.pullAllBy` without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n */\n function basePullAll(array, values, iteratee, comparator) {\n var indexOf = comparator ? baseIndexOfWith : baseIndexOf,\n index = -1,\n length = values.length,\n seen = array;\n\n if (array === values) {\n values = copyArray(values);\n }\n if (iteratee) {\n seen = arrayMap(array, baseUnary(iteratee));\n }\n while (++index < length) {\n var fromIndex = 0,\n value = values[index],\n computed = iteratee ? iteratee(value) : value;\n\n while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {\n if (seen !== array) {\n splice.call(seen, fromIndex, 1);\n }\n splice.call(array, fromIndex, 1);\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.pullAt` without support for individual\n * indexes or capturing the removed elements.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {number[]} indexes The indexes of elements to remove.\n * @returns {Array} Returns `array`.\n */\n function basePullAt(array, indexes) {\n var length = array ? indexes.length : 0,\n lastIndex = length - 1;\n\n while (length--) {\n var index = indexes[length];\n if (length == lastIndex || index !== previous) {\n var previous = index;\n if (isIndex(index)) {\n splice.call(array, index, 1);\n } else {\n baseUnset(array, index);\n }\n }\n }\n return array;\n }\n\n /**\n * The base implementation of `_.random` without support for returning\n * floating-point numbers.\n *\n * @private\n * @param {number} lower The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the random number.\n */\n function baseRandom(lower, upper) {\n return lower + nativeFloor(nativeRandom() * (upper - lower + 1));\n }\n\n /**\n * The base implementation of `_.range` and `_.rangeRight` which doesn't\n * coerce arguments.\n *\n * @private\n * @param {number} start The start of the range.\n * @param {number} end The end of the range.\n * @param {number} step The value to increment or decrement by.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the range of numbers.\n */\n function baseRange(start, end, step, fromRight) {\n var index = -1,\n length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),\n result = Array(length);\n\n while (length--) {\n result[fromRight ? length : ++index] = start;\n start += step;\n }\n return result;\n }\n\n /**\n * The base implementation of `_.repeat` which doesn't coerce arguments.\n *\n * @private\n * @param {string} string The string to repeat.\n * @param {number} n The number of times to repeat the string.\n * @returns {string} Returns the repeated string.\n */\n function baseRepeat(string, n) {\n var result = '';\n if (!string || n < 1 || n > MAX_SAFE_INTEGER) {\n return result;\n }\n // Leverage the exponentiation by squaring algorithm for a faster repeat.\n // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.\n do {\n if (n % 2) {\n result += string;\n }\n n = nativeFloor(n / 2);\n if (n) {\n string += string;\n }\n } while (n);\n\n return result;\n }\n\n /**\n * The base implementation of `_.rest` which doesn't validate or coerce arguments.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n */\n function baseRest(func, start) {\n return setToString(overRest(func, start, identity), func + '');\n }\n\n /**\n * The base implementation of `_.sample`.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n */\n function baseSample(collection) {\n return arraySample(values(collection));\n }\n\n /**\n * The base implementation of `_.sampleSize` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to sample.\n * @param {number} n The number of elements to sample.\n * @returns {Array} Returns the random elements.\n */\n function baseSampleSize(collection, n) {\n var array = values(collection);\n return shuffleSelf(array, baseClamp(n, 0, array.length));\n }\n\n /**\n * The base implementation of `_.set`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseSet(object, path, value, customizer) {\n if (!isObject(object)) {\n return object;\n }\n path = castPath(path, object);\n\n var index = -1,\n length = path.length,\n lastIndex = length - 1,\n nested = object;\n\n while (nested != null && ++index < length) {\n var key = toKey(path[index]),\n newValue = value;\n\n if (index != lastIndex) {\n var objValue = nested[key];\n newValue = customizer ? customizer(objValue, key, nested) : undefined;\n if (newValue === undefined) {\n newValue = isObject(objValue)\n ? objValue\n : (isIndex(path[index + 1]) ? [] : {});\n }\n }\n assignValue(nested, key, newValue);\n nested = nested[key];\n }\n return object;\n }\n\n /**\n * The base implementation of `setData` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var baseSetData = !metaMap ? identity : function(func, data) {\n metaMap.set(func, data);\n return func;\n };\n\n /**\n * The base implementation of `setToString` without support for hot loop shorting.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var baseSetToString = !defineProperty ? identity : function(func, string) {\n return defineProperty(func, 'toString', {\n 'configurable': true,\n 'enumerable': false,\n 'value': constant(string),\n 'writable': true\n });\n };\n\n /**\n * The base implementation of `_.shuffle`.\n *\n * @private\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n */\n function baseShuffle(collection) {\n return shuffleSelf(values(collection));\n }\n\n /**\n * The base implementation of `_.slice` without an iteratee call guard.\n *\n * @private\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseSlice(array, start, end) {\n var index = -1,\n length = array.length;\n\n if (start < 0) {\n start = -start > length ? 0 : (length + start);\n }\n end = end > length ? length : end;\n if (end < 0) {\n end += length;\n }\n length = start > end ? 0 : ((end - start) >>> 0);\n start >>>= 0;\n\n var result = Array(length);\n while (++index < length) {\n result[index] = array[index + start];\n }\n return result;\n }\n\n /**\n * The base implementation of `_.some` without support for iteratee shorthands.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\n function baseSome(collection, predicate) {\n var result;\n\n baseEach(collection, function(value, index, collection) {\n result = predicate(value, index, collection);\n return !result;\n });\n return !!result;\n }\n\n /**\n * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which\n * performs a binary search of `array` to determine the index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndex(array, value, retHighest) {\n var low = 0,\n high = array == null ? low : array.length;\n\n if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {\n while (low < high) {\n var mid = (low + high) >>> 1,\n computed = array[mid];\n\n if (computed !== null && !isSymbol(computed) &&\n (retHighest ? (computed <= value) : (computed < value))) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return high;\n }\n return baseSortedIndexBy(array, value, identity, retHighest);\n }\n\n /**\n * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`\n * which invokes `iteratee` for `value` and each element of `array` to compute\n * their sort ranking. The iteratee is invoked with one argument; (value).\n *\n * @private\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} iteratee The iteratee invoked per element.\n * @param {boolean} [retHighest] Specify returning the highest qualified index.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n */\n function baseSortedIndexBy(array, value, iteratee, retHighest) {\n value = iteratee(value);\n\n var low = 0,\n high = array == null ? 0 : array.length,\n valIsNaN = value !== value,\n valIsNull = value === null,\n valIsSymbol = isSymbol(value),\n valIsUndefined = value === undefined;\n\n while (low < high) {\n var mid = nativeFloor((low + high) / 2),\n computed = iteratee(array[mid]),\n othIsDefined = computed !== undefined,\n othIsNull = computed === null,\n othIsReflexive = computed === computed,\n othIsSymbol = isSymbol(computed);\n\n if (valIsNaN) {\n var setLow = retHighest || othIsReflexive;\n } else if (valIsUndefined) {\n setLow = othIsReflexive && (retHighest || othIsDefined);\n } else if (valIsNull) {\n setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);\n } else if (valIsSymbol) {\n setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);\n } else if (othIsNull || othIsSymbol) {\n setLow = false;\n } else {\n setLow = retHighest ? (computed <= value) : (computed < value);\n }\n if (setLow) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return nativeMin(high, MAX_ARRAY_INDEX);\n }\n\n /**\n * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without\n * support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseSortedUniq(array, iteratee) {\n var index = -1,\n length = array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n if (!index || !eq(computed, seen)) {\n var seen = computed;\n result[resIndex++] = value === 0 ? 0 : value;\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.toNumber` which doesn't ensure correct\n * conversions of binary, hexadecimal, or octal string values.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n */\n function baseToNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n return +value;\n }\n\n /**\n * The base implementation of `_.toString` which doesn't convert nullish\n * values to empty strings.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n */\n function baseToString(value) {\n // Exit early for strings to avoid a performance hit in some environments.\n if (typeof value == 'string') {\n return value;\n }\n if (isArray(value)) {\n // Recursively convert values (susceptible to call stack limits).\n return arrayMap(value, baseToString) + '';\n }\n if (isSymbol(value)) {\n return symbolToString ? symbolToString.call(value) : '';\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n }\n\n /**\n * The base implementation of `_.uniqBy` without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n */\n function baseUniq(array, iteratee, comparator) {\n var index = -1,\n includes = arrayIncludes,\n length = array.length,\n isCommon = true,\n result = [],\n seen = result;\n\n if (comparator) {\n isCommon = false;\n includes = arrayIncludesWith;\n }\n else if (length >= LARGE_ARRAY_SIZE) {\n var set = iteratee ? null : createSet(array);\n if (set) {\n return setToArray(set);\n }\n isCommon = false;\n includes = cacheHas;\n seen = new SetCache;\n }\n else {\n seen = iteratee ? [] : result;\n }\n outer:\n while (++index < length) {\n var value = array[index],\n computed = iteratee ? iteratee(value) : value;\n\n value = (comparator || value !== 0) ? value : 0;\n if (isCommon && computed === computed) {\n var seenIndex = seen.length;\n while (seenIndex--) {\n if (seen[seenIndex] === computed) {\n continue outer;\n }\n }\n if (iteratee) {\n seen.push(computed);\n }\n result.push(value);\n }\n else if (!includes(seen, computed, comparator)) {\n if (seen !== result) {\n seen.push(computed);\n }\n result.push(value);\n }\n }\n return result;\n }\n\n /**\n * The base implementation of `_.unset`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The property path to unset.\n * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n */\n function baseUnset(object, path) {\n path = castPath(path, object);\n object = parent(object, path);\n return object == null || delete object[toKey(last(path))];\n }\n\n /**\n * The base implementation of `_.update`.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to update.\n * @param {Function} updater The function to produce the updated value.\n * @param {Function} [customizer] The function to customize path creation.\n * @returns {Object} Returns `object`.\n */\n function baseUpdate(object, path, updater, customizer) {\n return baseSet(object, path, updater(baseGet(object, path)), customizer);\n }\n\n /**\n * The base implementation of methods like `_.dropWhile` and `_.takeWhile`\n * without support for iteratee shorthands.\n *\n * @private\n * @param {Array} array The array to query.\n * @param {Function} predicate The function invoked per iteration.\n * @param {boolean} [isDrop] Specify dropping elements instead of taking them.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Array} Returns the slice of `array`.\n */\n function baseWhile(array, predicate, isDrop, fromRight) {\n var length = array.length,\n index = fromRight ? length : -1;\n\n while ((fromRight ? index-- : ++index < length) &&\n predicate(array[index], index, array)) {}\n\n return isDrop\n ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))\n : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));\n }\n\n /**\n * The base implementation of `wrapperValue` which returns the result of\n * performing a sequence of actions on the unwrapped `value`, where each\n * successive action is supplied the return value of the previous.\n *\n * @private\n * @param {*} value The unwrapped value.\n * @param {Array} actions Actions to perform to resolve the unwrapped value.\n * @returns {*} Returns the resolved value.\n */\n function baseWrapperValue(value, actions) {\n var result = value;\n if (result instanceof LazyWrapper) {\n result = result.value();\n }\n return arrayReduce(actions, function(result, action) {\n return action.func.apply(action.thisArg, arrayPush([result], action.args));\n }, result);\n }\n\n /**\n * The base implementation of methods like `_.xor`, without support for\n * iteratee shorthands, that accepts an array of arrays to inspect.\n *\n * @private\n * @param {Array} arrays The arrays to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of values.\n */\n function baseXor(arrays, iteratee, comparator) {\n var length = arrays.length;\n if (length < 2) {\n return length ? baseUniq(arrays[0]) : [];\n }\n var index = -1,\n result = Array(length);\n\n while (++index < length) {\n var array = arrays[index],\n othIndex = -1;\n\n while (++othIndex < length) {\n if (othIndex != index) {\n result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);\n }\n }\n }\n return baseUniq(baseFlatten(result, 1), iteratee, comparator);\n }\n\n /**\n * This base implementation of `_.zipObject` which assigns values using `assignFunc`.\n *\n * @private\n * @param {Array} props The property identifiers.\n * @param {Array} values The property values.\n * @param {Function} assignFunc The function to assign values.\n * @returns {Object} Returns the new object.\n */\n function baseZipObject(props, values, assignFunc) {\n var index = -1,\n length = props.length,\n valsLength = values.length,\n result = {};\n\n while (++index < length) {\n var value = index < valsLength ? values[index] : undefined;\n assignFunc(result, props[index], value);\n }\n return result;\n }\n\n /**\n * Casts `value` to an empty array if it's not an array like object.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Array|Object} Returns the cast array-like object.\n */\n function castArrayLikeObject(value) {\n return isArrayLikeObject(value) ? value : [];\n }\n\n /**\n * Casts `value` to `identity` if it's not a function.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Function} Returns cast function.\n */\n function castFunction(value) {\n return typeof value == 'function' ? value : identity;\n }\n\n /**\n * Casts `value` to a path array if it's not one.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {Object} [object] The object to query keys on.\n * @returns {Array} Returns the cast property path array.\n */\n function castPath(value, object) {\n if (isArray(value)) {\n return value;\n }\n return isKey(value, object) ? [value] : stringToPath(toString(value));\n }\n\n /**\n * A `baseRest` alias which can be replaced with `identity` by module\n * replacement plugins.\n *\n * @private\n * @type {Function}\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n var castRest = baseRest;\n\n /**\n * Casts `array` to a slice if it's needed.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {number} start The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the cast slice.\n */\n function castSlice(array, start, end) {\n var length = array.length;\n end = end === undefined ? length : end;\n return (!start && end >= length) ? array : baseSlice(array, start, end);\n }\n\n /**\n * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).\n *\n * @private\n * @param {number|Object} id The timer id or timeout object of the timer to clear.\n */\n var clearTimeout = ctxClearTimeout || function(id) {\n return root.clearTimeout(id);\n };\n\n /**\n * Creates a clone of `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\n function cloneBuffer(buffer, isDeep) {\n if (isDeep) {\n return buffer.slice();\n }\n var length = buffer.length,\n result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);\n\n buffer.copy(result);\n return result;\n }\n\n /**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\n function cloneArrayBuffer(arrayBuffer) {\n var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n return result;\n }\n\n /**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\n function cloneDataView(dataView, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n }\n\n /**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\n function cloneRegExp(regexp) {\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n result.lastIndex = regexp.lastIndex;\n return result;\n }\n\n /**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\n function cloneSymbol(symbol) {\n return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n }\n\n /**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\n function cloneTypedArray(typedArray, isDeep) {\n var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n }\n\n /**\n * Compares values to sort them in ascending order.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {number} Returns the sort order indicator for `value`.\n */\n function compareAscending(value, other) {\n if (value !== other) {\n var valIsDefined = value !== undefined,\n valIsNull = value === null,\n valIsReflexive = value === value,\n valIsSymbol = isSymbol(value);\n\n var othIsDefined = other !== undefined,\n othIsNull = other === null,\n othIsReflexive = other === other,\n othIsSymbol = isSymbol(other);\n\n if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||\n (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||\n (valIsNull && othIsDefined && othIsReflexive) ||\n (!valIsDefined && othIsReflexive) ||\n !valIsReflexive) {\n return 1;\n }\n if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||\n (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||\n (othIsNull && valIsDefined && valIsReflexive) ||\n (!othIsDefined && valIsReflexive) ||\n !othIsReflexive) {\n return -1;\n }\n }\n return 0;\n }\n\n /**\n * Used by `_.orderBy` to compare multiple properties of a value to another\n * and stable sort them.\n *\n * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,\n * specify an order of \"desc\" for descending or \"asc\" for ascending sort order\n * of corresponding values.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {boolean[]|string[]} orders The order to sort by for each property.\n * @returns {number} Returns the sort order indicator for `object`.\n */\n function compareMultiple(object, other, orders) {\n var index = -1,\n objCriteria = object.criteria,\n othCriteria = other.criteria,\n length = objCriteria.length,\n ordersLength = orders.length;\n\n while (++index < length) {\n var result = compareAscending(objCriteria[index], othCriteria[index]);\n if (result) {\n if (index >= ordersLength) {\n return result;\n }\n var order = orders[index];\n return result * (order == 'desc' ? -1 : 1);\n }\n }\n // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications\n // that causes it, under certain circumstances, to provide the same value for\n // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247\n // for more details.\n //\n // This also ensures a stable sort in V8 and other engines.\n // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.\n return object.index - other.index;\n }\n\n /**\n * Creates an array that is the composition of partially applied arguments,\n * placeholders, and provided arguments into a single array of arguments.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to prepend to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgs(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersLength = holders.length,\n leftIndex = -1,\n leftLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(leftLength + rangeLength),\n isUncurried = !isCurried;\n\n while (++leftIndex < leftLength) {\n result[leftIndex] = partials[leftIndex];\n }\n while (++argsIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[holders[argsIndex]] = args[argsIndex];\n }\n }\n while (rangeLength--) {\n result[leftIndex++] = args[argsIndex++];\n }\n return result;\n }\n\n /**\n * This function is like `composeArgs` except that the arguments composition\n * is tailored for `_.partialRight`.\n *\n * @private\n * @param {Array} args The provided arguments.\n * @param {Array} partials The arguments to append to those provided.\n * @param {Array} holders The `partials` placeholder indexes.\n * @params {boolean} [isCurried] Specify composing for a curried function.\n * @returns {Array} Returns the new array of composed arguments.\n */\n function composeArgsRight(args, partials, holders, isCurried) {\n var argsIndex = -1,\n argsLength = args.length,\n holdersIndex = -1,\n holdersLength = holders.length,\n rightIndex = -1,\n rightLength = partials.length,\n rangeLength = nativeMax(argsLength - holdersLength, 0),\n result = Array(rangeLength + rightLength),\n isUncurried = !isCurried;\n\n while (++argsIndex < rangeLength) {\n result[argsIndex] = args[argsIndex];\n }\n var offset = argsIndex;\n while (++rightIndex < rightLength) {\n result[offset + rightIndex] = partials[rightIndex];\n }\n while (++holdersIndex < holdersLength) {\n if (isUncurried || argsIndex < argsLength) {\n result[offset + holders[holdersIndex]] = args[argsIndex++];\n }\n }\n return result;\n }\n\n /**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\n function copyArray(source, array) {\n var index = -1,\n length = source.length;\n\n array || (array = Array(length));\n while (++index < length) {\n array[index] = source[index];\n }\n return array;\n }\n\n /**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\n function copyObject(source, props, object, customizer) {\n var isNew = !object;\n object || (object = {});\n\n var index = -1,\n length = props.length;\n\n while (++index < length) {\n var key = props[index];\n\n var newValue = customizer\n ? customizer(object[key], source[key], key, object, source)\n : undefined;\n\n if (newValue === undefined) {\n newValue = source[key];\n }\n if (isNew) {\n baseAssignValue(object, key, newValue);\n } else {\n assignValue(object, key, newValue);\n }\n }\n return object;\n }\n\n /**\n * Copies own symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbols(source, object) {\n return copyObject(source, getSymbols(source), object);\n }\n\n /**\n * Copies own and inherited symbols of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\n function copySymbolsIn(source, object) {\n return copyObject(source, getSymbolsIn(source), object);\n }\n\n /**\n * Creates a function like `_.groupBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} [initializer] The accumulator object initializer.\n * @returns {Function} Returns the new aggregator function.\n */\n function createAggregator(setter, initializer) {\n return function(collection, iteratee) {\n var func = isArray(collection) ? arrayAggregator : baseAggregator,\n accumulator = initializer ? initializer() : {};\n\n return func(collection, setter, getIteratee(iteratee, 2), accumulator);\n };\n }\n\n /**\n * Creates a function like `_.assign`.\n *\n * @private\n * @param {Function} assigner The function to assign values.\n * @returns {Function} Returns the new assigner function.\n */\n function createAssigner(assigner) {\n return baseRest(function(object, sources) {\n var index = -1,\n length = sources.length,\n customizer = length > 1 ? sources[length - 1] : undefined,\n guard = length > 2 ? sources[2] : undefined;\n\n customizer = (assigner.length > 3 && typeof customizer == 'function')\n ? (length--, customizer)\n : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n customizer = length < 3 ? undefined : customizer;\n length = 1;\n }\n object = Object(object);\n while (++index < length) {\n var source = sources[index];\n if (source) {\n assigner(object, source, index, customizer);\n }\n }\n return object;\n });\n }\n\n /**\n * Creates a `baseEach` or `baseEachRight` function.\n *\n * @private\n * @param {Function} eachFunc The function to iterate over a collection.\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseEach(eachFunc, fromRight) {\n return function(collection, iteratee) {\n if (collection == null) {\n return collection;\n }\n if (!isArrayLike(collection)) {\n return eachFunc(collection, iteratee);\n }\n var length = collection.length,\n index = fromRight ? length : -1,\n iterable = Object(collection);\n\n while ((fromRight ? index-- : ++index < length)) {\n if (iteratee(iterable[index], index, iterable) === false) {\n break;\n }\n }\n return collection;\n };\n }\n\n /**\n * Creates a base function for methods like `_.forIn` and `_.forOwn`.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new base function.\n */\n function createBaseFor(fromRight) {\n return function(object, iteratee, keysFunc) {\n var index = -1,\n iterable = Object(object),\n props = keysFunc(object),\n length = props.length;\n\n while (length--) {\n var key = props[fromRight ? length : ++index];\n if (iteratee(iterable[key], key, iterable) === false) {\n break;\n }\n }\n return object;\n };\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the optional `this`\n * binding of `thisArg`.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createBind(func, bitmask, thisArg) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return fn.apply(isBind ? thisArg : this, arguments);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.lowerFirst`.\n *\n * @private\n * @param {string} methodName The name of the `String` case method to use.\n * @returns {Function} Returns the new case function.\n */\n function createCaseFirst(methodName) {\n return function(string) {\n string = toString(string);\n\n var strSymbols = hasUnicode(string)\n ? stringToArray(string)\n : undefined;\n\n var chr = strSymbols\n ? strSymbols[0]\n : string.charAt(0);\n\n var trailing = strSymbols\n ? castSlice(strSymbols, 1).join('')\n : string.slice(1);\n\n return chr[methodName]() + trailing;\n };\n }\n\n /**\n * Creates a function like `_.camelCase`.\n *\n * @private\n * @param {Function} callback The function to combine each word.\n * @returns {Function} Returns the new compounder function.\n */\n function createCompounder(callback) {\n return function(string) {\n return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');\n };\n }\n\n /**\n * Creates a function that produces an instance of `Ctor` regardless of\n * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n *\n * @private\n * @param {Function} Ctor The constructor to wrap.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCtor(Ctor) {\n return function() {\n // Use a `switch` statement to work with class constructors. See\n // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n // for more details.\n var args = arguments;\n switch (args.length) {\n case 0: return new Ctor;\n case 1: return new Ctor(args[0]);\n case 2: return new Ctor(args[0], args[1]);\n case 3: return new Ctor(args[0], args[1], args[2]);\n case 4: return new Ctor(args[0], args[1], args[2], args[3]);\n case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n }\n var thisBinding = baseCreate(Ctor.prototype),\n result = Ctor.apply(thisBinding, args);\n\n // Mimic the constructor's `return` behavior.\n // See https://es5.github.io/#x13.2.2 for more details.\n return isObject(result) ? result : thisBinding;\n };\n }\n\n /**\n * Creates a function that wraps `func` to enable currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {number} arity The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createCurry(func, bitmask, arity) {\n var Ctor = createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length,\n placeholder = getHolder(wrapper);\n\n while (index--) {\n args[index] = arguments[index];\n }\n var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)\n ? []\n : replaceHolders(args, placeholder);\n\n length -= holders.length;\n if (length < arity) {\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, undefined,\n args, holders, undefined, undefined, arity - length);\n }\n var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n return apply(fn, this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.find` or `_.findLast` function.\n *\n * @private\n * @param {Function} findIndexFunc The function to find the collection index.\n * @returns {Function} Returns the new find function.\n */\n function createFind(findIndexFunc) {\n return function(collection, predicate, fromIndex) {\n var iterable = Object(collection);\n if (!isArrayLike(collection)) {\n var iteratee = getIteratee(predicate, 3);\n collection = keys(collection);\n predicate = function(key) { return iteratee(iterable[key], key, iterable); };\n }\n var index = findIndexFunc(collection, predicate, fromIndex);\n return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;\n };\n }\n\n /**\n * Creates a `_.flow` or `_.flowRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new flow function.\n */\n function createFlow(fromRight) {\n return flatRest(function(funcs) {\n var length = funcs.length,\n index = length,\n prereq = LodashWrapper.prototype.thru;\n\n if (fromRight) {\n funcs.reverse();\n }\n while (index--) {\n var func = funcs[index];\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (prereq && !wrapper && getFuncName(func) == 'wrapper') {\n var wrapper = new LodashWrapper([], true);\n }\n }\n index = wrapper ? index : length;\n while (++index < length) {\n func = funcs[index];\n\n var funcName = getFuncName(func),\n data = funcName == 'wrapper' ? getData(func) : undefined;\n\n if (data && isLaziable(data[0]) &&\n data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) &&\n !data[4].length && data[9] == 1\n ) {\n wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);\n } else {\n wrapper = (func.length == 1 && isLaziable(func))\n ? wrapper[funcName]()\n : wrapper.thru(func);\n }\n }\n return function() {\n var args = arguments,\n value = args[0];\n\n if (wrapper && args.length == 1 && isArray(value)) {\n return wrapper.plant(value).value();\n }\n var index = 0,\n result = length ? funcs[index].apply(this, args) : value;\n\n while (++index < length) {\n result = funcs[index].call(this, result);\n }\n return result;\n };\n });\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with optional `this`\n * binding of `thisArg`, partial application, and currying.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [partialsRight] The arguments to append to those provided\n * to the new function.\n * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n var isAry = bitmask & WRAP_ARY_FLAG,\n isBind = bitmask & WRAP_BIND_FLAG,\n isBindKey = bitmask & WRAP_BIND_KEY_FLAG,\n isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),\n isFlip = bitmask & WRAP_FLIP_FLAG,\n Ctor = isBindKey ? undefined : createCtor(func);\n\n function wrapper() {\n var length = arguments.length,\n args = Array(length),\n index = length;\n\n while (index--) {\n args[index] = arguments[index];\n }\n if (isCurried) {\n var placeholder = getHolder(wrapper),\n holdersCount = countHolders(args, placeholder);\n }\n if (partials) {\n args = composeArgs(args, partials, holders, isCurried);\n }\n if (partialsRight) {\n args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n }\n length -= holdersCount;\n if (isCurried && length < arity) {\n var newHolders = replaceHolders(args, placeholder);\n return createRecurry(\n func, bitmask, createHybrid, wrapper.placeholder, thisArg,\n args, newHolders, argPos, ary, arity - length\n );\n }\n var thisBinding = isBind ? thisArg : this,\n fn = isBindKey ? thisBinding[func] : func;\n\n length = args.length;\n if (argPos) {\n args = reorder(args, argPos);\n } else if (isFlip && length > 1) {\n args.reverse();\n }\n if (isAry && ary < length) {\n args.length = ary;\n }\n if (this && this !== root && this instanceof wrapper) {\n fn = Ctor || createCtor(fn);\n }\n return fn.apply(thisBinding, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a function like `_.invertBy`.\n *\n * @private\n * @param {Function} setter The function to set accumulator values.\n * @param {Function} toIteratee The function to resolve iteratees.\n * @returns {Function} Returns the new inverter function.\n */\n function createInverter(setter, toIteratee) {\n return function(object, iteratee) {\n return baseInverter(object, setter, toIteratee(iteratee), {});\n };\n }\n\n /**\n * Creates a function that performs a mathematical operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @param {number} [defaultValue] The value used for `undefined` arguments.\n * @returns {Function} Returns the new mathematical operation function.\n */\n function createMathOperation(operator, defaultValue) {\n return function(value, other) {\n var result;\n if (value === undefined && other === undefined) {\n return defaultValue;\n }\n if (value !== undefined) {\n result = value;\n }\n if (other !== undefined) {\n if (result === undefined) {\n return other;\n }\n if (typeof value == 'string' || typeof other == 'string') {\n value = baseToString(value);\n other = baseToString(other);\n } else {\n value = baseToNumber(value);\n other = baseToNumber(other);\n }\n result = operator(value, other);\n }\n return result;\n };\n }\n\n /**\n * Creates a function like `_.over`.\n *\n * @private\n * @param {Function} arrayFunc The function to iterate over iteratees.\n * @returns {Function} Returns the new over function.\n */\n function createOver(arrayFunc) {\n return flatRest(function(iteratees) {\n iteratees = arrayMap(iteratees, baseUnary(getIteratee()));\n return baseRest(function(args) {\n var thisArg = this;\n return arrayFunc(iteratees, function(iteratee) {\n return apply(iteratee, thisArg, args);\n });\n });\n });\n }\n\n /**\n * Creates the padding for `string` based on `length`. The `chars` string\n * is truncated if the number of characters exceeds `length`.\n *\n * @private\n * @param {number} length The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padding for `string`.\n */\n function createPadding(length, chars) {\n chars = chars === undefined ? ' ' : baseToString(chars);\n\n var charsLength = chars.length;\n if (charsLength < 2) {\n return charsLength ? baseRepeat(chars, length) : chars;\n }\n var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));\n return hasUnicode(chars)\n ? castSlice(stringToArray(result), 0, length).join('')\n : result.slice(0, length);\n }\n\n /**\n * Creates a function that wraps `func` to invoke it with the `this` binding\n * of `thisArg` and `partials` prepended to the arguments it receives.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {Array} partials The arguments to prepend to those provided to\n * the new function.\n * @returns {Function} Returns the new wrapped function.\n */\n function createPartial(func, bitmask, thisArg, partials) {\n var isBind = bitmask & WRAP_BIND_FLAG,\n Ctor = createCtor(func);\n\n function wrapper() {\n var argsIndex = -1,\n argsLength = arguments.length,\n leftIndex = -1,\n leftLength = partials.length,\n args = Array(leftLength + argsLength),\n fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n\n while (++leftIndex < leftLength) {\n args[leftIndex] = partials[leftIndex];\n }\n while (argsLength--) {\n args[leftIndex++] = arguments[++argsIndex];\n }\n return apply(fn, isBind ? thisArg : this, args);\n }\n return wrapper;\n }\n\n /**\n * Creates a `_.range` or `_.rangeRight` function.\n *\n * @private\n * @param {boolean} [fromRight] Specify iterating from right to left.\n * @returns {Function} Returns the new range function.\n */\n function createRange(fromRight) {\n return function(start, end, step) {\n if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {\n end = step = undefined;\n }\n // Ensure the sign of `-0` is preserved.\n start = toFinite(start);\n if (end === undefined) {\n end = start;\n start = 0;\n } else {\n end = toFinite(end);\n }\n step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);\n return baseRange(start, end, step, fromRight);\n };\n }\n\n /**\n * Creates a function that performs a relational operation on two values.\n *\n * @private\n * @param {Function} operator The function to perform the operation.\n * @returns {Function} Returns the new relational operation function.\n */\n function createRelationalOperation(operator) {\n return function(value, other) {\n if (!(typeof value == 'string' && typeof other == 'string')) {\n value = toNumber(value);\n other = toNumber(other);\n }\n return operator(value, other);\n };\n }\n\n /**\n * Creates a function that wraps `func` to continue currying.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @param {Function} wrapFunc The function to create the `func` wrapper.\n * @param {*} placeholder The placeholder value.\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to prepend to those provided to\n * the new function.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n var isCurry = bitmask & WRAP_CURRY_FLAG,\n newHolders = isCurry ? holders : undefined,\n newHoldersRight = isCurry ? undefined : holders,\n newPartials = isCurry ? partials : undefined,\n newPartialsRight = isCurry ? undefined : partials;\n\n bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);\n bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);\n\n if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {\n bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);\n }\n var newData = [\n func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,\n newHoldersRight, argPos, ary, arity\n ];\n\n var result = wrapFunc.apply(undefined, newData);\n if (isLaziable(func)) {\n setData(result, newData);\n }\n result.placeholder = placeholder;\n return setWrapToString(result, func, bitmask);\n }\n\n /**\n * Creates a function like `_.round`.\n *\n * @private\n * @param {string} methodName The name of the `Math` method to use when rounding.\n * @returns {Function} Returns the new round function.\n */\n function createRound(methodName) {\n var func = Math[methodName];\n return function(number, precision) {\n number = toNumber(number);\n precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);\n if (precision) {\n // Shift with exponential notation to avoid floating-point issues.\n // See [MDN](https://mdn.io/round#Examples) for more details.\n var pair = (toString(number) + 'e').split('e'),\n value = func(pair[0] + 'e' + (+pair[1] + precision));\n\n pair = (toString(value) + 'e').split('e');\n return +(pair[0] + 'e' + (+pair[1] - precision));\n }\n return func(number);\n };\n }\n\n /**\n * Creates a set object of `values`.\n *\n * @private\n * @param {Array} values The values to add to the set.\n * @returns {Object} Returns the new set.\n */\n var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {\n return new Set(values);\n };\n\n /**\n * Creates a `_.toPairs` or `_.toPairsIn` function.\n *\n * @private\n * @param {Function} keysFunc The function to get the keys of a given object.\n * @returns {Function} Returns the new pairs function.\n */\n function createToPairs(keysFunc) {\n return function(object) {\n var tag = getTag(object);\n if (tag == mapTag) {\n return mapToArray(object);\n }\n if (tag == setTag) {\n return setToPairs(object);\n }\n return baseToPairs(object, keysFunc(object));\n };\n }\n\n /**\n * Creates a function that either curries or invokes `func` with optional\n * `this` binding and partially applied arguments.\n *\n * @private\n * @param {Function|string} func The function or method name to wrap.\n * @param {number} bitmask The bitmask flags.\n * 1 - `_.bind`\n * 2 - `_.bindKey`\n * 4 - `_.curry` or `_.curryRight` of a bound function\n * 8 - `_.curry`\n * 16 - `_.curryRight`\n * 32 - `_.partial`\n * 64 - `_.partialRight`\n * 128 - `_.rearg`\n * 256 - `_.ary`\n * 512 - `_.flip`\n * @param {*} [thisArg] The `this` binding of `func`.\n * @param {Array} [partials] The arguments to be partially applied.\n * @param {Array} [holders] The `partials` placeholder indexes.\n * @param {Array} [argPos] The argument positions of the new function.\n * @param {number} [ary] The arity cap of `func`.\n * @param {number} [arity] The arity of `func`.\n * @returns {Function} Returns the new wrapped function.\n */\n function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;\n if (!isBindKey && typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var length = partials ? partials.length : 0;\n if (!length) {\n bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);\n partials = holders = undefined;\n }\n ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n arity = arity === undefined ? arity : toInteger(arity);\n length -= holders ? holders.length : 0;\n\n if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {\n var partialsRight = partials,\n holdersRight = holders;\n\n partials = holders = undefined;\n }\n var data = isBindKey ? undefined : getData(func);\n\n var newData = [\n func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,\n argPos, ary, arity\n ];\n\n if (data) {\n mergeData(newData, data);\n }\n func = newData[0];\n bitmask = newData[1];\n thisArg = newData[2];\n partials = newData[3];\n holders = newData[4];\n arity = newData[9] = newData[9] === undefined\n ? (isBindKey ? 0 : func.length)\n : nativeMax(newData[9] - length, 0);\n\n if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {\n bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);\n }\n if (!bitmask || bitmask == WRAP_BIND_FLAG) {\n var result = createBind(func, bitmask, thisArg);\n } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {\n result = createCurry(func, bitmask, arity);\n } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {\n result = createPartial(func, bitmask, thisArg, partials);\n } else {\n result = createHybrid.apply(undefined, newData);\n }\n var setter = data ? baseSetData : setData;\n return setWrapToString(setter(result, newData), func, bitmask);\n }\n\n /**\n * Used by `_.defaults` to customize its `_.assignIn` use to assign properties\n * of source objects to the destination object for all destination properties\n * that resolve to `undefined`.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to assign.\n * @param {Object} object The parent object of `objValue`.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsAssignIn(objValue, srcValue, key, object) {\n if (objValue === undefined ||\n (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {\n return srcValue;\n }\n return objValue;\n }\n\n /**\n * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source\n * objects into destination objects that are passed thru.\n *\n * @private\n * @param {*} objValue The destination value.\n * @param {*} srcValue The source value.\n * @param {string} key The key of the property to merge.\n * @param {Object} object The parent object of `objValue`.\n * @param {Object} source The parent object of `srcValue`.\n * @param {Object} [stack] Tracks traversed source values and their merged\n * counterparts.\n * @returns {*} Returns the value to assign.\n */\n function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {\n if (isObject(objValue) && isObject(srcValue)) {\n // Recursively merge objects and arrays (susceptible to call stack limits).\n stack.set(srcValue, objValue);\n baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);\n stack['delete'](srcValue);\n }\n return objValue;\n }\n\n /**\n * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain\n * objects.\n *\n * @private\n * @param {*} value The value to inspect.\n * @param {string} key The key of the property to inspect.\n * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.\n */\n function customOmitClone(value) {\n return isPlainObject(value) ? undefined : value;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\n function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n arrLength = array.length,\n othLength = other.length;\n\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(array);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var index = -1,\n result = true,\n seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;\n\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, arrValue, index, other, array, stack)\n : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function(othValue, othIndex) {\n if (!cacheHas(seen, othIndex) &&\n (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n return seen.push(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(\n arrValue === othValue ||\n equalFunc(arrValue, othValue, bitmask, customizer, stack)\n )) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {\n switch (tag) {\n case dataViewTag:\n if ((object.byteLength != other.byteLength) ||\n (object.byteOffset != other.byteOffset)) {\n return false;\n }\n object = object.buffer;\n other = other.buffer;\n\n case arrayBufferTag:\n if ((object.byteLength != other.byteLength) ||\n !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n return false;\n }\n return true;\n\n case boolTag:\n case dateTag:\n case numberTag:\n // Coerce booleans to `1` or `0` and dates to milliseconds.\n // Invalid dates are coerced to `NaN`.\n return eq(+object, +other);\n\n case errorTag:\n return object.name == other.name && object.message == other.message;\n\n case regexpTag:\n case stringTag:\n // Coerce regexes to strings and treat strings, primitives and objects,\n // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n // for more details.\n return object == (other + '');\n\n case mapTag:\n var convert = mapToArray;\n\n case setTag:\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG;\n convert || (convert = setToArray);\n\n if (object.size != other.size && !isPartial) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked) {\n return stacked == other;\n }\n bitmask |= COMPARE_UNORDERED_FLAG;\n\n // Recursively compare objects (susceptible to call stack limits).\n stack.set(object, other);\n var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);\n stack['delete'](object);\n return result;\n\n case symbolTag:\n if (symbolValueOf) {\n return symbolValueOf.call(object) == symbolValueOf.call(other);\n }\n }\n return false;\n }\n\n /**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\n function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {\n var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n objProps = getAllKeys(object),\n objLength = objProps.length,\n othProps = getAllKeys(other),\n othLength = othProps.length;\n\n if (objLength != othLength && !isPartial) {\n return false;\n }\n var index = objLength;\n while (index--) {\n var key = objProps[index];\n if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n return false;\n }\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var result = true;\n stack.set(object, other);\n stack.set(other, object);\n\n var skipCtor = isPartial;\n while (++index < objLength) {\n key = objProps[index];\n var objValue = object[key],\n othValue = other[key];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, objValue, key, other, object, stack)\n : customizer(objValue, othValue, key, object, other, stack);\n }\n // Recursively compare objects (susceptible to call stack limits).\n if (!(compared === undefined\n ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))\n : compared\n )) {\n result = false;\n break;\n }\n skipCtor || (skipCtor = key == 'constructor');\n }\n if (result && !skipCtor) {\n var objCtor = object.constructor,\n othCtor = other.constructor;\n\n // Non `Object` object instances with different constructors are not equal.\n if (objCtor != othCtor &&\n ('constructor' in object && 'constructor' in other) &&\n !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n result = false;\n }\n }\n stack['delete'](object);\n stack['delete'](other);\n return result;\n }\n\n /**\n * A specialized version of `baseRest` which flattens the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @returns {Function} Returns the new function.\n */\n function flatRest(func) {\n return setToString(overRest(func, undefined, flatten), func + '');\n }\n\n /**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeys(object) {\n return baseGetAllKeys(object, keys, getSymbols);\n }\n\n /**\n * Creates an array of own and inherited enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\n function getAllKeysIn(object) {\n return baseGetAllKeys(object, keysIn, getSymbolsIn);\n }\n\n /**\n * Gets metadata for `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {*} Returns the metadata for `func`.\n */\n var getData = !metaMap ? noop : function(func) {\n return metaMap.get(func);\n };\n\n /**\n * Gets the name of `func`.\n *\n * @private\n * @param {Function} func The function to query.\n * @returns {string} Returns the function name.\n */\n function getFuncName(func) {\n var result = (func.name + ''),\n array = realNames[result],\n length = hasOwnProperty.call(realNames, result) ? array.length : 0;\n\n while (length--) {\n var data = array[length],\n otherFunc = data.func;\n if (otherFunc == null || otherFunc == func) {\n return data.name;\n }\n }\n return result;\n }\n\n /**\n * Gets the argument placeholder value for `func`.\n *\n * @private\n * @param {Function} func The function to inspect.\n * @returns {*} Returns the placeholder value.\n */\n function getHolder(func) {\n var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;\n return object.placeholder;\n }\n\n /**\n * Gets the appropriate \"iteratee\" function. If `_.iteratee` is customized,\n * this function returns the custom method, otherwise it returns `baseIteratee`.\n * If arguments are provided, the chosen function is invoked with them and\n * its result is returned.\n *\n * @private\n * @param {*} [value] The value to convert to an iteratee.\n * @param {number} [arity] The arity of the created iteratee.\n * @returns {Function} Returns the chosen function or its result.\n */\n function getIteratee() {\n var result = lodash.iteratee || iteratee;\n result = result === iteratee ? baseIteratee : result;\n return arguments.length ? result(arguments[0], arguments[1]) : result;\n }\n\n /**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\n function getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n }\n\n /**\n * Gets the property names, values, and compare flags of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the match data of `object`.\n */\n function getMatchData(object) {\n var result = keys(object),\n length = result.length;\n\n while (length--) {\n var key = result[length],\n value = object[key];\n\n result[length] = [key, value, isStrictComparable(value)];\n }\n return result;\n }\n\n /**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\n function getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n }\n\n /**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\n function getRawTag(value) {\n var isOwn = hasOwnProperty.call(value, symToStringTag),\n tag = value[symToStringTag];\n\n try {\n value[symToStringTag] = undefined;\n var unmasked = true;\n } catch (e) {}\n\n var result = nativeObjectToString.call(value);\n if (unmasked) {\n if (isOwn) {\n value[symToStringTag] = tag;\n } else {\n delete value[symToStringTag];\n }\n }\n return result;\n }\n\n /**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbols = !nativeGetSymbols ? stubArray : function(object) {\n if (object == null) {\n return [];\n }\n object = Object(object);\n return arrayFilter(nativeGetSymbols(object), function(symbol) {\n return propertyIsEnumerable.call(object, symbol);\n });\n };\n\n /**\n * Creates an array of the own and inherited enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\n var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {\n var result = [];\n while (object) {\n arrayPush(result, getSymbols(object));\n object = getPrototype(object);\n }\n return result;\n };\n\n /**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\n var getTag = baseGetTag;\n\n // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\n if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = baseGetTag(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : '';\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n }\n\n /**\n * Gets the view, applying any `transforms` to the `start` and `end` positions.\n *\n * @private\n * @param {number} start The start of the view.\n * @param {number} end The end of the view.\n * @param {Array} transforms The transformations to apply to the view.\n * @returns {Object} Returns an object containing the `start` and `end`\n * positions of the view.\n */\n function getView(start, end, transforms) {\n var index = -1,\n length = transforms.length;\n\n while (++index < length) {\n var data = transforms[index],\n size = data.size;\n\n switch (data.type) {\n case 'drop': start += size; break;\n case 'dropRight': end -= size; break;\n case 'take': end = nativeMin(end, start + size); break;\n case 'takeRight': start = nativeMax(start, end - size); break;\n }\n }\n return { 'start': start, 'end': end };\n }\n\n /**\n * Extracts wrapper details from the `source` body comment.\n *\n * @private\n * @param {string} source The source to inspect.\n * @returns {Array} Returns the wrapper details.\n */\n function getWrapDetails(source) {\n var match = source.match(reWrapDetails);\n return match ? match[1].split(reSplitDetails) : [];\n }\n\n /**\n * Checks if `path` exists on `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @param {Function} hasFunc The function to check properties.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n */\n function hasPath(object, path, hasFunc) {\n path = castPath(path, object);\n\n var index = -1,\n length = path.length,\n result = false;\n\n while (++index < length) {\n var key = toKey(path[index]);\n if (!(result = object != null && hasFunc(object, key))) {\n break;\n }\n object = object[key];\n }\n if (result || ++index != length) {\n return result;\n }\n length = object == null ? 0 : object.length;\n return !!length && isLength(length) && isIndex(key, length) &&\n (isArray(object) || isArguments(object));\n }\n\n /**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\n function initCloneArray(array) {\n var length = array.length,\n result = new array.constructor(length);\n\n // Add properties assigned by `RegExp#exec`.\n if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n result.index = array.index;\n result.input = array.input;\n }\n return result;\n }\n\n /**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneObject(object) {\n return (typeof object.constructor == 'function' && !isPrototype(object))\n ? baseCreate(getPrototype(object))\n : {};\n }\n\n /**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\n function initCloneByTag(object, tag, isDeep) {\n var Ctor = object.constructor;\n switch (tag) {\n case arrayBufferTag:\n return cloneArrayBuffer(object);\n\n case boolTag:\n case dateTag:\n return new Ctor(+object);\n\n case dataViewTag:\n return cloneDataView(object, isDeep);\n\n case float32Tag: case float64Tag:\n case int8Tag: case int16Tag: case int32Tag:\n case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n return cloneTypedArray(object, isDeep);\n\n case mapTag:\n return new Ctor;\n\n case numberTag:\n case stringTag:\n return new Ctor(object);\n\n case regexpTag:\n return cloneRegExp(object);\n\n case setTag:\n return new Ctor;\n\n case symbolTag:\n return cloneSymbol(object);\n }\n }\n\n /**\n * Inserts wrapper `details` in a comment at the top of the `source` body.\n *\n * @private\n * @param {string} source The source to modify.\n * @returns {Array} details The details to insert.\n * @returns {string} Returns the modified source.\n */\n function insertWrapDetails(source, details) {\n var length = details.length;\n if (!length) {\n return source;\n }\n var lastIndex = length - 1;\n details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];\n details = details.join(length > 2 ? ', ' : ' ');\n return source.replace(reWrapComment, '{\\n/* [wrapped with ' + details + '] */\\n');\n }\n\n /**\n * Checks if `value` is a flattenable `arguments` object or array.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n */\n function isFlattenable(value) {\n return isArray(value) || isArguments(value) ||\n !!(spreadableSymbol && value && value[spreadableSymbol]);\n }\n\n /**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\n function isIndex(value, length) {\n var type = typeof value;\n length = length == null ? MAX_SAFE_INTEGER : length;\n\n return !!length &&\n (type == 'number' ||\n (type != 'symbol' && reIsUint.test(value))) &&\n (value > -1 && value % 1 == 0 && value < length);\n }\n\n /**\n * Checks if the given arguments are from an iteratee call.\n *\n * @private\n * @param {*} value The potential iteratee value argument.\n * @param {*} index The potential iteratee index or key argument.\n * @param {*} object The potential iteratee object argument.\n * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n * else `false`.\n */\n function isIterateeCall(value, index, object) {\n if (!isObject(object)) {\n return false;\n }\n var type = typeof index;\n if (type == 'number'\n ? (isArrayLike(object) && isIndex(index, object.length))\n : (type == 'string' && index in object)\n ) {\n return eq(object[index], value);\n }\n return false;\n }\n\n /**\n * Checks if `value` is a property name and not a property path.\n *\n * @private\n * @param {*} value The value to check.\n * @param {Object} [object] The object to query keys on.\n * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n */\n function isKey(value, object) {\n if (isArray(value)) {\n return false;\n }\n var type = typeof value;\n if (type == 'number' || type == 'symbol' || type == 'boolean' ||\n value == null || isSymbol(value)) {\n return true;\n }\n return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||\n (object != null && value in Object(object));\n }\n\n /**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\n function isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n }\n\n /**\n * Checks if `func` has a lazy counterpart.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` has a lazy counterpart,\n * else `false`.\n */\n function isLaziable(func) {\n var funcName = getFuncName(func),\n other = lodash[funcName];\n\n if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {\n return false;\n }\n if (func === other) {\n return true;\n }\n var data = getData(other);\n return !!data && func === data[0];\n }\n\n /**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\n function isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n }\n\n /**\n * Checks if `func` is capable of being masked.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `func` is maskable, else `false`.\n */\n var isMaskable = coreJsData ? isFunction : stubFalse;\n\n /**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\n function isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n }\n\n /**\n * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` if suitable for strict\n * equality comparisons, else `false`.\n */\n function isStrictComparable(value) {\n return value === value && !isObject(value);\n }\n\n /**\n * A specialized version of `matchesProperty` for source values suitable\n * for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\n function matchesStrictComparable(key, srcValue) {\n return function(object) {\n if (object == null) {\n return false;\n }\n return object[key] === srcValue &&\n (srcValue !== undefined || (key in Object(object)));\n };\n }\n\n /**\n * A specialized version of `_.memoize` which clears the memoized function's\n * cache when it exceeds `MAX_MEMOIZE_SIZE`.\n *\n * @private\n * @param {Function} func The function to have its output memoized.\n * @returns {Function} Returns the new memoized function.\n */\n function memoizeCapped(func) {\n var result = memoize(func, function(key) {\n if (cache.size === MAX_MEMOIZE_SIZE) {\n cache.clear();\n }\n return key;\n });\n\n var cache = result.cache;\n return result;\n }\n\n /**\n * Merges the function metadata of `source` into `data`.\n *\n * Merging metadata reduces the number of wrappers used to invoke a function.\n * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`\n * may be applied regardless of execution order. Methods like `_.ary` and\n * `_.rearg` modify function arguments, making the order in which they are\n * executed important, preventing the merging of metadata. However, we make\n * an exception for a safe combined case where curried functions have `_.ary`\n * and or `_.rearg` applied.\n *\n * @private\n * @param {Array} data The destination metadata.\n * @param {Array} source The source metadata.\n * @returns {Array} Returns `data`.\n */\n function mergeData(data, source) {\n var bitmask = data[1],\n srcBitmask = source[1],\n newBitmask = bitmask | srcBitmask,\n isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);\n\n var isCombo =\n ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||\n ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||\n ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));\n\n // Exit early if metadata can't be merged.\n if (!(isCommon || isCombo)) {\n return data;\n }\n // Use source `thisArg` if available.\n if (srcBitmask & WRAP_BIND_FLAG) {\n data[2] = source[2];\n // Set when currying a bound function.\n newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;\n }\n // Compose partial arguments.\n var value = source[3];\n if (value) {\n var partials = data[3];\n data[3] = partials ? composeArgs(partials, value, source[4]) : value;\n data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];\n }\n // Compose partial right arguments.\n value = source[5];\n if (value) {\n partials = data[5];\n data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;\n data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];\n }\n // Use source `argPos` if available.\n value = source[7];\n if (value) {\n data[7] = value;\n }\n // Use source `ary` if it's smaller.\n if (srcBitmask & WRAP_ARY_FLAG) {\n data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);\n }\n // Use source `arity` if one is not provided.\n if (data[9] == null) {\n data[9] = source[9];\n }\n // Use source `func` and merge bitmasks.\n data[0] = source[0];\n data[1] = newBitmask;\n\n return data;\n }\n\n /**\n * This function is like\n * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * except that it includes inherited enumerable properties.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\n function nativeKeysIn(object) {\n var result = [];\n if (object != null) {\n for (var key in Object(object)) {\n result.push(key);\n }\n }\n return result;\n }\n\n /**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\n function objectToString(value) {\n return nativeObjectToString.call(value);\n }\n\n /**\n * A specialized version of `baseRest` which transforms the rest array.\n *\n * @private\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @param {Function} transform The rest array transform.\n * @returns {Function} Returns the new function.\n */\n function overRest(func, start, transform) {\n start = nativeMax(start === undefined ? (func.length - 1) : start, 0);\n return function() {\n var args = arguments,\n index = -1,\n length = nativeMax(args.length - start, 0),\n array = Array(length);\n\n while (++index < length) {\n array[index] = args[start + index];\n }\n index = -1;\n var otherArgs = Array(start + 1);\n while (++index < start) {\n otherArgs[index] = args[index];\n }\n otherArgs[start] = transform(array);\n return apply(func, this, otherArgs);\n };\n }\n\n /**\n * Gets the parent value at `path` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array} path The path to get the parent value of.\n * @returns {*} Returns the parent value.\n */\n function parent(object, path) {\n return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));\n }\n\n /**\n * Reorder `array` according to the specified indexes where the element at\n * the first index is assigned as the first element, the element at\n * the second index is assigned as the second element, and so on.\n *\n * @private\n * @param {Array} array The array to reorder.\n * @param {Array} indexes The arranged array indexes.\n * @returns {Array} Returns `array`.\n */\n function reorder(array, indexes) {\n var arrLength = array.length,\n length = nativeMin(indexes.length, arrLength),\n oldArray = copyArray(array);\n\n while (length--) {\n var index = indexes[length];\n array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;\n }\n return array;\n }\n\n /**\n * Gets the value at `key`, unless `key` is \"__proto__\".\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\n function safeGet(object, key) {\n if (key == '__proto__') {\n return;\n }\n\n return object[key];\n }\n\n /**\n * Sets metadata for `func`.\n *\n * **Note:** If this function becomes hot, i.e. is invoked a lot in a short\n * period of time, it will trip its breaker and transition to an identity\n * function to avoid garbage collection pauses in V8. See\n * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)\n * for more details.\n *\n * @private\n * @param {Function} func The function to associate metadata with.\n * @param {*} data The metadata.\n * @returns {Function} Returns `func`.\n */\n var setData = shortOut(baseSetData);\n\n /**\n * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).\n *\n * @private\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @returns {number|Object} Returns the timer id or timeout object.\n */\n var setTimeout = ctxSetTimeout || function(func, wait) {\n return root.setTimeout(func, wait);\n };\n\n /**\n * Sets the `toString` method of `func` to return `string`.\n *\n * @private\n * @param {Function} func The function to modify.\n * @param {Function} string The `toString` result.\n * @returns {Function} Returns `func`.\n */\n var setToString = shortOut(baseSetToString);\n\n /**\n * Sets the `toString` method of `wrapper` to mimic the source of `reference`\n * with wrapper details in a comment at the top of the source body.\n *\n * @private\n * @param {Function} wrapper The function to modify.\n * @param {Function} reference The reference function.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Function} Returns `wrapper`.\n */\n function setWrapToString(wrapper, reference, bitmask) {\n var source = (reference + '');\n return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));\n }\n\n /**\n * Creates a function that'll short out and invoke `identity` instead\n * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`\n * milliseconds.\n *\n * @private\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new shortable function.\n */\n function shortOut(func) {\n var count = 0,\n lastCalled = 0;\n\n return function() {\n var stamp = nativeNow(),\n remaining = HOT_SPAN - (stamp - lastCalled);\n\n lastCalled = stamp;\n if (remaining > 0) {\n if (++count >= HOT_COUNT) {\n return arguments[0];\n }\n } else {\n count = 0;\n }\n return func.apply(undefined, arguments);\n };\n }\n\n /**\n * A specialized version of `_.shuffle` which mutates and sets the size of `array`.\n *\n * @private\n * @param {Array} array The array to shuffle.\n * @param {number} [size=array.length] The size of `array`.\n * @returns {Array} Returns `array`.\n */\n function shuffleSelf(array, size) {\n var index = -1,\n length = array.length,\n lastIndex = length - 1;\n\n size = size === undefined ? length : size;\n while (++index < size) {\n var rand = baseRandom(index, lastIndex),\n value = array[rand];\n\n array[rand] = array[index];\n array[index] = value;\n }\n array.length = size;\n return array;\n }\n\n /**\n * Converts `string` to a property path array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the property path array.\n */\n var stringToPath = memoizeCapped(function(string) {\n var result = [];\n if (string.charCodeAt(0) === 46 /* . */) {\n result.push('');\n }\n string.replace(rePropName, function(match, number, quote, subString) {\n result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));\n });\n return result;\n });\n\n /**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\n function toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n }\n\n /**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\n function toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n }\n\n /**\n * Updates wrapper `details` based on `bitmask` flags.\n *\n * @private\n * @returns {Array} details The details to modify.\n * @param {number} bitmask The bitmask flags. See `createWrap` for more details.\n * @returns {Array} Returns `details`.\n */\n function updateWrapDetails(details, bitmask) {\n arrayEach(wrapFlags, function(pair) {\n var value = '_.' + pair[0];\n if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {\n details.push(value);\n }\n });\n return details.sort();\n }\n\n /**\n * Creates a clone of `wrapper`.\n *\n * @private\n * @param {Object} wrapper The wrapper to clone.\n * @returns {Object} Returns the cloned wrapper.\n */\n function wrapperClone(wrapper) {\n if (wrapper instanceof LazyWrapper) {\n return wrapper.clone();\n }\n var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);\n result.__actions__ = copyArray(wrapper.__actions__);\n result.__index__ = wrapper.__index__;\n result.__values__ = wrapper.__values__;\n return result;\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an array of elements split into groups the length of `size`.\n * If `array` can't be split evenly, the final chunk will be the remaining\n * elements.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to process.\n * @param {number} [size=1] The length of each chunk\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the new array of chunks.\n * @example\n *\n * _.chunk(['a', 'b', 'c', 'd'], 2);\n * // => [['a', 'b'], ['c', 'd']]\n *\n * _.chunk(['a', 'b', 'c', 'd'], 3);\n * // => [['a', 'b', 'c'], ['d']]\n */\n function chunk(array, size, guard) {\n if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {\n size = 1;\n } else {\n size = nativeMax(toInteger(size), 0);\n }\n var length = array == null ? 0 : array.length;\n if (!length || size < 1) {\n return [];\n }\n var index = 0,\n resIndex = 0,\n result = Array(nativeCeil(length / size));\n\n while (index < length) {\n result[resIndex++] = baseSlice(array, index, (index += size));\n }\n return result;\n }\n\n /**\n * Creates an array with all falsey values removed. The values `false`, `null`,\n * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to compact.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.compact([0, 1, false, 2, '', 3]);\n * // => [1, 2, 3]\n */\n function compact(array) {\n var index = -1,\n length = array == null ? 0 : array.length,\n resIndex = 0,\n result = [];\n\n while (++index < length) {\n var value = array[index];\n if (value) {\n result[resIndex++] = value;\n }\n }\n return result;\n }\n\n /**\n * Creates a new array concatenating `array` with any additional arrays\n * and/or values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to concatenate.\n * @param {...*} [values] The values to concatenate.\n * @returns {Array} Returns the new concatenated array.\n * @example\n *\n * var array = [1];\n * var other = _.concat(array, 2, [3], [[4]]);\n *\n * console.log(other);\n * // => [1, 2, 3, [4]]\n *\n * console.log(array);\n * // => [1]\n */\n function concat() {\n var length = arguments.length;\n if (!length) {\n return [];\n }\n var args = Array(length - 1),\n array = arguments[0],\n index = length;\n\n while (index--) {\n args[index - 1] = arguments[index];\n }\n return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));\n }\n\n /**\n * Creates an array of `array` values not included in the other given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * **Note:** Unlike `_.pullAll`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.without, _.xor\n * @example\n *\n * _.difference([2, 1], [2, 3]);\n * // => [1]\n */\n var difference = baseRest(function(array, values) {\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))\n : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `iteratee` which\n * is invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * **Note:** Unlike `_.pullAllBy`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var differenceBy = baseRest(function(array, values) {\n var iteratee = last(values);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))\n : [];\n });\n\n /**\n * This method is like `_.difference` except that it accepts `comparator`\n * which is invoked to compare elements of `array` to `values`. The order and\n * references of result values are determined by the first array. The comparator\n * is invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.pullAllWith`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...Array} [values] The values to exclude.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n *\n * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }]\n */\n var differenceWith = baseRest(function(array, values) {\n var comparator = last(values);\n if (isArrayLikeObject(comparator)) {\n comparator = undefined;\n }\n return isArrayLikeObject(array)\n ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)\n : [];\n });\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.drop([1, 2, 3]);\n * // => [2, 3]\n *\n * _.drop([1, 2, 3], 2);\n * // => [3]\n *\n * _.drop([1, 2, 3], 5);\n * // => []\n *\n * _.drop([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function drop(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with `n` elements dropped from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to drop.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.dropRight([1, 2, 3]);\n * // => [1, 2]\n *\n * _.dropRight([1, 2, 3], 2);\n * // => [1]\n *\n * _.dropRight([1, 2, 3], 5);\n * // => []\n *\n * _.dropRight([1, 2, 3], 0);\n * // => [1, 2, 3]\n */\n function dropRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the end.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.dropRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropRightWhile(users, ['active', false]);\n * // => objects for ['barney']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropRightWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropRightWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), true, true)\n : [];\n }\n\n /**\n * Creates a slice of `array` excluding elements dropped from the beginning.\n * Elements are dropped until `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.dropWhile(users, function(o) { return !o.active; });\n * // => objects for ['pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.dropWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.dropWhile(users, ['active', false]);\n * // => objects for ['pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.dropWhile(users, 'active');\n * // => objects for ['barney', 'fred', 'pebbles']\n */\n function dropWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), true)\n : [];\n }\n\n /**\n * Fills elements of `array` with `value` from `start` up to, but not\n * including, `end`.\n *\n * **Note:** This method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Array\n * @param {Array} array The array to fill.\n * @param {*} value The value to fill `array` with.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.fill(array, 'a');\n * console.log(array);\n * // => ['a', 'a', 'a']\n *\n * _.fill(Array(3), 2);\n * // => [2, 2, 2]\n *\n * _.fill([4, 6, 8, 10], '*', 1, 3);\n * // => [4, '*', '*', 10]\n */\n function fill(array, value, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {\n start = 0;\n end = length;\n }\n return baseFill(array, value, start, end);\n }\n\n /**\n * This method is like `_.find` except that it returns the index of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.findIndex(users, function(o) { return o.user == 'barney'; });\n * // => 0\n *\n * // The `_.matches` iteratee shorthand.\n * _.findIndex(users, { 'user': 'fred', 'active': false });\n * // => 1\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findIndex(users, ['active', false]);\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.findIndex(users, 'active');\n * // => 2\n */\n function findIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index);\n }\n\n /**\n * This method is like `_.findIndex` except that it iterates over elements\n * of `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the found element, else `-1`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });\n * // => 2\n *\n * // The `_.matches` iteratee shorthand.\n * _.findLastIndex(users, { 'user': 'barney', 'active': true });\n * // => 0\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findLastIndex(users, ['active', false]);\n * // => 2\n *\n * // The `_.property` iteratee shorthand.\n * _.findLastIndex(users, 'active');\n * // => 0\n */\n function findLastIndex(array, predicate, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length - 1;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = fromIndex < 0\n ? nativeMax(length + index, 0)\n : nativeMin(index, length - 1);\n }\n return baseFindIndex(array, getIteratee(predicate, 3), index, true);\n }\n\n /**\n * Flattens `array` a single level deep.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flatten([1, [2, [3, [4]], 5]]);\n * // => [1, 2, [3, [4]], 5]\n */\n function flatten(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, 1) : [];\n }\n\n /**\n * Recursively flattens `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * _.flattenDeep([1, [2, [3, [4]], 5]]);\n * // => [1, 2, 3, 4, 5]\n */\n function flattenDeep(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseFlatten(array, INFINITY) : [];\n }\n\n /**\n * Recursively flatten `array` up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.4.0\n * @category Array\n * @param {Array} array The array to flatten.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * var array = [1, [2, [3, [4]], 5]];\n *\n * _.flattenDepth(array, 1);\n * // => [1, 2, [3, [4]], 5]\n *\n * _.flattenDepth(array, 2);\n * // => [1, 2, 3, [4], 5]\n */\n function flattenDepth(array, depth) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(array, depth);\n }\n\n /**\n * The inverse of `_.toPairs`; this method returns an object composed\n * from key-value `pairs`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} pairs The key-value pairs.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.fromPairs([['a', 1], ['b', 2]]);\n * // => { 'a': 1, 'b': 2 }\n */\n function fromPairs(pairs) {\n var index = -1,\n length = pairs == null ? 0 : pairs.length,\n result = {};\n\n while (++index < length) {\n var pair = pairs[index];\n result[pair[0]] = pair[1];\n }\n return result;\n }\n\n /**\n * Gets the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias first\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the first element of `array`.\n * @example\n *\n * _.head([1, 2, 3]);\n * // => 1\n *\n * _.head([]);\n * // => undefined\n */\n function head(array) {\n return (array && array.length) ? array[0] : undefined;\n }\n\n /**\n * Gets the index at which the first occurrence of `value` is found in `array`\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. If `fromIndex` is negative, it's used as the\n * offset from the end of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.indexOf([1, 2, 1, 2], 2);\n * // => 1\n *\n * // Search from the `fromIndex`.\n * _.indexOf([1, 2, 1, 2], 2, 2);\n * // => 3\n */\n function indexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = fromIndex == null ? 0 : toInteger(fromIndex);\n if (index < 0) {\n index = nativeMax(length + index, 0);\n }\n return baseIndexOf(array, value, index);\n }\n\n /**\n * Gets all but the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.initial([1, 2, 3]);\n * // => [1, 2]\n */\n function initial(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 0, -1) : [];\n }\n\n /**\n * Creates an array of unique values that are included in all given arrays\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons. The order and references of result values are\n * determined by the first array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersection([2, 1], [2, 3]);\n * // => [2]\n */\n var intersection = baseRest(function(arrays) {\n var mapped = arrayMap(arrays, castArrayLikeObject);\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped)\n : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `iteratee`\n * which is invoked for each element of each `arrays` to generate the criterion\n * by which they're compared. The order and references of result values are\n * determined by the first array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [2.1]\n *\n * // The `_.property` iteratee shorthand.\n * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }]\n */\n var intersectionBy = baseRest(function(arrays) {\n var iteratee = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n\n if (iteratee === last(mapped)) {\n iteratee = undefined;\n } else {\n mapped.pop();\n }\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped, getIteratee(iteratee, 2))\n : [];\n });\n\n /**\n * This method is like `_.intersection` except that it accepts `comparator`\n * which is invoked to compare elements of `arrays`. The order and references\n * of result values are determined by the first array. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of intersecting values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.intersectionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }]\n */\n var intersectionWith = baseRest(function(arrays) {\n var comparator = last(arrays),\n mapped = arrayMap(arrays, castArrayLikeObject);\n\n comparator = typeof comparator == 'function' ? comparator : undefined;\n if (comparator) {\n mapped.pop();\n }\n return (mapped.length && mapped[0] === arrays[0])\n ? baseIntersection(mapped, undefined, comparator)\n : [];\n });\n\n /**\n * Converts all elements in `array` into a string separated by `separator`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to convert.\n * @param {string} [separator=','] The element separator.\n * @returns {string} Returns the joined string.\n * @example\n *\n * _.join(['a', 'b', 'c'], '~');\n * // => 'a~b~c'\n */\n function join(array, separator) {\n return array == null ? '' : nativeJoin.call(array, separator);\n }\n\n /**\n * Gets the last element of `array`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {*} Returns the last element of `array`.\n * @example\n *\n * _.last([1, 2, 3]);\n * // => 3\n */\n function last(array) {\n var length = array == null ? 0 : array.length;\n return length ? array[length - 1] : undefined;\n }\n\n /**\n * This method is like `_.indexOf` except that it iterates over elements of\n * `array` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=array.length-1] The index to search from.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.lastIndexOf([1, 2, 1, 2], 2);\n * // => 3\n *\n * // Search from the `fromIndex`.\n * _.lastIndexOf([1, 2, 1, 2], 2, 2);\n * // => 1\n */\n function lastIndexOf(array, value, fromIndex) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return -1;\n }\n var index = length;\n if (fromIndex !== undefined) {\n index = toInteger(fromIndex);\n index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);\n }\n return value === value\n ? strictLastIndexOf(array, value, index)\n : baseFindIndex(array, baseIsNaN, index, true);\n }\n\n /**\n * Gets the element at index `n` of `array`. If `n` is negative, the nth\n * element from the end is returned.\n *\n * @static\n * @memberOf _\n * @since 4.11.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=0] The index of the element to return.\n * @returns {*} Returns the nth element of `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n *\n * _.nth(array, 1);\n * // => 'b'\n *\n * _.nth(array, -2);\n * // => 'c';\n */\n function nth(array, n) {\n return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;\n }\n\n /**\n * Removes all given values from `array` using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`\n * to remove elements from an array by predicate.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...*} [values] The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pull(array, 'a', 'c');\n * console.log(array);\n * // => ['b', 'b']\n */\n var pull = baseRest(pullAll);\n\n /**\n * This method is like `_.pull` except that it accepts an array of values to remove.\n *\n * **Note:** Unlike `_.difference`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = ['a', 'b', 'c', 'a', 'b', 'c'];\n *\n * _.pullAll(array, ['a', 'c']);\n * console.log(array);\n * // => ['b', 'b']\n */\n function pullAll(array, values) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values)\n : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `iteratee` which is\n * invoked for each element of `array` and `values` to generate the criterion\n * by which they're compared. The iteratee is invoked with one argument: (value).\n *\n * **Note:** Unlike `_.differenceBy`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];\n *\n * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');\n * console.log(array);\n * // => [{ 'x': 2 }]\n */\n function pullAllBy(array, values, iteratee) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values, getIteratee(iteratee, 2))\n : array;\n }\n\n /**\n * This method is like `_.pullAll` except that it accepts `comparator` which\n * is invoked to compare elements of `array` to `values`. The comparator is\n * invoked with two arguments: (arrVal, othVal).\n *\n * **Note:** Unlike `_.differenceWith`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Array} values The values to remove.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];\n *\n * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);\n * console.log(array);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]\n */\n function pullAllWith(array, values, comparator) {\n return (array && array.length && values && values.length)\n ? basePullAll(array, values, undefined, comparator)\n : array;\n }\n\n /**\n * Removes elements from `array` corresponding to `indexes` and returns an\n * array of removed elements.\n *\n * **Note:** Unlike `_.at`, this method mutates `array`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {...(number|number[])} [indexes] The indexes of elements to remove.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = ['a', 'b', 'c', 'd'];\n * var pulled = _.pullAt(array, [1, 3]);\n *\n * console.log(array);\n * // => ['a', 'c']\n *\n * console.log(pulled);\n * // => ['b', 'd']\n */\n var pullAt = flatRest(function(array, indexes) {\n var length = array == null ? 0 : array.length,\n result = baseAt(array, indexes);\n\n basePullAt(array, arrayMap(indexes, function(index) {\n return isIndex(index, length) ? +index : index;\n }).sort(compareAscending));\n\n return result;\n });\n\n /**\n * Removes all elements from `array` that `predicate` returns truthy for\n * and returns an array of the removed elements. The predicate is invoked\n * with three arguments: (value, index, array).\n *\n * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`\n * to pull elements from an array by value.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new array of removed elements.\n * @example\n *\n * var array = [1, 2, 3, 4];\n * var evens = _.remove(array, function(n) {\n * return n % 2 == 0;\n * });\n *\n * console.log(array);\n * // => [1, 3]\n *\n * console.log(evens);\n * // => [2, 4]\n */\n function remove(array, predicate) {\n var result = [];\n if (!(array && array.length)) {\n return result;\n }\n var index = -1,\n indexes = [],\n length = array.length;\n\n predicate = getIteratee(predicate, 3);\n while (++index < length) {\n var value = array[index];\n if (predicate(value, index, array)) {\n result.push(value);\n indexes.push(index);\n }\n }\n basePullAt(array, indexes);\n return result;\n }\n\n /**\n * Reverses `array` so that the first element becomes the last, the second\n * element becomes the second to last, and so on.\n *\n * **Note:** This method mutates `array` and is based on\n * [`Array#reverse`](https://mdn.io/Array/reverse).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to modify.\n * @returns {Array} Returns `array`.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _.reverse(array);\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function reverse(array) {\n return array == null ? array : nativeReverse.call(array);\n }\n\n /**\n * Creates a slice of `array` from `start` up to, but not including, `end`.\n *\n * **Note:** This method is used instead of\n * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are\n * returned.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to slice.\n * @param {number} [start=0] The start position.\n * @param {number} [end=array.length] The end position.\n * @returns {Array} Returns the slice of `array`.\n */\n function slice(array, start, end) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {\n start = 0;\n end = length;\n }\n else {\n start = start == null ? 0 : toInteger(start);\n end = end === undefined ? length : toInteger(end);\n }\n return baseSlice(array, start, end);\n }\n\n /**\n * Uses a binary search to determine the lowest index at which `value`\n * should be inserted into `array` in order to maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedIndex([30, 50], 40);\n * // => 1\n */\n function sortedIndex(array, value) {\n return baseSortedIndex(array, value);\n }\n\n /**\n * This method is like `_.sortedIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 0\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedIndexBy(objects, { 'x': 4 }, 'x');\n * // => 0\n */\n function sortedIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));\n }\n\n /**\n * This method is like `_.indexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedIndexOf([4, 5, 5, 5, 6], 5);\n * // => 1\n */\n function sortedIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value);\n if (index < length && eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.sortedIndex` except that it returns the highest\n * index at which `value` should be inserted into `array` in order to\n * maintain its sort order.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * _.sortedLastIndex([4, 5, 5, 5, 6], 5);\n * // => 4\n */\n function sortedLastIndex(array, value) {\n return baseSortedIndex(array, value, true);\n }\n\n /**\n * This method is like `_.sortedLastIndex` except that it accepts `iteratee`\n * which is invoked for `value` and each element of `array` to compute their\n * sort ranking. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The sorted array to inspect.\n * @param {*} value The value to evaluate.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {number} Returns the index at which `value` should be inserted\n * into `array`.\n * @example\n *\n * var objects = [{ 'x': 4 }, { 'x': 5 }];\n *\n * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });\n * // => 1\n *\n * // The `_.property` iteratee shorthand.\n * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');\n * // => 1\n */\n function sortedLastIndexBy(array, value, iteratee) {\n return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);\n }\n\n /**\n * This method is like `_.lastIndexOf` except that it performs a binary\n * search on a sorted `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {*} value The value to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n * @example\n *\n * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);\n * // => 3\n */\n function sortedLastIndexOf(array, value) {\n var length = array == null ? 0 : array.length;\n if (length) {\n var index = baseSortedIndex(array, value, true) - 1;\n if (eq(array[index], value)) {\n return index;\n }\n }\n return -1;\n }\n\n /**\n * This method is like `_.uniq` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniq([1, 1, 2]);\n * // => [1, 2]\n */\n function sortedUniq(array) {\n return (array && array.length)\n ? baseSortedUniq(array)\n : [];\n }\n\n /**\n * This method is like `_.uniqBy` except that it's designed and optimized\n * for sorted arrays.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);\n * // => [1.1, 2.3]\n */\n function sortedUniqBy(array, iteratee) {\n return (array && array.length)\n ? baseSortedUniq(array, getIteratee(iteratee, 2))\n : [];\n }\n\n /**\n * Gets all but the first element of `array`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.tail([1, 2, 3]);\n * // => [2, 3]\n */\n function tail(array) {\n var length = array == null ? 0 : array.length;\n return length ? baseSlice(array, 1, length) : [];\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the beginning.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.take([1, 2, 3]);\n * // => [1]\n *\n * _.take([1, 2, 3], 2);\n * // => [1, 2]\n *\n * _.take([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.take([1, 2, 3], 0);\n * // => []\n */\n function take(array, n, guard) {\n if (!(array && array.length)) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n return baseSlice(array, 0, n < 0 ? 0 : n);\n }\n\n /**\n * Creates a slice of `array` with `n` elements taken from the end.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {number} [n=1] The number of elements to take.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * _.takeRight([1, 2, 3]);\n * // => [3]\n *\n * _.takeRight([1, 2, 3], 2);\n * // => [2, 3]\n *\n * _.takeRight([1, 2, 3], 5);\n * // => [1, 2, 3]\n *\n * _.takeRight([1, 2, 3], 0);\n * // => []\n */\n function takeRight(array, n, guard) {\n var length = array == null ? 0 : array.length;\n if (!length) {\n return [];\n }\n n = (guard || n === undefined) ? 1 : toInteger(n);\n n = length - n;\n return baseSlice(array, n < 0 ? 0 : n, length);\n }\n\n /**\n * Creates a slice of `array` with elements taken from the end. Elements are\n * taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': false }\n * ];\n *\n * _.takeRightWhile(users, function(o) { return !o.active; });\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });\n * // => objects for ['pebbles']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeRightWhile(users, ['active', false]);\n * // => objects for ['fred', 'pebbles']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeRightWhile(users, 'active');\n * // => []\n */\n function takeRightWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3), false, true)\n : [];\n }\n\n /**\n * Creates a slice of `array` with elements taken from the beginning. Elements\n * are taken until `predicate` returns falsey. The predicate is invoked with\n * three arguments: (value, index, array).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Array\n * @param {Array} array The array to query.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the slice of `array`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'active': false },\n * { 'user': 'fred', 'active': false },\n * { 'user': 'pebbles', 'active': true }\n * ];\n *\n * _.takeWhile(users, function(o) { return !o.active; });\n * // => objects for ['barney', 'fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.takeWhile(users, { 'user': 'barney', 'active': false });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.takeWhile(users, ['active', false]);\n * // => objects for ['barney', 'fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.takeWhile(users, 'active');\n * // => []\n */\n function takeWhile(array, predicate) {\n return (array && array.length)\n ? baseWhile(array, getIteratee(predicate, 3))\n : [];\n }\n\n /**\n * Creates an array of unique values, in order, from all given arrays using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.union([2], [1, 2]);\n * // => [2, 1]\n */\n var union = baseRest(function(arrays) {\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));\n });\n\n /**\n * This method is like `_.union` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which uniqueness is computed. Result values are chosen from the first\n * array in which the value occurs. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * _.unionBy([2.1], [1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n var unionBy = baseRest(function(arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.union` except that it accepts `comparator` which\n * is invoked to compare elements of `arrays`. Result values are chosen from\n * the first array in which the value occurs. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of combined values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.unionWith(objects, others, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var unionWith = baseRest(function(arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);\n });\n\n /**\n * Creates a duplicate-free version of an array, using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons, in which only the first occurrence of each element\n * is kept. The order of result values is determined by the order they occur\n * in the array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniq([2, 1, 2]);\n * // => [2, 1]\n */\n function uniq(array) {\n return (array && array.length) ? baseUniq(array) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * uniqueness is computed. The order of result values is determined by the\n * order they occur in the array. The iteratee is invoked with one argument:\n * (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n * // => [2.1, 1.2]\n *\n * // The `_.property` iteratee shorthand.\n * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 1 }, { 'x': 2 }]\n */\n function uniqBy(array, iteratee) {\n return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : [];\n }\n\n /**\n * This method is like `_.uniq` except that it accepts `comparator` which\n * is invoked to compare elements of `array`. The order of result values is\n * determined by the order they occur in the array.The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new duplicate free array.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.uniqWith(objects, _.isEqual);\n * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]\n */\n function uniqWith(array, comparator) {\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return (array && array.length) ? baseUniq(array, undefined, comparator) : [];\n }\n\n /**\n * This method is like `_.zip` except that it accepts an array of grouped\n * elements and creates an array regrouping the elements to their pre-zip\n * configuration.\n *\n * @static\n * @memberOf _\n * @since 1.2.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n *\n * _.unzip(zipped);\n * // => [['a', 'b'], [1, 2], [true, false]]\n */\n function unzip(array) {\n if (!(array && array.length)) {\n return [];\n }\n var length = 0;\n array = arrayFilter(array, function(group) {\n if (isArrayLikeObject(group)) {\n length = nativeMax(group.length, length);\n return true;\n }\n });\n return baseTimes(length, function(index) {\n return arrayMap(array, baseProperty(index));\n });\n }\n\n /**\n * This method is like `_.unzip` except that it accepts `iteratee` to specify\n * how regrouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {Array} array The array of grouped elements to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * regrouped values.\n * @returns {Array} Returns the new array of regrouped elements.\n * @example\n *\n * var zipped = _.zip([1, 2], [10, 20], [100, 200]);\n * // => [[1, 10, 100], [2, 20, 200]]\n *\n * _.unzipWith(zipped, _.add);\n * // => [3, 30, 300]\n */\n function unzipWith(array, iteratee) {\n if (!(array && array.length)) {\n return [];\n }\n var result = unzip(array);\n if (iteratee == null) {\n return result;\n }\n return arrayMap(result, function(group) {\n return apply(iteratee, undefined, group);\n });\n }\n\n /**\n * Creates an array excluding all given values using\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * **Note:** Unlike `_.pull`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {Array} array The array to inspect.\n * @param {...*} [values] The values to exclude.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.xor\n * @example\n *\n * _.without([2, 1, 2, 3], 1, 2);\n * // => [3]\n */\n var without = baseRest(function(array, values) {\n return isArrayLikeObject(array)\n ? baseDifference(array, values)\n : [];\n });\n\n /**\n * Creates an array of unique values that is the\n * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)\n * of the given arrays. The order of result values is determined by the order\n * they occur in the arrays.\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @returns {Array} Returns the new array of filtered values.\n * @see _.difference, _.without\n * @example\n *\n * _.xor([2, 1], [2, 3]);\n * // => [1, 3]\n */\n var xor = baseRest(function(arrays) {\n return baseXor(arrayFilter(arrays, isArrayLikeObject));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `iteratee` which is\n * invoked for each element of each `arrays` to generate the criterion by\n * which by which they're compared. The order of result values is determined\n * by the order they occur in the arrays. The iteratee is invoked with one\n * argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);\n * // => [1.2, 3.4]\n *\n * // The `_.property` iteratee shorthand.\n * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n * // => [{ 'x': 2 }]\n */\n var xorBy = baseRest(function(arrays) {\n var iteratee = last(arrays);\n if (isArrayLikeObject(iteratee)) {\n iteratee = undefined;\n }\n return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));\n });\n\n /**\n * This method is like `_.xor` except that it accepts `comparator` which is\n * invoked to compare elements of `arrays`. The order of result values is\n * determined by the order they occur in the arrays. The comparator is invoked\n * with two arguments: (arrVal, othVal).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Array\n * @param {...Array} [arrays] The arrays to inspect.\n * @param {Function} [comparator] The comparator invoked per element.\n * @returns {Array} Returns the new array of filtered values.\n * @example\n *\n * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n *\n * _.xorWith(objects, others, _.isEqual);\n * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n */\n var xorWith = baseRest(function(arrays) {\n var comparator = last(arrays);\n comparator = typeof comparator == 'function' ? comparator : undefined;\n return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);\n });\n\n /**\n * Creates an array of grouped elements, the first of which contains the\n * first elements of the given arrays, the second of which contains the\n * second elements of the given arrays, and so on.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zip(['a', 'b'], [1, 2], [true, false]);\n * // => [['a', 1, true], ['b', 2, false]]\n */\n var zip = baseRest(unzip);\n\n /**\n * This method is like `_.fromPairs` except that it accepts two arrays,\n * one of property identifiers and one of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 0.4.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObject(['a', 'b'], [1, 2]);\n * // => { 'a': 1, 'b': 2 }\n */\n function zipObject(props, values) {\n return baseZipObject(props || [], values || [], assignValue);\n }\n\n /**\n * This method is like `_.zipObject` except that it supports property paths.\n *\n * @static\n * @memberOf _\n * @since 4.1.0\n * @category Array\n * @param {Array} [props=[]] The property identifiers.\n * @param {Array} [values=[]] The property values.\n * @returns {Object} Returns the new object.\n * @example\n *\n * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);\n * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }\n */\n function zipObjectDeep(props, values) {\n return baseZipObject(props || [], values || [], baseSet);\n }\n\n /**\n * This method is like `_.zip` except that it accepts `iteratee` to specify\n * how grouped values should be combined. The iteratee is invoked with the\n * elements of each group: (...group).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Array\n * @param {...Array} [arrays] The arrays to process.\n * @param {Function} [iteratee=_.identity] The function to combine\n * grouped values.\n * @returns {Array} Returns the new array of grouped elements.\n * @example\n *\n * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {\n * return a + b + c;\n * });\n * // => [111, 222]\n */\n var zipWith = baseRest(function(arrays) {\n var length = arrays.length,\n iteratee = length > 1 ? arrays[length - 1] : undefined;\n\n iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;\n return unzipWith(arrays, iteratee);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates a `lodash` wrapper instance that wraps `value` with explicit method\n * chain sequences enabled. The result of such sequences must be unwrapped\n * with `_#value`.\n *\n * @static\n * @memberOf _\n * @since 1.3.0\n * @category Seq\n * @param {*} value The value to wrap.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'pebbles', 'age': 1 }\n * ];\n *\n * var youngest = _\n * .chain(users)\n * .sortBy('age')\n * .map(function(o) {\n * return o.user + ' is ' + o.age;\n * })\n * .head()\n * .value();\n * // => 'pebbles is 1'\n */\n function chain(value) {\n var result = lodash(value);\n result.__chain__ = true;\n return result;\n }\n\n /**\n * This method invokes `interceptor` and returns `value`. The interceptor\n * is invoked with one argument; (value). The purpose of this method is to\n * \"tap into\" a method chain sequence in order to modify intermediate results.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns `value`.\n * @example\n *\n * _([1, 2, 3])\n * .tap(function(array) {\n * // Mutate input array.\n * array.pop();\n * })\n * .reverse()\n * .value();\n * // => [2, 1]\n */\n function tap(value, interceptor) {\n interceptor(value);\n return value;\n }\n\n /**\n * This method is like `_.tap` except that it returns the result of `interceptor`.\n * The purpose of this method is to \"pass thru\" values replacing intermediate\n * results in a method chain sequence.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Seq\n * @param {*} value The value to provide to `interceptor`.\n * @param {Function} interceptor The function to invoke.\n * @returns {*} Returns the result of `interceptor`.\n * @example\n *\n * _(' abc ')\n * .chain()\n * .trim()\n * .thru(function(value) {\n * return [value];\n * })\n * .value();\n * // => ['abc']\n */\n function thru(value, interceptor) {\n return interceptor(value);\n }\n\n /**\n * This method is the wrapper version of `_.at`.\n *\n * @name at\n * @memberOf _\n * @since 1.0.0\n * @category Seq\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n *\n * _(object).at(['a[0].b.c', 'a[1]']).value();\n * // => [3, 4]\n */\n var wrapperAt = flatRest(function(paths) {\n var length = paths.length,\n start = length ? paths[0] : 0,\n value = this.__wrapped__,\n interceptor = function(object) { return baseAt(object, paths); };\n\n if (length > 1 || this.__actions__.length ||\n !(value instanceof LazyWrapper) || !isIndex(start)) {\n return this.thru(interceptor);\n }\n value = value.slice(start, +start + (length ? 1 : 0));\n value.__actions__.push({\n 'func': thru,\n 'args': [interceptor],\n 'thisArg': undefined\n });\n return new LodashWrapper(value, this.__chain__).thru(function(array) {\n if (length && !array.length) {\n array.push(undefined);\n }\n return array;\n });\n });\n\n /**\n * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.\n *\n * @name chain\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 }\n * ];\n *\n * // A sequence without explicit chaining.\n * _(users).head();\n * // => { 'user': 'barney', 'age': 36 }\n *\n * // A sequence with explicit chaining.\n * _(users)\n * .chain()\n * .head()\n * .pick('user')\n * .value();\n * // => { 'user': 'barney' }\n */\n function wrapperChain() {\n return chain(this);\n }\n\n /**\n * Executes the chain sequence and returns the wrapped result.\n *\n * @name commit\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2];\n * var wrapped = _(array).push(3);\n *\n * console.log(array);\n * // => [1, 2]\n *\n * wrapped = wrapped.commit();\n * console.log(array);\n * // => [1, 2, 3]\n *\n * wrapped.last();\n * // => 3\n *\n * console.log(array);\n * // => [1, 2, 3]\n */\n function wrapperCommit() {\n return new LodashWrapper(this.value(), this.__chain__);\n }\n\n /**\n * Gets the next value on a wrapped object following the\n * [iterator protocol](https://mdn.io/iteration_protocols#iterator).\n *\n * @name next\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the next iterator value.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 1 }\n *\n * wrapped.next();\n * // => { 'done': false, 'value': 2 }\n *\n * wrapped.next();\n * // => { 'done': true, 'value': undefined }\n */\n function wrapperNext() {\n if (this.__values__ === undefined) {\n this.__values__ = toArray(this.value());\n }\n var done = this.__index__ >= this.__values__.length,\n value = done ? undefined : this.__values__[this.__index__++];\n\n return { 'done': done, 'value': value };\n }\n\n /**\n * Enables the wrapper to be iterable.\n *\n * @name Symbol.iterator\n * @memberOf _\n * @since 4.0.0\n * @category Seq\n * @returns {Object} Returns the wrapper object.\n * @example\n *\n * var wrapped = _([1, 2]);\n *\n * wrapped[Symbol.iterator]() === wrapped;\n * // => true\n *\n * Array.from(wrapped);\n * // => [1, 2]\n */\n function wrapperToIterator() {\n return this;\n }\n\n /**\n * Creates a clone of the chain sequence planting `value` as the wrapped value.\n *\n * @name plant\n * @memberOf _\n * @since 3.2.0\n * @category Seq\n * @param {*} value The value to plant.\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var wrapped = _([1, 2]).map(square);\n * var other = wrapped.plant([3, 4]);\n *\n * other.value();\n * // => [9, 16]\n *\n * wrapped.value();\n * // => [1, 4]\n */\n function wrapperPlant(value) {\n var result,\n parent = this;\n\n while (parent instanceof baseLodash) {\n var clone = wrapperClone(parent);\n clone.__index__ = 0;\n clone.__values__ = undefined;\n if (result) {\n previous.__wrapped__ = clone;\n } else {\n result = clone;\n }\n var previous = clone;\n parent = parent.__wrapped__;\n }\n previous.__wrapped__ = value;\n return result;\n }\n\n /**\n * This method is the wrapper version of `_.reverse`.\n *\n * **Note:** This method mutates the wrapped array.\n *\n * @name reverse\n * @memberOf _\n * @since 0.1.0\n * @category Seq\n * @returns {Object} Returns the new `lodash` wrapper instance.\n * @example\n *\n * var array = [1, 2, 3];\n *\n * _(array).reverse().value()\n * // => [3, 2, 1]\n *\n * console.log(array);\n * // => [3, 2, 1]\n */\n function wrapperReverse() {\n var value = this.__wrapped__;\n if (value instanceof LazyWrapper) {\n var wrapped = value;\n if (this.__actions__.length) {\n wrapped = new LazyWrapper(this);\n }\n wrapped = wrapped.reverse();\n wrapped.__actions__.push({\n 'func': thru,\n 'args': [reverse],\n 'thisArg': undefined\n });\n return new LodashWrapper(wrapped, this.__chain__);\n }\n return this.thru(reverse);\n }\n\n /**\n * Executes the chain sequence to resolve the unwrapped value.\n *\n * @name value\n * @memberOf _\n * @since 0.1.0\n * @alias toJSON, valueOf\n * @category Seq\n * @returns {*} Returns the resolved unwrapped value.\n * @example\n *\n * _([1, 2, 3]).value();\n * // => [1, 2, 3]\n */\n function wrapperValue() {\n return baseWrapperValue(this.__wrapped__, this.__actions__);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the number of times the key was returned by `iteratee`. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.countBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': 1, '6': 2 }\n *\n * // The `_.property` iteratee shorthand.\n * _.countBy(['one', 'two', 'three'], 'length');\n * // => { '3': 2, '5': 1 }\n */\n var countBy = createAggregator(function(result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n ++result[key];\n } else {\n baseAssignValue(result, key, 1);\n }\n });\n\n /**\n * Checks if `predicate` returns truthy for **all** elements of `collection`.\n * Iteration is stopped once `predicate` returns falsey. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * **Note:** This method returns `true` for\n * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because\n * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of\n * elements of empty collections.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if all elements pass the predicate check,\n * else `false`.\n * @example\n *\n * _.every([true, 1, null, 'yes'], Boolean);\n * // => false\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.every(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.every(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.every(users, 'active');\n * // => false\n */\n function every(collection, predicate, guard) {\n var func = isArray(collection) ? arrayEvery : baseEvery;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning an array of all elements\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * **Note:** Unlike `_.remove`, this method returns a new array.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.reject\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false }\n * ];\n *\n * _.filter(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.filter(users, { 'age': 36, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.filter(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.filter(users, 'active');\n * // => objects for ['barney']\n */\n function filter(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Iterates over elements of `collection`, returning the first element\n * `predicate` returns truthy for. The predicate is invoked with three\n * arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=0] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': true },\n * { 'user': 'fred', 'age': 40, 'active': false },\n * { 'user': 'pebbles', 'age': 1, 'active': true }\n * ];\n *\n * _.find(users, function(o) { return o.age < 40; });\n * // => object for 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.find(users, { 'age': 1, 'active': true });\n * // => object for 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.find(users, ['active', false]);\n * // => object for 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.find(users, 'active');\n * // => object for 'barney'\n */\n var find = createFind(findIndex);\n\n /**\n * This method is like `_.find` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param {number} [fromIndex=collection.length-1] The index to search from.\n * @returns {*} Returns the matched element, else `undefined`.\n * @example\n *\n * _.findLast([1, 2, 3, 4], function(n) {\n * return n % 2 == 1;\n * });\n * // => 3\n */\n var findLast = createFind(findLastIndex);\n\n /**\n * Creates a flattened array of values by running each element in `collection`\n * thru `iteratee` and flattening the mapped results. The iteratee is invoked\n * with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [n, n];\n * }\n *\n * _.flatMap([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMap(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), 1);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDeep([1, 2], duplicate);\n * // => [1, 1, 2, 2]\n */\n function flatMapDeep(collection, iteratee) {\n return baseFlatten(map(collection, iteratee), INFINITY);\n }\n\n /**\n * This method is like `_.flatMap` except that it recursively flattens the\n * mapped results up to `depth` times.\n *\n * @static\n * @memberOf _\n * @since 4.7.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {number} [depth=1] The maximum recursion depth.\n * @returns {Array} Returns the new flattened array.\n * @example\n *\n * function duplicate(n) {\n * return [[[n, n]]];\n * }\n *\n * _.flatMapDepth([1, 2], duplicate, 2);\n * // => [[1, 1], [2, 2]]\n */\n function flatMapDepth(collection, iteratee, depth) {\n depth = depth === undefined ? 1 : toInteger(depth);\n return baseFlatten(map(collection, iteratee), depth);\n }\n\n /**\n * Iterates over elements of `collection` and invokes `iteratee` for each element.\n * The iteratee is invoked with three arguments: (value, index|key, collection).\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * **Note:** As with other \"Collections\" methods, objects with a \"length\"\n * property are iterated like arrays. To avoid this behavior use `_.forIn`\n * or `_.forOwn` for object iteration.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @alias each\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEachRight\n * @example\n *\n * _.forEach([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `1` then `2`.\n *\n * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n */\n function forEach(collection, iteratee) {\n var func = isArray(collection) ? arrayEach : baseEach;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.forEach` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @alias eachRight\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array|Object} Returns `collection`.\n * @see _.forEach\n * @example\n *\n * _.forEachRight([1, 2], function(value) {\n * console.log(value);\n * });\n * // => Logs `2` then `1`.\n */\n function forEachRight(collection, iteratee) {\n var func = isArray(collection) ? arrayEachRight : baseEachRight;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The order of grouped values\n * is determined by the order they occur in `collection`. The corresponding\n * value of each key is an array of elements responsible for generating the\n * key. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * _.groupBy([6.1, 4.2, 6.3], Math.floor);\n * // => { '4': [4.2], '6': [6.1, 6.3] }\n *\n * // The `_.property` iteratee shorthand.\n * _.groupBy(['one', 'two', 'three'], 'length');\n * // => { '3': ['one', 'two'], '5': ['three'] }\n */\n var groupBy = createAggregator(function(result, value, key) {\n if (hasOwnProperty.call(result, key)) {\n result[key].push(value);\n } else {\n baseAssignValue(result, key, [value]);\n }\n });\n\n /**\n * Checks if `value` is in `collection`. If `collection` is a string, it's\n * checked for a substring of `value`, otherwise\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * is used for equality comparisons. If `fromIndex` is negative, it's used as\n * the offset from the end of `collection`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @param {*} value The value to search for.\n * @param {number} [fromIndex=0] The index to search from.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {boolean} Returns `true` if `value` is found, else `false`.\n * @example\n *\n * _.includes([1, 2, 3], 1);\n * // => true\n *\n * _.includes([1, 2, 3], 1, 2);\n * // => false\n *\n * _.includes({ 'a': 1, 'b': 2 }, 1);\n * // => true\n *\n * _.includes('abcd', 'bc');\n * // => true\n */\n function includes(collection, value, fromIndex, guard) {\n collection = isArrayLike(collection) ? collection : values(collection);\n fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;\n\n var length = collection.length;\n if (fromIndex < 0) {\n fromIndex = nativeMax(length + fromIndex, 0);\n }\n return isString(collection)\n ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)\n : (!!length && baseIndexOf(collection, value, fromIndex) > -1);\n }\n\n /**\n * Invokes the method at `path` of each element in `collection`, returning\n * an array of the results of each invoked method. Any additional arguments\n * are provided to each invoked method. If `path` is a function, it's invoked\n * for, and `this` bound to, each element in `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array|Function|string} path The path of the method to invoke or\n * the function invoked per iteration.\n * @param {...*} [args] The arguments to invoke each method with.\n * @returns {Array} Returns the array of results.\n * @example\n *\n * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');\n * // => [[1, 5, 7], [1, 2, 3]]\n *\n * _.invokeMap([123, 456], String.prototype.split, '');\n * // => [['1', '2', '3'], ['4', '5', '6']]\n */\n var invokeMap = baseRest(function(collection, path, args) {\n var index = -1,\n isFunc = typeof path == 'function',\n result = isArrayLike(collection) ? Array(collection.length) : [];\n\n baseEach(collection, function(value) {\n result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);\n });\n return result;\n });\n\n /**\n * Creates an object composed of keys generated from the results of running\n * each element of `collection` thru `iteratee`. The corresponding value of\n * each key is the last element responsible for generating the key. The\n * iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee to transform keys.\n * @returns {Object} Returns the composed aggregate object.\n * @example\n *\n * var array = [\n * { 'dir': 'left', 'code': 97 },\n * { 'dir': 'right', 'code': 100 }\n * ];\n *\n * _.keyBy(array, function(o) {\n * return String.fromCharCode(o.code);\n * });\n * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }\n *\n * _.keyBy(array, 'dir');\n * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }\n */\n var keyBy = createAggregator(function(result, value, key) {\n baseAssignValue(result, key, value);\n });\n\n /**\n * Creates an array of values by running each element in `collection` thru\n * `iteratee`. The iteratee is invoked with three arguments:\n * (value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.\n *\n * The guarded methods are:\n * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,\n * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,\n * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,\n * `template`, `trim`, `trimEnd`, `trimStart`, and `words`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new mapped array.\n * @example\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * _.map([4, 8], square);\n * // => [16, 64]\n *\n * _.map({ 'a': 4, 'b': 8 }, square);\n * // => [16, 64] (iteration order is not guaranteed)\n *\n * var users = [\n * { 'user': 'barney' },\n * { 'user': 'fred' }\n * ];\n *\n * // The `_.property` iteratee shorthand.\n * _.map(users, 'user');\n * // => ['barney', 'fred']\n */\n function map(collection, iteratee) {\n var func = isArray(collection) ? arrayMap : baseMap;\n return func(collection, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.sortBy` except that it allows specifying the sort\n * orders of the iteratees to sort by. If `orders` is unspecified, all values\n * are sorted in ascending order. Otherwise, specify an order of \"desc\" for\n * descending or \"asc\" for ascending sort order of corresponding values.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @param {string[]} [orders] The sort orders of `iteratees`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 34 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'barney', 'age': 36 }\n * ];\n *\n * // Sort by `user` in ascending order and by `age` in descending order.\n * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n */\n function orderBy(collection, iteratees, orders, guard) {\n if (collection == null) {\n return [];\n }\n if (!isArray(iteratees)) {\n iteratees = iteratees == null ? [] : [iteratees];\n }\n orders = guard ? undefined : orders;\n if (!isArray(orders)) {\n orders = orders == null ? [] : [orders];\n }\n return baseOrderBy(collection, iteratees, orders);\n }\n\n /**\n * Creates an array of elements split into two groups, the first of which\n * contains elements `predicate` returns truthy for, the second of which\n * contains elements `predicate` returns falsey for. The predicate is\n * invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the array of grouped elements.\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true },\n * { 'user': 'pebbles', 'age': 1, 'active': false }\n * ];\n *\n * _.partition(users, function(o) { return o.active; });\n * // => objects for [['fred'], ['barney', 'pebbles']]\n *\n * // The `_.matches` iteratee shorthand.\n * _.partition(users, { 'age': 1, 'active': false });\n * // => objects for [['pebbles'], ['barney', 'fred']]\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.partition(users, ['active', false]);\n * // => objects for [['barney', 'pebbles'], ['fred']]\n *\n * // The `_.property` iteratee shorthand.\n * _.partition(users, 'active');\n * // => objects for [['fred'], ['barney', 'pebbles']]\n */\n var partition = createAggregator(function(result, value, key) {\n result[key ? 0 : 1].push(value);\n }, function() { return [[], []]; });\n\n /**\n * Reduces `collection` to a value which is the accumulated result of running\n * each element in `collection` thru `iteratee`, where each successive\n * invocation is supplied the return value of the previous. If `accumulator`\n * is not given, the first element of `collection` is used as the initial\n * value. The iteratee is invoked with four arguments:\n * (accumulator, value, index|key, collection).\n *\n * Many lodash methods are guarded to work as iteratees for methods like\n * `_.reduce`, `_.reduceRight`, and `_.transform`.\n *\n * The guarded methods are:\n * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,\n * and `sortBy`\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduceRight\n * @example\n *\n * _.reduce([1, 2], function(sum, n) {\n * return sum + n;\n * }, 0);\n * // => 3\n *\n * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n * (result[value] || (result[value] = [])).push(key);\n * return result;\n * }, {});\n * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)\n */\n function reduce(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduce : baseReduce,\n initAccum = arguments.length < 3;\n\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);\n }\n\n /**\n * This method is like `_.reduce` except that it iterates over elements of\n * `collection` from right to left.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @returns {*} Returns the accumulated value.\n * @see _.reduce\n * @example\n *\n * var array = [[0, 1], [2, 3], [4, 5]];\n *\n * _.reduceRight(array, function(flattened, other) {\n * return flattened.concat(other);\n * }, []);\n * // => [4, 5, 2, 3, 0, 1]\n */\n function reduceRight(collection, iteratee, accumulator) {\n var func = isArray(collection) ? arrayReduceRight : baseReduce,\n initAccum = arguments.length < 3;\n\n return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);\n }\n\n /**\n * The opposite of `_.filter`; this method returns the elements of `collection`\n * that `predicate` does **not** return truthy for.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n * @see _.filter\n * @example\n *\n * var users = [\n * { 'user': 'barney', 'age': 36, 'active': false },\n * { 'user': 'fred', 'age': 40, 'active': true }\n * ];\n *\n * _.reject(users, function(o) { return !o.active; });\n * // => objects for ['fred']\n *\n * // The `_.matches` iteratee shorthand.\n * _.reject(users, { 'age': 40, 'active': true });\n * // => objects for ['barney']\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.reject(users, ['active', false]);\n * // => objects for ['fred']\n *\n * // The `_.property` iteratee shorthand.\n * _.reject(users, 'active');\n * // => objects for ['barney']\n */\n function reject(collection, predicate) {\n var func = isArray(collection) ? arrayFilter : baseFilter;\n return func(collection, negate(getIteratee(predicate, 3)));\n }\n\n /**\n * Gets a random element from `collection`.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @returns {*} Returns the random element.\n * @example\n *\n * _.sample([1, 2, 3, 4]);\n * // => 2\n */\n function sample(collection) {\n var func = isArray(collection) ? arraySample : baseSample;\n return func(collection);\n }\n\n /**\n * Gets `n` random elements at unique keys from `collection` up to the\n * size of `collection`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Collection\n * @param {Array|Object} collection The collection to sample.\n * @param {number} [n=1] The number of elements to sample.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Array} Returns the random elements.\n * @example\n *\n * _.sampleSize([1, 2, 3], 2);\n * // => [3, 1]\n *\n * _.sampleSize([1, 2, 3], 4);\n * // => [2, 3, 1]\n */\n function sampleSize(collection, n, guard) {\n if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {\n n = 1;\n } else {\n n = toInteger(n);\n }\n var func = isArray(collection) ? arraySampleSize : baseSampleSize;\n return func(collection, n);\n }\n\n /**\n * Creates an array of shuffled values, using a version of the\n * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to shuffle.\n * @returns {Array} Returns the new shuffled array.\n * @example\n *\n * _.shuffle([1, 2, 3, 4]);\n * // => [4, 1, 3, 2]\n */\n function shuffle(collection) {\n var func = isArray(collection) ? arrayShuffle : baseShuffle;\n return func(collection);\n }\n\n /**\n * Gets the size of `collection` by returning its length for array-like\n * values or the number of own enumerable string keyed properties for objects.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object|string} collection The collection to inspect.\n * @returns {number} Returns the collection size.\n * @example\n *\n * _.size([1, 2, 3]);\n * // => 3\n *\n * _.size({ 'a': 1, 'b': 2 });\n * // => 2\n *\n * _.size('pebbles');\n * // => 7\n */\n function size(collection) {\n if (collection == null) {\n return 0;\n }\n if (isArrayLike(collection)) {\n return isString(collection) ? stringSize(collection) : collection.length;\n }\n var tag = getTag(collection);\n if (tag == mapTag || tag == setTag) {\n return collection.size;\n }\n return baseKeys(collection).length;\n }\n\n /**\n * Checks if `predicate` returns truthy for **any** element of `collection`.\n * Iteration is stopped once `predicate` returns truthy. The predicate is\n * invoked with three arguments: (value, index|key, collection).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n * @example\n *\n * _.some([null, 0, 'yes', false], Boolean);\n * // => true\n *\n * var users = [\n * { 'user': 'barney', 'active': true },\n * { 'user': 'fred', 'active': false }\n * ];\n *\n * // The `_.matches` iteratee shorthand.\n * _.some(users, { 'user': 'barney', 'active': false });\n * // => false\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.some(users, ['active', false]);\n * // => true\n *\n * // The `_.property` iteratee shorthand.\n * _.some(users, 'active');\n * // => true\n */\n function some(collection, predicate, guard) {\n var func = isArray(collection) ? arraySome : baseSome;\n if (guard && isIterateeCall(collection, predicate, guard)) {\n predicate = undefined;\n }\n return func(collection, getIteratee(predicate, 3));\n }\n\n /**\n * Creates an array of elements, sorted in ascending order by the results of\n * running each element in a collection thru each iteratee. This method\n * performs a stable sort, that is, it preserves the original sort order of\n * equal elements. The iteratees are invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Collection\n * @param {Array|Object} collection The collection to iterate over.\n * @param {...(Function|Function[])} [iteratees=[_.identity]]\n * The iteratees to sort by.\n * @returns {Array} Returns the new sorted array.\n * @example\n *\n * var users = [\n * { 'user': 'fred', 'age': 48 },\n * { 'user': 'barney', 'age': 36 },\n * { 'user': 'fred', 'age': 40 },\n * { 'user': 'barney', 'age': 34 }\n * ];\n *\n * _.sortBy(users, [function(o) { return o.user; }]);\n * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n *\n * _.sortBy(users, ['user', 'age']);\n * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]\n */\n var sortBy = baseRest(function(collection, iteratees) {\n if (collection == null) {\n return [];\n }\n var length = iteratees.length;\n if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {\n iteratees = [];\n } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {\n iteratees = [iteratees[0]];\n }\n return baseOrderBy(collection, baseFlatten(iteratees, 1), []);\n });\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n * console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\n var now = ctxNow || function() {\n return root.Date.now();\n };\n\n /*------------------------------------------------------------------------*/\n\n /**\n * The opposite of `_.before`; this method creates a function that invokes\n * `func` once it's called `n` or more times.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {number} n The number of calls before `func` is invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var saves = ['profile', 'settings'];\n *\n * var done = _.after(saves.length, function() {\n * console.log('done saving!');\n * });\n *\n * _.forEach(saves, function(type) {\n * asyncSave({ 'type': type, 'complete': done });\n * });\n * // => Logs 'done saving!' after the two async saves have completed.\n */\n function after(n, func) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function() {\n if (--n < 1) {\n return func.apply(this, arguments);\n }\n };\n }\n\n /**\n * Creates a function that invokes `func`, with up to `n` arguments,\n * ignoring any additional arguments.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @param {number} [n=func.length] The arity cap.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.ary(parseInt, 1));\n * // => [6, 8, 10]\n */\n function ary(func, n, guard) {\n n = guard ? undefined : n;\n n = (func && n == null) ? func.length : n;\n return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);\n }\n\n /**\n * Creates a function that invokes `func`, with the `this` binding and arguments\n * of the created function, while it's called less than `n` times. Subsequent\n * calls to the created function return the result of the last `func` invocation.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {number} n The number of calls at which `func` is no longer invoked.\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * jQuery(element).on('click', _.before(5, addContactToList));\n * // => Allows adding up to 4 contacts to the list.\n */\n function before(n, func) {\n var result;\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n n = toInteger(n);\n return function() {\n if (--n > 0) {\n result = func.apply(this, arguments);\n }\n if (n <= 1) {\n func = undefined;\n }\n return result;\n };\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of `thisArg`\n * and `partials` prepended to the arguments it receives.\n *\n * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for partially applied arguments.\n *\n * **Note:** Unlike native `Function#bind`, this method doesn't set the \"length\"\n * property of bound functions.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to bind.\n * @param {*} thisArg The `this` binding of `func`.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * function greet(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n *\n * var object = { 'user': 'fred' };\n *\n * var bound = _.bind(greet, object, 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bind(greet, object, _, '!');\n * bound('hi');\n * // => 'hi fred!'\n */\n var bind = baseRest(function(func, thisArg, partials) {\n var bitmask = WRAP_BIND_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bind));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(func, bitmask, thisArg, partials, holders);\n });\n\n /**\n * Creates a function that invokes the method at `object[key]` with `partials`\n * prepended to the arguments it receives.\n *\n * This method differs from `_.bind` by allowing bound functions to reference\n * methods that may be redefined or don't yet exist. See\n * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)\n * for more details.\n *\n * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Function\n * @param {Object} object The object to invoke the method on.\n * @param {string} key The key of the method.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new bound function.\n * @example\n *\n * var object = {\n * 'user': 'fred',\n * 'greet': function(greeting, punctuation) {\n * return greeting + ' ' + this.user + punctuation;\n * }\n * };\n *\n * var bound = _.bindKey(object, 'greet', 'hi');\n * bound('!');\n * // => 'hi fred!'\n *\n * object.greet = function(greeting, punctuation) {\n * return greeting + 'ya ' + this.user + punctuation;\n * };\n *\n * bound('!');\n * // => 'hiya fred!'\n *\n * // Bound with placeholders.\n * var bound = _.bindKey(object, 'greet', _, '!');\n * bound('hi');\n * // => 'hiya fred!'\n */\n var bindKey = baseRest(function(object, key, partials) {\n var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;\n if (partials.length) {\n var holders = replaceHolders(partials, getHolder(bindKey));\n bitmask |= WRAP_PARTIAL_FLAG;\n }\n return createWrap(key, bitmask, object, partials, holders);\n });\n\n /**\n * Creates a function that accepts arguments of `func` and either invokes\n * `func` returning its result, if at least `arity` number of arguments have\n * been provided, or returns a function that accepts the remaining `func`\n * arguments, and so on. The arity of `func` may be specified if `func.length`\n * is not sufficient.\n *\n * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,\n * may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curry(abc);\n *\n * curried(1)(2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2)(3);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(1)(_, 3)(2);\n * // => [1, 2, 3]\n */\n function curry(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curry.placeholder;\n return result;\n }\n\n /**\n * This method is like `_.curry` except that arguments are applied to `func`\n * in the manner of `_.partialRight` instead of `_.partial`.\n *\n * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for provided arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of curried functions.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to curry.\n * @param {number} [arity=func.length] The arity of `func`.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new curried function.\n * @example\n *\n * var abc = function(a, b, c) {\n * return [a, b, c];\n * };\n *\n * var curried = _.curryRight(abc);\n *\n * curried(3)(2)(1);\n * // => [1, 2, 3]\n *\n * curried(2, 3)(1);\n * // => [1, 2, 3]\n *\n * curried(1, 2, 3);\n * // => [1, 2, 3]\n *\n * // Curried with placeholders.\n * curried(3)(1, _)(2);\n * // => [1, 2, 3]\n */\n function curryRight(func, arity, guard) {\n arity = guard ? undefined : arity;\n var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n result.placeholder = curryRight.placeholder;\n return result;\n }\n\n /**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n * Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n * The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n * 'leading': true,\n * 'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\n function debounce(func, wait, options) {\n var lastArgs,\n lastThis,\n maxWait,\n result,\n timerId,\n lastCallTime,\n lastInvokeTime = 0,\n leading = false,\n maxing = false,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n wait = toNumber(wait) || 0;\n if (isObject(options)) {\n leading = !!options.leading;\n maxing = 'maxWait' in options;\n maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n\n function invokeFunc(time) {\n var args = lastArgs,\n thisArg = lastThis;\n\n lastArgs = lastThis = undefined;\n lastInvokeTime = time;\n result = func.apply(thisArg, args);\n return result;\n }\n\n function leadingEdge(time) {\n // Reset any `maxWait` timer.\n lastInvokeTime = time;\n // Start the timer for the trailing edge.\n timerId = setTimeout(timerExpired, wait);\n // Invoke the leading edge.\n return leading ? invokeFunc(time) : result;\n }\n\n function remainingWait(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime,\n timeWaiting = wait - timeSinceLastCall;\n\n return maxing\n ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)\n : timeWaiting;\n }\n\n function shouldInvoke(time) {\n var timeSinceLastCall = time - lastCallTime,\n timeSinceLastInvoke = time - lastInvokeTime;\n\n // Either this is the first call, activity has stopped and we're at the\n // trailing edge, the system time has gone backwards and we're treating\n // it as the trailing edge, or we've hit the `maxWait` limit.\n return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n }\n\n function timerExpired() {\n var time = now();\n if (shouldInvoke(time)) {\n return trailingEdge(time);\n }\n // Restart the timer.\n timerId = setTimeout(timerExpired, remainingWait(time));\n }\n\n function trailingEdge(time) {\n timerId = undefined;\n\n // Only invoke if we have `lastArgs` which means `func` has been\n // debounced at least once.\n if (trailing && lastArgs) {\n return invokeFunc(time);\n }\n lastArgs = lastThis = undefined;\n return result;\n }\n\n function cancel() {\n if (timerId !== undefined) {\n clearTimeout(timerId);\n }\n lastInvokeTime = 0;\n lastArgs = lastCallTime = lastThis = timerId = undefined;\n }\n\n function flush() {\n return timerId === undefined ? result : trailingEdge(now());\n }\n\n function debounced() {\n var time = now(),\n isInvoking = shouldInvoke(time);\n\n lastArgs = arguments;\n lastThis = this;\n lastCallTime = time;\n\n if (isInvoking) {\n if (timerId === undefined) {\n return leadingEdge(lastCallTime);\n }\n if (maxing) {\n // Handle invocations in a tight loop.\n timerId = setTimeout(timerExpired, wait);\n return invokeFunc(lastCallTime);\n }\n }\n if (timerId === undefined) {\n timerId = setTimeout(timerExpired, wait);\n }\n return result;\n }\n debounced.cancel = cancel;\n debounced.flush = flush;\n return debounced;\n }\n\n /**\n * Defers invoking the `func` until the current call stack has cleared. Any\n * additional arguments are provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to defer.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.defer(function(text) {\n * console.log(text);\n * }, 'deferred');\n * // => Logs 'deferred' after one millisecond.\n */\n var defer = baseRest(function(func, args) {\n return baseDelay(func, 1, args);\n });\n\n /**\n * Invokes `func` after `wait` milliseconds. Any additional arguments are\n * provided to `func` when it's invoked.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to delay.\n * @param {number} wait The number of milliseconds to delay invocation.\n * @param {...*} [args] The arguments to invoke `func` with.\n * @returns {number} Returns the timer id.\n * @example\n *\n * _.delay(function(text) {\n * console.log(text);\n * }, 1000, 'later');\n * // => Logs 'later' after one second.\n */\n var delay = baseRest(function(func, wait, args) {\n return baseDelay(func, toNumber(wait) || 0, args);\n });\n\n /**\n * Creates a function that invokes `func` with arguments reversed.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to flip arguments for.\n * @returns {Function} Returns the new flipped function.\n * @example\n *\n * var flipped = _.flip(function() {\n * return _.toArray(arguments);\n * });\n *\n * flipped('a', 'b', 'c', 'd');\n * // => ['d', 'c', 'b', 'a']\n */\n function flip(func) {\n return createWrap(func, WRAP_FLIP_FLAG);\n }\n\n /**\n * Creates a function that memoizes the result of `func`. If `resolver` is\n * provided, it determines the cache key for storing the result based on the\n * arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is used as the map cache key. The `func`\n * is invoked with the `this` binding of the memoized function.\n *\n * **Note:** The cache is exposed as the `cache` property on the memoized\n * function. Its creation may be customized by replacing the `_.memoize.Cache`\n * constructor with one whose instances implement the\n * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)\n * method interface of `clear`, `delete`, `get`, `has`, and `set`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to have its output memoized.\n * @param {Function} [resolver] The function to resolve the cache key.\n * @returns {Function} Returns the new memoized function.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n * var other = { 'c': 3, 'd': 4 };\n *\n * var values = _.memoize(_.values);\n * values(object);\n * // => [1, 2]\n *\n * values(other);\n * // => [3, 4]\n *\n * object.a = 2;\n * values(object);\n * // => [1, 2]\n *\n * // Modify the result cache.\n * values.cache.set(object, ['a', 'b']);\n * values(object);\n * // => ['a', 'b']\n *\n * // Replace `_.memoize.Cache`.\n * _.memoize.Cache = WeakMap;\n */\n function memoize(func, resolver) {\n if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var memoized = function() {\n var args = arguments,\n key = resolver ? resolver.apply(this, args) : args[0],\n cache = memoized.cache;\n\n if (cache.has(key)) {\n return cache.get(key);\n }\n var result = func.apply(this, args);\n memoized.cache = cache.set(key, result) || cache;\n return result;\n };\n memoized.cache = new (memoize.Cache || MapCache);\n return memoized;\n }\n\n // Expose `MapCache`.\n memoize.Cache = MapCache;\n\n /**\n * Creates a function that negates the result of the predicate `func`. The\n * `func` predicate is invoked with the `this` binding and arguments of the\n * created function.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} predicate The predicate to negate.\n * @returns {Function} Returns the new negated function.\n * @example\n *\n * function isEven(n) {\n * return n % 2 == 0;\n * }\n *\n * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));\n * // => [1, 3, 5]\n */\n function negate(predicate) {\n if (typeof predicate != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n return function() {\n var args = arguments;\n switch (args.length) {\n case 0: return !predicate.call(this);\n case 1: return !predicate.call(this, args[0]);\n case 2: return !predicate.call(this, args[0], args[1]);\n case 3: return !predicate.call(this, args[0], args[1], args[2]);\n }\n return !predicate.apply(this, args);\n };\n }\n\n /**\n * Creates a function that is restricted to invoking `func` once. Repeat calls\n * to the function return the value of the first invocation. The `func` is\n * invoked with the `this` binding and arguments of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to restrict.\n * @returns {Function} Returns the new restricted function.\n * @example\n *\n * var initialize = _.once(createApplication);\n * initialize();\n * initialize();\n * // => `createApplication` is invoked once\n */\n function once(func) {\n return before(2, func);\n }\n\n /**\n * Creates a function that invokes `func` with its arguments transformed.\n *\n * @static\n * @since 4.0.0\n * @memberOf _\n * @category Function\n * @param {Function} func The function to wrap.\n * @param {...(Function|Function[])} [transforms=[_.identity]]\n * The argument transforms.\n * @returns {Function} Returns the new function.\n * @example\n *\n * function doubled(n) {\n * return n * 2;\n * }\n *\n * function square(n) {\n * return n * n;\n * }\n *\n * var func = _.overArgs(function(x, y) {\n * return [x, y];\n * }, [square, doubled]);\n *\n * func(9, 3);\n * // => [81, 6]\n *\n * func(10, 5);\n * // => [100, 10]\n */\n var overArgs = castRest(function(func, transforms) {\n transforms = (transforms.length == 1 && isArray(transforms[0]))\n ? arrayMap(transforms[0], baseUnary(getIteratee()))\n : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));\n\n var funcsLength = transforms.length;\n return baseRest(function(args) {\n var index = -1,\n length = nativeMin(args.length, funcsLength);\n\n while (++index < length) {\n args[index] = transforms[index].call(this, args[index]);\n }\n return apply(func, this, args);\n });\n });\n\n /**\n * Creates a function that invokes `func` with `partials` prepended to the\n * arguments it receives. This method is like `_.bind` except it does **not**\n * alter the `this` binding.\n *\n * The `_.partial.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 0.2.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var sayHelloTo = _.partial(greet, 'hello');\n * sayHelloTo('fred');\n * // => 'hello fred'\n *\n * // Partially applied with placeholders.\n * var greetFred = _.partial(greet, _, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n */\n var partial = baseRest(function(func, partials) {\n var holders = replaceHolders(partials, getHolder(partial));\n return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);\n });\n\n /**\n * This method is like `_.partial` except that partially applied arguments\n * are appended to the arguments it receives.\n *\n * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic\n * builds, may be used as a placeholder for partially applied arguments.\n *\n * **Note:** This method doesn't set the \"length\" property of partially\n * applied functions.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Function\n * @param {Function} func The function to partially apply arguments to.\n * @param {...*} [partials] The arguments to be partially applied.\n * @returns {Function} Returns the new partially applied function.\n * @example\n *\n * function greet(greeting, name) {\n * return greeting + ' ' + name;\n * }\n *\n * var greetFred = _.partialRight(greet, 'fred');\n * greetFred('hi');\n * // => 'hi fred'\n *\n * // Partially applied with placeholders.\n * var sayHelloTo = _.partialRight(greet, 'hello', _);\n * sayHelloTo('fred');\n * // => 'hello fred'\n */\n var partialRight = baseRest(function(func, partials) {\n var holders = replaceHolders(partials, getHolder(partialRight));\n return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);\n });\n\n /**\n * Creates a function that invokes `func` with arguments arranged according\n * to the specified `indexes` where the argument value at the first index is\n * provided as the first argument, the argument value at the second index is\n * provided as the second argument, and so on.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to rearrange arguments for.\n * @param {...(number|number[])} indexes The arranged argument indexes.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var rearged = _.rearg(function(a, b, c) {\n * return [a, b, c];\n * }, [2, 0, 1]);\n *\n * rearged('b', 'c', 'a')\n * // => ['a', 'b', 'c']\n */\n var rearg = flatRest(function(func, indexes) {\n return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);\n });\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * created function and arguments from `start` and beyond provided as\n * an array.\n *\n * **Note:** This method is based on the\n * [rest parameter](https://mdn.io/rest_parameters).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to apply a rest parameter to.\n * @param {number} [start=func.length-1] The start position of the rest parameter.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.rest(function(what, names) {\n * return what + ' ' + _.initial(names).join(', ') +\n * (_.size(names) > 1 ? ', & ' : '') + _.last(names);\n * });\n *\n * say('hello', 'fred', 'barney', 'pebbles');\n * // => 'hello fred, barney, & pebbles'\n */\n function rest(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start === undefined ? start : toInteger(start);\n return baseRest(func, start);\n }\n\n /**\n * Creates a function that invokes `func` with the `this` binding of the\n * create function and an array of arguments much like\n * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).\n *\n * **Note:** This method is based on the\n * [spread operator](https://mdn.io/spread_operator).\n *\n * @static\n * @memberOf _\n * @since 3.2.0\n * @category Function\n * @param {Function} func The function to spread arguments over.\n * @param {number} [start=0] The start position of the spread.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var say = _.spread(function(who, what) {\n * return who + ' says ' + what;\n * });\n *\n * say(['fred', 'hello']);\n * // => 'fred says hello'\n *\n * var numbers = Promise.all([\n * Promise.resolve(40),\n * Promise.resolve(36)\n * ]);\n *\n * numbers.then(_.spread(function(x, y) {\n * return x + y;\n * }));\n * // => a Promise of 76\n */\n function spread(func, start) {\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n start = start == null ? 0 : nativeMax(toInteger(start), 0);\n return baseRest(function(args) {\n var array = args[start],\n otherArgs = castSlice(args, 0, start);\n\n if (array) {\n arrayPush(otherArgs, array);\n }\n return apply(func, this, otherArgs);\n });\n }\n\n /**\n * Creates a throttled function that only invokes `func` at most once per\n * every `wait` milliseconds. The throttled function comes with a `cancel`\n * method to cancel delayed `func` invocations and a `flush` method to\n * immediately invoke them. Provide `options` to indicate whether `func`\n * should be invoked on the leading and/or trailing edge of the `wait`\n * timeout. The `func` is invoked with the last arguments provided to the\n * throttled function. Subsequent calls to the throttled function return the\n * result of the last `func` invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the throttled function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.throttle` and `_.debounce`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to throttle.\n * @param {number} [wait=0] The number of milliseconds to throttle invocations to.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=true]\n * Specify invoking on the leading edge of the timeout.\n * @param {boolean} [options.trailing=true]\n * Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new throttled function.\n * @example\n *\n * // Avoid excessively updating the position while scrolling.\n * jQuery(window).on('scroll', _.throttle(updatePosition, 100));\n *\n * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });\n * jQuery(element).on('click', throttled);\n *\n * // Cancel the trailing throttled invocation.\n * jQuery(window).on('popstate', throttled.cancel);\n */\n function throttle(func, wait, options) {\n var leading = true,\n trailing = true;\n\n if (typeof func != 'function') {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n if (isObject(options)) {\n leading = 'leading' in options ? !!options.leading : leading;\n trailing = 'trailing' in options ? !!options.trailing : trailing;\n }\n return debounce(func, wait, {\n 'leading': leading,\n 'maxWait': wait,\n 'trailing': trailing\n });\n }\n\n /**\n * Creates a function that accepts up to one argument, ignoring any\n * additional arguments.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.unary(parseInt));\n * // => [6, 8, 10]\n */\n function unary(func) {\n return ary(func, 1);\n }\n\n /**\n * Creates a function that provides `value` to `wrapper` as its first\n * argument. Any additional arguments provided to the function are appended\n * to those provided to the `wrapper`. The wrapper is invoked with the `this`\n * binding of the created function.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {*} value The value to wrap.\n * @param {Function} [wrapper=identity] The wrapper function.\n * @returns {Function} Returns the new function.\n * @example\n *\n * var p = _.wrap(_.escape, function(func, text) {\n * return '

    ' + func(text) + '

    ';\n * });\n *\n * p('fred, barney, & pebbles');\n * // => '

    fred, barney, & pebbles

    '\n */\n function wrap(value, wrapper) {\n return partial(castFunction(wrapper), value);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Casts `value` as an array if it's not one.\n *\n * @static\n * @memberOf _\n * @since 4.4.0\n * @category Lang\n * @param {*} value The value to inspect.\n * @returns {Array} Returns the cast array.\n * @example\n *\n * _.castArray(1);\n * // => [1]\n *\n * _.castArray({ 'a': 1 });\n * // => [{ 'a': 1 }]\n *\n * _.castArray('abc');\n * // => ['abc']\n *\n * _.castArray(null);\n * // => [null]\n *\n * _.castArray(undefined);\n * // => [undefined]\n *\n * _.castArray();\n * // => []\n *\n * var array = [1, 2, 3];\n * console.log(_.castArray(array) === array);\n * // => true\n */\n function castArray() {\n if (!arguments.length) {\n return [];\n }\n var value = arguments[0];\n return isArray(value) ? value : [value];\n }\n\n /**\n * Creates a shallow clone of `value`.\n *\n * **Note:** This method is loosely based on the\n * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)\n * and supports cloning arrays, array buffers, booleans, date objects, maps,\n * numbers, `Object` objects, regexes, sets, strings, symbols, and typed\n * arrays. The own enumerable properties of `arguments` objects are cloned\n * as plain objects. An empty object is returned for uncloneable values such\n * as error objects, functions, DOM nodes, and WeakMaps.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to clone.\n * @returns {*} Returns the cloned value.\n * @see _.cloneDeep\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var shallow = _.clone(objects);\n * console.log(shallow[0] === objects[0]);\n * // => true\n */\n function clone(value) {\n return baseClone(value, CLONE_SYMBOLS_FLAG);\n }\n\n /**\n * This method is like `_.clone` except that it accepts `customizer` which\n * is invoked to produce the cloned value. If `customizer` returns `undefined`,\n * cloning is handled by the method instead. The `customizer` is invoked with\n * up to four arguments; (value [, index|key, object, stack]).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to clone.\n * @param {Function} [customizer] The function to customize cloning.\n * @returns {*} Returns the cloned value.\n * @see _.cloneDeepWith\n * @example\n *\n * function customizer(value) {\n * if (_.isElement(value)) {\n * return value.cloneNode(false);\n * }\n * }\n *\n * var el = _.cloneWith(document.body, customizer);\n *\n * console.log(el === document.body);\n * // => false\n * console.log(el.nodeName);\n * // => 'BODY'\n * console.log(el.childNodes.length);\n * // => 0\n */\n function cloneWith(value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseClone(value, CLONE_SYMBOLS_FLAG, customizer);\n }\n\n /**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\n function cloneDeep(value) {\n return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);\n }\n\n /**\n * This method is like `_.cloneWith` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @param {Function} [customizer] The function to customize cloning.\n * @returns {*} Returns the deep cloned value.\n * @see _.cloneWith\n * @example\n *\n * function customizer(value) {\n * if (_.isElement(value)) {\n * return value.cloneNode(true);\n * }\n * }\n *\n * var el = _.cloneDeepWith(document.body, customizer);\n *\n * console.log(el === document.body);\n * // => false\n * console.log(el.nodeName);\n * // => 'BODY'\n * console.log(el.childNodes.length);\n * // => 20\n */\n function cloneDeepWith(value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer);\n }\n\n /**\n * Checks if `object` conforms to `source` by invoking the predicate\n * properties of `source` with the corresponding property values of `object`.\n *\n * **Note:** This method is equivalent to `_.conforms` when `source` is\n * partially applied.\n *\n * @static\n * @memberOf _\n * @since 4.14.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property predicates to conform to.\n * @returns {boolean} Returns `true` if `object` conforms, else `false`.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n *\n * _.conformsTo(object, { 'b': function(n) { return n > 1; } });\n * // => true\n *\n * _.conformsTo(object, { 'b': function(n) { return n > 2; } });\n * // => false\n */\n function conformsTo(object, source) {\n return source == null || baseConformsTo(object, source, keys(source));\n }\n\n /**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\n function eq(value, other) {\n return value === other || (value !== value && other !== other);\n }\n\n /**\n * Checks if `value` is greater than `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n * @see _.lt\n * @example\n *\n * _.gt(3, 1);\n * // => true\n *\n * _.gt(3, 3);\n * // => false\n *\n * _.gt(1, 3);\n * // => false\n */\n var gt = createRelationalOperation(baseGt);\n\n /**\n * Checks if `value` is greater than or equal to `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than or equal to\n * `other`, else `false`.\n * @see _.lte\n * @example\n *\n * _.gte(3, 1);\n * // => true\n *\n * _.gte(3, 3);\n * // => true\n *\n * _.gte(1, 3);\n * // => false\n */\n var gte = createRelationalOperation(function(value, other) {\n return value >= other;\n });\n\n /**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\n var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {\n return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&\n !propertyIsEnumerable.call(value, 'callee');\n };\n\n /**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\n var isArray = Array.isArray;\n\n /**\n * Checks if `value` is classified as an `ArrayBuffer` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.\n * @example\n *\n * _.isArrayBuffer(new ArrayBuffer(2));\n * // => true\n *\n * _.isArrayBuffer(new Array(2));\n * // => false\n */\n var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;\n\n /**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\n function isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n }\n\n /**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\n function isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n }\n\n /**\n * Checks if `value` is classified as a boolean primitive or object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.\n * @example\n *\n * _.isBoolean(false);\n * // => true\n *\n * _.isBoolean(null);\n * // => false\n */\n function isBoolean(value) {\n return value === true || value === false ||\n (isObjectLike(value) && baseGetTag(value) == boolTag);\n }\n\n /**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\n var isBuffer = nativeIsBuffer || stubFalse;\n\n /**\n * Checks if `value` is classified as a `Date` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a date object, else `false`.\n * @example\n *\n * _.isDate(new Date);\n * // => true\n *\n * _.isDate('Mon April 23 2012');\n * // => false\n */\n var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;\n\n /**\n * Checks if `value` is likely a DOM element.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.\n * @example\n *\n * _.isElement(document.body);\n * // => true\n *\n * _.isElement('');\n * // => false\n */\n function isElement(value) {\n return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value);\n }\n\n /**\n * Checks if `value` is an empty object, collection, map, or set.\n *\n * Objects are considered empty if they have no own enumerable string keyed\n * properties.\n *\n * Array-like values such as `arguments` objects, arrays, buffers, strings, or\n * jQuery-like collections are considered empty if they have a `length` of `0`.\n * Similarly, maps and sets are considered empty if they have a `size` of `0`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is empty, else `false`.\n * @example\n *\n * _.isEmpty(null);\n * // => true\n *\n * _.isEmpty(true);\n * // => true\n *\n * _.isEmpty(1);\n * // => true\n *\n * _.isEmpty([1, 2, 3]);\n * // => false\n *\n * _.isEmpty({ 'a': 1 });\n * // => false\n */\n function isEmpty(value) {\n if (value == null) {\n return true;\n }\n if (isArrayLike(value) &&\n (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||\n isBuffer(value) || isTypedArray(value) || isArguments(value))) {\n return !value.length;\n }\n var tag = getTag(value);\n if (tag == mapTag || tag == setTag) {\n return !value.size;\n }\n if (isPrototype(value)) {\n return !baseKeys(value).length;\n }\n for (var key in value) {\n if (hasOwnProperty.call(value, key)) {\n return false;\n }\n }\n return true;\n }\n\n /**\n * Performs a deep comparison between two values to determine if they are\n * equivalent.\n *\n * **Note:** This method supports comparing arrays, array buffers, booleans,\n * date objects, error objects, maps, numbers, `Object` objects, regexes,\n * sets, strings, symbols, and typed arrays. `Object` objects are compared\n * by their own, not inherited, enumerable properties. Functions and DOM\n * nodes are compared by strict equality, i.e. `===`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.isEqual(object, other);\n * // => true\n *\n * object === other;\n * // => false\n */\n function isEqual(value, other) {\n return baseIsEqual(value, other);\n }\n\n /**\n * This method is like `_.isEqual` except that it accepts `customizer` which\n * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n * are handled by the method instead. The `customizer` is invoked with up to\n * six arguments: (objValue, othValue [, index|key, object, other, stack]).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, othValue) {\n * if (isGreeting(objValue) && isGreeting(othValue)) {\n * return true;\n * }\n * }\n *\n * var array = ['hello', 'goodbye'];\n * var other = ['hi', 'goodbye'];\n *\n * _.isEqualWith(array, other, customizer);\n * // => true\n */\n function isEqualWith(value, other, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n var result = customizer ? customizer(value, other) : undefined;\n return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;\n }\n\n /**\n * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,\n * `SyntaxError`, `TypeError`, or `URIError` object.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an error object, else `false`.\n * @example\n *\n * _.isError(new Error);\n * // => true\n *\n * _.isError(Error);\n * // => false\n */\n function isError(value) {\n if (!isObjectLike(value)) {\n return false;\n }\n var tag = baseGetTag(value);\n return tag == errorTag || tag == domExcTag ||\n (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value));\n }\n\n /**\n * Checks if `value` is a finite primitive number.\n *\n * **Note:** This method is based on\n * [`Number.isFinite`](https://mdn.io/Number/isFinite).\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.\n * @example\n *\n * _.isFinite(3);\n * // => true\n *\n * _.isFinite(Number.MIN_VALUE);\n * // => true\n *\n * _.isFinite(Infinity);\n * // => false\n *\n * _.isFinite('3');\n * // => false\n */\n function isFinite(value) {\n return typeof value == 'number' && nativeIsFinite(value);\n }\n\n /**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\n function isFunction(value) {\n if (!isObject(value)) {\n return false;\n }\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 9 which returns 'object' for typed arrays and other constructors.\n var tag = baseGetTag(value);\n return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;\n }\n\n /**\n * Checks if `value` is an integer.\n *\n * **Note:** This method is based on\n * [`Number.isInteger`](https://mdn.io/Number/isInteger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an integer, else `false`.\n * @example\n *\n * _.isInteger(3);\n * // => true\n *\n * _.isInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isInteger(Infinity);\n * // => false\n *\n * _.isInteger('3');\n * // => false\n */\n function isInteger(value) {\n return typeof value == 'number' && value == toInteger(value);\n }\n\n /**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\n function isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n }\n\n /**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\n function isObject(value) {\n var type = typeof value;\n return value != null && (type == 'object' || type == 'function');\n }\n\n /**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\n function isObjectLike(value) {\n return value != null && typeof value == 'object';\n }\n\n /**\n * Checks if `value` is classified as a `Map` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n * @example\n *\n * _.isMap(new Map);\n * // => true\n *\n * _.isMap(new WeakMap);\n * // => false\n */\n var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;\n\n /**\n * Performs a partial deep comparison between `object` and `source` to\n * determine if `object` contains equivalent property values.\n *\n * **Note:** This method is equivalent to `_.matches` when `source` is\n * partially applied.\n *\n * Partial comparisons will match empty array and empty object `source`\n * values against any array or object value, respectively. See `_.isEqual`\n * for a list of supported value comparisons.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n *\n * _.isMatch(object, { 'b': 2 });\n * // => true\n *\n * _.isMatch(object, { 'b': 1 });\n * // => false\n */\n function isMatch(object, source) {\n return object === source || baseIsMatch(object, source, getMatchData(source));\n }\n\n /**\n * This method is like `_.isMatch` except that it accepts `customizer` which\n * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n * are handled by the method instead. The `customizer` is invoked with five\n * arguments: (objValue, srcValue, index|key, object, source).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n * @example\n *\n * function isGreeting(value) {\n * return /^h(?:i|ello)$/.test(value);\n * }\n *\n * function customizer(objValue, srcValue) {\n * if (isGreeting(objValue) && isGreeting(srcValue)) {\n * return true;\n * }\n * }\n *\n * var object = { 'greeting': 'hello' };\n * var source = { 'greeting': 'hi' };\n *\n * _.isMatchWith(object, source, customizer);\n * // => true\n */\n function isMatchWith(object, source, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return baseIsMatch(object, source, getMatchData(source), customizer);\n }\n\n /**\n * Checks if `value` is `NaN`.\n *\n * **Note:** This method is based on\n * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as\n * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for\n * `undefined` and other non-number values.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n * @example\n *\n * _.isNaN(NaN);\n * // => true\n *\n * _.isNaN(new Number(NaN));\n * // => true\n *\n * isNaN(undefined);\n * // => true\n *\n * _.isNaN(undefined);\n * // => false\n */\n function isNaN(value) {\n // An `NaN` primitive is the only value that is not equal to itself.\n // Perform the `toStringTag` check first to avoid errors with some\n // ActiveX objects in IE.\n return isNumber(value) && value != +value;\n }\n\n /**\n * Checks if `value` is a pristine native function.\n *\n * **Note:** This method can't reliably detect native functions in the presence\n * of the core-js package because core-js circumvents this kind of detection.\n * Despite multiple requests, the core-js maintainer has made it clear: any\n * attempt to fix the detection will be obstructed. As a result, we're left\n * with little choice but to throw an error. Unfortunately, this also affects\n * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),\n * which rely on core-js.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n * @example\n *\n * _.isNative(Array.prototype.push);\n * // => true\n *\n * _.isNative(_);\n * // => false\n */\n function isNative(value) {\n if (isMaskable(value)) {\n throw new Error(CORE_ERROR_TEXT);\n }\n return baseIsNative(value);\n }\n\n /**\n * Checks if `value` is `null`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `null`, else `false`.\n * @example\n *\n * _.isNull(null);\n * // => true\n *\n * _.isNull(void 0);\n * // => false\n */\n function isNull(value) {\n return value === null;\n }\n\n /**\n * Checks if `value` is `null` or `undefined`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is nullish, else `false`.\n * @example\n *\n * _.isNil(null);\n * // => true\n *\n * _.isNil(void 0);\n * // => true\n *\n * _.isNil(NaN);\n * // => false\n */\n function isNil(value) {\n return value == null;\n }\n\n /**\n * Checks if `value` is classified as a `Number` primitive or object.\n *\n * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are\n * classified as numbers, use the `_.isFinite` method.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a number, else `false`.\n * @example\n *\n * _.isNumber(3);\n * // => true\n *\n * _.isNumber(Number.MIN_VALUE);\n * // => true\n *\n * _.isNumber(Infinity);\n * // => true\n *\n * _.isNumber('3');\n * // => false\n */\n function isNumber(value) {\n return typeof value == 'number' ||\n (isObjectLike(value) && baseGetTag(value) == numberTag);\n }\n\n /**\n * Checks if `value` is a plain object, that is, an object created by the\n * `Object` constructor or one with a `[[Prototype]]` of `null`.\n *\n * @static\n * @memberOf _\n * @since 0.8.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * _.isPlainObject(new Foo);\n * // => false\n *\n * _.isPlainObject([1, 2, 3]);\n * // => false\n *\n * _.isPlainObject({ 'x': 0, 'y': 0 });\n * // => true\n *\n * _.isPlainObject(Object.create(null));\n * // => true\n */\n function isPlainObject(value) {\n if (!isObjectLike(value) || baseGetTag(value) != objectTag) {\n return false;\n }\n var proto = getPrototype(value);\n if (proto === null) {\n return true;\n }\n var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;\n return typeof Ctor == 'function' && Ctor instanceof Ctor &&\n funcToString.call(Ctor) == objectCtorString;\n }\n\n /**\n * Checks if `value` is classified as a `RegExp` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.\n * @example\n *\n * _.isRegExp(/abc/);\n * // => true\n *\n * _.isRegExp('/abc/');\n * // => false\n */\n var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;\n\n /**\n * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754\n * double precision number which isn't the result of a rounded unsafe integer.\n *\n * **Note:** This method is based on\n * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.\n * @example\n *\n * _.isSafeInteger(3);\n * // => true\n *\n * _.isSafeInteger(Number.MIN_VALUE);\n * // => false\n *\n * _.isSafeInteger(Infinity);\n * // => false\n *\n * _.isSafeInteger('3');\n * // => false\n */\n function isSafeInteger(value) {\n return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;\n }\n\n /**\n * Checks if `value` is classified as a `Set` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a set, else `false`.\n * @example\n *\n * _.isSet(new Set);\n * // => true\n *\n * _.isSet(new WeakSet);\n * // => false\n */\n var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;\n\n /**\n * Checks if `value` is classified as a `String` primitive or object.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a string, else `false`.\n * @example\n *\n * _.isString('abc');\n * // => true\n *\n * _.isString(1);\n * // => false\n */\n function isString(value) {\n return typeof value == 'string' ||\n (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);\n }\n\n /**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\n function isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && baseGetTag(value) == symbolTag);\n }\n\n /**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\n var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\n /**\n * Checks if `value` is `undefined`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.\n * @example\n *\n * _.isUndefined(void 0);\n * // => true\n *\n * _.isUndefined(null);\n * // => false\n */\n function isUndefined(value) {\n return value === undefined;\n }\n\n /**\n * Checks if `value` is classified as a `WeakMap` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.\n * @example\n *\n * _.isWeakMap(new WeakMap);\n * // => true\n *\n * _.isWeakMap(new Map);\n * // => false\n */\n function isWeakMap(value) {\n return isObjectLike(value) && getTag(value) == weakMapTag;\n }\n\n /**\n * Checks if `value` is classified as a `WeakSet` object.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.\n * @example\n *\n * _.isWeakSet(new WeakSet);\n * // => true\n *\n * _.isWeakSet(new Set);\n * // => false\n */\n function isWeakSet(value) {\n return isObjectLike(value) && baseGetTag(value) == weakSetTag;\n }\n\n /**\n * Checks if `value` is less than `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than `other`,\n * else `false`.\n * @see _.gt\n * @example\n *\n * _.lt(1, 3);\n * // => true\n *\n * _.lt(3, 3);\n * // => false\n *\n * _.lt(3, 1);\n * // => false\n */\n var lt = createRelationalOperation(baseLt);\n\n /**\n * Checks if `value` is less than or equal to `other`.\n *\n * @static\n * @memberOf _\n * @since 3.9.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is less than or equal to\n * `other`, else `false`.\n * @see _.gte\n * @example\n *\n * _.lte(1, 3);\n * // => true\n *\n * _.lte(3, 3);\n * // => true\n *\n * _.lte(3, 1);\n * // => false\n */\n var lte = createRelationalOperation(function(value, other) {\n return value <= other;\n });\n\n /**\n * Converts `value` to an array.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {Array} Returns the converted array.\n * @example\n *\n * _.toArray({ 'a': 1, 'b': 2 });\n * // => [1, 2]\n *\n * _.toArray('abc');\n * // => ['a', 'b', 'c']\n *\n * _.toArray(1);\n * // => []\n *\n * _.toArray(null);\n * // => []\n */\n function toArray(value) {\n if (!value) {\n return [];\n }\n if (isArrayLike(value)) {\n return isString(value) ? stringToArray(value) : copyArray(value);\n }\n if (symIterator && value[symIterator]) {\n return iteratorToArray(value[symIterator]());\n }\n var tag = getTag(value),\n func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);\n\n return func(value);\n }\n\n /**\n * Converts `value` to a finite number.\n *\n * @static\n * @memberOf _\n * @since 4.12.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted number.\n * @example\n *\n * _.toFinite(3.2);\n * // => 3.2\n *\n * _.toFinite(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toFinite(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toFinite('3.2');\n * // => 3.2\n */\n function toFinite(value) {\n if (!value) {\n return value === 0 ? value : 0;\n }\n value = toNumber(value);\n if (value === INFINITY || value === -INFINITY) {\n var sign = (value < 0 ? -1 : 1);\n return sign * MAX_INTEGER;\n }\n return value === value ? value : 0;\n }\n\n /**\n * Converts `value` to an integer.\n *\n * **Note:** This method is loosely based on\n * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toInteger(3.2);\n * // => 3\n *\n * _.toInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toInteger(Infinity);\n * // => 1.7976931348623157e+308\n *\n * _.toInteger('3.2');\n * // => 3\n */\n function toInteger(value) {\n var result = toFinite(value),\n remainder = result % 1;\n\n return result === result ? (remainder ? result - remainder : result) : 0;\n }\n\n /**\n * Converts `value` to an integer suitable for use as the length of an\n * array-like object.\n *\n * **Note:** This method is based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toLength(3.2);\n * // => 3\n *\n * _.toLength(Number.MIN_VALUE);\n * // => 0\n *\n * _.toLength(Infinity);\n * // => 4294967295\n *\n * _.toLength('3.2');\n * // => 3\n */\n function toLength(value) {\n return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;\n }\n\n /**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\n function toNumber(value) {\n if (typeof value == 'number') {\n return value;\n }\n if (isSymbol(value)) {\n return NAN;\n }\n if (isObject(value)) {\n var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n value = isObject(other) ? (other + '') : other;\n }\n if (typeof value != 'string') {\n return value === 0 ? value : +value;\n }\n value = value.replace(reTrim, '');\n var isBinary = reIsBinary.test(value);\n return (isBinary || reIsOctal.test(value))\n ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n : (reIsBadHex.test(value) ? NAN : +value);\n }\n\n /**\n * Converts `value` to a plain object flattening inherited enumerable string\n * keyed properties of `value` to own properties of the plain object.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {Object} Returns the converted plain object.\n * @example\n *\n * function Foo() {\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.assign({ 'a': 1 }, new Foo);\n * // => { 'a': 1, 'b': 2 }\n *\n * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));\n * // => { 'a': 1, 'b': 2, 'c': 3 }\n */\n function toPlainObject(value) {\n return copyObject(value, keysIn(value));\n }\n\n /**\n * Converts `value` to a safe integer. A safe integer can be compared and\n * represented correctly.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.toSafeInteger(3.2);\n * // => 3\n *\n * _.toSafeInteger(Number.MIN_VALUE);\n * // => 0\n *\n * _.toSafeInteger(Infinity);\n * // => 9007199254740991\n *\n * _.toSafeInteger('3.2');\n * // => 3\n */\n function toSafeInteger(value) {\n return value\n ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)\n : (value === 0 ? value : 0);\n }\n\n /**\n * Converts `value` to a string. An empty string is returned for `null`\n * and `undefined` values. The sign of `-0` is preserved.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.toString(null);\n * // => ''\n *\n * _.toString(-0);\n * // => '-0'\n *\n * _.toString([1, 2, 3]);\n * // => '1,2,3'\n */\n function toString(value) {\n return value == null ? '' : baseToString(value);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Assigns own enumerable string keyed properties of source objects to the\n * destination object. Source objects are applied from left to right.\n * Subsequent sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object` and is loosely based on\n * [`Object.assign`](https://mdn.io/Object/assign).\n *\n * @static\n * @memberOf _\n * @since 0.10.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.assignIn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * function Bar() {\n * this.c = 3;\n * }\n *\n * Foo.prototype.b = 2;\n * Bar.prototype.d = 4;\n *\n * _.assign({ 'a': 0 }, new Foo, new Bar);\n * // => { 'a': 1, 'c': 3 }\n */\n var assign = createAssigner(function(object, source) {\n if (isPrototype(source) || isArrayLike(source)) {\n copyObject(source, keys(source), object);\n return;\n }\n for (var key in source) {\n if (hasOwnProperty.call(source, key)) {\n assignValue(object, key, source[key]);\n }\n }\n });\n\n /**\n * This method is like `_.assign` except that it iterates over own and\n * inherited source properties.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias extend\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.assign\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * }\n *\n * function Bar() {\n * this.c = 3;\n * }\n *\n * Foo.prototype.b = 2;\n * Bar.prototype.d = 4;\n *\n * _.assignIn({ 'a': 0 }, new Foo, new Bar);\n * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }\n */\n var assignIn = createAssigner(function(object, source) {\n copyObject(source, keysIn(source), object);\n });\n\n /**\n * This method is like `_.assignIn` except that it accepts `customizer`\n * which is invoked to produce the assigned values. If `customizer` returns\n * `undefined`, assignment is handled by the method instead. The `customizer`\n * is invoked with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias extendWith\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @see _.assignWith\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignInWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {\n copyObject(source, keysIn(source), object, customizer);\n });\n\n /**\n * This method is like `_.assign` except that it accepts `customizer`\n * which is invoked to produce the assigned values. If `customizer` returns\n * `undefined`, assignment is handled by the method instead. The `customizer`\n * is invoked with five arguments: (objValue, srcValue, key, object, source).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @see _.assignInWith\n * @example\n *\n * function customizer(objValue, srcValue) {\n * return _.isUndefined(objValue) ? srcValue : objValue;\n * }\n *\n * var defaults = _.partialRight(_.assignWith, customizer);\n *\n * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var assignWith = createAssigner(function(object, source, srcIndex, customizer) {\n copyObject(source, keys(source), object, customizer);\n });\n\n /**\n * Creates an array of values corresponding to `paths` of `object`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Array} Returns the picked values.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n *\n * _.at(object, ['a[0].b.c', 'a[1]']);\n * // => [3, 4]\n */\n var at = flatRest(baseAt);\n\n /**\n * Creates an object that inherits from the `prototype` object. If a\n * `properties` object is given, its own enumerable string keyed properties\n * are assigned to the created object.\n *\n * @static\n * @memberOf _\n * @since 2.3.0\n * @category Object\n * @param {Object} prototype The object to inherit from.\n * @param {Object} [properties] The properties to assign to the object.\n * @returns {Object} Returns the new object.\n * @example\n *\n * function Shape() {\n * this.x = 0;\n * this.y = 0;\n * }\n *\n * function Circle() {\n * Shape.call(this);\n * }\n *\n * Circle.prototype = _.create(Shape.prototype, {\n * 'constructor': Circle\n * });\n *\n * var circle = new Circle;\n * circle instanceof Circle;\n * // => true\n *\n * circle instanceof Shape;\n * // => true\n */\n function create(prototype, properties) {\n var result = baseCreate(prototype);\n return properties == null ? result : baseAssign(result, properties);\n }\n\n /**\n * Assigns own and inherited enumerable string keyed properties of source\n * objects to the destination object for all destination properties that\n * resolve to `undefined`. Source objects are applied from left to right.\n * Once a property is set, additional values of the same property are ignored.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.defaultsDeep\n * @example\n *\n * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n * // => { 'a': 1, 'b': 2 }\n */\n var defaults = baseRest(function(object, sources) {\n object = Object(object);\n\n var index = -1;\n var length = sources.length;\n var guard = length > 2 ? sources[2] : undefined;\n\n if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n length = 1;\n }\n\n while (++index < length) {\n var source = sources[index];\n var props = keysIn(source);\n var propsIndex = -1;\n var propsLength = props.length;\n\n while (++propsIndex < propsLength) {\n var key = props[propsIndex];\n var value = object[key];\n\n if (value === undefined ||\n (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) {\n object[key] = source[key];\n }\n }\n }\n\n return object;\n });\n\n /**\n * This method is like `_.defaults` except that it recursively assigns\n * default properties.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 3.10.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @see _.defaults\n * @example\n *\n * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });\n * // => { 'a': { 'b': 2, 'c': 3 } }\n */\n var defaultsDeep = baseRest(function(args) {\n args.push(undefined, customDefaultsMerge);\n return apply(mergeWith, undefined, args);\n });\n\n /**\n * This method is like `_.find` except that it returns the key of the first\n * element `predicate` returns truthy for instead of the element itself.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {string|undefined} Returns the key of the matched element,\n * else `undefined`.\n * @example\n *\n * var users = {\n * 'barney': { 'age': 36, 'active': true },\n * 'fred': { 'age': 40, 'active': false },\n * 'pebbles': { 'age': 1, 'active': true }\n * };\n *\n * _.findKey(users, function(o) { return o.age < 40; });\n * // => 'barney' (iteration order is not guaranteed)\n *\n * // The `_.matches` iteratee shorthand.\n * _.findKey(users, { 'age': 1, 'active': true });\n * // => 'pebbles'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findKey(users, ['active', false]);\n * // => 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.findKey(users, 'active');\n * // => 'barney'\n */\n function findKey(object, predicate) {\n return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);\n }\n\n /**\n * This method is like `_.findKey` except that it iterates over elements of\n * a collection in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @param {Function} [predicate=_.identity] The function invoked per iteration.\n * @returns {string|undefined} Returns the key of the matched element,\n * else `undefined`.\n * @example\n *\n * var users = {\n * 'barney': { 'age': 36, 'active': true },\n * 'fred': { 'age': 40, 'active': false },\n * 'pebbles': { 'age': 1, 'active': true }\n * };\n *\n * _.findLastKey(users, function(o) { return o.age < 40; });\n * // => returns 'pebbles' assuming `_.findKey` returns 'barney'\n *\n * // The `_.matches` iteratee shorthand.\n * _.findLastKey(users, { 'age': 36, 'active': true });\n * // => 'barney'\n *\n * // The `_.matchesProperty` iteratee shorthand.\n * _.findLastKey(users, ['active', false]);\n * // => 'fred'\n *\n * // The `_.property` iteratee shorthand.\n * _.findLastKey(users, 'active');\n * // => 'pebbles'\n */\n function findLastKey(object, predicate) {\n return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);\n }\n\n /**\n * Iterates over own and inherited enumerable string keyed properties of an\n * object and invokes `iteratee` for each property. The iteratee is invoked\n * with three arguments: (value, key, object). Iteratee functions may exit\n * iteration early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 0.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forInRight\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forIn(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).\n */\n function forIn(object, iteratee) {\n return object == null\n ? object\n : baseFor(object, getIteratee(iteratee, 3), keysIn);\n }\n\n /**\n * This method is like `_.forIn` except that it iterates over properties of\n * `object` in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forIn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forInRight(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.\n */\n function forInRight(object, iteratee) {\n return object == null\n ? object\n : baseForRight(object, getIteratee(iteratee, 3), keysIn);\n }\n\n /**\n * Iterates over own enumerable string keyed properties of an object and\n * invokes `iteratee` for each property. The iteratee is invoked with three\n * arguments: (value, key, object). Iteratee functions may exit iteration\n * early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 0.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forOwnRight\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forOwn(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n */\n function forOwn(object, iteratee) {\n return object && baseForOwn(object, getIteratee(iteratee, 3));\n }\n\n /**\n * This method is like `_.forOwn` except that it iterates over properties of\n * `object` in the opposite order.\n *\n * @static\n * @memberOf _\n * @since 2.0.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns `object`.\n * @see _.forOwn\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.forOwnRight(new Foo, function(value, key) {\n * console.log(key);\n * });\n * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.\n */\n function forOwnRight(object, iteratee) {\n return object && baseForOwnRight(object, getIteratee(iteratee, 3));\n }\n\n /**\n * Creates an array of function property names from own enumerable properties\n * of `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the function names.\n * @see _.functionsIn\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functions(new Foo);\n * // => ['a', 'b']\n */\n function functions(object) {\n return object == null ? [] : baseFunctions(object, keys(object));\n }\n\n /**\n * Creates an array of function property names from own and inherited\n * enumerable properties of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to inspect.\n * @returns {Array} Returns the function names.\n * @see _.functions\n * @example\n *\n * function Foo() {\n * this.a = _.constant('a');\n * this.b = _.constant('b');\n * }\n *\n * Foo.prototype.c = _.constant('c');\n *\n * _.functionsIn(new Foo);\n * // => ['a', 'b', 'c']\n */\n function functionsIn(object) {\n return object == null ? [] : baseFunctions(object, keysIn(object));\n }\n\n /**\n * Gets the value at `path` of `object`. If the resolved value is\n * `undefined`, the `defaultValue` is returned in its place.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.get(object, 'a[0].b.c');\n * // => 3\n *\n * _.get(object, ['a', '0', 'b', 'c']);\n * // => 3\n *\n * _.get(object, 'a.b.c', 'default');\n * // => 'default'\n */\n function get(object, path, defaultValue) {\n var result = object == null ? undefined : baseGet(object, path);\n return result === undefined ? defaultValue : result;\n }\n\n /**\n * Checks if `path` is a direct property of `object`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = { 'a': { 'b': 2 } };\n * var other = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.has(object, 'a');\n * // => true\n *\n * _.has(object, 'a.b');\n * // => true\n *\n * _.has(object, ['a', 'b']);\n * // => true\n *\n * _.has(other, 'a');\n * // => false\n */\n function has(object, path) {\n return object != null && hasPath(object, path, baseHas);\n }\n\n /**\n * Checks if `path` is a direct or inherited property of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.hasIn(object, 'a');\n * // => true\n *\n * _.hasIn(object, 'a.b');\n * // => true\n *\n * _.hasIn(object, ['a', 'b']);\n * // => true\n *\n * _.hasIn(object, 'b');\n * // => false\n */\n function hasIn(object, path) {\n return object != null && hasPath(object, path, baseHasIn);\n }\n\n /**\n * Creates an object composed of the inverted keys and values of `object`.\n * If `object` contains duplicate values, subsequent values overwrite\n * property assignments of previous values.\n *\n * @static\n * @memberOf _\n * @since 0.7.0\n * @category Object\n * @param {Object} object The object to invert.\n * @returns {Object} Returns the new inverted object.\n * @example\n *\n * var object = { 'a': 1, 'b': 2, 'c': 1 };\n *\n * _.invert(object);\n * // => { '1': 'c', '2': 'b' }\n */\n var invert = createInverter(function(result, value, key) {\n if (value != null &&\n typeof value.toString != 'function') {\n value = nativeObjectToString.call(value);\n }\n\n result[value] = key;\n }, constant(identity));\n\n /**\n * This method is like `_.invert` except that the inverted object is generated\n * from the results of running each element of `object` thru `iteratee`. The\n * corresponding inverted value of each inverted key is an array of keys\n * responsible for generating the inverted value. The iteratee is invoked\n * with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.1.0\n * @category Object\n * @param {Object} object The object to invert.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {Object} Returns the new inverted object.\n * @example\n *\n * var object = { 'a': 1, 'b': 2, 'c': 1 };\n *\n * _.invertBy(object);\n * // => { '1': ['a', 'c'], '2': ['b'] }\n *\n * _.invertBy(object, function(value) {\n * return 'group' + value;\n * });\n * // => { 'group1': ['a', 'c'], 'group2': ['b'] }\n */\n var invertBy = createInverter(function(result, value, key) {\n if (value != null &&\n typeof value.toString != 'function') {\n value = nativeObjectToString.call(value);\n }\n\n if (hasOwnProperty.call(result, value)) {\n result[value].push(key);\n } else {\n result[value] = [key];\n }\n }, getIteratee);\n\n /**\n * Invokes the method at `path` of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the method to invoke.\n * @param {...*} [args] The arguments to invoke the method with.\n * @returns {*} Returns the result of the invoked method.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };\n *\n * _.invoke(object, 'a[0].b.c.slice', 1, 3);\n * // => [2, 3]\n */\n var invoke = baseRest(baseInvoke);\n\n /**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\n function keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n }\n\n /**\n * Creates an array of the own and inherited enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keysIn(new Foo);\n * // => ['a', 'b', 'c'] (iteration order is not guaranteed)\n */\n function keysIn(object) {\n return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);\n }\n\n /**\n * The opposite of `_.mapValues`; this method creates an object with the\n * same values as `object` and keys generated by running each own enumerable\n * string keyed property of `object` thru `iteratee`. The iteratee is invoked\n * with three arguments: (value, key, object).\n *\n * @static\n * @memberOf _\n * @since 3.8.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns the new mapped object.\n * @see _.mapValues\n * @example\n *\n * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {\n * return key + value;\n * });\n * // => { 'a1': 1, 'b2': 2 }\n */\n function mapKeys(object, iteratee) {\n var result = {};\n iteratee = getIteratee(iteratee, 3);\n\n baseForOwn(object, function(value, key, object) {\n baseAssignValue(result, iteratee(value, key, object), value);\n });\n return result;\n }\n\n /**\n * Creates an object with the same keys as `object` and values generated\n * by running each own enumerable string keyed property of `object` thru\n * `iteratee`. The iteratee is invoked with three arguments:\n * (value, key, object).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @returns {Object} Returns the new mapped object.\n * @see _.mapKeys\n * @example\n *\n * var users = {\n * 'fred': { 'user': 'fred', 'age': 40 },\n * 'pebbles': { 'user': 'pebbles', 'age': 1 }\n * };\n *\n * _.mapValues(users, function(o) { return o.age; });\n * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n *\n * // The `_.property` iteratee shorthand.\n * _.mapValues(users, 'age');\n * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n */\n function mapValues(object, iteratee) {\n var result = {};\n iteratee = getIteratee(iteratee, 3);\n\n baseForOwn(object, function(value, key, object) {\n baseAssignValue(result, key, iteratee(value, key, object));\n });\n return result;\n }\n\n /**\n * This method is like `_.assign` except that it recursively merges own and\n * inherited enumerable string keyed properties of source objects into the\n * destination object. Source properties that resolve to `undefined` are\n * skipped if a destination value exists. Array and plain object properties\n * are merged recursively. Other objects and value types are overridden by\n * assignment. Source objects are applied from left to right. Subsequent\n * sources overwrite property assignments of previous sources.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 0.5.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} [sources] The source objects.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {\n * 'a': [{ 'b': 2 }, { 'd': 4 }]\n * };\n *\n * var other = {\n * 'a': [{ 'c': 3 }, { 'e': 5 }]\n * };\n *\n * _.merge(object, other);\n * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }\n */\n var merge = createAssigner(function(object, source, srcIndex) {\n baseMerge(object, source, srcIndex);\n });\n\n /**\n * This method is like `_.merge` except that it accepts `customizer` which\n * is invoked to produce the merged values of the destination and source\n * properties. If `customizer` returns `undefined`, merging is handled by the\n * method instead. The `customizer` is invoked with six arguments:\n * (objValue, srcValue, key, object, source, stack).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The destination object.\n * @param {...Object} sources The source objects.\n * @param {Function} customizer The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * function customizer(objValue, srcValue) {\n * if (_.isArray(objValue)) {\n * return objValue.concat(srcValue);\n * }\n * }\n *\n * var object = { 'a': [1], 'b': [2] };\n * var other = { 'a': [3], 'b': [4] };\n *\n * _.mergeWith(object, other, customizer);\n * // => { 'a': [1, 3], 'b': [2, 4] }\n */\n var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {\n baseMerge(object, source, srcIndex, customizer);\n });\n\n /**\n * The opposite of `_.pick`; this method creates an object composed of the\n * own and inherited enumerable property paths of `object` that are not omitted.\n *\n * **Note:** This method is considerably slower than `_.pick`.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to omit.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omit(object, ['a', 'c']);\n * // => { 'b': '2' }\n */\n var omit = flatRest(function(object, paths) {\n var result = {};\n if (object == null) {\n return result;\n }\n var isDeep = false;\n paths = arrayMap(paths, function(path) {\n path = castPath(path, object);\n isDeep || (isDeep = path.length > 1);\n return path;\n });\n copyObject(object, getAllKeysIn(object), result);\n if (isDeep) {\n result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);\n }\n var length = paths.length;\n while (length--) {\n baseUnset(result, paths[length]);\n }\n return result;\n });\n\n /**\n * The opposite of `_.pickBy`; this method creates an object composed of\n * the own and inherited enumerable string keyed properties of `object` that\n * `predicate` doesn't return truthy for. The predicate is invoked with two\n * arguments: (value, key).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The source object.\n * @param {Function} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.omitBy(object, _.isNumber);\n * // => { 'b': '2' }\n */\n function omitBy(object, predicate) {\n return pickBy(object, negate(getIteratee(predicate)));\n }\n\n /**\n * Creates an object composed of the picked `object` properties.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The source object.\n * @param {...(string|string[])} [paths] The property paths to pick.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pick(object, ['a', 'c']);\n * // => { 'a': 1, 'c': 3 }\n */\n var pick = flatRest(function(object, paths) {\n return object == null ? {} : basePick(object, paths);\n });\n\n /**\n * Creates an object composed of the `object` properties `predicate` returns\n * truthy for. The predicate is invoked with two arguments: (value, key).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The source object.\n * @param {Function} [predicate=_.identity] The function invoked per property.\n * @returns {Object} Returns the new object.\n * @example\n *\n * var object = { 'a': 1, 'b': '2', 'c': 3 };\n *\n * _.pickBy(object, _.isNumber);\n * // => { 'a': 1, 'c': 3 }\n */\n function pickBy(object, predicate) {\n if (object == null) {\n return {};\n }\n var props = arrayMap(getAllKeysIn(object), function(prop) {\n return [prop];\n });\n predicate = getIteratee(predicate);\n return basePickBy(object, props, function(value, path) {\n return predicate(value, path[0]);\n });\n }\n\n /**\n * This method is like `_.get` except that if the resolved value is a\n * function it's invoked with the `this` binding of its parent object and\n * its result is returned.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to resolve.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };\n *\n * _.result(object, 'a[0].b.c1');\n * // => 3\n *\n * _.result(object, 'a[0].b.c2');\n * // => 4\n *\n * _.result(object, 'a[0].b.c3', 'default');\n * // => 'default'\n *\n * _.result(object, 'a[0].b.c3', _.constant('default'));\n * // => 'default'\n */\n function result(object, path, defaultValue) {\n path = castPath(path, object);\n\n var index = -1,\n length = path.length;\n\n // Ensure the loop is entered when path is empty.\n if (!length) {\n length = 1;\n object = undefined;\n }\n while (++index < length) {\n var value = object == null ? undefined : object[toKey(path[index])];\n if (value === undefined) {\n index = length;\n value = defaultValue;\n }\n object = isFunction(value) ? value.call(object) : value;\n }\n return object;\n }\n\n /**\n * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,\n * it's created. Arrays are created for missing index properties while objects\n * are created for all other missing properties. Use `_.setWith` to customize\n * `path` creation.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.set(object, 'a[0].b.c', 4);\n * console.log(object.a[0].b.c);\n * // => 4\n *\n * _.set(object, ['x', '0', 'y', 'z'], 5);\n * console.log(object.x[0].y.z);\n * // => 5\n */\n function set(object, path, value) {\n return object == null ? object : baseSet(object, path, value);\n }\n\n /**\n * This method is like `_.set` except that it accepts `customizer` which is\n * invoked to produce the objects of `path`. If `customizer` returns `undefined`\n * path creation is handled by the method instead. The `customizer` is invoked\n * with three arguments: (nsValue, key, nsObject).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {*} value The value to set.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {};\n *\n * _.setWith(object, '[0][1]', 'a', Object);\n * // => { '0': { '1': 'a' } }\n */\n function setWith(object, path, value, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return object == null ? object : baseSet(object, path, value, customizer);\n }\n\n /**\n * Creates an array of own enumerable string keyed-value pairs for `object`\n * which can be consumed by `_.fromPairs`. If `object` is a map or set, its\n * entries are returned.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias entries\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the key-value pairs.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.toPairs(new Foo);\n * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)\n */\n var toPairs = createToPairs(keys);\n\n /**\n * Creates an array of own and inherited enumerable string keyed-value pairs\n * for `object` which can be consumed by `_.fromPairs`. If `object` is a map\n * or set, its entries are returned.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @alias entriesIn\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the key-value pairs.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.toPairsIn(new Foo);\n * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)\n */\n var toPairsIn = createToPairs(keysIn);\n\n /**\n * An alternative to `_.reduce`; this method transforms `object` to a new\n * `accumulator` object which is the result of running each of its own\n * enumerable string keyed properties thru `iteratee`, with each invocation\n * potentially mutating the `accumulator` object. If `accumulator` is not\n * provided, a new object with the same `[[Prototype]]` will be used. The\n * iteratee is invoked with four arguments: (accumulator, value, key, object).\n * Iteratee functions may exit iteration early by explicitly returning `false`.\n *\n * @static\n * @memberOf _\n * @since 1.3.0\n * @category Object\n * @param {Object} object The object to iterate over.\n * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n * @param {*} [accumulator] The custom accumulator value.\n * @returns {*} Returns the accumulated value.\n * @example\n *\n * _.transform([2, 3, 4], function(result, n) {\n * result.push(n *= n);\n * return n % 2 == 0;\n * }, []);\n * // => [4, 9]\n *\n * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n * (result[value] || (result[value] = [])).push(key);\n * }, {});\n * // => { '1': ['a', 'c'], '2': ['b'] }\n */\n function transform(object, iteratee, accumulator) {\n var isArr = isArray(object),\n isArrLike = isArr || isBuffer(object) || isTypedArray(object);\n\n iteratee = getIteratee(iteratee, 4);\n if (accumulator == null) {\n var Ctor = object && object.constructor;\n if (isArrLike) {\n accumulator = isArr ? new Ctor : [];\n }\n else if (isObject(object)) {\n accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};\n }\n else {\n accumulator = {};\n }\n }\n (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) {\n return iteratee(accumulator, value, index, object);\n });\n return accumulator;\n }\n\n /**\n * Removes the property at `path` of `object`.\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to unset.\n * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 7 } }] };\n * _.unset(object, 'a[0].b.c');\n * // => true\n *\n * console.log(object);\n * // => { 'a': [{ 'b': {} }] };\n *\n * _.unset(object, ['a', '0', 'b', 'c']);\n * // => true\n *\n * console.log(object);\n * // => { 'a': [{ 'b': {} }] };\n */\n function unset(object, path) {\n return object == null ? true : baseUnset(object, path);\n }\n\n /**\n * This method is like `_.set` except that accepts `updater` to produce the\n * value to set. Use `_.updateWith` to customize `path` creation. The `updater`\n * is invoked with one argument: (value).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {Function} updater The function to produce the updated value.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.update(object, 'a[0].b.c', function(n) { return n * n; });\n * console.log(object.a[0].b.c);\n * // => 9\n *\n * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });\n * console.log(object.x[0].y.z);\n * // => 0\n */\n function update(object, path, updater) {\n return object == null ? object : baseUpdate(object, path, castFunction(updater));\n }\n\n /**\n * This method is like `_.update` except that it accepts `customizer` which is\n * invoked to produce the objects of `path`. If `customizer` returns `undefined`\n * path creation is handled by the method instead. The `customizer` is invoked\n * with three arguments: (nsValue, key, nsObject).\n *\n * **Note:** This method mutates `object`.\n *\n * @static\n * @memberOf _\n * @since 4.6.0\n * @category Object\n * @param {Object} object The object to modify.\n * @param {Array|string} path The path of the property to set.\n * @param {Function} updater The function to produce the updated value.\n * @param {Function} [customizer] The function to customize assigned values.\n * @returns {Object} Returns `object`.\n * @example\n *\n * var object = {};\n *\n * _.updateWith(object, '[0][1]', _.constant('a'), Object);\n * // => { '0': { '1': 'a' } }\n */\n function updateWith(object, path, updater, customizer) {\n customizer = typeof customizer == 'function' ? customizer : undefined;\n return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);\n }\n\n /**\n * Creates an array of the own enumerable string keyed property values of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property values.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.values(new Foo);\n * // => [1, 2] (iteration order is not guaranteed)\n *\n * _.values('hi');\n * // => ['h', 'i']\n */\n function values(object) {\n return object == null ? [] : baseValues(object, keys(object));\n }\n\n /**\n * Creates an array of the own and inherited enumerable string keyed property\n * values of `object`.\n *\n * **Note:** Non-object values are coerced to objects.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property values.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.valuesIn(new Foo);\n * // => [1, 2, 3] (iteration order is not guaranteed)\n */\n function valuesIn(object) {\n return object == null ? [] : baseValues(object, keysIn(object));\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Clamps `number` within the inclusive `lower` and `upper` bounds.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Number\n * @param {number} number The number to clamp.\n * @param {number} [lower] The lower bound.\n * @param {number} upper The upper bound.\n * @returns {number} Returns the clamped number.\n * @example\n *\n * _.clamp(-10, -5, 5);\n * // => -5\n *\n * _.clamp(10, -5, 5);\n * // => 5\n */\n function clamp(number, lower, upper) {\n if (upper === undefined) {\n upper = lower;\n lower = undefined;\n }\n if (upper !== undefined) {\n upper = toNumber(upper);\n upper = upper === upper ? upper : 0;\n }\n if (lower !== undefined) {\n lower = toNumber(lower);\n lower = lower === lower ? lower : 0;\n }\n return baseClamp(toNumber(number), lower, upper);\n }\n\n /**\n * Checks if `n` is between `start` and up to, but not including, `end`. If\n * `end` is not specified, it's set to `start` with `start` then set to `0`.\n * If `start` is greater than `end` the params are swapped to support\n * negative ranges.\n *\n * @static\n * @memberOf _\n * @since 3.3.0\n * @category Number\n * @param {number} number The number to check.\n * @param {number} [start=0] The start of the range.\n * @param {number} end The end of the range.\n * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n * @see _.range, _.rangeRight\n * @example\n *\n * _.inRange(3, 2, 4);\n * // => true\n *\n * _.inRange(4, 8);\n * // => true\n *\n * _.inRange(4, 2);\n * // => false\n *\n * _.inRange(2, 2);\n * // => false\n *\n * _.inRange(1.2, 2);\n * // => true\n *\n * _.inRange(5.2, 4);\n * // => false\n *\n * _.inRange(-3, -2, -6);\n * // => true\n */\n function inRange(number, start, end) {\n start = toFinite(start);\n if (end === undefined) {\n end = start;\n start = 0;\n } else {\n end = toFinite(end);\n }\n number = toNumber(number);\n return baseInRange(number, start, end);\n }\n\n /**\n * Produces a random number between the inclusive `lower` and `upper` bounds.\n * If only one argument is provided a number between `0` and the given number\n * is returned. If `floating` is `true`, or either `lower` or `upper` are\n * floats, a floating-point number is returned instead of an integer.\n *\n * **Note:** JavaScript follows the IEEE-754 standard for resolving\n * floating-point values which can produce unexpected results.\n *\n * @static\n * @memberOf _\n * @since 0.7.0\n * @category Number\n * @param {number} [lower=0] The lower bound.\n * @param {number} [upper=1] The upper bound.\n * @param {boolean} [floating] Specify returning a floating-point number.\n * @returns {number} Returns the random number.\n * @example\n *\n * _.random(0, 5);\n * // => an integer between 0 and 5\n *\n * _.random(5);\n * // => also an integer between 0 and 5\n *\n * _.random(5, true);\n * // => a floating-point number between 0 and 5\n *\n * _.random(1.2, 5.2);\n * // => a floating-point number between 1.2 and 5.2\n */\n function random(lower, upper, floating) {\n if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {\n upper = floating = undefined;\n }\n if (floating === undefined) {\n if (typeof upper == 'boolean') {\n floating = upper;\n upper = undefined;\n }\n else if (typeof lower == 'boolean') {\n floating = lower;\n lower = undefined;\n }\n }\n if (lower === undefined && upper === undefined) {\n lower = 0;\n upper = 1;\n }\n else {\n lower = toFinite(lower);\n if (upper === undefined) {\n upper = lower;\n lower = 0;\n } else {\n upper = toFinite(upper);\n }\n }\n if (lower > upper) {\n var temp = lower;\n lower = upper;\n upper = temp;\n }\n if (floating || lower % 1 || upper % 1) {\n var rand = nativeRandom();\n return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);\n }\n return baseRandom(lower, upper);\n }\n\n /*------------------------------------------------------------------------*/\n\n /**\n * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the camel cased string.\n * @example\n *\n * _.camelCase('Foo Bar');\n * // => 'fooBar'\n *\n * _.camelCase('--foo-bar--');\n * // => 'fooBar'\n *\n * _.camelCase('__FOO_BAR__');\n * // => 'fooBar'\n */\n var camelCase = createCompounder(function(result, word, index) {\n word = word.toLowerCase();\n return result + (index ? capitalize(word) : word);\n });\n\n /**\n * Converts the first character of `string` to upper case and the remaining\n * to lower case.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to capitalize.\n * @returns {string} Returns the capitalized string.\n * @example\n *\n * _.capitalize('FRED');\n * // => 'Fred'\n */\n function capitalize(string) {\n return upperFirst(toString(string).toLowerCase());\n }\n\n /**\n * Deburrs `string` by converting\n * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)\n * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)\n * letters to basic Latin letters and removing\n * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to deburr.\n * @returns {string} Returns the deburred string.\n * @example\n *\n * _.deburr('déjà vu');\n * // => 'deja vu'\n */\n function deburr(string) {\n string = toString(string);\n return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');\n }\n\n /**\n * Checks if `string` ends with the given target string.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to inspect.\n * @param {string} [target] The string to search for.\n * @param {number} [position=string.length] The position to search up to.\n * @returns {boolean} Returns `true` if `string` ends with `target`,\n * else `false`.\n * @example\n *\n * _.endsWith('abc', 'c');\n * // => true\n *\n * _.endsWith('abc', 'b');\n * // => false\n *\n * _.endsWith('abc', 'b', 2);\n * // => true\n */\n function endsWith(string, target, position) {\n string = toString(string);\n target = baseToString(target);\n\n var length = string.length;\n position = position === undefined\n ? length\n : baseClamp(toInteger(position), 0, length);\n\n var end = position;\n position -= target.length;\n return position >= 0 && string.slice(position, end) == target;\n }\n\n /**\n * Converts the characters \"&\", \"<\", \">\", '\"', and \"'\" in `string` to their\n * corresponding HTML entities.\n *\n * **Note:** No other characters are escaped. To escape additional\n * characters use a third-party library like [_he_](https://mths.be/he).\n *\n * Though the \">\" character is escaped for symmetry, characters like\n * \">\" and \"/\" don't need escaping in HTML and have no special meaning\n * unless they're part of a tag or unquoted attribute value. See\n * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)\n * (under \"semi-related fun fact\") for more details.\n *\n * When working with HTML you should always\n * [quote attribute values](http://wonko.com/post/html-escaping) to reduce\n * XSS vectors.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escape('fred, barney, & pebbles');\n * // => 'fred, barney, & pebbles'\n */\n function escape(string) {\n string = toString(string);\n return (string && reHasUnescapedHtml.test(string))\n ? string.replace(reUnescapedHtml, escapeHtmlChar)\n : string;\n }\n\n /**\n * Escapes the `RegExp` special characters \"^\", \"$\", \"\\\", \".\", \"*\", \"+\",\n * \"?\", \"(\", \")\", \"[\", \"]\", \"{\", \"}\", and \"|\" in `string`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to escape.\n * @returns {string} Returns the escaped string.\n * @example\n *\n * _.escapeRegExp('[lodash](https://lodash.com/)');\n * // => '\\[lodash\\]\\(https://lodash\\.com/\\)'\n */\n function escapeRegExp(string) {\n string = toString(string);\n return (string && reHasRegExpChar.test(string))\n ? string.replace(reRegExpChar, '\\\\$&')\n : string;\n }\n\n /**\n * Converts `string` to\n * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the kebab cased string.\n * @example\n *\n * _.kebabCase('Foo Bar');\n * // => 'foo-bar'\n *\n * _.kebabCase('fooBar');\n * // => 'foo-bar'\n *\n * _.kebabCase('__FOO_BAR__');\n * // => 'foo-bar'\n */\n var kebabCase = createCompounder(function(result, word, index) {\n return result + (index ? '-' : '') + word.toLowerCase();\n });\n\n /**\n * Converts `string`, as space separated words, to lower case.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the lower cased string.\n * @example\n *\n * _.lowerCase('--Foo-Bar--');\n * // => 'foo bar'\n *\n * _.lowerCase('fooBar');\n * // => 'foo bar'\n *\n * _.lowerCase('__FOO_BAR__');\n * // => 'foo bar'\n */\n var lowerCase = createCompounder(function(result, word, index) {\n return result + (index ? ' ' : '') + word.toLowerCase();\n });\n\n /**\n * Converts the first character of `string` to lower case.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the converted string.\n * @example\n *\n * _.lowerFirst('Fred');\n * // => 'fred'\n *\n * _.lowerFirst('FRED');\n * // => 'fRED'\n */\n var lowerFirst = createCaseFirst('toLowerCase');\n\n /**\n * Pads `string` on the left and right sides if it's shorter than `length`.\n * Padding characters are truncated if they can't be evenly divided by `length`.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.pad('abc', 8);\n * // => ' abc '\n *\n * _.pad('abc', 8, '_-');\n * // => '_-abc_-_'\n *\n * _.pad('abc', 3);\n * // => 'abc'\n */\n function pad(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n\n var strLength = length ? stringSize(string) : 0;\n if (!length || strLength >= length) {\n return string;\n }\n var mid = (length - strLength) / 2;\n return (\n createPadding(nativeFloor(mid), chars) +\n string +\n createPadding(nativeCeil(mid), chars)\n );\n }\n\n /**\n * Pads `string` on the right side if it's shorter than `length`. Padding\n * characters are truncated if they exceed `length`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.padEnd('abc', 6);\n * // => 'abc '\n *\n * _.padEnd('abc', 6, '_-');\n * // => 'abc_-_'\n *\n * _.padEnd('abc', 3);\n * // => 'abc'\n */\n function padEnd(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n\n var strLength = length ? stringSize(string) : 0;\n return (length && strLength < length)\n ? (string + createPadding(length - strLength, chars))\n : string;\n }\n\n /**\n * Pads `string` on the left side if it's shorter than `length`. Padding\n * characters are truncated if they exceed `length`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to pad.\n * @param {number} [length=0] The padding length.\n * @param {string} [chars=' '] The string used as padding.\n * @returns {string} Returns the padded string.\n * @example\n *\n * _.padStart('abc', 6);\n * // => ' abc'\n *\n * _.padStart('abc', 6, '_-');\n * // => '_-_abc'\n *\n * _.padStart('abc', 3);\n * // => 'abc'\n */\n function padStart(string, length, chars) {\n string = toString(string);\n length = toInteger(length);\n\n var strLength = length ? stringSize(string) : 0;\n return (length && strLength < length)\n ? (createPadding(length - strLength, chars) + string)\n : string;\n }\n\n /**\n * Converts `string` to an integer of the specified radix. If `radix` is\n * `undefined` or `0`, a `radix` of `10` is used unless `value` is a\n * hexadecimal, in which case a `radix` of `16` is used.\n *\n * **Note:** This method aligns with the\n * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.\n *\n * @static\n * @memberOf _\n * @since 1.1.0\n * @category String\n * @param {string} string The string to convert.\n * @param {number} [radix=10] The radix to interpret `value` by.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {number} Returns the converted integer.\n * @example\n *\n * _.parseInt('08');\n * // => 8\n *\n * _.map(['6', '08', '10'], _.parseInt);\n * // => [6, 8, 10]\n */\n function parseInt(string, radix, guard) {\n if (guard || radix == null) {\n radix = 0;\n } else if (radix) {\n radix = +radix;\n }\n return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);\n }\n\n /**\n * Repeats the given string `n` times.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to repeat.\n * @param {number} [n=1] The number of times to repeat the string.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {string} Returns the repeated string.\n * @example\n *\n * _.repeat('*', 3);\n * // => '***'\n *\n * _.repeat('abc', 2);\n * // => 'abcabc'\n *\n * _.repeat('abc', 0);\n * // => ''\n */\n function repeat(string, n, guard) {\n if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {\n n = 1;\n } else {\n n = toInteger(n);\n }\n return baseRepeat(toString(string), n);\n }\n\n /**\n * Replaces matches for `pattern` in `string` with `replacement`.\n *\n * **Note:** This method is based on\n * [`String#replace`](https://mdn.io/String/replace).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to modify.\n * @param {RegExp|string} pattern The pattern to replace.\n * @param {Function|string} replacement The match replacement.\n * @returns {string} Returns the modified string.\n * @example\n *\n * _.replace('Hi Fred', 'Fred', 'Barney');\n * // => 'Hi Barney'\n */\n function replace() {\n var args = arguments,\n string = toString(args[0]);\n\n return args.length < 3 ? string : string.replace(args[1], args[2]);\n }\n\n /**\n * Converts `string` to\n * [snake case](https://en.wikipedia.org/wiki/Snake_case).\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the snake cased string.\n * @example\n *\n * _.snakeCase('Foo Bar');\n * // => 'foo_bar'\n *\n * _.snakeCase('fooBar');\n * // => 'foo_bar'\n *\n * _.snakeCase('--FOO-BAR--');\n * // => 'foo_bar'\n */\n var snakeCase = createCompounder(function(result, word, index) {\n return result + (index ? '_' : '') + word.toLowerCase();\n });\n\n /**\n * Splits `string` by `separator`.\n *\n * **Note:** This method is based on\n * [`String#split`](https://mdn.io/String/split).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category String\n * @param {string} [string=''] The string to split.\n * @param {RegExp|string} separator The separator pattern to split by.\n * @param {number} [limit] The length to truncate results to.\n * @returns {Array} Returns the string segments.\n * @example\n *\n * _.split('a-b-c', '-', 2);\n * // => ['a', 'b']\n */\n function split(string, separator, limit) {\n if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {\n separator = limit = undefined;\n }\n limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;\n if (!limit) {\n return [];\n }\n string = toString(string);\n if (string && (\n typeof separator == 'string' ||\n (separator != null && !isRegExp(separator))\n )) {\n separator = baseToString(separator);\n if (!separator && hasUnicode(string)) {\n return castSlice(stringToArray(string), 0, limit);\n }\n }\n return string.split(separator, limit);\n }\n\n /**\n * Converts `string` to\n * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).\n *\n * @static\n * @memberOf _\n * @since 3.1.0\n * @category String\n * @param {string} [string=''] The string to convert.\n * @returns {string} Returns the start cased string.\n * @example\n *\n * _.startCase('--foo-bar--');\n * // => 'Foo Bar'\n *\n * _.startCase('fooBar');\n * // => 'Foo Bar'\n *\n * _.startCase('__FOO_BAR__');\n * // => 'FOO BAR'\n */\n var startCase = createCompounder(function(result, word, index) {\n return result + (index ? ' ' : '') + upperFirst(word);\n });\n\n /**\n * Checks if `string` starts with the given target string.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category String\n * @param {string} [string=''] The string to inspect.\n * @param {string} [target] The string to search for.\n * @param {number} [position=0] The position to search from.\n * @returns {boolean} Returns `true` if `string` starts with `target`,\n * else `false`.\n * @example\n *\n * _.startsWith('abc', 'a');\n * // => true\n *\n * _.startsWith('abc', 'b');\n * // => false\n *\n * _.startsWith('abc', 'b', 1);\n * // => true\n */\n function startsWith(string, target, position) {\n string = toString(string);\n position = position == null\n ? 0\n : baseClamp(toInteger(position), 0, string.length);\n\n target = baseToString(target);\n return string.slice(position, position + target.length) == target;\n }\n\n /**\n * Creates a compiled template function that can interpolate data properties\n * in \"interpolate\" delimiters, HTML-escape interpolated data properties in\n * \"escape\" delimiters, and execute JavaScript in \"evaluate\" delimiters. Data\n * properties may be accessed as free variables in the template. If a setting\n * object is given, it takes precedence over `_.templateSettings` values.\n *\n * **Note:** In the development build `_.template` utilizes\n * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)\n * for easier debugging.\n *\n * For more information on precompiling templates see\n * [lodash's custom builds documentation](https://lodash.com/custom-builds).\n *\n * For more information on Chrome extension sandboxes see\n * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category String\n * @param {string} [string=''] The template string.\n * @param {Object} [options={}] The options object.\n * @param {RegExp} [options.escape=_.templateSettings.escape]\n * The HTML \"escape\" delimiter.\n * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]\n * The \"evaluate\" delimiter.\n * @param {Object} [options.imports=_.templateSettings.imports]\n * An object to import into the template as free variables.\n * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]\n * The \"interpolate\" delimiter.\n * @param {string} [options.sourceURL='lodash.templateSources[n]']\n * The sourceURL of the compiled template.\n * @param {string} [options.variable='obj']\n * The data object variable name.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the compiled template function.\n * @example\n *\n * // Use the \"interpolate\" delimiter to create a compiled template.\n * var compiled = _.template('hello <%= user %>!');\n * compiled({ 'user': 'fred' });\n * // => 'hello fred!'\n *\n * // Use the HTML \"escape\" delimiter to escape data property values.\n * var compiled = _.template('<%- value %>');\n * compiled({ 'value': ' + + + + + + + + + + + + + + + + + + + + +
    +
    +
    Loading...
    +
    +
    + +
    +
    + + + + + + + + diff --git a/presto-main/src/main/resources/webapp/index.html b/presto-main/src/main/resources/webapp/index.html index 4b9a8d5825573..84fbe187fe7ca 100644 --- a/presto-main/src/main/resources/webapp/index.html +++ b/presto-main/src/main/resources/webapp/index.html @@ -18,16 +18,6 @@ - - - - - - - @@ -42,52 +32,12 @@ - - -
    - +
    @@ -104,14 +54,7 @@
    - - - - - - + diff --git a/presto-main/src/main/resources/webapp/legacy-plan.html b/presto-main/src/main/resources/webapp/legacy-plan.html deleted file mode 100644 index 4cb48e2384874..0000000000000 --- a/presto-main/src/main/resources/webapp/legacy-plan.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/presto-main/src/main/resources/webapp/plan.html b/presto-main/src/main/resources/webapp/plan.html index ac5eb858ee599..9c68e658a31e0 100644 --- a/presto-main/src/main/resources/webapp/plan.html +++ b/presto-main/src/main/resources/webapp/plan.html @@ -18,18 +18,6 @@ - - - - - - - - @@ -39,9 +27,6 @@ - - - @@ -49,58 +34,14 @@ - - - - - - -
    - +
    Loading...
    @@ -110,13 +51,7 @@
    - - - - - + diff --git a/presto-main/src/main/resources/webapp/query.html b/presto-main/src/main/resources/webapp/query.html index 16acf7ec9d9ee..0ae21e601bb83 100644 --- a/presto-main/src/main/resources/webapp/query.html +++ b/presto-main/src/main/resources/webapp/query.html @@ -18,16 +18,6 @@ - - - - - - - @@ -37,9 +27,6 @@ - - - @@ -52,64 +39,18 @@ - - -
    - +
    Loading...
    - - - - - + diff --git a/presto-main/src/main/resources/webapp/src/.flowconfig b/presto-main/src/main/resources/webapp/src/.flowconfig new file mode 100644 index 0000000000000..1fed445333e85 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/.flowconfig @@ -0,0 +1,11 @@ +[ignore] + +[include] + +[libs] + +[lints] + +[options] + +[strict] diff --git a/presto-main/src/main/resources/webapp/assets/cluster-hud.js b/presto-main/src/main/resources/webapp/src/components/ClusterHUD.jsx similarity index 89% rename from presto-main/src/main/resources/webapp/assets/cluster-hud.js rename to presto-main/src/main/resources/webapp/src/components/ClusterHUD.jsx index 4a9ee22024f45..78ae5784a4c64 100644 --- a/presto-main/src/main/resources/webapp/assets/cluster-hud.js +++ b/presto-main/src/main/resources/webapp/src/components/ClusterHUD.jsx @@ -12,19 +12,30 @@ * limitations under the License. */ +import React from "react"; + +import { + addExponentiallyWeightedToHistory, + addToHistory, + formatCount, + formatDataSizeBytes, + precisionRound +} from "../utils"; + const SPARKLINE_PROPERTIES = { - width:'100%', + width: '100%', height: '75px', - fillColor:'#3F4552', + fillColor: '#3F4552', lineColor: '#747F96', spotColor: '#1EDCFF', tooltipClassname: 'sparkline-tooltip', disableHiddenCheck: true, }; -let ClusterHUD = React.createClass({ - getInitialState: function() { - return { +export class ClusterHUD extends React.Component { + constructor(props) { + super(props); + this.state = { runningQueries: [], queuedQueries: [], blockedQueries: [], @@ -44,15 +55,19 @@ let ClusterHUD = React.createClass({ initialized: false, }; - }, - resetTimer: function() { + + this.refreshLoop = this.refreshLoop.bind(this); + } + + resetTimer() { clearTimeout(this.timeoutId); // stop refreshing when query finishes or fails if (this.state.query === null || !this.state.ended) { this.timeoutId = setTimeout(this.refreshLoop, 1000); } - }, - refreshLoop: function() { + } + + refreshLoop() { clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously $.get('/v1/cluster', function (clusterState) { @@ -96,14 +111,16 @@ let ClusterHUD = React.createClass({ }); this.resetTimer(); }.bind(this)) - .error(function() { - this.resetTimer(); - }.bind(this)); - }, - componentDidMount: function() { + .error(function () { + this.resetTimer(); + }.bind(this)); + } + + componentDidMount() { this.refreshLoop(); - }, - componentDidUpdate: function() { + } + + componentDidUpdate() { // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts if (this.state.lastRender === null || (Date.now() - this.state.lastRender) >= 1000) { const renderTimestamp = Date.now(); @@ -125,8 +142,9 @@ let ClusterHUD = React.createClass({ } $('[data-toggle="tooltip"]').tooltip(); - }, - render: function() { + } + + render() { return (
    @@ -156,7 +174,7 @@ let ClusterHUD = React.createClass({
    - { this.state.runningQueries[this.state.runningQueries.length - 1] } + {this.state.runningQueries[this.state.runningQueries.length - 1]}
    Loading ...
    @@ -164,7 +182,7 @@ let ClusterHUD = React.createClass({
    - { this.state.activeWorkers[this.state.activeWorkers.length - 1] } + {this.state.activeWorkers[this.state.activeWorkers.length - 1]}
    Loading ...
    @@ -172,7 +190,7 @@ let ClusterHUD = React.createClass({
    - { formatCount(this.state.rowInputRate[this.state.rowInputRate.length - 1]) } + {formatCount(this.state.rowInputRate[this.state.rowInputRate.length - 1])}
    Loading ...
    @@ -205,7 +223,7 @@ let ClusterHUD = React.createClass({
    - { this.state.queuedQueries[this.state.queuedQueries.length - 1] } + {this.state.queuedQueries[this.state.queuedQueries.length - 1]}
    Loading ...
    @@ -213,7 +231,7 @@ let ClusterHUD = React.createClass({
    - { formatCount(this.state.runningDrivers[this.state.runningDrivers.length - 1]) } + {formatCount(this.state.runningDrivers[this.state.runningDrivers.length - 1])}
    Loading ...
    @@ -221,7 +239,7 @@ let ClusterHUD = React.createClass({
    - { formatDataSizeBytes(this.state.byteInputRate[this.state.byteInputRate.length - 1]) } + {formatDataSizeBytes(this.state.byteInputRate[this.state.byteInputRate.length - 1])}
    Loading ...
    @@ -254,7 +272,7 @@ let ClusterHUD = React.createClass({
    - { this.state.blockedQueries[this.state.blockedQueries.length - 1] } + {this.state.blockedQueries[this.state.blockedQueries.length - 1]}
    Loading ...
    @@ -262,7 +280,7 @@ let ClusterHUD = React.createClass({
    - { formatDataSizeBytes(this.state.reservedMemory[this.state.reservedMemory.length - 1]) } + {formatDataSizeBytes(this.state.reservedMemory[this.state.reservedMemory.length - 1])}
    Loading ...
    @@ -270,7 +288,7 @@ let ClusterHUD = React.createClass({
    - { formatCount(this.state.perWorkerCpuTimeRate[this.state.perWorkerCpuTimeRate.length - 1]) } + {formatCount(this.state.perWorkerCpuTimeRate[this.state.perWorkerCpuTimeRate.length - 1])}
    Loading ...
    @@ -279,9 +297,5 @@ let ClusterHUD = React.createClass({
    ); } -}); +} -ReactDOM.render( - , - document.getElementById('cluster-hud') -); diff --git a/presto-main/src/main/resources/webapp/src/components/LivePlan.jsx b/presto-main/src/main/resources/webapp/src/components/LivePlan.jsx new file mode 100644 index 0000000000000..3cd10ea373715 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/components/LivePlan.jsx @@ -0,0 +1,289 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//@flow + +import React from "react"; +import ReactDOMServer from "react-dom/server"; +import * as dagreD3 from "dagre-d3"; +import * as d3 from "d3"; + +import { + computeSources, + formatCount, + getFirstParameter, + getStageStateColor, + initializeGraph, + initializeSvg +} from "../utils"; +import {QueryHeader} from "./QueryHeader"; + +type StageStatisticsProps = { + stage: any, +} +type StageStatisticsState = {} + +class StageStatistics extends React.Component { + static flatten(queryInfo) { + const stages = new Map(); + StageStatistics.flattenStage(queryInfo.outputStage, stages); + + return { + id: queryInfo.queryId, + root: queryInfo.outputStage.plan.id, + stageStats: {}, + stages: stages + } + } + + static flattenStage(stageInfo, result) { + stageInfo.subStages.forEach(function (stage) { + StageStatistics.flattenStage(stage, result); + }); + + const nodes = new Map(); + StageStatistics.flattenNode(result, stageInfo.plan.root, nodes); + + result.set(stageInfo.plan.id, { + stageId: stageInfo.stageId, + id: stageInfo.plan.id, + root: stageInfo.plan.root.id, + distribution: stageInfo.plan.distribution, + stageStats: stageInfo.stageStats, + state: stageInfo.state, + nodes: nodes + }); + } + + static flattenNode(stages, nodeInfo, result) { + const allSources = computeSources(nodeInfo); + const sources = allSources[0]; + const remoteSources = allSources[1]; + + result.set(nodeInfo.id, { + id: nodeInfo.id, + type: nodeInfo['@type'], + sources: sources.map(function (node) { return node.id }), + remoteSources: remoteSources, + stats: {} + }); + + sources.forEach(function (child) { + StageStatistics.flattenNode(stages, child, result); + }); + } + + render() { + const stage = this.props.stage; + const stats = this.props.stage.stageStats; + return ( +
    +
    + Output: {stats.outputDataSize + " / " + formatCount(stats.outputPositions) + " rows"} +
    + Buffered: {stats.bufferedDataSize} +
    + {stage.state} +
    + CPU: {stats.totalCpuTime} + {stats.fullyBlocked ? +
    Blocked: {stats.totalBlockedTime}
    : +
    Blocked: {stats.totalBlockedTime}
    + } + Memory: {stats.userMemoryReservation} +
    + Splits: {"Q:" + stats.queuedDrivers + ", R:" + stats.runningDrivers + ", F:" + stats.completedDrivers} +
    + Input: {stats.rawInputDataSize + " / " + formatCount(stats.rawInputPositions)} rows +
    +
    + ); + } +} + +type LivePlanProps = { + queryId: string, + isEmbedded: boolean, +} + +type LivePlanState = { + initialized: boolean, + ended: boolean, + + query: ?any, + + graph: any, + svg: any, + render: any, +} + +export class LivePlan extends React.Component { + timeoutId: TimeoutID; + + constructor(props: LivePlanProps) { + super(props); + this.state = { + initialized: false, + ended: false, + + query: null, + + graph: initializeGraph(), + svg: initializeSvg("#plan-canvas"), + render: new dagreD3.render(), + }; + } + + resetTimer() { + clearTimeout(this.timeoutId); + // stop refreshing when query finishes or fails + if (this.state.query === null || !this.state.ended) { + this.timeoutId = setTimeout(this.refreshLoop.bind(this), 1000); + } + } + + refreshLoop() { + clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously + fetch('/v1/query/' + this.props.queryId) + .then(response => response.json()) + .then(query => { + this.setState({ + query: query, + + initialized: true, + ended: query.finalQueryInfo, + }); + this.resetTimer(); + }) + .catch(() => { + this.setState({ + initialized: true, + }); + this.resetTimer(); + }); + } + + static handleStageClick(stageCssId: string) { + window.open("stage.html?" + stageCssId, '_blank'); + } + + componentDidMount() { + this.refreshLoop.bind(this)(); + } + + updateD3Stage(stage: any, graph: any) { + const clusterId = stage.stageId; + const stageRootNodeId = "stage-" + stage.id + "-root"; + const color = getStageStateColor(stage); + + graph.setNode(clusterId, {label: "Stage " + stage.id + " ", clusterLabelPos: 'top-right', style: 'fill: ' + color, labelStyle: 'fill: #fff'}); + + // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React + const html = ReactDOMServer.renderToString(); + + graph.setNode(stageRootNodeId, {class: "stage-stats", label: html, labelType: "html"}); + graph.setParent(stageRootNodeId, clusterId); + graph.setEdge("node-" + stage.root, stageRootNodeId, {style: "visibility: hidden"}); + + stage.nodes.forEach(node => { + const nodeId = "node-" + node.id; + + graph.setNode(nodeId, {label: node.type, style: 'fill: #fff'}); + graph.setParent(nodeId, clusterId); + + node.sources.forEach(source => { + graph.setEdge("node-" + source, nodeId, {arrowheadStyle: "fill: #fff; stroke-width: 0;"}); + }); + + if (node.type === 'remoteSource') { + graph.setNode(nodeId, {label: '', shape: "circle"}); + + node.remoteSources.forEach(sourceId => { + graph.setEdge("stage-" + sourceId + "-root", nodeId, {style: "stroke-width: 5px;", arrowheadStyle: "fill: #fff; stroke-width: 0;"}); + }); + } + }); + } + + updateD3Graph() { + if (!this.state.query) { + return; + } + + const graph = this.state.graph; + const stages = StageStatistics.flatten(this.state.query).stages; + stages.forEach(stage => { + this.updateD3Stage(stage, graph); + }); + + this.state.render(d3.select("#plan-canvas g"), graph); + + const svg = this.state.svg; + svg.selectAll("g.cluster").on("click", LivePlan.handleStageClick); + svg.attr("height", graph.graph().height); + svg.attr("width", graph.graph().width); + } + + render() { + const query = this.state.query; + + if (query === null || this.state.initialized === false) { + let label = (
    Loading...
    ); + if (this.state.initialized) { + label = "Query not found"; + } + return ( +
    +

    {label}

    +
    + ); + } + + let livePlanGraph = null; + if (query && !query.outputStage) { + livePlanGraph = ( +
    +
    +

    Live plan graph will appear automatically when query starts running.

    +
    Loading...
    +
    +
    + ) + } + else { + this.updateD3Graph(); + } + + // TODO: Refactor components to move refreshLoop to parent rather than using this property + if (this.props.isEmbedded) { + return ( +
    +
    + {livePlanGraph} +
    +
    + ) + } + + return ( +
    + +
    +
    + {livePlanGraph} +
    +
    +
    + ); + } +} diff --git a/presto-main/src/main/resources/webapp/src/components/PageTitle.jsx b/presto-main/src/main/resources/webapp/src/components/PageTitle.jsx new file mode 100644 index 0000000000000..dc5a6fe7a8d41 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/components/PageTitle.jsx @@ -0,0 +1,165 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +//@flow +import React from "react"; + +type Props = { + title: string +} + +type State = { + noConnection: boolean, + lightShown: boolean, + info: ?any, + lastSuccess: number, + modalShown: boolean, + errorText: ?string, +} + +export class PageTitle extends React.Component { + timeoutId: TimeoutID; + + constructor(props: Props) { + super(props); + this.state = { + noConnection: false, + lightShown: false, + info: null, + lastSuccess: Date.now(), + modalShown: false, + errorText: null, + }; + } + + refreshLoop() { + clearTimeout(this.timeoutId); + fetch("/v1/info") + .then(response => response.json()) + .then(info => { + this.setState({ + info: info, + noConnection: false, + lastSuccess: Date.now(), + modalShown: false, + }); + //$FlowFixMe$ Bootstrap 3 plugin + $('#no-connection-modal').modal('hide'); + this.resetTimer(); + }) + .catch(error => { + this.setState({ + noConnection: true, + lightShown: !this.state.lightShown, + errorText: error + }); + this.resetTimer(); + + if (!this.state.modalShown && (error || (Date.now() - this.state.lastSuccess) > 30 * 1000)) { + //$FlowFixMe$ Bootstrap 3 plugin + $('#no-connection-modal').modal(); + this.setState({modalShown: true}); + } + }); + } + + resetTimer() { + clearTimeout(this.timeoutId); + this.timeoutId = setTimeout(this.refreshLoop.bind(this), 1000); + } + + componentDidMount() { + this.refreshLoop.bind(this)(); + } + + renderStatusLight() { + if (this.state.noConnection) { + if (this.state.lightShown) { + return ; + } + else { + return + } + } + return ; + } + + render() { + const info = this.state.info; + if (!info) { + return null; + } + + return ( +
    + + +
    + ); + } +} diff --git a/presto-main/src/main/resources/webapp/src/components/QueryDetail.jsx b/presto-main/src/main/resources/webapp/src/components/QueryDetail.jsx new file mode 100644 index 0000000000000..52aacad925f4e --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/components/QueryDetail.jsx @@ -0,0 +1,1502 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react"; +import Reactable from "reactable"; + +import { + addToHistory, + computeRate, + formatCount, + formatDataSize, + formatDataSizeBytes, + formatDuration, + formatShortDateTime, + getFirstParameter, + getHostAndPort, + getHostname, + getPort, + getStageNumber, + getStageStateColor, + getTaskIdSuffix, + getTaskNumber, + GLYPHICON_HIGHLIGHT, + parseDataSize, + parseDuration, + precisionRound +} from "../utils"; +import {QueryHeader} from "./QueryHeader"; + +const Table = Reactable.Table, + Thead = Reactable.Thead, + Th = Reactable.Th, + Tr = Reactable.Tr, + Td = Reactable.Td; + +class TaskList extends React.Component { + static removeQueryId(id) { + const pos = id.indexOf('.'); + if (pos !== -1) { + return id.substring(pos + 1); + } + return id; + } + + static compareTaskId(taskA, taskB) { + const taskIdArrA = TaskList.removeQueryId(taskA).split("."); + const taskIdArrB = TaskList.removeQueryId(taskB).split("."); + + if (taskIdArrA.length > taskIdArrB.length) { + return 1; + } + for (let i = 0; i < taskIdArrA.length; i++) { + const anum = Number.parseInt(taskIdArrA[i]); + const bnum = Number.parseInt(taskIdArrB[i]); + if (anum !== bnum) { + return anum > bnum ? 1 : -1; + } + } + + return 0; + } + + static showPortNumbers(tasks) { + // check if any host has multiple port numbers + const hostToPortNumber = {}; + for (let i = 0; i < tasks.length; i++) { + const taskUri = tasks[i].taskStatus.self; + const hostname = getHostname(taskUri); + const port = getPort(taskUri); + if ((hostname in hostToPortNumber) && (hostToPortNumber[hostname] !== port)) { + return true; + } + hostToPortNumber[hostname] = port; + } + + return false; + } + + static formatState(state, fullyBlocked) { + if (fullyBlocked && state === "RUNNING") { + return "BLOCKED"; + } + else { + return state; + } + } + + render() { + const tasks = this.props.tasks; + + if (tasks === undefined || tasks.length === 0) { + return ( +
    +

    No threads in the selected group

    +
    ); + } + + const showPortNumbers = TaskList.showPortNumbers(tasks); + + const renderedTasks = tasks.map(task => { + let elapsedTime = parseDuration(task.stats.elapsedTime); + if (elapsedTime === 0) { + elapsedTime = Date.now() - Date.parse(task.stats.createTime); + } + + return ( + + + + {getTaskIdSuffix(task.taskStatus.taskId)} + + + + + {showPortNumbers ? getHostAndPort(task.taskStatus.self) : getHostname(task.taskStatus.self)} + + + + {TaskList.formatState(task.taskStatus.state, task.stats.fullyBlocked)} + + + {formatCount(task.stats.rawInputPositions)} + + + {formatCount(computeRate(task.stats.rawInputPositions, elapsedTime))} + + + {formatDataSizeBytes(parseDataSize(task.stats.rawInputDataSize))} + + + {formatDataSizeBytes(computeRate(parseDataSize(task.stats.rawInputDataSize), elapsedTime))} + + + {task.stats.queuedDrivers} + + + {task.stats.runningDrivers} + + + {task.stats.blockedDrivers} + + + {task.stats.completedDrivers} + + + {task.stats.elapsedTime} + + + {task.stats.totalCpuTime} + + + {formatDataSizeBytes(task.outputBuffers.totalBufferedBytes)} + + + ); + }); + + return ( + + + + + + + + + + + + + + + + + + {renderedTasks} +
    IDHostStateRowsRows/sBytesBytes/sElapsedCPU TimeBuffered
    + ); + } +} + +const BAR_CHART_WIDTH = 800; + +const BAR_CHART_PROPERTIES = { + type: 'bar', + barSpacing: '0', + height: '80px', + barColor: '#747F96', + zeroColor: '#8997B3', + tooltipClassname: 'sparkline-tooltip', + tooltipFormat: 'Task {{offset:offset}} - {{value}}', + disableHiddenCheck: true, +}; + +const HISTOGRAM_WIDTH = 175; + +const HISTOGRAM_PROPERTIES = { + type: 'bar', + barSpacing: '0', + height: '80px', + barColor: '#747F96', + zeroColor: '#747F96', + zeroAxis: true, + tooltipClassname: 'sparkline-tooltip', + tooltipFormat: '{{offset:offset}} -- {{value}} tasks', + disableHiddenCheck: true, +}; + +class StageDetail extends React.Component { + constructor(props) { + super(props); + this.state = { + expanded: false, + lastRender: null + }; + } + + getExpandedIcon() { + return this.state.expanded ? "glyphicon-chevron-up" : "glyphicon-chevron-down"; + } + + getExpandedStyle() { + return this.state.expanded ? {} : {display: "none"}; + } + + toggleExpanded() { + this.setState({ + expanded: !this.state.expanded, + }) + } + + static renderHistogram(histogramId, inputData, numberFormatter) { + const numBuckets = Math.min(HISTOGRAM_WIDTH, Math.sqrt(inputData.length)); + const dataMin = Math.min.apply(null, inputData); + const dataMax = Math.max.apply(null, inputData); + const bucketSize = (dataMax - dataMin) / numBuckets; + + let histogramData = []; + if (bucketSize === 0) { + histogramData = [inputData.length]; + } + else { + for (let i = 0; i < numBuckets + 1; i++) { + histogramData.push(0); + } + + for (let i in inputData) { + const dataPoint = inputData[i]; + const bucket = Math.floor((dataPoint - dataMin) / bucketSize); + histogramData[bucket] = histogramData[bucket] + 1; + } + } + + const tooltipValueLookups = {'offset': {}}; + for (let i = 0; i < histogramData.length; i++) { + tooltipValueLookups['offset'][i] = numberFormatter(dataMin + (i * bucketSize)) + "-" + numberFormatter(dataMin + ((i + 1) * bucketSize)); + } + + const stageHistogramProperties = $.extend({}, HISTOGRAM_PROPERTIES, {barWidth: (HISTOGRAM_WIDTH / histogramData.length), tooltipValueLookups: tooltipValueLookups}); + $(histogramId).sparkline(histogramData, stageHistogramProperties); + } + + componentDidUpdate() { + const stage = this.props.stage; + const numTasks = stage.tasks.length; + + // sort the x-axis + stage.tasks.sort((taskA, taskB) => getTaskNumber(taskA.taskStatus.taskId) - getTaskNumber(taskB.taskStatus.taskId)); + + const scheduledTimes = stage.tasks.map(task => parseDuration(task.stats.totalScheduledTime)); + const cpuTimes = stage.tasks.map(task => parseDuration(task.stats.totalCpuTime)); + + // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts + if (this.state.lastRender === null || (Date.now() - this.state.lastRender) >= 1000) { + const renderTimestamp = Date.now(); + const stageId = getStageNumber(stage.stageId); + + StageDetail.renderHistogram('#scheduled-time-histogram-' + stageId, scheduledTimes, formatDuration); + StageDetail.renderHistogram('#cpu-time-histogram-' + stageId, cpuTimes, formatDuration); + + if (this.state.expanded) { + // this needs to be a string otherwise it will also be passed to numberFormatter + const tooltipValueLookups = {'offset': {}}; + for (let i = 0; i < numTasks; i++) { + tooltipValueLookups['offset'][i] = getStageNumber(stage.stageId) + "." + i; + } + + const stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, {barWidth: BAR_CHART_WIDTH / numTasks, tooltipValueLookups: tooltipValueLookups}); + + $('#scheduled-time-bar-chart-' + stageId).sparkline(scheduledTimes, $.extend({}, stageBarChartProperties, {numberFormatter: formatDuration})); + $('#cpu-time-bar-chart-' + stageId).sparkline(cpuTimes, $.extend({}, stageBarChartProperties, {numberFormatter: formatDuration})); + } + + this.setState({ + lastRender: renderTimestamp + }); + } + } + + render() { + const stage = this.props.stage; + if (stage === undefined || !stage.hasOwnProperty('plan')) { + return ( + + Information about this stage is unavailable. + ); + } + + const totalBufferedBytes = stage.tasks + .map(task => task.outputBuffers.totalBufferedBytes) + .reduce((a, b) => a + b, 0); + + const stageId = getStageNumber(stage.stageId); + + return ( + + +
    {stageId}
    + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + +
    + Time + +
    + Scheduled + + {stage.stageStats.totalScheduledTime} +
    + Blocked + + {stage.stageStats.totalBlockedTime} +
    + CPU + + {stage.stageStats.totalCpuTime} +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + Memory + +
    + Cumulative + + {formatDataSizeBytes(stage.stageStats.cumulativeUserMemory / 1000)} +
    + Current + + {stage.stageStats.userMemoryReservation} +
    + Buffers + + {formatDataSize(totalBufferedBytes)} +
    + Peak + + {stage.stageStats.peakUserMemoryReservation} +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + Tasks + +
    + Pending + + {stage.tasks.filter(task => task.taskStatus.state === "PLANNED").length} +
    + Running + + {stage.tasks.filter(task => task.taskStatus.state === "RUNNING").length} +
    + Blocked + + {stage.tasks.filter(task => task.stats.fullyBlocked).length} +
    + Total + + {stage.tasks.length} +
    +
    + + + + + + + + + + + +
    + Scheduled Time Skew +
    +
    +
    +
    + + + + + + + + + + + +
    + CPU Time Skew +
    +
    +
    +
    + + + +
    + + + + + + + +
    + Task Scheduled Time + +
    +
    +
    + + + + + + + +
    + Task CPU Time + +
    +
    +
    + + ); + } +} + +class StageList extends React.Component { + getStages(stage) { + if (stage === undefined || !stage.hasOwnProperty('subStages')) { + return [] + } + + return [].concat.apply(stage, stage.subStages.map(this.getStages, this)); + } + + render() { + const stages = this.getStages(this.props.outputStage); + + if (stages === undefined || stages.length === 0) { + return ( +
    +
    + No stage information available. +
    +
    + ); + } + + const renderedStages = stages.map(stage => ); + + return ( +
    +
    + + + {renderedStages} + +
    +
    +
    + ); + } +} + +const SMALL_SPARKLINE_PROPERTIES = { + width: '100%', + height: '57px', + fillColor: '#3F4552', + lineColor: '#747F96', + spotColor: '#1EDCFF', + tooltipClassname: 'sparkline-tooltip', + disableHiddenCheck: true, +}; + +const TASK_FILTER = { + ALL: function () { return true }, + PLANNED: function (state) { return state === 'PLANNED' }, + RUNNING: function (state) { return state === 'RUNNING' }, + FINISHED: function (state) { return state === 'FINISHED' }, + FAILED: function (state) { return state === 'FAILED' || state === 'ABORTED' || state === 'CANCELED' }, +}; + +export class QueryDetail extends React.Component { + + constructor(props) { + super(props); + this.state = { + query: null, + lastSnapshotStages: null, + lastSnapshotTasks: null, + + lastScheduledTime: 0, + lastCpuTime: 0, + lastRowInput: 0, + lastByteInput: 0, + + scheduledTimeRate: [], + cpuTimeRate: [], + rowInputRate: [], + byteInputRate: [], + + reservedMemory: [], + + initialized: false, + ended: false, + + lastRefresh: null, + lastRender: null, + + stageRefresh: true, + taskRefresh: true, + + taskFilter: TASK_FILTER.ALL, + }; + + this.refreshLoop = this.refreshLoop.bind(this); + } + + static formatStackTrace(info) { + return QueryDetail.formatStackTraceHelper(info, [], "", ""); + } + + static formatStackTraceHelper(info, parentStack, prefix, linePrefix) { + let s = linePrefix + prefix + QueryDetail.failureInfoToString(info) + "\n"; + + if (info.stack) { + let sharedStackFrames = 0; + if (parentStack !== null) { + sharedStackFrames = QueryDetail.countSharedStackFrames(info.stack, parentStack); + } + + for (let i = 0; i < info.stack.length - sharedStackFrames; i++) { + s += linePrefix + "\tat " + info.stack[i] + "\n"; + } + if (sharedStackFrames !== 0) { + s += linePrefix + "\t... " + sharedStackFrames + " more" + "\n"; + } + } + + if (info.suppressed) { + for (let i = 0; i < info.suppressed.length; i++) { + s += QueryDetail.formatStackTraceHelper(info.suppressed[i], info.stack, "Suppressed: ", linePrefix + "\t"); + } + } + + if (info.cause) { + s += QueryDetail.formatStackTraceHelper(info.cause, info.stack, "Caused by: ", linePrefix); + } + + return s; + } + + static countSharedStackFrames(stack, parentStack) { + let n = 0; + const minStackLength = Math.min(stack.length, parentStack.length); + while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) { + n++; + } + return n; + } + + static failureInfoToString(t) { + return (t.message !== null) ? (t.type + ": " + t.message) : t.type; + } + + resetTimer() { + clearTimeout(this.timeoutId); + // stop refreshing when query finishes or fails + if (this.state.query === null || !this.state.ended) { + // task.info-update-interval is set to 3 seconds by default + this.timeoutId = setTimeout(this.refreshLoop, 3000); + } + } + + refreshLoop() { + clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously + const queryId = getFirstParameter(window.location.search); + $.get('/v1/query/' + queryId, function (query) { + let lastSnapshotStages = this.state.lastSnapshotStage; + if (this.state.stageRefresh) { + lastSnapshotStages = query.outputStage; + } + let lastSnapshotTasks = this.state.lastSnapshotTasks; + if (this.state.taskRefresh) { + lastSnapshotTasks = query.outputStage; + } + + let lastRefresh = this.state.lastRefresh; + const lastScheduledTime = this.state.lastScheduledTime; + const lastCpuTime = this.state.lastCpuTime; + const lastRowInput = this.state.lastRowInput; + const lastByteInput = this.state.lastByteInput; + const alreadyEnded = this.state.ended; + const nowMillis = Date.now(); + + this.setState({ + query: query, + lastSnapshotStage: lastSnapshotStages, + lastSnapshotTasks: lastSnapshotTasks, + + lastScheduledTime: parseDuration(query.queryStats.totalScheduledTime), + lastCpuTime: parseDuration(query.queryStats.totalCpuTime), + lastRowInput: query.queryStats.processedInputPositions, + lastByteInput: parseDataSize(query.queryStats.processedInputDataSize), + + initialized: true, + ended: query.finalQueryInfo, + + lastRefresh: nowMillis, + }); + + // i.e. don't show sparklines if we've already decided not to update or if we don't have one previous measurement + if (alreadyEnded || (lastRefresh === null && query.state === "RUNNING")) { + this.resetTimer(); + return; + } + + if (lastRefresh === null) { + lastRefresh = nowMillis - parseDuration(query.queryStats.elapsedTime); + } + + const elapsedSecsSinceLastRefresh = (nowMillis - lastRefresh) / 1000.0; + if (elapsedSecsSinceLastRefresh >= 0) { + const currentScheduledTimeRate = (parseDuration(query.queryStats.totalScheduledTime) - lastScheduledTime) / (elapsedSecsSinceLastRefresh * 1000); + const currentCpuTimeRate = (parseDuration(query.queryStats.totalCpuTime) - lastCpuTime) / (elapsedSecsSinceLastRefresh * 1000); + const currentRowInputRate = (query.queryStats.processedInputPositions - lastRowInput) / elapsedSecsSinceLastRefresh; + const currentByteInputRate = (parseDataSize(query.queryStats.processedInputDataSize) - lastByteInput) / elapsedSecsSinceLastRefresh; + this.setState({ + scheduledTimeRate: addToHistory(currentScheduledTimeRate, this.state.scheduledTimeRate), + cpuTimeRate: addToHistory(currentCpuTimeRate, this.state.cpuTimeRate), + rowInputRate: addToHistory(currentRowInputRate, this.state.rowInputRate), + byteInputRate: addToHistory(currentByteInputRate, this.state.byteInputRate), + reservedMemory: addToHistory(parseDataSize(query.queryStats.userMemoryReservation), this.state.reservedMemory), + }); + } + this.resetTimer(); + }.bind(this)) + .error(() => { + this.setState({ + initialized: true, + }); + this.resetTimer(); + }); + } + + handleTaskRefreshClick() { + if (this.state.taskRefresh) { + this.setState({ + taskRefresh: false, + lastSnapshotTasks: this.state.query.outputStage, + }); + } + else { + this.setState({ + taskRefresh: true, + }); + } + } + + renderTaskRefreshButton() { + if (this.state.taskRefresh) { + return + } + else { + return + } + } + + handleStageRefreshClick() { + if (this.state.stageRefresh) { + this.setState({ + stageRefresh: false, + lastSnapshotStages: this.state.query.outputStage, + }); + } + else { + this.setState({ + stageRefresh: true, + }); + } + } + + renderStageRefreshButton() { + if (this.state.stageRefresh) { + return + } + else { + return + } + } + + renderTaskFilterListItem(taskFilter, taskFilterText) { + return ( +
  • {taskFilterText}
  • + ); + } + + handleTaskFilterClick(filter, event) { + this.setState({ + taskFilter: filter + }); + event.preventDefault(); + } + + getTasksFromStage(stage) { + if (stage === undefined || !stage.hasOwnProperty('subStages') || !stage.hasOwnProperty('tasks')) { + return [] + } + + return [].concat.apply(stage.tasks, stage.subStages.map(this.getTasksFromStage, this)); + } + + componentDidMount() { + this.refreshLoop(); + } + + componentDidUpdate() { + // prevent multiple calls to componentDidUpdate (resulting from calls to setState or otherwise) within the refresh interval from re-rendering sparklines/charts + if (this.state.lastRender === null || (Date.now() - this.state.lastRender) >= 1000) { + const renderTimestamp = Date.now(); + $('#scheduled-time-rate-sparkline').sparkline(this.state.scheduledTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, { + chartRangeMin: 0, + numberFormatter: precisionRound + })); + $('#cpu-time-rate-sparkline').sparkline(this.state.cpuTimeRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); + $('#row-input-rate-sparkline').sparkline(this.state.rowInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatCount})); + $('#byte-input-rate-sparkline').sparkline(this.state.byteInputRate, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatDataSize})); + $('#reserved-memory-sparkline').sparkline(this.state.reservedMemory, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {numberFormatter: formatDataSize})); + + if (this.state.lastRender === null) { + $('#query').each((i, block) => { + hljs.highlightBlock(block); + }); + } + + this.setState({ + lastRender: renderTimestamp, + }); + } + + $('[data-toggle="tooltip"]').tooltip(); + new Clipboard('.copy-button'); + } + + renderTasks() { + if (this.state.lastSnapshotTasks === null) { + return; + } + + const tasks = this.getTasksFromStage(this.state.lastSnapshotTasks).filter(task => this.state.taskFilter(task.taskStatus.state), this); + + return ( +
    +
    +
    +

    Tasks

    +
    +
    + + + + + + + +
    +
    + +
      + {this.renderTaskFilterListItem(TASK_FILTER.ALL, "All")} + {this.renderTaskFilterListItem(TASK_FILTER.PLANNED, "Planned")} + {this.renderTaskFilterListItem(TASK_FILTER.RUNNING, "Running")} + {this.renderTaskFilterListItem(TASK_FILTER.FINISHED, "Finished")} + {this.renderTaskFilterListItem(TASK_FILTER.FAILED, "Aborted/Canceled/Failed")} +
    +
    +
      {this.renderTaskRefreshButton()}
    +
    +
    +
    +
    + +
    +
    +
    + ); + } + + renderStages() { + if (this.state.lastSnapshotStage === null) { + return; + } + + return ( +
    +
    +
    +

    Stages

    +
    +
    + + + + + + +
    + {this.renderStageRefreshButton()} +
    +
    +
    +
    +
    + +
    +
    +
    + ); + } + + renderSessionProperties() { + const query = this.state.query; + + const properties = []; + for (let property in query.session.systemProperties) { + if (query.session.systemProperties.hasOwnProperty(property)) { + properties.push( + - {property + "=" + query.session.systemProperties[property]}
    + ); + } + } + + for (let catalog in query.session.catalogProperties) { + if (query.session.catalogProperties.hasOwnProperty(catalog)) { + for (let property in query.session.catalogProperties[catalog]) { + if (query.session.catalogProperties[catalog].hasOwnProperty(property)) { + properties.push( + - {catalog + "." + property + "=" + query.session.catalogProperties[catalog][property]}
    + ); + } + } + } + } + + return properties; + } + + renderResourceEstimates() { + const query = this.state.query; + const estimates = query.session.resourceEstimates; + const renderedEstimates = []; + + for (let resource in estimates) { + if (estimates.hasOwnProperty(resource)) { + const upperChars = resource.match(/([A-Z])/g) || []; + let snakeCased = resource; + for (let i = 0, n = upperChars.length; i < n; i++) { + snakeCased = snakeCased.replace(new RegExp(upperChars[i]), '_' + upperChars[i].toLowerCase()); + } + + renderedEstimates.push( + - {snakeCased + "=" + query.session.resourceEstimates[resource]}
    + ) + } + } + + return renderedEstimates; + } + + renderWarningInfo() { + const query = this.state.query; + if (query.warnings.length > 0) { + return ( +
    +
    +

    Warnings

    +
    + + {query.warnings.map((warning) => + + + + + )} +
    + {warning.warningCode.name} + + {warning.message} +
    +
    +
    + ); + } + else { + return null; + } + } + + renderFailureInfo() { + const query = this.state.query; + if (query.failureInfo) { + return ( +
    +
    +

    Error Information

    +
    + + + + + + + + + + + + + + + +
    + Error Type + + {query.errorType} +
    + Error Code + + {query.errorCode.name + " (" + this.state.query.errorCode.code + ")"} +
    + Stack Trace + + + +
    +                                            {QueryDetail.formatStackTrace(query.failureInfo)}
    +                                        
    +
    +
    +
    + ); + } + else { + return ""; + } + } + + render() { + const query = this.state.query; + + if (query === null || this.state.initialized === false) { + let label = (
    Loading...
    ); + if (this.state.initialized) { + label = "Query not found"; + } + return ( +
    +

    {label}

    +
    + ); + } + + return ( +
    + +
    +
    +

    Session

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + User + + {query.session.user} +    + + +
    + Principal + + {query.session.principal} +
    + Source + + {query.session.source} +
    + Catalog + + {query.session.catalog} +
    + Schema + + {query.session.schema} +
    + Client Address + + {query.session.remoteUserAddress} +
    + Client Tags + + {query.session.clientTags.join(", ")} +
    + Session Properties + + {this.renderSessionProperties()} +
    + Resource Estimates + + {this.renderResourceEstimates()} +
    +
    +
    +

    Execution

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Resource Group + + {query.resourceGroupId ? query.resourceGroupId.join(".") : "n/a"} +
    + Submission Time + + {formatShortDateTime(new Date(query.queryStats.createTime))} +
    + Completion Time + + {query.queryStats.endTime ? formatShortDateTime(new Date(query.queryStats.endTime)) : ""} +
    + Elapsed Time + + {query.queryStats.elapsedTime} +
    + Queued Time + + {query.queryStats.queuedTime} +
    + Execution Time + + {query.queryStats.executionTime} +
    +
    +
    +
    +
    +
    +
    +

    Resource Utilization Summary

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + CPU Time + + {query.queryStats.totalCpuTime} +
    + Scheduled Time + + {query.queryStats.totalScheduledTime} +
    + Blocked Time + + {query.queryStats.totalBlockedTime} +
    + Input Rows + + {formatCount(query.queryStats.processedInputPositions)} +
    + Input Data + + {query.queryStats.processedInputDataSize} +
    + Raw Input Rows + + {formatCount(query.queryStats.rawInputPositions)} +
    + Raw Input Data + + {query.queryStats.rawInputDataSize} +
    + Peak User Memory + + {query.queryStats.peakUserMemoryReservation} +
    + Peak Total Memory + + {query.queryStats.peakTotalMemoryReservation} +
    + Memory Pool + + {query.memoryPool} +
    + Cumulative User Memory + + {formatDataSizeBytes(query.queryStats.cumulativeUserMemory / 1000.0) + " seconds"} +
    + Output Rows + + {formatCount(query.queryStats.outputPositions)} +
    + Output Data + + {query.queryStats.outputDataSize} +
    + Written Rows + + {formatCount(query.queryStats.writtenPositions)} +
    + Logical Written Data + + {query.queryStats.logicalWrittenDataSize} +
    + Physical Written Data + + {query.queryStats.physicalWrittenDataSize} +
    +
    +
    +

    Timeline

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Parallelism + +
    +
    Loading ...
    +
    +
    + {formatCount(this.state.cpuTimeRate[this.state.cpuTimeRate.length - 1])} +
    + Scheduled Time/s + +
    +
    Loading ...
    +
    +
    + {formatCount(this.state.scheduledTimeRate[this.state.scheduledTimeRate.length - 1])} +
    + Input Rows/s + +
    +
    Loading ...
    +
    +
    + {formatCount(this.state.rowInputRate[this.state.rowInputRate.length - 1])} +
    + Input Bytes/s + +
    +
    Loading ...
    +
    +
    + {formatDataSize(this.state.byteInputRate[this.state.byteInputRate.length - 1])} +
    + Memory Utilization + +
    +
    Loading ...
    +
    +
    + {formatDataSize(this.state.reservedMemory[this.state.reservedMemory.length - 1])} +
    +
    +
    +
    +
    + {this.renderWarningInfo()} + {this.renderFailureInfo()} +
    +
    +

    + Query + + +

    +
    +                            
    +                                {query.query}
    +                            
    +                        
    +
    +
    + {this.renderStages()} + {this.renderTasks()} +
    + ); + } +} diff --git a/presto-main/src/main/resources/webapp/src/components/QueryHeader.jsx b/presto-main/src/main/resources/webapp/src/components/QueryHeader.jsx new file mode 100644 index 0000000000000..20d2295d8f957 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/components/QueryHeader.jsx @@ -0,0 +1,125 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react"; + +import { + getProgressBarPercentage, + getProgressBarTitle, + getQueryStateColor, + isQueryEnded +} from "../utils"; + +export class QueryHeader extends React.Component { + constructor(props) { + super(props); + } + + renderProgressBar() { + const query = this.props.query; + const progressBarStyle = {width: getProgressBarPercentage(query) + "%", backgroundColor: getQueryStateColor(query)}; + + if (isQueryEnded(query)) { + return ( +
    +
    + {getProgressBarTitle(query)} +
    +
    + ); + } + + return ( + + + + + + + + +
    +
    +
    + {getProgressBarTitle(query)} +
    +
    +
    + $.ajax({url: '/v1/query/' + query.queryId + '/preempted', type: 'PUT', data: "Preempted via web UI"})} className="btn btn-warning" + target="_blank"> + Preempt + + + $.ajax({url: '/v1/query/' + query.queryId + '/killed', type: 'PUT', data: "Killed via web UI"})} className="btn btn-warning" + target="_blank"> + Kill + +
    + ); + } + + renderTab(path, name) { + const queryId = this.props.query.queryId; + if (window.location.pathname.includes(path)) { + return {name}; + } + + return {name}; + } + + render() { + const query = this.props.query; + return ( +
    +
    +
    +

    + {query.queryId} + + +

    +
    +
    + + + + + + +
    + {this.renderTab("query.html", "Overview")} +   + {this.renderTab("plan.html", "Live Plan")} +   + {this.renderTab("stage.html", "Stage Performance")} +   + {this.renderTab("timeline.html", "Splits")} +   + JSON +
    +
    +
    +
    +
    +
    + {this.renderProgressBar()} +
    +
    +
    + ); + } +} diff --git a/presto-main/src/main/resources/webapp/assets/query-list.js b/presto-main/src/main/resources/webapp/src/components/QueryList.jsx similarity index 59% rename from presto-main/src/main/resources/webapp/assets/query-list.js rename to presto-main/src/main/resources/webapp/src/components/QueryList.jsx index 2b1aee9cd3a04..07679cf29bfec 100644 --- a/presto-main/src/main/resources/webapp/assets/query-list.js +++ b/presto-main/src/main/resources/webapp/src/components/QueryList.jsx @@ -12,9 +12,24 @@ * limitations under the License. */ - let QueryListItem = React.createClass({ - formatQueryText: function(queryText) - { +import React from "react"; + +import { + formatDataSizeBytes, + formatShortTime, + getHumanReadableState, + getProgressBarPercentage, + getProgressBarTitle, + getQueryStateColor, + GLYPHICON_DEFAULT, + GLYPHICON_HIGHLIGHT, + parseDataSize, + parseDuration, + truncateString +} from "../utils"; + +export class QueryListItem extends React.Component { + static stripQueryTextWhitespace(queryText) { const lines = queryText.split("\n"); let minLeadingWhitespace = -1; for (let i = 0; i < lines.length; i++) { @@ -29,7 +44,7 @@ const leadingWhitespace = lines[i].search(/\S/); if (leadingWhitespace > -1 && ((leadingWhitespace < minLeadingWhitespace) || minLeadingWhitespace === -1)) { - minLeadingWhitespace = leadingWhitespace; + minLeadingWhitespace = leadingWhitespace; } } @@ -39,73 +54,73 @@ const trimmedLine = lines[i].substring(minLeadingWhitespace).replace(/\s+$/g, ''); if (trimmedLine.length > 0) { - formattedQueryText += trimmedLine; + formattedQueryText += trimmedLine; - if (i < (lines.length -1)) { + if (i < (lines.length - 1)) { formattedQueryText += "\n"; } } } return truncateString(formattedQueryText, 300); - }, - render: function() - { + } + + render() { const query = this.props.query; const progressBarStyle = {width: getProgressBarPercentage(query) + "%", backgroundColor: getQueryStateColor(query)}; const splitDetails = (
    -    - { query.queryStats.completedDrivers } +    + {query.queryStats.completedDrivers} -    - { (query.state === "FINISHED" || query.state === "FAILED") ? 0 : query.queryStats.runningDrivers } +    + {(query.state === "FINISHED" || query.state === "FAILED") ? 0 : query.queryStats.runningDrivers} -    - { (query.state === "FINISHED" || query.state === "FAILED") ? 0 : query.queryStats.queuedDrivers } +    + {(query.state === "FINISHED" || query.state === "FAILED") ? 0 : query.queryStats.queuedDrivers} -
    ); +
    ); const timingDetails = (
    -    - { query.queryStats.executionTime } +    + {query.queryStats.executionTime} -    - { query.queryStats.elapsedTime } +    + {query.queryStats.elapsedTime} -    - { query.queryStats.totalCpuTime } +    + {query.queryStats.totalCpuTime} -
    ); +
    ); const memoryDetails = (
    -    - { query.queryStats.userMemoryReservation } +    + {query.queryStats.userMemoryReservation} -    - { query.queryStats.peakUserMemoryReservation } +    + {query.queryStats.peakUserMemoryReservation} - -    - { formatDataSizeBytes(query.queryStats.cumulativeUserMemory) } + +    + {formatDataSizeBytes(query.queryStats.cumulativeUserMemory / 1000.0)} -
    ); +
    ); - let user = ({ query.session.user }); + let user = ({query.session.user}); if (query.session.principal) { user = ( - { query.session.user } + {query.session.user} ); } @@ -115,51 +130,60 @@
    - { formatShortTime(new Date(Date.parse(query.queryStats.createTime))) } + {formatShortTime(new Date(Date.parse(query.queryStats.createTime)))}
    -    - { truncateString(user, 35) } +    + {truncateString(user, 35)}
    -    - { truncateString(query.session.source, 35) } +    + {truncateString(query.session.source, 35)}
    - { splitDetails } +
    + +    + {truncateString(query.resourceGroupId ? query.resourceGroupId.join(".") : "n/a", 35)} + +
    - { timingDetails } + {splitDetails}
    - { memoryDetails } + {timingDetails} +
    +
    + {memoryDetails}
    -
    - { getProgressBarTitle(query) } +
    + {getProgressBarTitle(query)}
    -
    { this.formatQueryText(query.query) }
    +
    {QueryListItem.stripQueryTextWhitespace(query.query)}
    @@ -167,32 +191,29 @@
    ); } -}); +} -let DisplayedQueriesList = React.createClass({ - render: function() - { +class DisplayedQueriesList extends React.Component { + render() { const queryNodes = this.props.queries.map(function (query) { return ( - + ); }.bind(this)); return ( -
    - { queryNodes } -
    +
    + {queryNodes} +
    ); } -}); +} const FILTER_TYPE = { - RUNNING_BLOCKED: function (query) { - return query.state === "PLANNING" || query.state === "STARTING" || query.state === "RUNNING" || query.state === "FINISHING"; + RUNNING: function (query) { + return !(query.state === "QUEUED" || query.state === "FINISHED" || query.state === "FAILED"); }, QUEUED: function (query) { return query.state === "QUEUED"}, FINISHED: function (query) { return query.state === "FINISHED"}, - FAILED: function (query) { return query.state === "FAILED" && query.errorType !== "USER_ERROR"}, - USER_ERROR: function (query) { return query.state === "FAILED" && query.errorType === "USER_ERROR"}, }; const SORT_TYPE = { @@ -204,28 +225,43 @@ const SORT_TYPE = { CURRENT_MEMORY: function (query) {return parseDataSize(query.queryStats.userMemoryReservation)}, }; +const ERROR_TYPE = { + USER_ERROR: function (query) {return query.state === "FAILED" && query.errorType === "USER_ERROR"}, + INTERNAL_ERROR: function (query) {return query.state === "FAILED" && query.errorType === "INTERNAL_ERROR"}, + INSUFFICIENT_RESOURCES: function (query) {return query.state === "FAILED" && query.errorType === "INSUFFICIENT_RESOURCES"}, + EXTERNAL: function (query) {return query.state === "FAILED" && query.errorType === "EXTERNAL"}, +}; + const SORT_ORDER = { ASCENDING: function (value) {return value}, DESCENDING: function (value) {return -value} }; -let QueryList = React.createClass({ - getInitialState: function () { - return { +export class QueryList extends React.Component { + constructor(props) { + super(props); + this.state = { allQueries: [], displayedQueries: [], reorderInterval: 5000, currentSortType: SORT_TYPE.CREATED, currentSortOrder: SORT_ORDER.DESCENDING, - filters: [FILTER_TYPE.RUNNING_BLOCKED, FILTER_TYPE.QUEUED, FILTER_TYPE.FAILED], + stateFilters: [FILTER_TYPE.RUNNING, FILTER_TYPE.QUEUED], + errorTypeFilters: [ERROR_TYPE.INTERNAL_ERROR, ERROR_TYPE.INSUFFICIENT_RESOURCES, ERROR_TYPE.EXTERNAL], searchString: '', maxQueries: 100, lastRefresh: Date.now(), lastReorder: Date.now(), initialized: false }; - }, - sortAndLimitQueries: function (queries, sortType, sortOrder, maxQueries) { + + this.refreshLoop = this.refreshLoop.bind(this); + this.handleSearchStringChange = this.handleSearchStringChange.bind(this); + this.executeSearch = this.executeSearch.bind(this); + this.handleSortClick = this.handleSortClick.bind(this); + } + + sortAndLimitQueries(queries, sortType, sortOrder, maxQueries) { queries.sort(function (queryA, queryB) { return sortOrder(sortType(queryA) - sortType(queryB)); }, this); @@ -233,11 +269,17 @@ let QueryList = React.createClass({ if (maxQueries !== 0 && queries.length > maxQueries) { queries.splice(maxQueries, (queries.length - maxQueries)); } - }, - filterQueries: function (queries, filters, searchString) { + } + + filterQueries(queries, stateFilters, errorTypeFilters, searchString) { const stateFilteredQueries = queries.filter(function (query) { - for (let i = 0; i < filters.length; i++) { - if (filters[i](query)) { + for (let i = 0; i < stateFilters.length; i++) { + if (stateFilters[i](query)) { + return true; + } + } + for (let i = 0; i < errorTypeFilters.length; i++) { + if (errorTypeFilters[i](query)) { return true; } } @@ -264,17 +306,23 @@ let QueryList = React.createClass({ return true; } + if (query.resourceGroupId && query.resourceGroupId.join(".").toLowerCase().indexOf(term) !== -1) { + return true; + } + }, this); } - }, - resetTimer: function () { + } + + resetTimer() { clearTimeout(this.timeoutId); // stop refreshing when query finishes or fails if (this.state.query === null || !this.state.ended) { this.timeoutId = setTimeout(this.refreshLoop, 1000); } - }, - refreshLoop: function () { + } + + refreshLoop() { clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously clearTimeout(this.searchTimeoutId); @@ -298,13 +346,13 @@ let QueryList = React.createClass({ newQueries.push(queryMap[queryId]); } } - newQueries = this.filterQueries(newQueries, this.state.filters, this.state.searchString); + newQueries = this.filterQueries(newQueries, this.state.stateFilters, this.state.errorTypeFilters, this.state.searchString); const lastRefresh = Date.now(); let lastReorder = this.state.lastReorder; if (this.state.reorderInterval !== 0 && ((lastRefresh - lastReorder) >= this.state.reorderInterval)) { - updatedQueries = this.filterQueries(updatedQueries, this.state.filters, this.state.searchString); + updatedQueries = this.filterQueries(updatedQueries, this.state.stateFilters, this.state.errorTypeFilters, this.state.searchString); updatedQueries = updatedQueries.concat(newQueries); this.sortAndLimitQueries(updatedQueries, this.state.currentSortType, this.state.currentSortOrder, 0); lastReorder = Date.now(); @@ -333,11 +381,13 @@ let QueryList = React.createClass({ }); this.resetTimer(); }.bind(this)); - }, - componentDidMount: function () { + } + + componentDidMount() { this.refreshLoop(); - }, - handleSearchStringChange: function (event) { + } + + handleSearchStringChange(event) { const newSearchString = event.target.value; clearTimeout(this.searchTimeoutId); @@ -346,65 +396,72 @@ let QueryList = React.createClass({ }); this.searchTimeoutId = setTimeout(this.executeSearch, 200); - }, - executeSearch: function () { + } + + executeSearch() { clearTimeout(this.searchTimeoutId); - const newDisplayedQueries = this.filterQueries(this.state.allQueries, this.state.filters, this.state.searchString); + const newDisplayedQueries = this.filterQueries(this.state.allQueries, this.state.stateFilters, this.state.errorTypeFilters, this.state.searchString); this.sortAndLimitQueries(newDisplayedQueries, this.state.currentSortType, this.state.currentSortOrder, this.state.maxQueries); this.setState({ displayedQueries: newDisplayedQueries }); - }, - renderMaxQueriesListItem: function (maxQueries, maxQueriesText) { + } + + renderMaxQueriesListItem(maxQueries, maxQueriesText) { return ( -
  • { maxQueriesText } +
  • {maxQueriesText}
  • ); - }, - handleMaxQueriesClick: function (newMaxQueries) { - const filteredQueries = this.filterQueries(this.state.allQueries, this.state.filters, this.state.searchString); + } + + handleMaxQueriesClick(newMaxQueries) { + const filteredQueries = this.filterQueries(this.state.allQueries, this.state.stateFilters, this.state.errorTypeFilters, this.state.searchString); this.sortAndLimitQueries(filteredQueries, this.state.currentSortType, this.state.currentSortOrder, newMaxQueries); this.setState({ maxQueries: newMaxQueries, displayedQueries: filteredQueries }); - }, - renderReorderListItem: function (interval, intervalText) { + } + + renderReorderListItem(interval, intervalText) { return ( -
  • { intervalText }
  • +
  • {intervalText}
  • ); - }, - handleReorderClick: function (interval) { + } + + handleReorderClick(interval) { if (this.state.reorderInterval !== interval) { this.setState({ reorderInterval: interval, }); } - }, - renderSortListItem: function (sortType, sortText) { + } + + renderSortListItem(sortType, sortText) { if (this.state.currentSortType === sortType) { const directionArrow = this.state.currentSortOrder === SORT_ORDER.ASCENDING ? : ; return (
  • - { sortText } { directionArrow } + {sortText} {directionArrow}
  • ); } else { return (
  • - - { sortText } + + {sortText}
  • ); } - }, - handleSortClick: function (sortType) { + } + + handleSortClick(sortType) { const newSortType = sortType; let newSortOrder = SORT_ORDER.DESCENDING; @@ -412,7 +469,7 @@ let QueryList = React.createClass({ newSortOrder = SORT_ORDER.ASCENDING; } - const newDisplayedQueries = this.filterQueries(this.state.allQueries, this.state.filters, this.state.searchString); + const newDisplayedQueries = this.filterQueries(this.state.allQueries, this.state.stateFilters, this.state.errorTypeFilters, this.state.searchString); this.sortAndLimitQueries(newDisplayedQueries, newSortType, newSortOrder, this.state.maxQueries); this.setState({ @@ -420,36 +477,75 @@ let QueryList = React.createClass({ currentSortType: newSortType, currentSortOrder: newSortOrder }); - }, - renderFilterButton: function (filterType, filterText) { + } + + renderFilterButton(filterType, filterText) { + let checkmarkStyle = {color: '#57aac7'}; let classNames = "btn btn-sm btn-info style-check"; - if (this.state.filters.indexOf(filterType) > -1) { + if (this.state.stateFilters.indexOf(filterType) > -1) { classNames += " active"; + checkmarkStyle = {color: '#ffffff'}; } return ( - + ); - }, - handleFilterClick: function (filter) { - const newFilters = this.state.filters.slice(); - if (this.state.filters.indexOf(filter) > -1) { + } + + handleStateFilterClick(filter) { + const newFilters = this.state.stateFilters.slice(); + if (this.state.stateFilters.indexOf(filter) > -1) { newFilters.splice(newFilters.indexOf(filter), 1); } else { newFilters.push(filter); } - const filteredQueries = this.filterQueries(this.state.allQueries, newFilters, this.state.searchString); + const filteredQueries = this.filterQueries(this.state.allQueries, newFilters, this.state.errorTypeFilters, this.state.searchString); this.sortAndLimitQueries(filteredQueries, this.state.currentSortType, this.state.currentSortOrder); this.setState({ - filters: newFilters, + stateFilters: newFilters, displayedQueries: filteredQueries }); - }, - render: function () { - let queryList = ; + } + + renderErrorTypeListItem(errorType, errorTypeText) { + let checkmarkStyle = {color: '#ffffff'}; + if (this.state.errorTypeFilters.indexOf(errorType) > -1) { + checkmarkStyle = GLYPHICON_HIGHLIGHT; + } + return ( +
  • + + +  {errorTypeText} + +
  • ); + } + + handleErrorTypeFilterClick(errorType) { + const newFilters = this.state.errorTypeFilters.slice(); + if (this.state.errorTypeFilters.indexOf(errorType) > -1) { + newFilters.splice(newFilters.indexOf(errorType), 1); + } + else { + newFilters.push(errorType); + } + + const filteredQueries = this.filterQueries(this.state.allQueries, this.state.stateFilters, newFilters, this.state.searchString); + this.sortAndLimitQueries(filteredQueries, this.state.currentSortType, this.state.currentSortOrder); + + this.setState({ + errorTypeFilters: newFilters, + displayedQueries: filteredQueries + }); + } + + render() { + let queryList = ; if (this.state.displayedQueries === null || this.state.displayedQueries.length === 0) { let label = (
    Loading...
    ); if (this.state.initialized) { @@ -462,7 +558,7 @@ let QueryList = React.createClass({ } queryList = (
    -

    { label }

    +

    {label}

    ); } @@ -472,15 +568,22 @@ let QueryList = React.createClass({
    - - Filter: + State:
    - { this.renderFilterButton(FILTER_TYPE.RUNNING_BLOCKED, "Running/blocked") } - { this.renderFilterButton(FILTER_TYPE.QUEUED, "Queued") } - { this.renderFilterButton(FILTER_TYPE.FINISHED, "Finished") } - { this.renderFilterButton(FILTER_TYPE.FAILED, "Failed") } - { this.renderFilterButton(FILTER_TYPE.USER_ERROR, "User error") } + {this.renderFilterButton(FILTER_TYPE.RUNNING, "Running")} + {this.renderFilterButton(FILTER_TYPE.QUEUED, "Queued")} + {this.renderFilterButton(FILTER_TYPE.FINISHED, "Finished")} + +
      + {this.renderErrorTypeListItem(ERROR_TYPE.INTERNAL_ERROR, "Internal Error")} + {this.renderErrorTypeListItem(ERROR_TYPE.EXTERNAL, "External Error")} + {this.renderErrorTypeListItem(ERROR_TYPE.INSUFFICIENT_RESOURCES, "Resources Error")} + {this.renderErrorTypeListItem(ERROR_TYPE.USER_ERROR, "User Error")} +
     
    @@ -488,12 +591,12 @@ let QueryList = React.createClass({ Sort
      - { this.renderSortListItem(SORT_TYPE.CREATED, "Creation Time") } - { this.renderSortListItem(SORT_TYPE.ELAPSED, "Elapsed Time") } - { this.renderSortListItem(SORT_TYPE.CPU, "CPU Time") } - { this.renderSortListItem(SORT_TYPE.EXECUTION, "Execution Time") } - { this.renderSortListItem(SORT_TYPE.CURRENT_MEMORY, "Current Memory") } - { this.renderSortListItem(SORT_TYPE.CUMULATIVE_MEMORY, "Cumulative Memory") } + {this.renderSortListItem(SORT_TYPE.CREATED, "Creation Time")} + {this.renderSortListItem(SORT_TYPE.ELAPSED, "Elapsed Time")} + {this.renderSortListItem(SORT_TYPE.CPU, "CPU Time")} + {this.renderSortListItem(SORT_TYPE.EXECUTION, "Execution Time")} + {this.renderSortListItem(SORT_TYPE.CURRENT_MEMORY, "Current Memory")} + {this.renderSortListItem(SORT_TYPE.CUMULATIVE_MEMORY, "Cumulative User Memory")}
      @@ -502,12 +605,12 @@ let QueryList = React.createClass({ Reorder Interval
      - { this.renderReorderListItem(1000, "1s") } - { this.renderReorderListItem(5000, "5s") } - { this.renderReorderListItem(10000, "10s") } - { this.renderReorderListItem(30000, "30s") } + {this.renderReorderListItem(1000, "1s")} + {this.renderReorderListItem(5000, "5s")} + {this.renderReorderListItem(10000, "10s")} + {this.renderReorderListItem(30000, "30s")}
    • - { this.renderReorderListItem(0, "Off") } + {this.renderReorderListItem(0, "Off")}
      @@ -516,23 +619,19 @@ let QueryList = React.createClass({ Show
      - { this.renderMaxQueriesListItem(20, "20 queries") } - { this.renderMaxQueriesListItem(50, "50 queries") } - { this.renderMaxQueriesListItem(100, "100 queries") } + {this.renderMaxQueriesListItem(20, "20 queries")} + {this.renderMaxQueriesListItem(50, "50 queries")} + {this.renderMaxQueriesListItem(100, "100 queries")}
    • - { this.renderMaxQueriesListItem(0, "All queries") } + {this.renderMaxQueriesListItem(0, "All queries")}
    - { queryList } + {queryList}
    ); } -}); +} -ReactDOM.render( - , - document.getElementById('query-list') -); diff --git a/presto-main/src/main/resources/webapp/assets/stage.js b/presto-main/src/main/resources/webapp/src/components/StageDetail.jsx similarity index 68% rename from presto-main/src/main/resources/webapp/assets/stage.js rename to presto-main/src/main/resources/webapp/src/components/StageDetail.jsx index e0fcbc2a801be..5a802d8040fba 100644 --- a/presto-main/src/main/resources/webapp/assets/stage.js +++ b/presto-main/src/main/resources/webapp/src/components/StageDetail.jsx @@ -12,12 +12,33 @@ * limitations under the License. */ +import React from "react"; +import ReactDOM from "react-dom"; +import ReactDOMServer from "react-dom/server"; +import * as dagreD3 from "dagre-d3"; +import * as d3 from "d3"; + +import { + computeSources, + formatCount, + formatDataSize, + formatDuration, + getFirstParameter, + getTaskNumber, + initializeGraph, + initializeSvg, + isQueryEnded, + parseDataSize, + parseDuration +} from "../utils"; +import {QueryHeader} from "./QueryHeader"; + function getTotalWallTime(operator) { return parseDuration(operator.addInputWall) + parseDuration(operator.getOutputWall) + parseDuration(operator.finishWall) + parseDuration(operator.blockedWall) } -let OperatorSummary = React.createClass({ - render: function() { +class OperatorSummary extends React.Component { + render() { const operator = this.props.operator; const totalWallTime = parseDuration(operator.addInputWall) + parseDuration(operator.getOutputWall) + parseDuration(operator.finishWall) + parseDuration(operator.blockedWall); @@ -29,60 +50,60 @@ let OperatorSummary = React.createClass({
    - { operator.operatorType } -
    + {operator.operatorType} +
    - { formatCount(rowInputRate) + " rows/s (" + formatDataSize(byteInputRate) + "/s)" } + {formatCount(rowInputRate) + " rows/s (" + formatDataSize(byteInputRate) + "/s)"}
    - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
    - Output - - { formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")" } -
    - Drivers - - { operator.totalDrivers } -
    - Wall Time - - { formatDuration(totalWallTime) } -
    - Blocked - - { formatDuration(parseDuration(operator.blockedWall)) } -
    - Input - - { formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")" } -
    + Output + + {formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")"} +
    + Drivers + + {operator.totalDrivers} +
    + Wall Time + + {formatDuration(totalWallTime)} +
    + Blocked + + {formatDuration(parseDuration(operator.blockedWall))} +
    + Input + + {formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")"} +
    ); } -}); +} const BAR_CHART_PROPERTIES = { type: 'bar', @@ -95,41 +116,44 @@ const BAR_CHART_PROPERTIES = { disableHiddenCheck: true, }; -let OperatorStatistic = React.createClass({ - componentDidMount: function() { +class OperatorStatistic extends React.Component { + componentDidMount() { const operators = this.props.operators; const statistic = operators.map(this.props.supplier); const numTasks = operators.length; - const tooltipValueLookups = {'offset' : {}}; + const tooltipValueLookups = {'offset': {}}; for (let i = 0; i < numTasks; i++) { tooltipValueLookups['offset'][i] = "" + i; } const stageBarChartProperties = $.extend({}, BAR_CHART_PROPERTIES, {barWidth: 800 / numTasks, tooltipValueLookups: tooltipValueLookups}); $('#' + this.props.id).sparkline(statistic, $.extend({}, stageBarChartProperties, {numberFormatter: this.props.renderer})); - }, - render: function() { + } + + render() { return (
    - { this.props.name } + {this.props.name}
    - +
    ); } -}); +} -let OperatorDetail = React.createClass({ - getInitialState: function() { - return { +class OperatorDetail extends React.Component { + constructor(props) { + super(props); + this.state = { selectedStatistics: this.getInitialStatistics() - } - }, - getInitialStatistics: function () { + }; + } + + getInitialStatistics() { return [ { name: "Total Wall Time", @@ -162,11 +186,12 @@ let OperatorDetail = React.createClass({ renderer: formatDataSize }, ]; - }, - getOperatorTasks: function() { + } + + getOperatorTasks() { // sort the x-axis const tasks = this.props.tasks.sort(function (taskA, taskB) { - return getTaskIdInStage(taskA.taskStatus.taskId) - getTaskIdInStage(taskB.taskStatus.taskId); + return getTaskNumber(taskA.taskStatus.taskId) - getTaskNumber(taskB.taskStatus.taskId); }); const operatorSummary = this.props.operator; @@ -185,8 +210,9 @@ let OperatorDetail = React.createClass({ }); return operatorTasks; - }, - render: function() { + } + + render() { const operator = this.props.operator; const operatorTasks = this.getOperatorTasks(); const totalWallTime = getTotalWallTime(operator); @@ -203,9 +229,9 @@ let OperatorDetail = React.createClass({

    - Pipeline { operator.pipelineId } + Pipeline {operator.pipelineId}
    - { operator.operatorType } + {operator.operatorType}

    @@ -217,7 +243,7 @@ let OperatorDetail = React.createClass({ Input - { formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")" } + {formatCount(operator.inputPositions) + " rows (" + operator.inputDataSize + ")"} @@ -225,7 +251,7 @@ let OperatorDetail = React.createClass({ Input Rate - { formatCount(rowInputRate) + " rows/s (" + formatDataSize(byteInputRate) + "/s)" } + {formatCount(rowInputRate) + " rows/s (" + formatDataSize(byteInputRate) + "/s)"} @@ -233,7 +259,7 @@ let OperatorDetail = React.createClass({ Output - { formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")" } + {formatCount(operator.outputPositions) + " rows (" + operator.outputDataSize + ")"} @@ -241,7 +267,7 @@ let OperatorDetail = React.createClass({ Output Rate - { formatCount(rowOutputRate) + " rows/s (" + formatDataSize(byteOutputRate) + "/s)" } + {formatCount(rowOutputRate) + " rows/s (" + formatDataSize(byteOutputRate) + "/s)"} @@ -255,7 +281,7 @@ let OperatorDetail = React.createClass({ Wall Time - { formatDuration(totalWallTime) } + {formatDuration(totalWallTime)} @@ -263,7 +289,7 @@ let OperatorDetail = React.createClass({ Blocked - { formatDuration(parseDuration(operator.blockedWall)) } + {formatDuration(parseDuration(operator.blockedWall))} @@ -271,7 +297,7 @@ let OperatorDetail = React.createClass({ Drivers - { operator.totalDrivers } + {operator.totalDrivers} @@ -279,7 +305,7 @@ let OperatorDetail = React.createClass({ Tasks - { operatorTasks.length } + {operatorTasks.length} @@ -302,31 +328,33 @@ let OperatorDetail = React.createClass({ this.state.selectedStatistics.map(function (statistic) { return ( + key={statistic.id} + id={statistic.id} + name={statistic.name} + supplier={statistic.supplier} + renderer={statistic.renderer} + operators={operatorTasks}/> ); }.bind(this)) } -

    -

    +

    +

    ); } -}); +} -let StageOperatorGraph = React.createClass({ - componentDidMount: function() { +class StageOperatorGraph extends React.Component { + componentDidMount() { this.updateD3Graph(); - }, - componentDidUpdate: function() { + } + + componentDidUpdate() { this.updateD3Graph(); - }, - handleOperatorClick: function(operatorCssId) { + } + + handleOperatorClick(operatorCssId) { $('#operator-detail-modal').modal(); const pipelineId = parseInt(operatorCssId.split('-')[1]); @@ -341,10 +369,11 @@ let StageOperatorGraph = React.createClass({ } } - ReactDOM.render(, + ReactDOM.render(, document.getElementById('operator-detail')); - }, - computeOperatorGraphs: function(planNode, operatorMap) { + } + + computeOperatorGraphs(planNode, operatorMap) { const sources = computeSources(planNode)[0]; const sourceResults = new Map(); @@ -403,8 +432,9 @@ let StageOperatorGraph = React.createClass({ }); return result; - }, - computeOperatorMap: function() { + } + + computeOperatorMap() { const operatorMap = new Map(); this.props.stage.stageStats.operatorSummaries.forEach(operator => { if (!operatorMap.has(operator.planNodeId)) { @@ -415,12 +445,13 @@ let StageOperatorGraph = React.createClass({ }); return operatorMap; - }, - computeD3StageOperatorGraph: function(graph, operator, sink, pipelineNode) { + } + + computeD3StageOperatorGraph(graph, operator, sink, pipelineNode) { const operatorNodeId = "operator-" + operator.pipelineId + "-" + operator.operatorId; // this is a non-standard use of ReactDOMServer, but it's the cleanest way to unify DagreD3 with React - const html = ReactDOMServer.renderToString(); + const html = ReactDOMServer.renderToString(); graph.setNode(operatorNodeId, {class: "operator-stats", label: html, labelType: "html"}); if (operator.hasOwnProperty("child")) { @@ -432,8 +463,9 @@ let StageOperatorGraph = React.createClass({ } graph.setParent(operatorNodeId, pipelineNode); - }, - updateD3Graph: function() { + } + + updateD3Graph() { if (!this.props.stage) { return; } @@ -457,15 +489,16 @@ let StageOperatorGraph = React.createClass({ const render = new dagreD3.render(); render(d3.select("#operator-canvas g"), graph); - svg.selectAll("g.operator-stats").on("click", this.handleOperatorClick); + svg.selectAll("g.operator-stats").on("click", this.handleOperatorClick.bind(this)); svg.attr("height", graph.graph().height); svg.attr("width", graph.graph().width); } else { $(".graph-container").css("display", "none"); } - }, - render: function() { + } + + render() { const stage = this.props.stage; if (!stage.hasOwnProperty('plan')) { @@ -480,7 +513,7 @@ let StageOperatorGraph = React.createClass({ return (
    -

    Operator data not available for { stage.stageId }

    +

    Operator data not available for {stage.stageId}

    ); @@ -488,11 +521,12 @@ let StageOperatorGraph = React.createClass({ return null; } -}); +} -let StagePerformance = React.createClass({ - getInitialState: function() { - return { +export class StageDetail extends React.Component { + constructor(props) { + super(props); + this.state = { initialized: false, ended: false, @@ -502,50 +536,19 @@ let StagePerformance = React.createClass({ lastRefresh: null, lastRender: null }; - }, - resetTimer: function() { + + this.refreshLoop = this.refreshLoop.bind(this); + } + + resetTimer() { clearTimeout(this.timeoutId); // stop refreshing when query finishes or fails if (this.state.query === null || !this.state.ended) { this.timeoutId = setTimeout(this.refreshLoop, 1000); } - }, - renderProgressBar: function() { - const query = this.state.query; - const progressBarStyle = { width: getProgressBarPercentage(query) + "%", backgroundColor: getQueryStateColor(query) }; - - if (isQueryComplete(query)) { - return ( -
    -
    - { getProgressBarTitle(query) } -
    -
    - ); - } + } - return ( - - - - - - - -
    -
    -
    - { getProgressBarTitle(query) } -
    -
    -
    - $.ajax({url: '/v1/query/' + query.queryId, type: 'DELETE'}) } className="btn btn-warning" target="_blank"> - Kill - -
    - ); - }, - refreshLoop: function() { + refreshLoop() { clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously const queryString = getFirstParameter(window.location.search).split('.'); const queryId = queryString[0]; @@ -573,11 +576,13 @@ let StagePerformance = React.createClass({ }); this.resetTimer(); }); - }, - componentDidMount: function() { + } + + componentDidMount() { this.refreshLoop(); - }, - findStage: function (stageId, currentStage) { + } + + findStage(stageId, currentStage) { if (stageId === null) { return null; } @@ -594,14 +599,16 @@ let StagePerformance = React.createClass({ } return null; - }, - getAllStageIds: function (result, currentStage) { + } + + getAllStageIds(result, currentStage) { result.push(currentStage.plan.id); currentStage.subStages.forEach(stage => { this.getAllStageIds(result, stage); }); - }, - render: function() { + } + + render() { if (!this.state.query) { let label = (
    Loading...
    ); if (this.state.initialized) { @@ -609,7 +616,7 @@ let StagePerformance = React.createClass({ } return (
    -

    { label }

    +

    {label}

    ); } @@ -626,7 +633,7 @@ let StagePerformance = React.createClass({ const allStages = []; this.getAllStageIds(allStages, query.outputStage); - const stage = this.findStage(query.queryId + "." + this.state.selectedStageId, query.outputStage); + const stage = this.findStage(query.queryId + "." + this.state.selectedStageId, query.outputStage); if (stage === null) { return (
    @@ -636,7 +643,7 @@ let StagePerformance = React.createClass({ } let stageOperatorGraph = null; - if (!isQueryComplete(query)) { + if (!isQueryEnded(query)) { stageOperatorGraph = (
    @@ -647,60 +654,26 @@ let StagePerformance = React.createClass({ ) } else { - stageOperatorGraph = ; + stageOperatorGraph = ; } return (
    -
    -
    -

    - { query.queryId } - - -

    -
    -
    - - - - - - -
    - Overview -   - Live Plan -   - Stage Performance -   - Splits -   - JSON -
    -
    -
    -
    -
    -
    - { this.renderProgressBar() } -
    -
    +
    -

    Stage { stage.plan.id }

    +

    Stage {stage.plan.id}

    @@ -710,16 +683,10 @@ let StagePerformance = React.createClass({
    - { stageOperatorGraph } + {stageOperatorGraph}
    ); - } -}); - -ReactDOM.render( - , - document.getElementById('stage-performance-header') -); +} diff --git a/presto-main/src/main/resources/webapp/assets/worker.js b/presto-main/src/main/resources/webapp/src/components/WorkerStatus.jsx similarity index 58% rename from presto-main/src/main/resources/webapp/assets/worker.js rename to presto-main/src/main/resources/webapp/src/components/WorkerStatus.jsx index b63524d4ca8cd..d5e6bfc935d51 100644 --- a/presto-main/src/main/resources/webapp/assets/worker.js +++ b/presto-main/src/main/resources/webapp/src/components/WorkerStatus.jsx @@ -12,19 +12,30 @@ * limitations under the License. */ +import React from "react"; + +import { + addToHistory, + formatCount, + formatDataSize, + getFirstParameter, + precisionRound +} from "../utils"; + const SMALL_SPARKLINE_PROPERTIES = { - width:'100%', + width: '100%', height: '57px', - fillColor:'#3F4552', + fillColor: '#3F4552', lineColor: '#747F96', spotColor: '#1EDCFF', tooltipClassname: 'sparkline-tooltip', disableHiddenCheck: true, }; -let WorkerStatus = React.createClass({ - getInitialState: function() { - return { +export class WorkerStatus extends React.Component { + constructor(props) { + super(props); + this.state = { serverInfo: null, initialized: false, ended: false, @@ -34,15 +45,19 @@ let WorkerStatus = React.createClass({ heapPercentUsed: [], nonHeapUsed: [], }; - }, - resetTimer: function() { + + this.refreshLoop = this.refreshLoop.bind(this); + } + + resetTimer() { clearTimeout(this.timeoutId); // stop refreshing when query finishes or fails if (this.state.query === null || !this.state.ended) { this.timeoutId = setTimeout(this.refreshLoop, 1000); } - }, - refreshLoop: function() { + } + + refreshLoop() { clearTimeout(this.timeoutId); // to stop multiple series of refreshLoop from going on simultaneously const nodeId = getFirstParameter(window.location.search); $.get('/v1/worker/' + nodeId + '/status', function (serverInfo) { @@ -52,23 +67,25 @@ let WorkerStatus = React.createClass({ processCpuLoad: addToHistory(serverInfo.processCpuLoad * 100.0, this.state.processCpuLoad), systemCpuLoad: addToHistory(serverInfo.systemCpuLoad * 100.0, this.state.systemCpuLoad), - heapPercentUsed: addToHistory(serverInfo.heapUsed * 100.0/ serverInfo.heapAvailable, this.state.heapPercentUsed), + heapPercentUsed: addToHistory(serverInfo.heapUsed * 100.0 / serverInfo.heapAvailable, this.state.heapPercentUsed), nonHeapUsed: addToHistory(serverInfo.nonHeapUsed * 100.0, this.state.nonHeapUsed), }); this.resetTimer(); }.bind(this)) - .error(function() { + .error(function () { this.setState({ initialized: true, }); this.resetTimer(); }.bind(this)); - }, - componentDidMount: function() { + } + + componentDidMount() { this.refreshLoop(); - }, - componentDidUpdate: function () { + } + + componentDidUpdate() { $('#process-cpu-load-sparkline').sparkline(this.state.processCpuLoad, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); $('#system-cpu-load-sparkline').sparkline(this.state.systemCpuLoad, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); $('#heap-percent-used-sparkline').sparkline(this.state.heapPercentUsed, $.extend({}, SMALL_SPARKLINE_PROPERTIES, {chartRangeMin: 0, numberFormatter: precisionRound})); @@ -76,14 +93,19 @@ let WorkerStatus = React.createClass({ $('[data-toggle="tooltip"]').tooltip(); new Clipboard('.copy-button'); - }, - renderPoolBar: function(name, pool) { + } + + static renderPoolBar(name, pool) { + if (!pool) { + return; + } + const size = pool.maxBytes; const reserved = pool.reservedBytes; const revocable = pool.reservedRevocableBytes; - const percentageReservedNonRevocable = (reserved - revocable) === 0 ? 0 : Math.max(Math.round((reserved - revocable) * 100.0/size), 15); - const percentageRevocable = revocable === 0 ? 0 : Math.max(Math.round(revocable * 100.0/size), 15); + const percentageReservedNonRevocable = (reserved - revocable) === 0 ? 0 : Math.max(Math.round((reserved - revocable) * 100.0 / size), 15); + const percentageRevocable = revocable === 0 ? 0 : Math.max(Math.round(revocable * 100.0 / size), 15); const percentageFree = 100 - (percentageRevocable + percentageReservedNonRevocable); return ( @@ -94,9 +116,9 @@ let WorkerStatus = React.createClass({

    {name} Pool

    -
    +
    - { formatDataSize(size) } total + {formatDataSize(size)} total
    @@ -107,44 +129,44 @@ let WorkerStatus = React.createClass({
    - { formatDataSize(reserved - revocable) } + {formatDataSize(reserved - revocable)}
    - { formatDataSize(revocable) } + {formatDataSize(revocable)}
    - { formatDataSize(size - reserved) } + {formatDataSize(size - reserved)}
    - ) - }, - renderPoolQuery: function(query, reserved, revocable, total) { + } + + static renderPoolQuery(query, reserved, revocable, total) { return (
    - { Math.round(reserved * 100.0 / total) }% + {Math.round(reserved * 100.0 / total)}%
    - { formatDataSize(reserved) } + title={"Reserved: " + formatDataSize(reserved) + ". Revocable: " + formatDataSize(revocable)}> + {formatDataSize(reserved)}
    @@ -153,8 +175,13 @@ let WorkerStatus = React.createClass({ ) - }, - renderPoolQueries: function(pool) { + } + + renderPoolQueries(pool) { + if (!pool) { + return; + } + const queries = {}; const reservations = pool.queryMemoryReservations; const revocableReservations = pool.queryMemoryRevocableReservations; @@ -179,11 +206,11 @@ let WorkerStatus = React.createClass({
    - - - + + +
    - No queries using pool -
    + No queries using pool +
    @@ -194,13 +221,14 @@ let WorkerStatus = React.createClass({
    - { Object.keys(queries).map(key => this.renderPoolQuery(key, queries[key][0], queries[key][1], size)) } + {Object.keys(queries).map(key => WorkerStatus.renderPoolQuery(key, queries[key][0], queries[key][1], size))}
    ) - }, - render: function() { + } + + render() { const serverInfo = this.state.serverInfo; if (serverInfo === null) { @@ -322,7 +350,7 @@ let WorkerStatus = React.createClass({ - { formatCount(this.state.processCpuLoad[this.state.processCpuLoad.length - 1]) }% + {formatCount(this.state.processCpuLoad[this.state.processCpuLoad.length - 1])}% @@ -337,7 +365,7 @@ let WorkerStatus = React.createClass({ - { formatCount(this.state.systemCpuLoad[this.state.systemCpuLoad.length - 1]) }% + {formatCount(this.state.systemCpuLoad[this.state.systemCpuLoad.length - 1])}% @@ -358,7 +386,7 @@ let WorkerStatus = React.createClass({ - { formatCount(this.state.heapPercentUsed[this.state.heapPercentUsed.length - 1]) }% + {formatCount(this.state.heapPercentUsed[this.state.heapPercentUsed.length - 1])}% @@ -373,7 +401,7 @@ let WorkerStatus = React.createClass({ - { formatDataSize(this.state.nonHeapUsed[this.state.nonHeapUsed.length - 1]) } + {formatDataSize(this.state.nonHeapUsed[this.state.nonHeapUsed.length - 1])} @@ -390,12 +418,12 @@ let WorkerStatus = React.createClass({
    - { this.renderPoolBar("General", serverInfo.memoryInfo.pools.general) } - { this.renderPoolQueries(serverInfo.memoryInfo.pools.general) } + {WorkerStatus.renderPoolBar("General", serverInfo.memoryInfo.pools.general)} + {this.renderPoolQueries(serverInfo.memoryInfo.pools.general)}
    - { this.renderPoolBar("Reserved", serverInfo.memoryInfo.pools.reserved) } - { this.renderPoolQueries(serverInfo.memoryInfo.pools.reserved) } + {WorkerStatus.renderPoolBar("Reserved", serverInfo.memoryInfo.pools.reserved)} + {this.renderPoolQueries(serverInfo.memoryInfo.pools.reserved)}
    @@ -403,258 +431,4 @@ let WorkerStatus = React.createClass({
    ); } -}); - -const ALL_THREADS = "All Threads"; -const QUERY_THREADS = "Running Queries"; - -const ALL_THREAD_STATE = "ALL"; -const THREAD_STATES = [ALL_THREAD_STATE, "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "NEW", "TERMINATED"]; - -let WorkerThreads = React.createClass({ - getInitialState: function() { - return { - serverInfo: null, - initialized: false, - ended: false, - - threads: null, - - snapshotTime: null, - - selectedGroup: ALL_THREADS, - selectedThreadState: ALL_THREAD_STATE, - }; - }, - componentDidMount: function() { - const nodeId = getFirstParameter(window.location.search); - $.get('/v1/worker/' + nodeId + '/thread', function (threads) { - this.setState({ - threads: this.processThreads(threads), - snapshotTime: new Date(), - initialized: true, - }); - }.bind(this)) - .error(function() { - this.setState({ - initialized: true, - }); - }.bind(this)); - }, - componentDidUpdate: function () { - new Clipboard('.copy-button'); - }, - spliceThreadsByRegex: function(threads, regex) { - return [threads.filter(t => t.name.match(regex) !== null), threads.filter(t => t.name.match(regex) === null)]; - }, - processThreads: function(threads) { - const result = {}; - - result[ALL_THREADS] = threads; - - // first, pull out threads that are running queries - let [matched, remaining] = this.spliceThreadsByRegex(threads, /([0-9])*_([0-9])*_([0-9])*_.*?\.([0-9])*\.([0-9])*-([0-9])*-([0-9])*/); - result[QUERY_THREADS] = matched; - - if (matched.length !== 0) { - this.setState({ - selectedGroup: QUERY_THREADS - }) - } - - while (remaining.length > 0) { - const match = /(.*?)-[0-9]+/.exec(remaining[0].name); - - if (match === null) { - [result[remaining[0].name], remaining] = this.spliceThreadsByRegex(remaining, remaining[0].name); - } - else { - const [, namePrefix, ...ignored] = match; - [result[namePrefix], remaining] = this.spliceThreadsByRegex(remaining, namePrefix + "-[0-9]+"); - } - } - - return result - }, - handleGroupClick: function(selectedGroup, event) { - this.setState({ - selectedGroup: selectedGroup - }); - event.preventDefault(); - }, - handleThreadStateClick: function(selectedThreadState, event) { - this.setState({ - selectedThreadState: selectedThreadState - }); - event.preventDefault(); - }, - handleNewSnapshotClick: function(event) { - this.setState({ - initialized: false - }); - this.componentDidMount(); - event.preventDefault(); - }, - filterThreads(group, state) { - return this.state.threads[group].filter(t => t.state === state || state === ALL_THREAD_STATE); - }, - renderGroupListItem: function(group) { - return ( -
  • - - { group } ({ this.filterThreads(group, this.state.selectedThreadState).length }) - -
  • - ); - }, - renderThreadStateListItem: function(threadState) { - return ( -
  • - - { threadState } ({ this.filterThreads(this.state.selectedGroup, threadState).length }) - -
  • - ); - }, - renderStackLine(threadId) { - return (stackLine, index) => { - return ( -
    -   at {stackLine.className}.{stackLine.method} - ({ stackLine.file }:{stackLine.line}) -
    ); - }; - }, - renderThread(threadInfo) { - return ( -
    - {threadInfo.name} {threadInfo.state} #{threadInfo.id} {threadInfo.lockOwnerId} - - - -
    - - {threadInfo.stackTrace.map(this.renderStackLine(threadInfo.id))} - -
    -   -
    -
    - ); - }, - render: function() { - const threads = this.state.threads; - - if (threads === null) { - if (this.state.initialized === false) { - return ( -
    Loading...
    - ); - } - else { - return ( -
    -

    Thread snapshot could not be loaded

    -
    - ); - } - } - - const filteredThreads = this.filterThreads(this.state.selectedGroup, this.state.selectedThreadState); - let renderedThreads; - if (filteredThreads.length === 0 && this.state.selectedThreadState === ALL_THREAD_STATE) { - renderedThreads = ( -
    -

    No threads in group '{ this.state.selectedGroup }'

    -
    ); - } - else if (filteredThreads.length === 0 && this.state.selectedGroup === ALL_THREADS) { - renderedThreads = ( -
    -

    No threads with state { this.state.selectedThreadState }

    -
    ); - } - else if (filteredThreads.length === 0) { - renderedThreads = ( -
    -

    No threads in group '{ this.state.selectedGroup }' with state {this.state.selectedThreadState}

    -
    ); - } - else { - renderedThreads = ( -
    -                    { filteredThreads.map(t => this.renderThread(t)) }
    -                
    ); - } - - return ( -
    -
    -
    -

    - Thread Snapshot - - - -   -

    -
    -
    - - - - - -    - - - - -
    - Snapshot at { this.state.snapshotTime.toTimeString() } -    - - -
    - -
      - { Object.keys(threads).map(group => this.renderGroupListItem(group)) } -
    -
    -
    -
    - -
      - { THREAD_STATES.map(state => this.renderThreadStateListItem(state)) } -
    -
    -
    -
    -
    -
    -
    -
    -
    - {renderedThreads} -
    -
    -
    -
    - ); - } -}); - -ReactDOM.render( - , - document.getElementById('worker-status') -); - -ReactDOM.render( - , - document.getElementById('worker-threads') -); +} diff --git a/presto-main/src/main/resources/webapp/src/components/WorkerThreadList.jsx b/presto-main/src/main/resources/webapp/src/components/WorkerThreadList.jsx new file mode 100644 index 0000000000000..615bb5f65a87c --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/components/WorkerThreadList.jsx @@ -0,0 +1,284 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react"; + +import { + getFirstParameter +} from "../utils"; + +const ALL_THREADS = "All Threads"; +const QUERY_THREADS = "Running Queries"; + +const ALL_THREAD_STATE = "ALL"; +const THREAD_STATES = [ALL_THREAD_STATE, "RUNNABLE", "BLOCKED", "WAITING", "TIMED_WAITING", "NEW", "TERMINATED"]; +const QUERY_THREAD_REGEX = new RegExp(/([0-9])*_([0-9])*_([0-9])*_.*?\.([0-9])*\.([0-9])*-([0-9])*-([0-9])*/); +const THREAD_GROUP_REGEXP = new RegExp(/(.*?)-[0-9]+/); + +export class WorkerThreadList extends React.Component { + constructor(props) { + super(props); + this.state = { + serverInfo: null, + initialized: false, + ended: false, + + threads: null, + + snapshotTime: null, + + selectedGroup: ALL_THREADS, + selectedThreadState: ALL_THREAD_STATE, + }; + } + + captureSnapshot() { + const nodeId = getFirstParameter(window.location.search); + $.get('/v1/worker/' + nodeId + '/thread', function (threads) { + this.setState({ + threads: WorkerThreadList.processThreads(threads), + snapshotTime: new Date(), + initialized: true, + }); + }.bind(this)) + .error(function () { + this.setState({ + initialized: true, + }); + }.bind(this)); + } + + componentDidUpdate() { + new Clipboard('.copy-button'); + } + + static processThreads(threads) { + const result = {}; + + result[ALL_THREADS] = threads; + + for (let i = 0; i < threads.length; i++) { + const thread = threads[i]; + if (thread.name.match(QUERY_THREAD_REGEX)) { + result[QUERY_THREADS].push(thread) + } + + const match = THREAD_GROUP_REGEXP.exec(thread.name); + const threadGroup = match ? match[1] : thread.name; + if (!result[threadGroup]) { + result[threadGroup] = []; + } + result[threadGroup].push(thread); + } + + return result + } + + handleGroupClick(selectedGroup, event) { + this.setState({ + selectedGroup: selectedGroup + }); + event.preventDefault(); + } + + handleThreadStateClick(selectedThreadState, event) { + this.setState({ + selectedThreadState: selectedThreadState + }); + event.preventDefault(); + } + + handleNewSnapshotClick(event) { + this.setState({ + initialized: false + }); + this.captureSnapshot(); + event.preventDefault(); + } + + filterThreads(group, state) { + return this.state.threads[group].filter(t => t.state === state || state === ALL_THREAD_STATE); + } + + renderGroupListItem(group) { + return ( +
  • + + {group} ({this.filterThreads(group, this.state.selectedThreadState).length}) + +
  • + ); + } + + renderThreadStateListItem(threadState) { + return ( +
  • + + {threadState} ({this.filterThreads(this.state.selectedGroup, threadState).length}) + +
  • + ); + } + + renderStackLine(threadId) { + return (stackLine, index) => { + return ( +
    +   at {stackLine.className}.{stackLine.method} + ({stackLine.file}:{stackLine.line}) +
    ); + }; + } + + renderThread(threadInfo) { + return ( +
    + {threadInfo.name} {threadInfo.state} #{threadInfo.id} {threadInfo.lockOwnerId} + + + +
    + + {threadInfo.stackTrace.map(this.renderStackLine(threadInfo.id))} + +
    +   +
    +
    + ); + } + + render() { + const threads = this.state.threads; + + let display = null; + let toolbar = null; + if (threads === null) { + if (this.state.initialized === false) { + display = ( +
    +
    +
    + ); + } + else { + display = ( +
    +

    Thread snapshot could not be loaded

    +
    + ); + } + } + else { + toolbar = ( +
    + + + + + + + + + +
    + Snapshot at {this.state.snapshotTime.toTimeString()} +    + + +    +    + +
    + +
      + {Object.keys(threads).map(group => this.renderGroupListItem(group))} +
    +
    +
    +
    + +
      + {THREAD_STATES.map(state => this.renderThreadStateListItem(state))} +
    +
    +
    +
    + ); + + const filteredThreads = this.filterThreads(this.state.selectedGroup, this.state.selectedThreadState); + let displayedThreads; + if (filteredThreads.length === 0 && this.state.selectedThreadState === ALL_THREAD_STATE) { + displayedThreads = ( +
    +

    No threads in group '{this.state.selectedGroup}'

    +
    ); + } + else if (filteredThreads.length === 0 && this.state.selectedGroup === ALL_THREADS) { + displayedThreads = ( +
    +

    No threads with state {this.state.selectedThreadState}

    +
    ); + } + else if (filteredThreads.length === 0) { + displayedThreads = ( +
    +

    No threads in group '{this.state.selectedGroup}' with state {this.state.selectedThreadState}

    +
    ); + } + else { + displayedThreads = ( +
    +                        {filteredThreads.map(t => this.renderThread(t))}
    +                    
    ); + } + + display = ( +
    + {displayedThreads} +
    + ); + } + + return ( +
    +
    +
    +

    + Thread Snapshot + + + +   +

    +
    + {toolbar} +
    +
    +
    +
    + {display} +
    +
    +
    + ); + } +} diff --git a/presto-main/src/main/resources/webapp/src/embedded_plan.jsx b/presto-main/src/main/resources/webapp/src/embedded_plan.jsx new file mode 100644 index 0000000000000..151b4d2074af9 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/embedded_plan.jsx @@ -0,0 +1,9 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {LivePlan} from "./components/LivePlan"; +import {getFirstParameter} from "./utils"; + +ReactDOM.render( + , + document.getElementById('live-plan-header') +); diff --git a/presto-main/src/main/resources/webapp/src/index.jsx b/presto-main/src/main/resources/webapp/src/index.jsx new file mode 100644 index 0000000000000..815b2250987d5 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/index.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {ClusterHUD} from "./components/ClusterHUD"; +import {QueryList} from "./components/QueryList"; +import {PageTitle} from "./components/PageTitle"; + +ReactDOM.render( + , + document.getElementById('title') +); + +ReactDOM.render( + , + document.getElementById('cluster-hud') +); + +ReactDOM.render( + , + document.getElementById('query-list') +); diff --git a/presto-main/src/main/resources/webapp/src/package.json b/presto-main/src/main/resources/webapp/src/package.json new file mode 100644 index 0000000000000..e61d2bc8d9410 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/package.json @@ -0,0 +1,35 @@ +{ + "name": "presto-webui", + "version": "0.0.1", + "license": "Apache-2.0", + "private": true, + "devDependencies": { + "babel-core": "^6.26.3", + "babel-loader": "^7.1.5", + "babel-preset-env": "^1.7.0", + "babel-preset-flow": "^6.23.0", + "babel-preset-react": "^6.24.1", + "flow-bin": "^0.85.0", + "webpack": "^4.16.0", + "webpack-command": "^0.4.1" + }, + "dependencies": { + "d3": "^5.7.0", + "dagre-d3": "^0.6.1", + "react": "^16.4.1", + "react-dom": "^16.4.1", + "reactable": "^1.0.2" + }, + "babel": { + "presets": [ + "env", + "react", + "flow" + ] + }, + "scripts": { + "install": "webpack --config webpack.config.js", + "package": "webpack --config webpack.config.js", + "watch": "webpack --config webpack.config.js --watch" + } +} diff --git a/presto-main/src/main/resources/webapp/src/plan.jsx b/presto-main/src/main/resources/webapp/src/plan.jsx new file mode 100644 index 0000000000000..e1cad2fb75a22 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/plan.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {LivePlan} from "./components/LivePlan"; +import {PageTitle} from "./components/PageTitle"; +import {getFirstParameter} from "./utils"; + +ReactDOM.render( + , + document.getElementById('title') +); + +ReactDOM.render( + , + document.getElementById('live-plan-header') +); diff --git a/presto-main/src/main/resources/webapp/src/query.jsx b/presto-main/src/main/resources/webapp/src/query.jsx new file mode 100644 index 0000000000000..56cb2b2a7d311 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/query.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {QueryDetail} from "./components/QueryDetail"; +import {PageTitle} from "./components/PageTitle"; + +ReactDOM.render( + , + document.getElementById('title') +); + +ReactDOM.render( + , + document.getElementById('query-detail') +); diff --git a/presto-main/src/main/resources/webapp/src/stage.jsx b/presto-main/src/main/resources/webapp/src/stage.jsx new file mode 100644 index 0000000000000..559376b34e822 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/stage.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {StageDetail} from "./components/StageDetail"; +import {PageTitle} from "./components/PageTitle"; + +ReactDOM.render( + , + document.getElementById('title') +); + +ReactDOM.render( + , + document.getElementById('stage-performance-header') +); diff --git a/presto-main/src/main/resources/webapp/assets/utils.js b/presto-main/src/main/resources/webapp/src/utils.js similarity index 68% rename from presto-main/src/main/resources/webapp/assets/utils.js rename to presto-main/src/main/resources/webapp/src/utils.js index bc73f130c778d..bb631ab2b3b21 100644 --- a/presto-main/src/main/resources/webapp/assets/utils.js +++ b/presto-main/src/main/resources/webapp/src/utils.js @@ -11,14 +11,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +//@flow + +import * as dagreD3 from "dagre-d3"; +import * as d3 from "d3"; // Query display // ============= -var GLYPHICON_DEFAULT = {color: '#1edcff'}; -var GLYPHICON_HIGHLIGHT = {color: '#999999'}; +export const GLYPHICON_DEFAULT = {color: '#1edcff'}; +export const GLYPHICON_HIGHLIGHT = {color: '#999999'}; -var STATE_COLOR_MAP = { +const STATE_COLOR_MAP = { QUEUED: '#1b8f72', RUNNING: '#19874e', PLANNING: '#674f98', @@ -31,7 +35,7 @@ var STATE_COLOR_MAP = { UNKNOWN_ERROR: '#943524' }; -function getQueryStateColor(query) +export function getQueryStateColor(query: any): string { switch (query.state) { case "QUEUED": @@ -66,7 +70,7 @@ function getQueryStateColor(query) } } -function getStageStateColor(stage) +export function getStageStateColor(stage: any): string { switch (stage.state) { case "PLANNED": @@ -93,10 +97,10 @@ function getStageStateColor(stage) // This relies on the fact that BasicQueryInfo and QueryInfo have all the fields // necessary to compute this string, and that these fields are consistently named. -function getHumanReadableState(query) +export function getHumanReadableState(query: any): string { if (query.state === "RUNNING") { - var title = "RUNNING"; + let title = "RUNNING"; if (query.scheduled && query.queryStats.totalDrivers > 0 && query.queryStats.runningDrivers >= 0) { if (query.queryStats.fullyBlocked) { @@ -134,9 +138,9 @@ function getHumanReadableState(query) return query.state; } -function getProgressBarPercentage(query) +export function getProgressBarPercentage(query: any): number { - var progress = query.queryStats.progressPercentage; + const progress = query.queryStats.progressPercentage; // progress bars should appear 'full' when query progress is not meaningful if (!progress || query.state !== "RUNNING") { @@ -146,7 +150,7 @@ function getProgressBarPercentage(query) return Math.round(progress); } -function getProgressBarTitle(query) +export function getProgressBarTitle(query: any): string { if (query.queryStats.progressPercentage && query.state === "RUNNING") { return getHumanReadableState(query) + " (" + getProgressBarPercentage(query) + "%)" @@ -155,7 +159,7 @@ function getProgressBarTitle(query) return getHumanReadableState(query) } -function isQueryComplete(query) +export function isQueryEnded(query: any): boolean { return ["FINISHED", "FAILED", "CANCELED"].indexOf(query.state) > -1; } @@ -164,23 +168,23 @@ function isQueryComplete(query) // =========================== // display at most 5 minutes worth of data on the sparklines -var MAX_HISTORY = 60 * 5; - // alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness -var MOVING_AVERAGE_ALPHA = 0.2; +const MAX_HISTORY = 60 * 5; +// alpha param of exponentially weighted moving average. picked arbitrarily - lower values means more smoothness +const MOVING_AVERAGE_ALPHA = 0.2; -function addToHistory (value, valuesArray) { +export function addToHistory (value: number, valuesArray: number[]): number[] { if (valuesArray.length === 0) { return valuesArray.concat([value]); } return valuesArray.concat([value]).slice(Math.max(valuesArray.length - MAX_HISTORY, 0)); } -function addExponentiallyWeightedToHistory (value, valuesArray) { +export function addExponentiallyWeightedToHistory (value: number, valuesArray: number[]): number[] { if (valuesArray.length === 0) { return valuesArray.concat([value]); } - var movingAverage = (value * MOVING_AVERAGE_ALPHA) + (valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA)); + let movingAverage = (value * MOVING_AVERAGE_ALPHA) + (valuesArray[valuesArray.length - 1] * (1 - MOVING_AVERAGE_ALPHA)); if (value < 1) { movingAverage = 0; } @@ -191,14 +195,14 @@ function addExponentiallyWeightedToHistory (value, valuesArray) { // DagreD3 Graph-related functions // =============================== -function initializeGraph() +export function initializeGraph() { return new dagreD3.graphlib.Graph({compound: true}) .setGraph({rankdir: 'BT'}) .setDefaultEdgeLabel(function () { return {}; }); } -function initializeSvg(selector) +export function initializeSvg(selector: any) { const svg = d3.select(selector); svg.append("g"); @@ -206,10 +210,10 @@ function initializeSvg(selector) return svg; } -function computeSources(nodeInfo) +export function computeSources(nodeInfo: any) { - var sources = []; - var remoteSources = []; // TODO: put remoteSources in node-specific section + let sources = []; + let remoteSources = []; // TODO: put remoteSources in node-specific section switch (nodeInfo['@type']) { case 'output': case 'explainAnalyze': @@ -240,8 +244,11 @@ function computeSources(nodeInfo) case 'semijoin': sources = [nodeInfo.source, nodeInfo.filteringSource]; break; + case 'spatialjoin': + sources = [nodeInfo.left, nodeInfo.right]; + break; case 'indexjoin': - sources = [nodeInfo.probeSource, nodeInfo.filterSource]; + sources = [nodeInfo.probeSource, nodeInfo.indexSource]; break; case 'union': case 'exchange': @@ -264,18 +271,7 @@ function computeSources(nodeInfo) // Utility functions // ================= -function updateClusterInfo() { - $.get("/v1/info", function (info) { - $('#version-number').text(info.nodeVersion.version); - $('#environment').text(info.environment); - $('#uptime').text(info.uptime); - $('#status-indicator').removeClass("status-light-red").removeClass("status-light-green").addClass("status-light-green"); - }).error(function() { - $('#status-indicator').removeClass("status-light-red").removeClass("status-light-green").addClass("status-light-red"); - }); -} - -function truncateString(inputString, length) { +export function truncateString(inputString: string, length: number): string { if (inputString && inputString.length > length) { return inputString.substring(0, length) + "..."; } @@ -283,29 +279,20 @@ function truncateString(inputString, length) { return inputString; } -function getStageId(stageId) { - return stageId.slice(stageId.indexOf('.') + 1, stageId.length) +export function getStageNumber(stageId: string): number { + return Number.parseInt(stageId.slice(stageId.indexOf('.') + 1, stageId.length)) } -function getTaskIdSuffix(taskId) { +export function getTaskIdSuffix(taskId: string): string { return taskId.slice(taskId.indexOf('.') + 1, taskId.length) } -function getTaskIdInStage(taskId) { +export function getTaskNumber(taskId: string): number { return Number.parseInt(getTaskIdSuffix(getTaskIdSuffix(taskId))); } -function formatState(state, fullyBlocked) { - if (fullyBlocked && state === "RUNNING") { - return "BLOCKED"; - } - else { - return state; - } -} - -function getFirstParameter(searchString) { - var searchText = searchString.substring(1); +export function getFirstParameter(searchString: string): string { + const searchText = searchString.substring(1); if (searchText.indexOf('&') !== -1) { return searchText.substring(0, searchText.indexOf('&')); @@ -314,42 +301,42 @@ function getFirstParameter(searchString) { return searchText; } -function getHostname(url) { - var hostname = new URL(url).hostname; +export function getHostname(url: string): string { + let hostname = new URL(url).hostname; if ((hostname.charAt(0) === '[') && (hostname.charAt(hostname.length - 1) === ']')) { hostname = hostname.substr(1, hostname.length - 2); } return hostname; } -function getPort(url) { +export function getPort(url: string): string { return new URL(url).port; } -function getHostAndPort(url) { - var url = new URL(url); +export function getHostAndPort(urlStr: string): string { + const url = new URL(urlStr); return url.hostname + ":" + url.port; } -function computeRate(count, ms) { - if (ms == 0) { +export function computeRate(count: number, ms: number): number { + if (ms === 0) { return 0; } return (count / ms) * 1000.0; } -function precisionRound(n) { +export function precisionRound(n: number): string { if (n < 10) { return n.toFixed(2); } if (n < 100) { return n.toFixed(1); } - return Math.round(n); + return Math.round(n).toString(); } -function formatDuration(duration) { - var unit = "ms"; +export function formatDuration(duration: number): string { + let unit = "ms"; if (duration > 1000) { duration /= 1000; unit = "s"; @@ -373,8 +360,8 @@ function formatDuration(duration) { return precisionRound(duration) + unit; } -function formatCount(count) { - var unit = ""; +export function formatCount(count: number): string { + let unit = ""; if (count > 1000) { count /= 1000; unit = "K"; @@ -398,16 +385,16 @@ function formatCount(count) { return precisionRound(count) + unit; } -function formatDataSizeBytes(size) { +export function formatDataSizeBytes(size: number): string { return formatDataSizeMinUnit(size, ""); } -function formatDataSize(size) { +export function formatDataSize(size: number): string { return formatDataSizeMinUnit(size, "B"); } -function formatDataSizeMinUnit(size, minUnit) { - var unit = minUnit; +function formatDataSizeMinUnit(size: number, minUnit: string): string { + let unit = minUnit; if (size === 0) { return "0" + unit; } @@ -434,13 +421,13 @@ function formatDataSizeMinUnit(size, minUnit) { return precisionRound(size) + unit; } -function parseDataSize(value) { - var DATA_SIZE_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$/ - var match = DATA_SIZE_PATTERN.exec(value); +export function parseDataSize(value: string): ?number { + const DATA_SIZE_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$/; + const match = DATA_SIZE_PATTERN.exec(value); if (match === null) { return null; } - var number = parseFloat(match[1]); + const number = parseFloat(match[1]); switch (match[2]) { case "B": return number; @@ -459,14 +446,14 @@ function parseDataSize(value) { } } -function parseDuration(value) { - var DURATION_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$/ +export function parseDuration(value: string): ?number { + const DURATION_PATTERN = /^\s*(\d+(?:\.\d+)?)\s*([a-zA-Z]+)\s*$/; - var match = DURATION_PATTERN.exec(value); + const match = DURATION_PATTERN.exec(value); if (match === null) { return null; } - var number = parseFloat(match[1]); + const number = parseFloat(match[1]); switch (match[2]) { case "ns": return number / 1000000.0; @@ -487,70 +474,15 @@ function parseDuration(value) { } } -function formatStackTrace(info) { - return doFormatStackTrace(info, [], "", ""); -} - -function doFormatStackTrace(info, parentStack, prefix, linePrefix) { - var s = linePrefix + prefix + failureInfoToString(info) + "\n"; - - if (info.stack) { - var sharedStackFrames = 0; - if (parentStack !== null) { - sharedStackFrames = countSharedStackFrames(info.stack, parentStack); - } - - for (var i = 0; i < info.stack.length - sharedStackFrames; i++) { - s += linePrefix + "\tat " + info.stack[i] + "\n"; - } - if (sharedStackFrames !== 0) { - s += linePrefix + "\t... " + sharedStackFrames + " more" + "\n"; - } - } - - if (info.suppressed) { - for (var i = 0; i < info.suppressed.length; i++) { - s += doFormatStackTrace(info.suppressed[i], info.stack, "Suppressed: ", linePrefix + "\t"); - } - } - - if (info.cause) { - s += doFormatStackTrace(info.cause, info.stack, "Caused by: ", linePrefix); - } - - return s; -} - -function countSharedStackFrames(stack, parentStack) { - var n = 0; - var minStackLength = Math.min(stack.length, parentStack.length); - while (n < minStackLength && stack[stack.length - 1 - n] === parentStack[parentStack.length - 1 - n]) { - n++; - } - return n; -} - -function failureInfoToString(t) { - return (t.message !== null) ? (t.type + ": " + t.message) : t.type; -} - -function formatShortTime(date) { - var hours = (date.getHours() % 12) || 12; - var minutes = (date.getMinutes() < 10 ? "0" : "") + date.getMinutes(); +export function formatShortTime(date: Date): string { + const hours = (date.getHours() % 12) || 12; + const minutes = (date.getMinutes() < 10 ? "0" : "") + date.getMinutes(); return hours + ":" + minutes + (date.getHours() >= 12 ? "pm" : "am"); } -function formatShortDateTime(date) { - var year = date.getFullYear(); - var month = "" + (date.getMonth() + 1); - var dayOfMonth = "" + date.getDate(); +export function formatShortDateTime(date: Date): string { + const year = date.getFullYear(); + const month = "" + (date.getMonth() + 1); + const dayOfMonth = "" + date.getDate(); return year + "-" + (month[1] ? month : "0" + month[0]) + "-" + (dayOfMonth[1] ? dayOfMonth: "0" + dayOfMonth[0]) + " " + formatShortTime(date); } - -function removeQueryId(id) { - var pos = id.indexOf('.'); - if (pos !== -1) { - return id.substring(pos + 1); - } - return id; -} diff --git a/presto-main/src/main/resources/webapp/src/webpack.config.js b/presto-main/src/main/resources/webapp/src/webpack.config.js new file mode 100644 index 0000000000000..ccab952b0ae17 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/webpack.config.js @@ -0,0 +1,27 @@ +module.exports = { + entry: { + 'index': __dirname +'/index.jsx', + 'query': __dirname +'/query.jsx', + 'plan': __dirname +'/plan.jsx', + 'embedded_plan': __dirname +'/embedded_plan.jsx', + 'stage': __dirname +'/stage.jsx', + 'worker': __dirname +'/worker.jsx', + }, + mode: "development", + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /node_modules/, + use: ['babel-loader'] + } + ] + }, + resolve: { + extensions: ['*', '.js', '.jsx'] + }, + output: { + path: __dirname + '/../dist', + filename: '[name].js' + } +}; diff --git a/presto-main/src/main/resources/webapp/src/worker.jsx b/presto-main/src/main/resources/webapp/src/worker.jsx new file mode 100644 index 0000000000000..8234f338ee804 --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/worker.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import {WorkerStatus} from "./components/WorkerStatus"; +import {WorkerThreadList} from "./components/WorkerThreadList"; +import {PageTitle} from "./components/PageTitle"; + +ReactDOM.render( + , + document.getElementById('title') +); + +ReactDOM.render( + , + document.getElementById('worker-status') +); + +ReactDOM.render( + , + document.getElementById('worker-threads') +); diff --git a/presto-main/src/main/resources/webapp/src/yarn.lock b/presto-main/src/main/resources/webapp/src/yarn.lock new file mode 100644 index 0000000000000..706ad4d5a55bb --- /dev/null +++ b/presto-main/src/main/resources/webapp/src/yarn.lock @@ -0,0 +1,4314 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@webassemblyjs/ast@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" + dependencies: + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/floating-point-hex-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" + +"@webassemblyjs/helper-api-error@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" + +"@webassemblyjs/helper-buffer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" + dependencies: + debug "^3.1.0" + +"@webassemblyjs/helper-code-frame@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" + dependencies: + "@webassemblyjs/wast-printer" "1.5.13" + +"@webassemblyjs/helper-fsm@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" + +"@webassemblyjs/helper-module-context@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" + dependencies: + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" + +"@webassemblyjs/helper-wasm-section@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/ieee754@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" + dependencies: + ieee754 "^1.1.11" + +"@webassemblyjs/leb128@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" + dependencies: + long "4.0.0" + +"@webassemblyjs/utf8@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" + +"@webassemblyjs/wasm-edit@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/helper-wasm-section" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + "@webassemblyjs/wast-printer" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-gen@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wasm-opt@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wast-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/floating-point-hex-parser" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-code-frame" "1.5.13" + "@webassemblyjs/helper-fsm" "1.5.13" + long "^3.2.0" + mamacro "^0.0.3" + +"@webassemblyjs/wast-printer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + long "^3.2.0" + +"@webpack-contrib/config-loader@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@webpack-contrib/config-loader/-/config-loader-1.2.1.tgz#5b3dd474e207437939d294d200c68b7b00008e04" + dependencies: + "@webpack-contrib/schema-utils" "^1.0.0-beta.0" + chalk "^2.1.0" + cosmiconfig "^5.0.2" + is-plain-obj "^1.1.0" + loud-rejection "^1.6.0" + merge-options "^1.0.1" + minimist "^1.2.0" + resolve "^1.6.0" + webpack-log "^1.1.2" + +"@webpack-contrib/schema-utils@^1.0.0-beta.0": + version "1.0.0-beta.0" + resolved "https://registry.yarnpkg.com/@webpack-contrib/schema-utils/-/schema-utils-1.0.0-beta.0.tgz#bf9638c9464d177b48209e84209e23bee2eb4f65" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chalk "^2.3.2" + strip-ansi "^4.0.0" + text-table "^0.2.0" + webpack-log "^1.1.2" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +acorn-dynamic-import@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" + dependencies: + acorn "^5.0.0" + +acorn@^5.0.0, acorn@^5.6.2: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + +ajv-keywords@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" + +ajv@^6.1.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.1" + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + dependencies: + string-width "^2.0.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0, babel-core@^6.26.3: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-builder-react-jsx@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + esutils "^2.0.2" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-loader@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68" + dependencies: + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-flow@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-flow-strip-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-display-name@^6.23.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-self@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx-source@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-react-jsx@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3" + dependencies: + babel-helper-builder-react-jsx "^6.24.1" + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-preset-flow@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d" + dependencies: + babel-plugin-transform-flow-strip-types "^6.22.0" + +babel-preset-react@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380" + dependencies: + babel-plugin-syntax-jsx "^6.3.13" + babel-plugin-transform-react-display-name "^6.23.0" + babel-plugin-transform-react-jsx "^6.24.1" + babel-plugin-transform-react-jsx-self "^6.22.0" + babel-plugin-transform-react-jsx-source "^6.22.0" + babel-preset-flow "^6.23.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +boxen@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +buffer-from@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + +camelcase@^4.0.0, camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +camelcase@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + +caniuse-lite@^1.0.30000844: + version "1.0.30000865" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz#70026616e8afe6e1442f8bb4e1092987d81a2f25" + +capture-stack-trace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.3.2: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chokidar@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + dependencies: + tslib "^1.9.0" + +ci-info@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-spinners@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + dependencies: + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +commander@2: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +configstore@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" + dependencies: + dot-prop "^4.1.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +convert-source-map@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cosmiconfig@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.5.tgz#a809e3c2306891ce17ab70359dc8bdf661fe2cd0" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + +d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" + +d3-array@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc" + +d3-axis@1: + version "1.0.12" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9" + +d3-axis@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.8.tgz#31a705a0b535e65759de14173a31933137f18efa" + +d3-brush@1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.6.tgz#33691f2032d9db6c5d8cb684ff255a9883629e21" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3-brush@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.4.tgz#00c2f238019f24f6c0a194a26d41a1530ffe7bc4" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3-chord@1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f" + dependencies: + d3-array "1" + d3-path "1" + +d3-chord@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.4.tgz#7dec4f0ba886f713fe111c45f763414f6f74ca2c" + dependencies: + d3-array "1" + d3-path "1" + +d3-collection@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" + +d3-collection@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2" + +d3-color@1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.3.tgz#6c67bb2af6df3cc8d79efcc4d3a3e83e28c8048f" + +d3-color@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b" + +d3-contour@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3" + dependencies: + d3-array "^1.1.1" + +d3-dispatch@1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.5.tgz#e25c10a186517cd6c82dd19ea018f07e01e39015" + +d3-dispatch@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8" + +d3-drag@1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.3.tgz#46e206ad863ec465d88c588098a1df444cd33c64" + dependencies: + d3-dispatch "1" + d3-selection "1" + +d3-drag@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d" + dependencies: + d3-dispatch "1" + d3-selection "1" + +d3-dsv@1: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.10.tgz#4371c489a2a654a297aca16fcaf605a6f31a6f51" + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +d3-dsv@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae" + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +d3-ease@1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb" + +d3-ease@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.3.tgz#68bfbc349338a380c44d8acc4fbc3304aa2d8c0e" + +d3-fetch@1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.1.2.tgz#957c8fbc6d4480599ba191b1b2518bf86b3e1be2" + dependencies: + d3-dsv "1" + +d3-force@1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.2.tgz#16664d0ac71d8727ef5effe0b374feac8050d6cd" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-quadtree "1" + d3-timer "1" + +d3-force@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-quadtree "1" + d3-timer "1" + +d3-format@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562" + +d3-format@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a" + +d3-geo@1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.11.1.tgz#3f35e582c0d29296618b02a8ade0fdffb2c0e63c" + dependencies: + d3-array "1" + +d3-geo@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" + dependencies: + d3-array "1" + +d3-hierarchy@1: + version "1.1.8" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz#7a6317bd3ed24e324641b6f1e76e978836b008cc" + +d3-hierarchy@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26" + +d3-interpolate@1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68" + dependencies: + d3-color "1" + +d3-interpolate@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6" + dependencies: + d3-color "1" + +d3-path@1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.7.tgz#8de7cd693a75ac0b5480d3abaccd94793e58aae8" + +d3-path@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764" + +d3-polygon@1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.5.tgz#9a645a0a64ff6cbf9efda96ee0b4a6909184c363" + +d3-polygon@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.3.tgz#16888e9026460933f2b179652ad378224d382c62" + +d3-quadtree@1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.5.tgz#305394840b01f51a341a0da5008585e837fe7e9b" + +d3-quadtree@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438" + +d3-queue@3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.7.tgz#c93a2e54b417c0959129d7d73f6cf7d4292e7618" + +d3-random@1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291" + +d3-random@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.0.tgz#6642e506c6fa3a648595d2b2469788a8d12529d3" + +d3-request@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.6.tgz#a1044a9ef4ec28c824171c9379fae6d79474b19f" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-dsv "1" + xmlhttprequest "1" + +d3-scale-chromatic@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz#dad4366f0edcb288f490128979c3c793583ed3c0" + dependencies: + d3-color "1" + d3-interpolate "1" + +d3-scale@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d" + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-color "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-scale@2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.1.2.tgz#4e932b7b60182aee9073ede8764c98423e5f9a94" + dependencies: + d3-array "^1.2.0" + d3-collection "1" + d3-format "1" + d3-interpolate "1" + d3-time "1" + d3-time-format "2" + +d3-selection@1, d3-selection@^1.1.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.2.tgz#6e70a9df60801c8af28ac24d10072d82cbfdf652" + +d3-selection@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.3.0.tgz#d53772382d3dc4f7507bfb28bcd2d6aed2a0ad6d" + +d3-shape@1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.2.tgz#f9dba3777a5825f9a8ce8bc928da08c17679e9a7" + dependencies: + d3-path "1" + +d3-shape@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777" + dependencies: + d3-path "1" + +d3-time-format@2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b" + dependencies: + d3-time "1" + +d3-time-format@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31" + dependencies: + d3-time "1" + +d3-time@1: + version "1.0.10" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.10.tgz#8259dd71288d72eeacfd8de281c4bf5c7393053c" + +d3-time@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84" + +d3-timer@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.9.tgz#f7bb8c0d597d792ff7131e1c24a36dd471a471ba" + +d3-timer@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531" + +d3-transition@1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.3.tgz#3a435b05ce9cef9524fe0d38121cfb6905331ca6" + dependencies: + d3-color "1" + d3-dispatch "1" + d3-ease "1" + d3-interpolate "1" + d3-selection "^1.1.0" + d3-timer "1" + +d3-transition@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039" + dependencies: + d3-color "1" + d3-dispatch "1" + d3-ease "1" + d3-interpolate "1" + d3-selection "^1.1.0" + d3-timer "1" + +d3-voronoi@1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297" + +d3-voronoi@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" + +d3-zoom@1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.3.tgz#f444effdc9055c38077c4299b4df999eb1d47ccb" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3-zoom@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.1.tgz#02f43b3c3e2db54f364582d7e4a236ccc5506b63" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + +d3@^4.12.2: + version "4.13.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.13.0.tgz#ab236ff8cf0cfc27a81e69bf2fb7518bc9b4f33d" + dependencies: + d3-array "1.2.1" + d3-axis "1.0.8" + d3-brush "1.0.4" + d3-chord "1.0.4" + d3-collection "1.0.4" + d3-color "1.0.3" + d3-dispatch "1.0.3" + d3-drag "1.2.1" + d3-dsv "1.0.8" + d3-ease "1.0.3" + d3-force "1.1.0" + d3-format "1.2.2" + d3-geo "1.9.1" + d3-hierarchy "1.1.5" + d3-interpolate "1.1.6" + d3-path "1.0.5" + d3-polygon "1.0.3" + d3-quadtree "1.0.3" + d3-queue "3.0.7" + d3-random "1.1.0" + d3-request "1.0.6" + d3-scale "1.0.7" + d3-selection "1.3.0" + d3-shape "1.2.0" + d3-time "1.0.8" + d3-time-format "2.1.1" + d3-timer "1.0.7" + d3-transition "1.1.1" + d3-voronoi "1.1.2" + d3-zoom "1.7.1" + +d3@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-5.7.0.tgz#f189d338bdde62acf02f308918e0ec34dd7568f9" + dependencies: + d3-array "1" + d3-axis "1" + d3-brush "1" + d3-chord "1" + d3-collection "1" + d3-color "1" + d3-contour "1" + d3-dispatch "1" + d3-drag "1" + d3-dsv "1" + d3-ease "1" + d3-fetch "1" + d3-force "1" + d3-format "1" + d3-geo "1" + d3-hierarchy "1" + d3-interpolate "1" + d3-path "1" + d3-polygon "1" + d3-quadtree "1" + d3-random "1" + d3-scale "2" + d3-scale-chromatic "1" + d3-selection "1" + d3-shape "1" + d3-time "1" + d3-time-format "2" + d3-timer "1" + d3-transition "1" + d3-voronoi "1" + d3-zoom "1" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dagre-d3@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/dagre-d3/-/dagre-d3-0.6.1.tgz#a1ac34ce8844c27842029ec661c3cb4a0bad0b45" + dependencies: + d3 "^4.12.2" + dagre "^0.8.1" + graphlib "^2.1.5" + lodash "^4.17.4" + +dagre@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/dagre/-/dagre-0.8.2.tgz#755b79f4d5499d63cf74c3368fb08add93eceafe" + dependencies: + graphlib "^2.1.5" + lodash "^4.17.4" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decamelize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" + dependencies: + xregexp "4.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + +dot-prop@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + dependencies: + is-obj "^1.0.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +electron-to-chromium@^1.3.47: + version "1.3.52" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz#d2d9f1270ba4a3b967b831c40ef71fb4d9ab5ce0" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.6.1: + version "1.12.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.45" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653" + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + next-tick "1" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + dependencies: + estraverse "^4.1.0" + +estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fbjs@^0.8.16: + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.18" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flow-bin@^0.85.0: + version "0.85.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.85.0.tgz#a3ca80748a35a071d5bbb2fcd61d64d977fc53a6" + +flush-write-stream@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +function-bind@^1.1.0, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob@^7.0.5, glob@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + dependencies: + ini "^1.3.4" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graphlib@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.5.tgz#6afe1afcc5148555ec799e499056795bd6938c87" + dependencies: + lodash "^4.11.1" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +iconv-lite@0.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.4.4, iconv-lite@~0.4.13: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.11, ieee754@^1.1.4: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4, ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + +irregular-plurals@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-2.0.0.tgz#39d40f05b00f656d0b7fa471230dd3b714af2872" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + +is-ci@^1.0.10: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" + dependencies: + ci-info "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.1, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.9.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +latest-version@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + dependencies: + package-json "^4.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + +lodash@^4.11.1: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + +lodash@^4.17.4: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +log-symbols@^2.1.0, log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +loglevelnext@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" + dependencies: + es6-symbol "^3.1.1" + object.assign "^4.1.0" + +long@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + +long@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0, loud-rejection@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + +lru-cache@^4.0.1, lru-cache@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +meant@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/meant/-/meant-1.0.1.tgz#66044fea2f23230ec806fb515efea29c44d2115d" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + +merge-options@^1.0.0, merge-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-1.0.1.tgz#2a64b24457becd4e4dc608283247e94ce589aa32" + dependencies: + is-plain-obj "^1.1" + +micromatch@^3.1.4, micromatch@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +nan@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +needle@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" + +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +node-fetch@^1.0.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-keys@^1.0.11, object-keys@^1.0.8: + version "1.0.12" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +object.values@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.6.1" + function-bind "^1.1.0" + has "^1.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +opn@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c" + dependencies: + is-wsl "^1.1.0" + +ora@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b" + dependencies: + chalk "^2.3.1" + cli-cursor "^2.1.0" + cli-spinners "^1.1.0" + log-symbols "^2.2.0" + strip-ansi "^4.0.0" + wcwidth "^1.0.1" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + +package-json@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" + dependencies: + got "^6.7.1" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +plur@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/plur/-/plur-3.0.1.tgz#268652d605f816699b42b86248de73c9acd06a7c" + dependencies: + irregular-plurals "^2.0.0" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +pretty-bytes@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.1.0.tgz#6237ecfbdc6525beaef4de722cc60a58ae0e6c6d" + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + +promise@^7.1.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +prop-types@^15.6.0: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +react-dom@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.1.tgz#7f8b0223b3a5fbe205116c56deb85de32685dad6" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +react@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + +reactable@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reactable/-/reactable-1.0.2.tgz#67a579fee3af68b991b5f04df921a4a40ece0b72" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +regenerate@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +registry-auth-token@^3.0.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.6.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +schema-utils@^0.4.4, schema-utils@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + dependencies: + semver "^5.0.3" + +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +serialize-javascript@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + dependencies: + safe-buffer "^5.1.1" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +tapable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" + +tar@^4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + dependencies: + execa "^0.7.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + dependencies: + setimmediate "^1.0.4" + +titleize@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.1.tgz#21bc24fcca658eadc6d3bd3c38f2bd173769b4c5" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +ua-parser-js@^0.7.18: + version "0.7.18" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" + +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + +uglifyjs-webpack-plugin@^1.2.4: + version "1.2.7" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unique-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + dependencies: + crypto-random-string "^1.0.0" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + +update-notifier@^2.3.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" + dependencies: + boxen "^1.2.1" + chalk "^2.0.1" + configstore "^3.0.0" + import-lazy "^2.1.0" + is-ci "^1.0.10" + is-installed-globally "^0.1.0" + is-npm "^1.0.0" + latest-version "^3.0.0" + semver-diff "^2.0.0" + xdg-basedir "^3.0.0" + +uri-js@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + dependencies: + inherits "2.0.3" + +uuid@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +v8-compile-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" + +validate-npm-package-license@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + +webpack-command@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/webpack-command/-/webpack-command-0.4.1.tgz#3f88aae87c28292ed0a97293615a2e962a1c66f4" + dependencies: + "@webpack-contrib/config-loader" "^1.2.0" + "@webpack-contrib/schema-utils" "^1.0.0-beta.0" + camelcase "^5.0.0" + chalk "^2.3.2" + debug "^3.1.0" + decamelize "^2.0.0" + enhanced-resolve "^4.0.0" + import-local "^1.0.0" + isobject "^3.0.1" + loader-utils "^1.1.0" + log-symbols "^2.2.0" + loud-rejection "^1.6.0" + meant "^1.0.1" + meow "^5.0.0" + merge-options "^1.0.0" + object.values "^1.0.4" + opn "^5.3.0" + ora "^2.1.0" + plur "^3.0.0" + pretty-bytes "^5.0.0" + strip-ansi "^4.0.0" + text-table "^0.2.0" + titleize "^1.0.1" + update-notifier "^2.3.0" + v8-compile-cache "^2.0.0" + webpack-log "^1.1.2" + wordwrap "^1.0.0" + +webpack-log@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d" + dependencies: + chalk "^2.1.0" + log-symbols "^2.1.0" + loglevelnext "^1.0.1" + uuid "^3.1.0" + +webpack-sources@^1.0.1, webpack-sources@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.16.0: + version "4.16.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.3.tgz#861be3176d81e7e3d71c66c8acc9bba35588b525" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/wasm-edit" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + acorn "^5.6.2" + acorn-dynamic-import "^3.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^0.4.4" + tapable "^1.0.0" + uglifyjs-webpack-plugin "^1.2.4" + watchpack "^1.5.0" + webpack-sources "^1.0.1" + +whatwg-fetch@>=0.10.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +widest-line@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" + dependencies: + string-width "^2.1.1" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + dependencies: + errno "~0.1.7" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + +xmlhttprequest@1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + +xregexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" + +xtend@^4.0.0, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + +yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + dependencies: + camelcase "^4.1.0" diff --git a/presto-main/src/main/resources/webapp/stage.html b/presto-main/src/main/resources/webapp/stage.html index 5ea038333135d..a05cee416cee8 100644 --- a/presto-main/src/main/resources/webapp/stage.html +++ b/presto-main/src/main/resources/webapp/stage.html @@ -18,18 +18,6 @@ - - - - - - - - @@ -39,9 +27,6 @@ - - - @@ -49,58 +34,14 @@ - - - - - - -
    - +
    Loading...
    @@ -118,12 +59,7 @@
    - - - - + diff --git a/presto-main/src/main/resources/webapp/vendor/d3-tip/d3.tip.v0.6.3.js b/presto-main/src/main/resources/webapp/vendor/d3-tip/d3.tip.v0.6.3.js deleted file mode 100644 index 6cfa3cb66bbf6..0000000000000 --- a/presto-main/src/main/resources/webapp/vendor/d3-tip/d3.tip.v0.6.3.js +++ /dev/null @@ -1,280 +0,0 @@ -// d3.tip -// Copyright (c) 2013 Justin Palmer -// -// Tooltips for d3.js SVG visualizations - -// Public - contructs a new tooltip -// -// Returns a tip -d3.tip = function() { - var direction = d3_tip_direction, - offset = d3_tip_offset, - html = d3_tip_html, - node = initNode(), - svg = null, - point = null, - target = null - - function tip(vis) { - svg = getSVGNode(vis) - point = svg.createSVGPoint() - document.body.appendChild(node) - } - - // Public - show the tooltip on the screen - // - // Returns a tip - tip.show = function() { - var args = Array.prototype.slice.call(arguments) - if(args[args.length - 1] instanceof SVGElement) target = args.pop() - - var content = html.apply(this, args), - poffset = offset.apply(this, args), - dir = direction.apply(this, args), - nodel = d3.select(node), i = 0, - coords - - nodel.html(content) - .style({ opacity: 1, 'pointer-events': 'all' }) - - while(i--) nodel.classed(directions[i], false) - coords = direction_callbacks.get(dir).apply(this) - nodel.classed(dir, true).style({ - top: (coords.top + poffset[0]) + 'px', - left: (coords.left + poffset[1]) + 'px' - }) - - return tip - } - - // Public - hide the tooltip - // - // Returns a tip - tip.hide = function() { - nodel = d3.select(node) - nodel.style({ opacity: 0, 'pointer-events': 'none' }) - return tip - } - - // Public: Proxy attr calls to the d3 tip container. Sets or gets attribute value. - // - // n - name of the attribute - // v - value of the attribute - // - // Returns tip or attribute value - tip.attr = function(n, v) { - if (arguments.length < 2 && typeof n === 'string') { - return d3.select(node).attr(n) - } else { - var args = Array.prototype.slice.call(arguments) - d3.selection.prototype.attr.apply(d3.select(node), args) - } - - return tip - } - - // Public: Proxy style calls to the d3 tip container. Sets or gets a style value. - // - // n - name of the property - // v - value of the property - // - // Returns tip or style property value - tip.style = function(n, v) { - if (arguments.length < 2 && typeof n === 'string') { - return d3.select(node).style(n) - } else { - var args = Array.prototype.slice.call(arguments) - d3.selection.prototype.style.apply(d3.select(node), args) - } - - return tip - } - - // Public: Set or get the direction of the tooltip - // - // v - One of n(north), s(south), e(east), or w(west), nw(northwest), - // sw(southwest), ne(northeast) or se(southeast) - // - // Returns tip or direction - tip.direction = function(v) { - if (!arguments.length) return direction - direction = v == null ? v : d3.functor(v) - - return tip - } - - // Public: Sets or gets the offset of the tip - // - // v - Array of [x, y] offset - // - // Returns offset or - tip.offset = function(v) { - if (!arguments.length) return offset - offset = v == null ? v : d3.functor(v) - - return tip - } - - // Public: sets or gets the html value of the tooltip - // - // v - String value of the tip - // - // Returns html value or tip - tip.html = function(v) { - if (!arguments.length) return html - html = v == null ? v : d3.functor(v) - - return tip - } - - function d3_tip_direction() { return 'n' } - function d3_tip_offset() { return [0, 0] } - function d3_tip_html() { return ' ' } - - var direction_callbacks = d3.map({ - n: direction_n, - s: direction_s, - e: direction_e, - w: direction_w, - nw: direction_nw, - ne: direction_ne, - sw: direction_sw, - se: direction_se - }), - - directions = direction_callbacks.keys() - - function direction_n() { - var bbox = getScreenBBox() - return { - top: bbox.n.y - node.offsetHeight, - left: bbox.n.x - node.offsetWidth / 2 - } - } - - function direction_s() { - var bbox = getScreenBBox() - return { - top: bbox.s.y, - left: bbox.s.x - node.offsetWidth / 2 - } - } - - function direction_e() { - var bbox = getScreenBBox() - return { - top: bbox.e.y - node.offsetHeight / 2, - left: bbox.e.x - } - } - - function direction_w() { - var bbox = getScreenBBox() - return { - top: bbox.w.y - node.offsetHeight / 2, - left: bbox.w.x - node.offsetWidth - } - } - - function direction_nw() { - var bbox = getScreenBBox() - return { - top: bbox.nw.y - node.offsetHeight, - left: bbox.nw.x - node.offsetWidth - } - } - - function direction_ne() { - var bbox = getScreenBBox() - return { - top: bbox.ne.y - node.offsetHeight, - left: bbox.ne.x - } - } - - function direction_sw() { - var bbox = getScreenBBox() - return { - top: bbox.sw.y, - left: bbox.sw.x - node.offsetWidth - } - } - - function direction_se() { - var bbox = getScreenBBox() - return { - top: bbox.se.y, - left: bbox.e.x - } - } - - function initNode() { - var node = d3.select(document.createElement('div')) - node.style({ - position: 'absolute', - opacity: 0, - pointerEvents: 'none', - boxSizing: 'border-box' - }) - - return node.node() - } - - function getSVGNode(el) { - el = el.node() - if(el.tagName.toLowerCase() == 'svg') - return el - - return el.ownerSVGElement - } - - // Private - gets the screen coordinates of a shape - // - // Given a shape on the screen, will return an SVGPoint for the directions - // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest), - // sw(southwest). - // - // +-+-+ - // | | - // + + - // | | - // +-+-+ - // - // Returns an Object {n, s, e, w, nw, sw, ne, se} - function getScreenBBox() { - var targetel = target || d3.event.target, - bbox = {}, - matrix = targetel.getScreenCTM(), - tbbox = targetel.getBBox(), - width = tbbox.width, - height = tbbox.height, - x = tbbox.x, - y = tbbox.y, - scrollTop = document.documentElement.scrollTop || document.body.scrollTop, - scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft - - - point.x = x + scrollLeft - point.y = y + scrollTop - bbox.nw = point.matrixTransform(matrix) - point.x += width - bbox.ne = point.matrixTransform(matrix) - point.y += height - bbox.se = point.matrixTransform(matrix) - point.x -= width - bbox.sw = point.matrixTransform(matrix) - point.y -= height / 2 - bbox.w = point.matrixTransform(matrix) - point.x += width - bbox.e = point.matrixTransform(matrix) - point.x -= width / 2 - point.y -= height / 2 - bbox.n = point.matrixTransform(matrix) - point.y += height - bbox.s = point.matrixTransform(matrix) - - return bbox - } - - return tip -}; diff --git a/presto-main/src/main/resources/webapp/vendor/d3/LICENSE.txt b/presto-main/src/main/resources/webapp/vendor/d3/LICENSE.txt deleted file mode 100644 index 721bd22ece658..0000000000000 --- a/presto-main/src/main/resources/webapp/vendor/d3/LICENSE.txt +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2010-2016 Mike Bostock -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the author nor the names of contributors may be used to - endorse or promote products derived from this software without specific prior - written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/presto-main/src/main/resources/webapp/vendor/d3/d3-3.3.4.js b/presto-main/src/main/resources/webapp/vendor/d3/d3-3.3.4.js deleted file mode 100644 index a3e4b951e371d..0000000000000 --- a/presto-main/src/main/resources/webapp/vendor/d3/d3-3.3.4.js +++ /dev/null @@ -1,9294 +0,0 @@ -!function() { - var d3 = { - version: "3.4.4" - }; - if (!Date.now) Date.now = function() { - return +new Date(); - }; - var d3_arraySlice = [].slice, d3_array = function(list) { - return d3_arraySlice.call(list); - }; - var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window; - try { - d3_array(d3_documentElement.childNodes)[0].nodeType; - } catch (e) { - d3_array = function(list) { - var i = list.length, array = new Array(i); - while (i--) array[i] = list[i]; - return array; - }; - } - try { - d3_document.createElement("div").style.setProperty("opacity", 0, ""); - } catch (error) { - var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; - d3_element_prototype.setAttribute = function(name, value) { - d3_element_setAttribute.call(this, name, value + ""); - }; - d3_element_prototype.setAttributeNS = function(space, local, value) { - d3_element_setAttributeNS.call(this, space, local, value + ""); - }; - d3_style_prototype.setProperty = function(name, value, priority) { - d3_style_setProperty.call(this, name, value + "", priority); - }; - } - d3.ascending = d3_ascending; - function d3_ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - d3.descending = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; - }; - d3.min = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && a > b) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; - } - return a; - }; - d3.max = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && b > a) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; - } - return a; - }; - d3.extent = function(array, f) { - var i = -1, n = array.length, a, b, c; - if (arguments.length === 1) { - while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined; - while (++i < n) if ((b = array[i]) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } else { - while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - return [ a, c ]; - }; - d3.sum = function(array, f) { - var s = 0, n = array.length, a, i = -1; - if (arguments.length === 1) { - while (++i < n) if (!isNaN(a = +array[i])) s += a; - } else { - while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; - } - return s; - }; - function d3_number(x) { - return x != null && !isNaN(x); - } - d3.mean = function(array, f) { - var n = array.length, a, m = 0, i = -1, j = 0; - if (arguments.length === 1) { - while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; - } else { - while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; - } - return j ? m : undefined; - }; - d3.quantile = function(values, p) { - var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; - return e ? v + e * (values[h] - v) : v; - }; - d3.median = function(array, f) { - if (arguments.length > 1) array = array.map(f); - array = array.filter(d3_number); - return array.length ? d3.quantile(array.sort(d3_ascending), .5) : undefined; - }; - function d3_bisector(compare) { - return { - left: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; - } - return lo; - } - }; - } - var d3_bisect = d3_bisector(d3_ascending); - d3.bisectLeft = d3_bisect.left; - d3.bisect = d3.bisectRight = d3_bisect.right; - d3.bisector = function(f) { - return d3_bisector(f.length === 1 ? function(d, x) { - return d3_ascending(f(d), x); - } : f); - }; - d3.shuffle = function(array) { - var m = array.length, t, i; - while (m) { - i = Math.random() * m-- | 0; - t = array[m], array[m] = array[i], array[i] = t; - } - return array; - }; - d3.permute = function(array, indexes) { - var i = indexes.length, permutes = new Array(i); - while (i--) permutes[i] = array[indexes[i]]; - return permutes; - }; - d3.pairs = function(array) { - var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); - while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; - return pairs; - }; - d3.zip = function() { - if (!(n = arguments.length)) return []; - for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { - for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { - zip[j] = arguments[j][i]; - } - } - return zips; - }; - function d3_zipLength(d) { - return d.length; - } - d3.transpose = function(matrix) { - return d3.zip.apply(d3, matrix); - }; - d3.keys = function(map) { - var keys = []; - for (var key in map) keys.push(key); - return keys; - }; - d3.values = function(map) { - var values = []; - for (var key in map) values.push(map[key]); - return values; - }; - d3.entries = function(map) { - var entries = []; - for (var key in map) entries.push({ - key: key, - value: map[key] - }); - return entries; - }; - d3.merge = function(arrays) { - var n = arrays.length, m, i = -1, j = 0, merged, array; - while (++i < n) j += arrays[i].length; - merged = new Array(j); - while (--n >= 0) { - array = arrays[n]; - m = array.length; - while (--m >= 0) { - merged[--j] = array[m]; - } - } - return merged; - }; - var abs = Math.abs; - d3.range = function(start, stop, step) { - if (arguments.length < 3) { - step = 1; - if (arguments.length < 2) { - stop = start; - start = 0; - } - } - if ((stop - start) / step === Infinity) throw new Error("infinite range"); - var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; - start *= k, stop *= k, step *= k; - if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); - return range; - }; - function d3_range_integerScale(x) { - var k = 1; - while (x * k % 1) k *= 10; - return k; - } - function d3_class(ctor, properties) { - try { - for (var key in properties) { - Object.defineProperty(ctor.prototype, key, { - value: properties[key], - enumerable: false - }); - } - } catch (e) { - ctor.prototype = properties; - } - } - d3.map = function(object) { - var map = new d3_Map(); - if (object instanceof d3_Map) object.forEach(function(key, value) { - map.set(key, value); - }); else for (var key in object) map.set(key, object[key]); - return map; - }; - function d3_Map() {} - d3_class(d3_Map, { - has: d3_map_has, - get: function(key) { - return this[d3_map_prefix + key]; - }, - set: function(key, value) { - return this[d3_map_prefix + key] = value; - }, - remove: d3_map_remove, - keys: d3_map_keys, - values: function() { - var values = []; - this.forEach(function(key, value) { - values.push(value); - }); - return values; - }, - entries: function() { - var entries = []; - this.forEach(function(key, value) { - entries.push({ - key: key, - value: value - }); - }); - return entries; - }, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]); - } - }); - var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); - function d3_map_has(key) { - return d3_map_prefix + key in this; - } - function d3_map_remove(key) { - key = d3_map_prefix + key; - return key in this && delete this[key]; - } - function d3_map_keys() { - var keys = []; - this.forEach(function(key) { - keys.push(key); - }); - return keys; - } - function d3_map_size() { - var size = 0; - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size; - return size; - } - function d3_map_empty() { - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false; - return true; - } - d3.nest = function() { - var nest = {}, keys = [], sortKeys = [], sortValues, rollup; - function map(mapType, array, depth) { - if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; - var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; - while (++i < n) { - if (values = valuesByKey.get(keyValue = key(object = array[i]))) { - values.push(object); - } else { - valuesByKey.set(keyValue, [ object ]); - } - } - if (mapType) { - object = mapType(); - setter = function(keyValue, values) { - object.set(keyValue, map(mapType, values, depth)); - }; - } else { - object = {}; - setter = function(keyValue, values) { - object[keyValue] = map(mapType, values, depth); - }; - } - valuesByKey.forEach(setter); - return object; - } - function entries(map, depth) { - if (depth >= keys.length) return map; - var array = [], sortKey = sortKeys[depth++]; - map.forEach(function(key, keyMap) { - array.push({ - key: key, - values: entries(keyMap, depth) - }); - }); - return sortKey ? array.sort(function(a, b) { - return sortKey(a.key, b.key); - }) : array; - } - nest.map = function(array, mapType) { - return map(mapType, array, 0); - }; - nest.entries = function(array) { - return entries(map(d3.map, array, 0), 0); - }; - nest.key = function(d) { - keys.push(d); - return nest; - }; - nest.sortKeys = function(order) { - sortKeys[keys.length - 1] = order; - return nest; - }; - nest.sortValues = function(order) { - sortValues = order; - return nest; - }; - nest.rollup = function(f) { - rollup = f; - return nest; - }; - return nest; - }; - d3.set = function(array) { - var set = new d3_Set(); - if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); - return set; - }; - function d3_Set() {} - d3_class(d3_Set, { - has: d3_map_has, - add: function(value) { - this[d3_map_prefix + value] = true; - return value; - }, - remove: function(value) { - value = d3_map_prefix + value; - return value in this && delete this[value]; - }, - values: d3_map_keys, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1)); - } - }); - d3.behavior = {}; - d3.rebind = function(target, source) { - var i = 1, n = arguments.length, method; - while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); - return target; - }; - function d3_rebind(target, source, method) { - return function() { - var value = method.apply(source, arguments); - return value === source ? target : value; - }; - } - function d3_vendorSymbol(object, name) { - if (name in object) return name; - name = name.charAt(0).toUpperCase() + name.substring(1); - for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { - var prefixName = d3_vendorPrefixes[i] + name; - if (prefixName in object) return prefixName; - } - } - var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; - function d3_noop() {} - d3.dispatch = function() { - var dispatch = new d3_dispatch(), i = -1, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - return dispatch; - }; - function d3_dispatch() {} - d3_dispatch.prototype.on = function(type, listener) { - var i = type.indexOf("."), name = ""; - if (i >= 0) { - name = type.substring(i + 1); - type = type.substring(0, i); - } - if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); - if (arguments.length === 2) { - if (listener == null) for (type in this) { - if (this.hasOwnProperty(type)) this[type].on(name, null); - } - return this; - } - }; - function d3_dispatch_event(dispatch) { - var listeners = [], listenerByName = new d3_Map(); - function event() { - var z = listeners, i = -1, n = z.length, l; - while (++i < n) if (l = z[i].on) l.apply(this, arguments); - return dispatch; - } - event.on = function(name, listener) { - var l = listenerByName.get(name), i; - if (arguments.length < 2) return l && l.on; - if (l) { - l.on = null; - listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); - listenerByName.remove(name); - } - if (listener) listeners.push(listenerByName.set(name, { - on: listener - })); - return dispatch; - }; - return event; - } - d3.event = null; - function d3_eventPreventDefault() { - d3.event.preventDefault(); - } - function d3_eventSource() { - var e = d3.event, s; - while (s = e.sourceEvent) e = s; - return e; - } - function d3_eventDispatch(target) { - var dispatch = new d3_dispatch(), i = 0, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - dispatch.of = function(thiz, argumentz) { - return function(e1) { - try { - var e0 = e1.sourceEvent = d3.event; - e1.target = target; - d3.event = e1; - dispatch[e1.type].apply(thiz, argumentz); - } finally { - d3.event = e0; - } - }; - }; - return dispatch; - } - d3.requote = function(s) { - return s.replace(d3_requote_re, "\\$&"); - }; - var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - var d3_subclass = {}.__proto__ ? function(object, prototype) { - object.__proto__ = prototype; - } : function(object, prototype) { - for (var property in prototype) object[property] = prototype[property]; - }; - function d3_selection(groups) { - d3_subclass(groups, d3_selectionPrototype); - return groups; - } - var d3_select = function(s, n) { - return n.querySelector(s); - }, d3_selectAll = function(s, n) { - return n.querySelectorAll(s); - }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) { - return d3_selectMatcher.call(n, s); - }; - if (typeof Sizzle === "function") { - d3_select = function(s, n) { - return Sizzle(s, n)[0] || null; - }; - d3_selectAll = Sizzle; - d3_selectMatches = Sizzle.matchesSelector; - } - d3.selection = function() { - return d3_selectionRoot; - }; - var d3_selectionPrototype = d3.selection.prototype = []; - d3_selectionPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, group, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(subnode = selector.call(node, node.__data__, i, j)); - if (subnode && "__data__" in node) subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selector(selector) { - return typeof selector === "function" ? selector : function() { - return d3_select(selector, this); - }; - } - d3_selectionPrototype.selectAll = function(selector) { - var subgroups = [], subgroup, node; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); - subgroup.parentNode = node; - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selectorAll(selector) { - return typeof selector === "function" ? selector : function() { - return d3_selectAll(selector, this); - }; - } - var d3_nsPrefix = { - svg: "http://www.w3.org/2000/svg", - xhtml: "http://www.w3.org/1999/xhtml", - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" - }; - d3.ns = { - prefix: d3_nsPrefix, - qualify: function(name) { - var i = name.indexOf(":"), prefix = name; - if (i >= 0) { - prefix = name.substring(0, i); - name = name.substring(i + 1); - } - return d3_nsPrefix.hasOwnProperty(prefix) ? { - space: d3_nsPrefix[prefix], - local: name - } : name; - } - }; - d3_selectionPrototype.attr = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(); - name = d3.ns.qualify(name); - return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); - } - for (value in name) this.each(d3_selection_attr(value, name[value])); - return this; - } - return this.each(d3_selection_attr(name, value)); - }; - function d3_selection_attr(name, value) { - name = d3.ns.qualify(name); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrConstant() { - this.setAttribute(name, value); - } - function attrConstantNS() { - this.setAttributeNS(name.space, name.local, value); - } - function attrFunction() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); - } - function attrFunctionNS() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); - } - return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; - } - function d3_collapse(s) { - return s.trim().replace(/\s+/g, " "); - } - d3_selectionPrototype.classed = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; - if (value = node.classList) { - while (++i < n) if (!value.contains(name[i])) return false; - } else { - value = node.getAttribute("class"); - while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; - } - return true; - } - for (value in name) this.each(d3_selection_classed(value, name[value])); - return this; - } - return this.each(d3_selection_classed(name, value)); - }; - function d3_selection_classedRe(name) { - return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); - } - function d3_selection_classes(name) { - return name.trim().split(/^|\s+/); - } - function d3_selection_classed(name, value) { - name = d3_selection_classes(name).map(d3_selection_classedName); - var n = name.length; - function classedConstant() { - var i = -1; - while (++i < n) name[i](this, value); - } - function classedFunction() { - var i = -1, x = value.apply(this, arguments); - while (++i < n) name[i](this, x); - } - return typeof value === "function" ? classedFunction : classedConstant; - } - function d3_selection_classedName(name) { - var re = d3_selection_classedRe(name); - return function(node, value) { - if (c = node.classList) return value ? c.add(name) : c.remove(name); - var c = node.getAttribute("class") || ""; - if (value) { - re.lastIndex = 0; - if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); - } else { - node.setAttribute("class", d3_collapse(c.replace(re, " "))); - } - }; - } - d3_selectionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); - return this; - } - if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); - priority = ""; - } - return this.each(d3_selection_style(name, value, priority)); - }; - function d3_selection_style(name, value, priority) { - function styleNull() { - this.style.removeProperty(name); - } - function styleConstant() { - this.style.setProperty(name, value, priority); - } - function styleFunction() { - var x = value.apply(this, arguments); - if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); - } - return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; - } - d3_selectionPrototype.property = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") return this.node()[name]; - for (value in name) this.each(d3_selection_property(value, name[value])); - return this; - } - return this.each(d3_selection_property(name, value)); - }; - function d3_selection_property(name, value) { - function propertyNull() { - delete this[name]; - } - function propertyConstant() { - this[name] = value; - } - function propertyFunction() { - var x = value.apply(this, arguments); - if (x == null) delete this[name]; else this[name] = x; - } - return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; - } - d3_selectionPrototype.text = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - } : value == null ? function() { - this.textContent = ""; - } : function() { - this.textContent = value; - }) : this.node().textContent; - }; - d3_selectionPrototype.html = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - } : value == null ? function() { - this.innerHTML = ""; - } : function() { - this.innerHTML = value; - }) : this.node().innerHTML; - }; - d3_selectionPrototype.append = function(name) { - name = d3_selection_creator(name); - return this.select(function() { - return this.appendChild(name.apply(this, arguments)); - }); - }; - function d3_selection_creator(name) { - return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() { - return this.ownerDocument.createElementNS(name.space, name.local); - } : function() { - return this.ownerDocument.createElementNS(this.namespaceURI, name); - }; - } - d3_selectionPrototype.insert = function(name, before) { - name = d3_selection_creator(name); - before = d3_selection_selector(before); - return this.select(function() { - return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); - }); - }; - d3_selectionPrototype.remove = function() { - return this.each(function() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - }); - }; - d3_selectionPrototype.data = function(value, key) { - var i = -1, n = this.length, group, node; - if (!arguments.length) { - value = new Array(n = (group = this[0]).length); - while (++i < n) { - if (node = group[i]) { - value[i] = node.__data__; - } - } - return value; - } - function bind(group, groupData) { - var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; - if (key) { - var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; - for (i = -1; ++i < n; ) { - keyValue = key.call(node = group[i], node.__data__, i); - if (nodeByKeyValue.has(keyValue)) { - exitNodes[i] = node; - } else { - nodeByKeyValue.set(keyValue, node); - } - keyValues.push(keyValue); - } - for (i = -1; ++i < m; ) { - keyValue = key.call(groupData, nodeData = groupData[i], i); - if (node = nodeByKeyValue.get(keyValue)) { - updateNodes[i] = node; - node.__data__ = nodeData; - } else if (!dataByKeyValue.has(keyValue)) { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - dataByKeyValue.set(keyValue, nodeData); - nodeByKeyValue.remove(keyValue); - } - for (i = -1; ++i < n; ) { - if (nodeByKeyValue.has(keyValues[i])) { - exitNodes[i] = group[i]; - } - } - } else { - for (i = -1; ++i < n0; ) { - node = group[i]; - nodeData = groupData[i]; - if (node) { - node.__data__ = nodeData; - updateNodes[i] = node; - } else { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - } - for (;i < m; ++i) { - enterNodes[i] = d3_selection_dataNode(groupData[i]); - } - for (;i < n; ++i) { - exitNodes[i] = group[i]; - } - } - enterNodes.update = updateNodes; - enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; - enter.push(enterNodes); - update.push(updateNodes); - exit.push(exitNodes); - } - var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); - if (typeof value === "function") { - while (++i < n) { - bind(group = this[i], value.call(group, group.parentNode.__data__, i)); - } - } else { - while (++i < n) { - bind(group = this[i], value); - } - } - update.enter = function() { - return enter; - }; - update.exit = function() { - return exit; - }; - return update; - }; - function d3_selection_dataNode(data) { - return { - __data__: data - }; - } - d3_selectionPrototype.datum = function(value) { - return arguments.length ? this.property("__data__", value) : this.property("__data__"); - }; - d3_selectionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_filter(selector) { - return function() { - return d3_selectMatches(this, selector); - }; - } - d3_selectionPrototype.order = function() { - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - return this; - }; - d3_selectionPrototype.sort = function(comparator) { - comparator = d3_selection_sortComparator.apply(this, arguments); - for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); - return this.order(); - }; - function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3_ascending; - return function(a, b) { - return a && b ? comparator(a.__data__, b.__data__) : !a - !b; - }; - } - d3_selectionPrototype.each = function(callback) { - return d3_selection_each(this, function(node, i, j) { - callback.call(node, node.__data__, i, j); - }); - }; - function d3_selection_each(groups, callback) { - for (var j = 0, m = groups.length; j < m; j++) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { - if (node = group[i]) callback(node, i, j); - } - } - return groups; - } - d3_selectionPrototype.call = function(callback) { - var args = d3_array(arguments); - callback.apply(args[0] = this, args); - return this; - }; - d3_selectionPrototype.empty = function() { - return !this.node(); - }; - d3_selectionPrototype.node = function() { - for (var j = 0, m = this.length; j < m; j++) { - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - var node = group[i]; - if (node) return node; - } - } - return null; - }; - d3_selectionPrototype.size = function() { - var n = 0; - this.each(function() { - ++n; - }); - return n; - }; - function d3_selection_enter(selection) { - d3_subclass(selection, d3_selection_enterPrototype); - return selection; - } - var d3_selection_enterPrototype = []; - d3.selection.enter = d3_selection_enter; - d3.selection.enter.prototype = d3_selection_enterPrototype; - d3_selection_enterPrototype.append = d3_selectionPrototype.append; - d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; - d3_selection_enterPrototype.node = d3_selectionPrototype.node; - d3_selection_enterPrototype.call = d3_selectionPrototype.call; - d3_selection_enterPrototype.size = d3_selectionPrototype.size; - d3_selection_enterPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, upgroup, group, node; - for (var j = -1, m = this.length; ++j < m; ) { - upgroup = (group = this[j]).update; - subgroups.push(subgroup = []); - subgroup.parentNode = group.parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); - subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - d3_selection_enterPrototype.insert = function(name, before) { - if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); - return d3_selectionPrototype.insert.call(this, name, before); - }; - function d3_selection_enterInsertBefore(enter) { - var i0, j0; - return function(d, i, j) { - var group = enter[j].update, n = group.length, node; - if (j != j0) j0 = j, i0 = 0; - if (i >= i0) i0 = i + 1; - while (!(node = group[i0]) && ++i0 < n) ; - return node; - }; - } - d3_selectionPrototype.transition = function() { - var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || { - time: Date.now(), - ease: d3_ease_cubicInOut, - delay: 0, - duration: 250 - }; - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) d3_transitionNode(node, i, id, transition); - subgroup.push(node); - } - } - return d3_transition(subgroups, id); - }; - d3_selectionPrototype.interrupt = function() { - return this.each(d3_selection_interrupt); - }; - function d3_selection_interrupt() { - var lock = this.__transition__; - if (lock) ++lock.active; - } - d3.select = function(node) { - var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ]; - group.parentNode = d3_documentElement; - return d3_selection([ group ]); - }; - d3.selectAll = function(nodes) { - var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); - group.parentNode = d3_documentElement; - return d3_selection([ group ]); - }; - var d3_selectionRoot = d3.select(d3_documentElement); - d3_selectionPrototype.on = function(type, listener, capture) { - var n = arguments.length; - if (n < 3) { - if (typeof type !== "string") { - if (n < 2) listener = false; - for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); - return this; - } - if (n < 2) return (n = this.node()["__on" + type]) && n._; - capture = false; - } - return this.each(d3_selection_on(type, listener, capture)); - }; - function d3_selection_on(type, listener, capture) { - var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; - if (i > 0) type = type.substring(0, i); - var filter = d3_selection_onFilters.get(type); - if (filter) type = filter, wrap = d3_selection_onFilter; - function onRemove() { - var l = this[name]; - if (l) { - this.removeEventListener(type, l, l.$); - delete this[name]; - } - } - function onAdd() { - var l = wrap(listener, d3_array(arguments)); - onRemove.call(this); - this.addEventListener(type, this[name] = l, l.$ = capture); - l._ = listener; - } - function removeAll() { - var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; - for (var name in this) { - if (match = name.match(re)) { - var l = this[name]; - this.removeEventListener(match[1], l, l.$); - delete this[name]; - } - } - } - return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; - } - var d3_selection_onFilters = d3.map({ - mouseenter: "mouseover", - mouseleave: "mouseout" - }); - d3_selection_onFilters.forEach(function(k) { - if ("on" + k in d3_document) d3_selection_onFilters.remove(k); - }); - function d3_selection_onListener(listener, argumentz) { - return function(e) { - var o = d3.event; - d3.event = e; - argumentz[0] = this.__data__; - try { - listener.apply(this, argumentz); - } finally { - d3.event = o; - } - }; - } - function d3_selection_onFilter(listener, argumentz) { - var l = d3_selection_onListener(listener, argumentz); - return function(e) { - var target = this, related = e.relatedTarget; - if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { - l.call(target, e); - } - }; - } - var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0; - function d3_event_dragSuppress() { - var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); - if (d3_event_dragSelect) { - var style = d3_documentElement.style, select = style[d3_event_dragSelect]; - style[d3_event_dragSelect] = "none"; - } - return function(suppressClick) { - w.on(name, null); - if (d3_event_dragSelect) style[d3_event_dragSelect] = select; - if (suppressClick) { - function off() { - w.on(click, null); - } - w.on(click, function() { - d3_eventPreventDefault(); - off(); - }, true); - setTimeout(off, 0); - } - }; - } - d3.mouse = function(container) { - return d3_mousePoint(container, d3_eventSource()); - }; - function d3_mousePoint(container, e) { - if (e.changedTouches) e = e.changedTouches[0]; - var svg = container.ownerSVGElement || container; - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - point.x = e.clientX, point.y = e.clientY; - point = point.matrixTransform(container.getScreenCTM().inverse()); - return [ point.x, point.y ]; - } - var rect = container.getBoundingClientRect(); - return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; - } - d3.touches = function(container, touches) { - if (arguments.length < 2) touches = d3_eventSource().touches; - return touches ? d3_array(touches).map(function(touch) { - var point = d3_mousePoint(container, touch); - point.identifier = touch.identifier; - return point; - }) : []; - }; - d3.behavior.drag = function() { - var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend"); - function drag() { - this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); - } - function dragstart(id, position, subject, move, end) { - return function() { - var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(), position0 = position(parent, dragId); - if (origin) { - dragOffset = origin.apply(that, arguments); - dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; - } else { - dragOffset = [ 0, 0 ]; - } - dispatch({ - type: "dragstart" - }); - function moved() { - var position1 = position(parent, dragId), dx, dy; - if (!position1) return; - dx = position1[0] - position0[0]; - dy = position1[1] - position0[1]; - dragged |= dx | dy; - position0 = position1; - dispatch({ - type: "drag", - x: position1[0] + dragOffset[0], - y: position1[1] + dragOffset[1], - dx: dx, - dy: dy - }); - } - function ended() { - if (!position(parent, dragId)) return; - dragSubject.on(move + dragName, null).on(end + dragName, null); - dragRestore(dragged && d3.event.target === target); - dispatch({ - type: "dragend" - }); - } - }; - } - drag.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return drag; - }; - return d3.rebind(drag, event, "on"); - }; - function d3_behavior_dragTouchId() { - return d3.event.changedTouches[0].identifier; - } - function d3_behavior_dragTouchSubject() { - return d3.event.target; - } - function d3_behavior_dragMouseSubject() { - return d3_window; - } - var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π; - function d3_sgn(x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } - function d3_cross2d(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); - } - function d3_acos(x) { - return x > 1 ? 0 : x < -1 ? π : Math.acos(x); - } - function d3_asin(x) { - return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); - } - function d3_sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; - } - function d3_cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; - } - function d3_tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); - } - function d3_haversin(x) { - return (x = Math.sin(x / 2)) * x; - } - var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; - d3.interpolateZoom = function(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; - var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; - function interpolate(t) { - var s = t * S; - if (dr) { - var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); - return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; - } - return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; - } - interpolate.duration = S * 1e3; - return interpolate; - }; - d3.behavior.zoom = function() { - var view = { - x: 0, - y: 0, - k: 1 - }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; - function zoom(g) { - g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); - } - zoom.event = function(g) { - g.each(function() { - var dispatch = event.of(this, arguments), view1 = view; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.zoom", function() { - view = this.__chart__ || { - x: 0, - y: 0, - k: 1 - }; - zoomstarted(dispatch); - }).tween("zoom:zoom", function() { - var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); - return function(t) { - var l = i(t), k = dx / l[2]; - this.__chart__ = view = { - x: cx - l[0] * k, - y: cy - l[1] * k, - k: k - }; - zoomed(dispatch); - }; - }).each("end.zoom", function() { - zoomended(dispatch); - }); - } else { - this.__chart__ = view; - zoomstarted(dispatch); - zoomed(dispatch); - zoomended(dispatch); - } - }); - }; - zoom.translate = function(_) { - if (!arguments.length) return [ view.x, view.y ]; - view = { - x: +_[0], - y: +_[1], - k: view.k - }; - rescale(); - return zoom; - }; - zoom.scale = function(_) { - if (!arguments.length) return view.k; - view = { - x: view.x, - y: view.y, - k: +_ - }; - rescale(); - return zoom; - }; - zoom.scaleExtent = function(_) { - if (!arguments.length) return scaleExtent; - scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; - return zoom; - }; - zoom.center = function(_) { - if (!arguments.length) return center; - center = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.size = function(_) { - if (!arguments.length) return size; - size = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.x = function(z) { - if (!arguments.length) return x1; - x1 = z; - x0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - zoom.y = function(z) { - if (!arguments.length) return y1; - y1 = z; - y0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - function location(p) { - return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; - } - function point(l) { - return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; - } - function scaleTo(s) { - view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); - } - function translateTo(p, l) { - l = point(l); - view.x += p[0] - l[0]; - view.y += p[1] - l[1]; - } - function rescale() { - if (x1) x1.domain(x0.range().map(function(x) { - return (x - view.x) / view.k; - }).map(x0.invert)); - if (y1) y1.domain(y0.range().map(function(y) { - return (y - view.y) / view.k; - }).map(y0.invert)); - } - function zoomstarted(dispatch) { - dispatch({ - type: "zoomstart" - }); - } - function zoomed(dispatch) { - rescale(); - dispatch({ - type: "zoom", - scale: view.k, - translate: [ view.x, view.y ] - }); - } - function zoomended(dispatch) { - dispatch({ - type: "zoomend" - }); - } - function mousedowned() { - var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(that); - zoomstarted(dispatch); - function moved() { - dragged = 1; - translateTo(d3.mouse(that), location0); - zoomed(dispatch); - } - function ended() { - subject.on(mousemove, d3_window === that ? mousewheelreset : null).on(mouseup, null); - dragRestore(dragged && d3.event.target === target); - zoomended(dispatch); - } - } - function touchstarted() { - var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, target = d3.select(d3.event.target).on(touchmove, moved).on(touchend, ended), subject = d3.select(that).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(that); - started(); - zoomstarted(dispatch); - function relocate() { - var touches = d3.touches(that); - scale0 = view.k; - touches.forEach(function(t) { - if (t.identifier in locations0) locations0[t.identifier] = location(t); - }); - return touches; - } - function started() { - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - locations0[changed[i].identifier] = null; - } - var touches = relocate(), now = Date.now(); - if (touches.length === 1) { - if (now - touchtime < 500) { - var p = touches[0], l = locations0[p.identifier]; - scaleTo(view.k * 2); - translateTo(p, l); - d3_eventPreventDefault(); - zoomed(dispatch); - } - touchtime = now; - } else if (touches.length > 1) { - var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; - distance0 = dx * dx + dy * dy; - } - } - function moved() { - var touches = d3.touches(that), p0, l0, p1, l1; - for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { - p1 = touches[i]; - if (l1 = locations0[p1.identifier]) { - if (l0) break; - p0 = p1, l0 = l1; - } - } - if (l1) { - var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); - p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; - l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; - scaleTo(scale1 * scale0); - } - touchtime = null; - translateTo(p0, l0); - zoomed(dispatch); - } - function ended() { - if (d3.event.touches.length) { - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - delete locations0[changed[i].identifier]; - } - for (var identifier in locations0) { - return void relocate(); - } - } - target.on(zoomName, null); - subject.on(mousedown, mousedowned).on(touchstart, touchstarted); - dragRestore(); - zoomended(dispatch); - } - } - function mousewheeled() { - var dispatch = event.of(this, arguments); - if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), - zoomstarted(dispatch); - mousewheelTimer = setTimeout(function() { - mousewheelTimer = null; - zoomended(dispatch); - }, 50); - d3_eventPreventDefault(); - var point = center || d3.mouse(this); - if (!translate0) translate0 = location(point); - scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); - translateTo(point, translate0); - zoomed(dispatch); - } - function mousewheelreset() { - translate0 = null; - } - function dblclicked() { - var dispatch = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2; - zoomstarted(dispatch); - scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); - translateTo(p, l); - zoomed(dispatch); - zoomended(dispatch); - } - return d3.rebind(zoom, event, "on"); - }; - var d3_behavior_zoomInfinity = [ 0, Infinity ]; - var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); - }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return d3.event.wheelDelta; - }, "mousewheel") : (d3_behavior_zoomDelta = function() { - return -d3.event.detail; - }, "MozMousePixelScroll"); - function d3_Color() {} - d3_Color.prototype.toString = function() { - return this.rgb() + ""; - }; - d3.hsl = function(h, s, l) { - return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); - }; - function d3_hsl(h, s, l) { - return new d3_Hsl(h, s, l); - } - function d3_Hsl(h, s, l) { - this.h = h; - this.s = s; - this.l = l; - } - var d3_hslPrototype = d3_Hsl.prototype = new d3_Color(); - d3_hslPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, this.l / k); - }; - d3_hslPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, k * this.l); - }; - d3_hslPrototype.rgb = function() { - return d3_hsl_rgb(this.h, this.s, this.l); - }; - function d3_hsl_rgb(h, s, l) { - var m1, m2; - h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; - s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; - l = l < 0 ? 0 : l > 1 ? 1 : l; - m2 = l <= .5 ? l * (1 + s) : l + s - l * s; - m1 = 2 * l - m2; - function v(h) { - if (h > 360) h -= 360; else if (h < 0) h += 360; - if (h < 60) return m1 + (m2 - m1) * h / 60; - if (h < 180) return m2; - if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; - return m1; - } - function vv(h) { - return Math.round(v(h) * 255); - } - return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); - } - d3.hcl = function(h, c, l) { - return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); - }; - function d3_hcl(h, c, l) { - return new d3_Hcl(h, c, l); - } - function d3_Hcl(h, c, l) { - this.h = h; - this.c = c; - this.l = l; - } - var d3_hclPrototype = d3_Hcl.prototype = new d3_Color(); - d3_hclPrototype.brighter = function(k) { - return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.darker = function(k) { - return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.rgb = function() { - return d3_hcl_lab(this.h, this.c, this.l).rgb(); - }; - function d3_hcl_lab(h, c, l) { - if (isNaN(h)) h = 0; - if (isNaN(c)) c = 0; - return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); - } - d3.lab = function(l, a, b) { - return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); - }; - function d3_lab(l, a, b) { - return new d3_Lab(l, a, b); - } - function d3_Lab(l, a, b) { - this.l = l; - this.a = a; - this.b = b; - } - var d3_lab_K = 18; - var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; - var d3_labPrototype = d3_Lab.prototype = new d3_Color(); - d3_labPrototype.brighter = function(k) { - return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.darker = function(k) { - return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.rgb = function() { - return d3_lab_rgb(this.l, this.a, this.b); - }; - function d3_lab_rgb(l, a, b) { - var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; - x = d3_lab_xyz(x) * d3_lab_X; - y = d3_lab_xyz(y) * d3_lab_Y; - z = d3_lab_xyz(z) * d3_lab_Z; - return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); - } - function d3_lab_hcl(l, a, b) { - return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l); - } - function d3_lab_xyz(x) { - return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; - } - function d3_xyz_lab(x) { - return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; - } - function d3_xyz_rgb(r) { - return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); - } - d3.rgb = function(r, g, b) { - return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); - }; - function d3_rgbNumber(value) { - return d3_rgb(value >> 16, value >> 8 & 255, value & 255); - } - function d3_rgbString(value) { - return d3_rgbNumber(value) + ""; - } - function d3_rgb(r, g, b) { - return new d3_Rgb(r, g, b); - } - function d3_Rgb(r, g, b) { - this.r = r; - this.g = g; - this.b = b; - } - var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color(); - d3_rgbPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - var r = this.r, g = this.g, b = this.b, i = 30; - if (!r && !g && !b) return d3_rgb(i, i, i); - if (r && r < i) r = i; - if (g && g < i) g = i; - if (b && b < i) b = i; - return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k))); - }; - d3_rgbPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b)); - }; - d3_rgbPrototype.hsl = function() { - return d3_rgb_hsl(this.r, this.g, this.b); - }; - d3_rgbPrototype.toString = function() { - return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); - }; - function d3_rgb_hex(v) { - return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); - } - function d3_rgb_parse(format, rgb, hsl) { - var r = 0, g = 0, b = 0, m1, m2, color; - m1 = /([a-z]+)\((.*)\)/i.exec(format); - if (m1) { - m2 = m1[2].split(","); - switch (m1[1]) { - case "hsl": - { - return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); - } - - case "rgb": - { - return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); - } - } - } - if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b); - if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) { - if (format.length === 4) { - r = (color & 3840) >> 4; - r = r >> 4 | r; - g = color & 240; - g = g >> 4 | g; - b = color & 15; - b = b << 4 | b; - } else if (format.length === 7) { - r = (color & 16711680) >> 16; - g = (color & 65280) >> 8; - b = color & 255; - } - } - return rgb(r, g, b); - } - function d3_rgb_hsl(r, g, b) { - var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; - if (d) { - s = l < .5 ? d / (max + min) : d / (2 - max - min); - if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; - h *= 60; - } else { - h = NaN; - s = l > 0 && l < 1 ? 0 : h; - } - return d3_hsl(h, s, l); - } - function d3_rgb_lab(r, g, b) { - r = d3_rgb_xyz(r); - g = d3_rgb_xyz(g); - b = d3_rgb_xyz(b); - var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); - return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); - } - function d3_rgb_xyz(r) { - return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); - } - function d3_rgb_parseNumber(c) { - var f = parseFloat(c); - return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; - } - var d3_rgb_names = d3.map({ - aliceblue: 15792383, - antiquewhite: 16444375, - aqua: 65535, - aquamarine: 8388564, - azure: 15794175, - beige: 16119260, - bisque: 16770244, - black: 0, - blanchedalmond: 16772045, - blue: 255, - blueviolet: 9055202, - brown: 10824234, - burlywood: 14596231, - cadetblue: 6266528, - chartreuse: 8388352, - chocolate: 13789470, - coral: 16744272, - cornflowerblue: 6591981, - cornsilk: 16775388, - crimson: 14423100, - cyan: 65535, - darkblue: 139, - darkcyan: 35723, - darkgoldenrod: 12092939, - darkgray: 11119017, - darkgreen: 25600, - darkgrey: 11119017, - darkkhaki: 12433259, - darkmagenta: 9109643, - darkolivegreen: 5597999, - darkorange: 16747520, - darkorchid: 10040012, - darkred: 9109504, - darksalmon: 15308410, - darkseagreen: 9419919, - darkslateblue: 4734347, - darkslategray: 3100495, - darkslategrey: 3100495, - darkturquoise: 52945, - darkviolet: 9699539, - deeppink: 16716947, - deepskyblue: 49151, - dimgray: 6908265, - dimgrey: 6908265, - dodgerblue: 2003199, - firebrick: 11674146, - floralwhite: 16775920, - forestgreen: 2263842, - fuchsia: 16711935, - gainsboro: 14474460, - ghostwhite: 16316671, - gold: 16766720, - goldenrod: 14329120, - gray: 8421504, - green: 32768, - greenyellow: 11403055, - grey: 8421504, - honeydew: 15794160, - hotpink: 16738740, - indianred: 13458524, - indigo: 4915330, - ivory: 16777200, - khaki: 15787660, - lavender: 15132410, - lavenderblush: 16773365, - lawngreen: 8190976, - lemonchiffon: 16775885, - lightblue: 11393254, - lightcoral: 15761536, - lightcyan: 14745599, - lightgoldenrodyellow: 16448210, - lightgray: 13882323, - lightgreen: 9498256, - lightgrey: 13882323, - lightpink: 16758465, - lightsalmon: 16752762, - lightseagreen: 2142890, - lightskyblue: 8900346, - lightslategray: 7833753, - lightslategrey: 7833753, - lightsteelblue: 11584734, - lightyellow: 16777184, - lime: 65280, - limegreen: 3329330, - linen: 16445670, - magenta: 16711935, - maroon: 8388608, - mediumaquamarine: 6737322, - mediumblue: 205, - mediumorchid: 12211667, - mediumpurple: 9662683, - mediumseagreen: 3978097, - mediumslateblue: 8087790, - mediumspringgreen: 64154, - mediumturquoise: 4772300, - mediumvioletred: 13047173, - midnightblue: 1644912, - mintcream: 16121850, - mistyrose: 16770273, - moccasin: 16770229, - navajowhite: 16768685, - navy: 128, - oldlace: 16643558, - olive: 8421376, - olivedrab: 7048739, - orange: 16753920, - orangered: 16729344, - orchid: 14315734, - palegoldenrod: 15657130, - palegreen: 10025880, - paleturquoise: 11529966, - palevioletred: 14381203, - papayawhip: 16773077, - peachpuff: 16767673, - peru: 13468991, - pink: 16761035, - plum: 14524637, - powderblue: 11591910, - purple: 8388736, - red: 16711680, - rosybrown: 12357519, - royalblue: 4286945, - saddlebrown: 9127187, - salmon: 16416882, - sandybrown: 16032864, - seagreen: 3050327, - seashell: 16774638, - sienna: 10506797, - silver: 12632256, - skyblue: 8900331, - slateblue: 6970061, - slategray: 7372944, - slategrey: 7372944, - snow: 16775930, - springgreen: 65407, - steelblue: 4620980, - tan: 13808780, - teal: 32896, - thistle: 14204888, - tomato: 16737095, - turquoise: 4251856, - violet: 15631086, - wheat: 16113331, - white: 16777215, - whitesmoke: 16119285, - yellow: 16776960, - yellowgreen: 10145074 - }); - d3_rgb_names.forEach(function(key, value) { - d3_rgb_names.set(key, d3_rgbNumber(value)); - }); - function d3_functor(v) { - return typeof v === "function" ? v : function() { - return v; - }; - } - d3.functor = d3_functor; - function d3_identity(d) { - return d; - } - d3.xhr = d3_xhrType(d3_identity); - function d3_xhrType(response) { - return function(url, mimeType, callback) { - if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, - mimeType = null; - return d3_xhr(url, mimeType, response, callback); - }; - } - function d3_xhr(url, mimeType, response, callback) { - var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; - if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); - "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { - request.readyState > 3 && respond(); - }; - function respond() { - var status = request.status, result; - if (!status && request.responseText || status >= 200 && status < 300 || status === 304) { - try { - result = response.call(xhr, request); - } catch (e) { - dispatch.error.call(xhr, e); - return; - } - dispatch.load.call(xhr, result); - } else { - dispatch.error.call(xhr, request); - } - } - request.onprogress = function(event) { - var o = d3.event; - d3.event = event; - try { - dispatch.progress.call(xhr, request); - } finally { - d3.event = o; - } - }; - xhr.header = function(name, value) { - name = (name + "").toLowerCase(); - if (arguments.length < 2) return headers[name]; - if (value == null) delete headers[name]; else headers[name] = value + ""; - return xhr; - }; - xhr.mimeType = function(value) { - if (!arguments.length) return mimeType; - mimeType = value == null ? null : value + ""; - return xhr; - }; - xhr.responseType = function(value) { - if (!arguments.length) return responseType; - responseType = value; - return xhr; - }; - xhr.response = function(value) { - response = value; - return xhr; - }; - [ "get", "post" ].forEach(function(method) { - xhr[method] = function() { - return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); - }; - }); - xhr.send = function(method, data, callback) { - if (arguments.length === 2 && typeof data === "function") callback = data, data = null; - request.open(method, url, true); - if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; - if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); - if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); - if (responseType != null) request.responseType = responseType; - if (callback != null) xhr.on("error", callback).on("load", function(request) { - callback(null, request); - }); - dispatch.beforesend.call(xhr, request); - request.send(data == null ? null : data); - return xhr; - }; - xhr.abort = function() { - request.abort(); - return xhr; - }; - d3.rebind(xhr, dispatch, "on"); - return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); - } - function d3_xhr_fixCallback(callback) { - return callback.length === 1 ? function(error, request) { - callback(error == null ? request : null); - } : callback; - } - d3.dsv = function(delimiter, mimeType) { - var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); - function dsv(url, row, callback) { - if (arguments.length < 3) callback = row, row = null; - var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); - xhr.row = function(_) { - return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; - }; - return xhr; - } - function response(request) { - return dsv.parse(request.responseText); - } - function typedResponse(f) { - return function(request) { - return dsv.parse(request.responseText, f); - }; - } - dsv.parse = function(text, f) { - var o; - return dsv.parseRows(text, function(row, i) { - if (o) return o(row, i - 1); - var a = new Function("d", "return {" + row.map(function(name, i) { - return JSON.stringify(name) + ": d[" + i + "]"; - }).join(",") + "}"); - o = f ? function(row, i) { - return f(a(row), i); - } : a; - }); - }; - dsv.parseRows = function(text, f) { - var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; - function token() { - if (I >= N) return EOF; - if (eol) return eol = false, EOL; - var j = I; - if (text.charCodeAt(j) === 34) { - var i = j; - while (i++ < N) { - if (text.charCodeAt(i) === 34) { - if (text.charCodeAt(i + 1) !== 34) break; - ++i; - } - } - I = i + 2; - var c = text.charCodeAt(i + 1); - if (c === 13) { - eol = true; - if (text.charCodeAt(i + 2) === 10) ++I; - } else if (c === 10) { - eol = true; - } - return text.substring(j + 1, i).replace(/""/g, '"'); - } - while (I < N) { - var c = text.charCodeAt(I++), k = 1; - if (c === 10) eol = true; else if (c === 13) { - eol = true; - if (text.charCodeAt(I) === 10) ++I, ++k; - } else if (c !== delimiterCode) continue; - return text.substring(j, I - k); - } - return text.substring(j); - } - while ((t = token()) !== EOF) { - var a = []; - while (t !== EOL && t !== EOF) { - a.push(t); - t = token(); - } - if (f && !(a = f(a, n++))) continue; - rows.push(a); - } - return rows; - }; - dsv.format = function(rows) { - if (Array.isArray(rows[0])) return dsv.formatRows(rows); - var fieldSet = new d3_Set(), fields = []; - rows.forEach(function(row) { - for (var field in row) { - if (!fieldSet.has(field)) { - fields.push(fieldSet.add(field)); - } - } - }); - return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { - return fields.map(function(field) { - return formatValue(row[field]); - }).join(delimiter); - })).join("\n"); - }; - dsv.formatRows = function(rows) { - return rows.map(formatRow).join("\n"); - }; - function formatRow(row) { - return row.map(formatValue).join(delimiter); - } - function formatValue(text) { - return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; - } - return dsv; - }; - d3.csv = d3.dsv(",", "text/csv"); - d3.tsv = d3.dsv(" ", "text/tab-separated-values"); - d3.touch = function(container, touches, identifier) { - if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; - if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { - if ((touch = touches[i]).identifier === identifier) { - return d3_mousePoint(container, touch); - } - } - }; - var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { - setTimeout(callback, 17); - }; - d3.timer = function(callback, delay, then) { - var n = arguments.length; - if (n < 2) delay = 0; - if (n < 3) then = Date.now(); - var time = then + delay, timer = { - c: callback, - t: time, - f: false, - n: null - }; - if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; - d3_timer_queueTail = timer; - if (!d3_timer_interval) { - d3_timer_timeout = clearTimeout(d3_timer_timeout); - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - }; - function d3_timer_step() { - var now = d3_timer_mark(), delay = d3_timer_sweep() - now; - if (delay > 24) { - if (isFinite(delay)) { - clearTimeout(d3_timer_timeout); - d3_timer_timeout = setTimeout(d3_timer_step, delay); - } - d3_timer_interval = 0; - } else { - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - } - d3.timer.flush = function() { - d3_timer_mark(); - d3_timer_sweep(); - }; - function d3_timer_mark() { - var now = Date.now(); - d3_timer_active = d3_timer_queueHead; - while (d3_timer_active) { - if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); - d3_timer_active = d3_timer_active.n; - } - return now; - } - function d3_timer_sweep() { - var t0, t1 = d3_timer_queueHead, time = Infinity; - while (t1) { - if (t1.f) { - t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; - } else { - if (t1.t < time) time = t1.t; - t1 = (t0 = t1).n; - } - } - d3_timer_queueTail = t0; - return time; - } - function d3_format_precision(x, p) { - return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); - } - d3.round = function(x, n) { - return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); - }; - var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); - d3.formatPrefix = function(value, precision) { - var i = 0; - if (value) { - if (value < 0) value *= -1; - if (precision) value = d3.round(value, d3_format_precision(value, precision)); - i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); - i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); - } - return d3_formatPrefixes[8 + i / 3]; - }; - function d3_formatPrefix(d, i) { - var k = Math.pow(10, abs(8 - i) * 3); - return { - scale: i > 8 ? function(d) { - return d / k; - } : function(d) { - return d * k; - }, - symbol: d - }; - } - function d3_locale_numberFormat(locale) { - var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) { - var i = value.length, t = [], j = 0, g = locale_grouping[0]; - while (i > 0 && g > 0) { - t.push(value.substring(i -= g, i + g)); - g = locale_grouping[j = (j + 1) % locale_grouping.length]; - } - return t.reverse().join(locale_thousands); - } : d3_identity; - return function(specifier) { - var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false; - if (precision) precision = +precision.substring(1); - if (zfill || fill === "0" && align === "=") { - zfill = fill = "0"; - align = "="; - if (comma) width -= Math.floor((width - 1) / 4); - } - switch (type) { - case "n": - comma = true; - type = "g"; - break; - - case "%": - scale = 100; - suffix = "%"; - type = "f"; - break; - - case "p": - scale = 100; - suffix = "%"; - type = "r"; - break; - - case "b": - case "o": - case "x": - case "X": - if (symbol === "#") prefix = "0" + type.toLowerCase(); - - case "c": - case "d": - integer = true; - precision = 0; - break; - - case "s": - scale = -1; - type = "r"; - break; - } - if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; - if (type == "r" && !precision) type = "g"; - if (precision != null) { - if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); - } - type = d3_format_types.get(type) || d3_format_typeDefault; - var zcomma = zfill && comma; - return function(value) { - var fullSuffix = suffix; - if (integer && value % 1) return ""; - var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; - if (scale < 0) { - var unit = d3.formatPrefix(value, precision); - value = unit.scale(value); - fullSuffix = unit.symbol + suffix; - } else { - value *= scale; - } - value = type(value, precision); - var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : locale_decimal + value.substring(i + 1); - if (!zfill && comma) before = formatGroup(before); - var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; - if (zcomma) before = formatGroup(padding + before); - negative += prefix; - value = before + after; - return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; - }; - }; - } - var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; - var d3_format_types = d3.map({ - b: function(x) { - return x.toString(2); - }, - c: function(x) { - return String.fromCharCode(x); - }, - o: function(x) { - return x.toString(8); - }, - x: function(x) { - return x.toString(16); - }, - X: function(x) { - return x.toString(16).toUpperCase(); - }, - g: function(x, p) { - return x.toPrecision(p); - }, - e: function(x, p) { - return x.toExponential(p); - }, - f: function(x, p) { - return x.toFixed(p); - }, - r: function(x, p) { - return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); - } - }); - function d3_format_typeDefault(x) { - return x + ""; - } - var d3_time = d3.time = {}, d3_date = Date; - function d3_date_utc() { - this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); - } - d3_date_utc.prototype = { - getDate: function() { - return this._.getUTCDate(); - }, - getDay: function() { - return this._.getUTCDay(); - }, - getFullYear: function() { - return this._.getUTCFullYear(); - }, - getHours: function() { - return this._.getUTCHours(); - }, - getMilliseconds: function() { - return this._.getUTCMilliseconds(); - }, - getMinutes: function() { - return this._.getUTCMinutes(); - }, - getMonth: function() { - return this._.getUTCMonth(); - }, - getSeconds: function() { - return this._.getUTCSeconds(); - }, - getTime: function() { - return this._.getTime(); - }, - getTimezoneOffset: function() { - return 0; - }, - valueOf: function() { - return this._.valueOf(); - }, - setDate: function() { - d3_time_prototype.setUTCDate.apply(this._, arguments); - }, - setDay: function() { - d3_time_prototype.setUTCDay.apply(this._, arguments); - }, - setFullYear: function() { - d3_time_prototype.setUTCFullYear.apply(this._, arguments); - }, - setHours: function() { - d3_time_prototype.setUTCHours.apply(this._, arguments); - }, - setMilliseconds: function() { - d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); - }, - setMinutes: function() { - d3_time_prototype.setUTCMinutes.apply(this._, arguments); - }, - setMonth: function() { - d3_time_prototype.setUTCMonth.apply(this._, arguments); - }, - setSeconds: function() { - d3_time_prototype.setUTCSeconds.apply(this._, arguments); - }, - setTime: function() { - d3_time_prototype.setTime.apply(this._, arguments); - } - }; - var d3_time_prototype = Date.prototype; - function d3_time_interval(local, step, number) { - function round(date) { - var d0 = local(date), d1 = offset(d0, 1); - return date - d0 < d1 - date ? d0 : d1; - } - function ceil(date) { - step(date = local(new d3_date(date - 1)), 1); - return date; - } - function offset(date, k) { - step(date = new d3_date(+date), k); - return date; - } - function range(t0, t1, dt) { - var time = ceil(t0), times = []; - if (dt > 1) { - while (time < t1) { - if (!(number(time) % dt)) times.push(new Date(+time)); - step(time, 1); - } - } else { - while (time < t1) times.push(new Date(+time)), step(time, 1); - } - return times; - } - function range_utc(t0, t1, dt) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = t0; - return range(utc, t1, dt); - } finally { - d3_date = Date; - } - } - local.floor = local; - local.round = round; - local.ceil = ceil; - local.offset = offset; - local.range = range; - var utc = local.utc = d3_time_interval_utc(local); - utc.floor = utc; - utc.round = d3_time_interval_utc(round); - utc.ceil = d3_time_interval_utc(ceil); - utc.offset = d3_time_interval_utc(offset); - utc.range = range_utc; - return local; - } - function d3_time_interval_utc(method) { - return function(date, k) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = date; - return method(utc, k)._; - } finally { - d3_date = Date; - } - }; - } - d3_time.year = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setMonth(0, 1); - return date; - }, function(date, offset) { - date.setFullYear(date.getFullYear() + offset); - }, function(date) { - return date.getFullYear(); - }); - d3_time.years = d3_time.year.range; - d3_time.years.utc = d3_time.year.utc.range; - d3_time.day = d3_time_interval(function(date) { - var day = new d3_date(2e3, 0); - day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); - return day; - }, function(date, offset) { - date.setDate(date.getDate() + offset); - }, function(date) { - return date.getDate() - 1; - }); - d3_time.days = d3_time.day.range; - d3_time.days.utc = d3_time.day.utc.range; - d3_time.dayOfYear = function(date) { - var year = d3_time.year(date); - return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); - }; - [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { - i = 7 - i; - var interval = d3_time[day] = d3_time_interval(function(date) { - (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); - return date; - }, function(date, offset) { - date.setDate(date.getDate() + Math.floor(offset) * 7); - }, function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); - }); - d3_time[day + "s"] = interval.range; - d3_time[day + "s"].utc = interval.utc.range; - d3_time[day + "OfYear"] = function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); - }; - }); - d3_time.week = d3_time.sunday; - d3_time.weeks = d3_time.sunday.range; - d3_time.weeks.utc = d3_time.sunday.utc.range; - d3_time.weekOfYear = d3_time.sundayOfYear; - function d3_locale_timeFormat(locale) { - var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; - function d3_time_format(template) { - var n = template.length; - function format(date) { - var string = [], i = -1, j = 0, c, p, f; - while (++i < n) { - if (template.charCodeAt(i) === 37) { - string.push(template.substring(j, i)); - if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); - if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); - string.push(c); - j = i + 1; - } - } - string.push(template.substring(j, i)); - return string.join(""); - } - format.parse = function(string) { - var d = { - y: 1900, - m: 0, - d: 1, - H: 0, - M: 0, - S: 0, - L: 0, - Z: null - }, i = d3_time_parse(d, template, string, 0); - if (i != string.length) return null; - if ("p" in d) d.H = d.H % 12 + d.p * 12; - var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); - if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { - date.setFullYear(d.y, 0, 1); - date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); - } else date.setFullYear(d.y, d.m, d.d); - date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L); - return localZ ? date._ : date; - }; - format.toString = function() { - return template; - }; - return format; - } - function d3_time_parse(date, template, string, j) { - var c, p, t, i = 0, n = template.length, m = string.length; - while (i < n) { - if (j >= m) return -1; - c = template.charCodeAt(i++); - if (c === 37) { - t = template.charAt(i++); - p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; - if (!p || (j = p(date, string, j)) < 0) return -1; - } else if (c != string.charCodeAt(j++)) { - return -1; - } - } - return j; - } - d3_time_format.utc = function(template) { - var local = d3_time_format(template); - function format(date) { - try { - d3_date = d3_date_utc; - var utc = new d3_date(); - utc._ = date; - return local(utc); - } finally { - d3_date = Date; - } - } - format.parse = function(string) { - try { - d3_date = d3_date_utc; - var date = local.parse(string); - return date && date._; - } finally { - d3_date = Date; - } - }; - format.toString = local.toString; - return format; - }; - d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; - var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); - locale_periods.forEach(function(p, i) { - d3_time_periodLookup.set(p.toLowerCase(), i); - }); - var d3_time_formats = { - a: function(d) { - return locale_shortDays[d.getDay()]; - }, - A: function(d) { - return locale_days[d.getDay()]; - }, - b: function(d) { - return locale_shortMonths[d.getMonth()]; - }, - B: function(d) { - return locale_months[d.getMonth()]; - }, - c: d3_time_format(locale_dateTime), - d: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - e: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - H: function(d, p) { - return d3_time_formatPad(d.getHours(), p, 2); - }, - I: function(d, p) { - return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); - }, - j: function(d, p) { - return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); - }, - L: function(d, p) { - return d3_time_formatPad(d.getMilliseconds(), p, 3); - }, - m: function(d, p) { - return d3_time_formatPad(d.getMonth() + 1, p, 2); - }, - M: function(d, p) { - return d3_time_formatPad(d.getMinutes(), p, 2); - }, - p: function(d) { - return locale_periods[+(d.getHours() >= 12)]; - }, - S: function(d, p) { - return d3_time_formatPad(d.getSeconds(), p, 2); - }, - U: function(d, p) { - return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); - }, - w: function(d) { - return d.getDay(); - }, - W: function(d, p) { - return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); - }, - x: d3_time_format(locale_date), - X: d3_time_format(locale_time), - y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 100, p, 2); - }, - Y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); - }, - Z: d3_time_zone, - "%": function() { - return "%"; - } - }; - var d3_time_parsers = { - a: d3_time_parseWeekdayAbbrev, - A: d3_time_parseWeekday, - b: d3_time_parseMonthAbbrev, - B: d3_time_parseMonth, - c: d3_time_parseLocaleFull, - d: d3_time_parseDay, - e: d3_time_parseDay, - H: d3_time_parseHour24, - I: d3_time_parseHour24, - j: d3_time_parseDayOfYear, - L: d3_time_parseMilliseconds, - m: d3_time_parseMonthNumber, - M: d3_time_parseMinutes, - p: d3_time_parseAmPm, - S: d3_time_parseSeconds, - U: d3_time_parseWeekNumberSunday, - w: d3_time_parseWeekdayNumber, - W: d3_time_parseWeekNumberMonday, - x: d3_time_parseLocaleDate, - X: d3_time_parseLocaleTime, - y: d3_time_parseYear, - Y: d3_time_parseFullYear, - Z: d3_time_parseZone, - "%": d3_time_parseLiteralPercent - }; - function d3_time_parseWeekdayAbbrev(date, string, i) { - d3_time_dayAbbrevRe.lastIndex = 0; - var n = d3_time_dayAbbrevRe.exec(string.substring(i)); - return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseWeekday(date, string, i) { - d3_time_dayRe.lastIndex = 0; - var n = d3_time_dayRe.exec(string.substring(i)); - return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonthAbbrev(date, string, i) { - d3_time_monthAbbrevRe.lastIndex = 0; - var n = d3_time_monthAbbrevRe.exec(string.substring(i)); - return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonth(date, string, i) { - d3_time_monthRe.lastIndex = 0; - var n = d3_time_monthRe.exec(string.substring(i)); - return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseLocaleFull(date, string, i) { - return d3_time_parse(date, d3_time_formats.c.toString(), string, i); - } - function d3_time_parseLocaleDate(date, string, i) { - return d3_time_parse(date, d3_time_formats.x.toString(), string, i); - } - function d3_time_parseLocaleTime(date, string, i) { - return d3_time_parse(date, d3_time_formats.X.toString(), string, i); - } - function d3_time_parseAmPm(date, string, i) { - var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase()); - return n == null ? -1 : (date.p = n, i); - } - return d3_time_format; - } - var d3_time_formatPads = { - "-": "", - _: " ", - "0": "0" - }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; - function d3_time_formatPad(value, fill, width) { - var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; - return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); - } - function d3_time_formatRe(names) { - return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); - } - function d3_time_formatLookup(names) { - var map = new d3_Map(), i = -1, n = names.length; - while (++i < n) map.set(names[i].toLowerCase(), i); - return map; - } - function d3_time_parseWeekdayNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 1)); - return n ? (date.w = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberSunday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i)); - return n ? (date.U = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberMonday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i)); - return n ? (date.W = +n[0], i + n[0].length) : -1; - } - function d3_time_parseFullYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 4)); - return n ? (date.y = +n[0], i + n[0].length) : -1; - } - function d3_time_parseYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; - } - function d3_time_parseZone(date, string, i) { - return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = +string, - i + 5) : -1; - } - function d3_time_expandYear(d) { - return d + (d > 68 ? 1900 : 2e3); - } - function d3_time_parseMonthNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.m = n[0] - 1, i + n[0].length) : -1; - } - function d3_time_parseDay(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.d = +n[0], i + n[0].length) : -1; - } - function d3_time_parseDayOfYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 3)); - return n ? (date.j = +n[0], i + n[0].length) : -1; - } - function d3_time_parseHour24(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.H = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMinutes(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.M = +n[0], i + n[0].length) : -1; - } - function d3_time_parseSeconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.S = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMilliseconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 3)); - return n ? (date.L = +n[0], i + n[0].length) : -1; - } - function d3_time_zone(d) { - var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60; - return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); - } - function d3_time_parseLiteralPercent(date, string, i) { - d3_time_percentRe.lastIndex = 0; - var n = d3_time_percentRe.exec(string.substring(i, i + 1)); - return n ? i + n[0].length : -1; - } - function d3_time_formatMulti(formats) { - var n = formats.length, i = -1; - while (++i < n) formats[i][0] = this(formats[i][0]); - return function(date) { - var i = 0, f = formats[i]; - while (!f[1](date)) f = formats[++i]; - return f[0](date); - }; - } - d3.locale = function(locale) { - return { - numberFormat: d3_locale_numberFormat(locale), - timeFormat: d3_locale_timeFormat(locale) - }; - }; - var d3_locale_enUS = d3.locale({ - decimal: ".", - thousands: ",", - grouping: [ 3 ], - currency: [ "$", "" ], - dateTime: "%a %b %e %X %Y", - date: "%m/%d/%Y", - time: "%H:%M:%S", - periods: [ "AM", "PM" ], - days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], - shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], - months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] - }); - d3.format = d3_locale_enUS.numberFormat; - d3.geo = {}; - function d3_adder() {} - d3_adder.prototype = { - s: 0, - t: 0, - add: function(y) { - d3_adderSum(y, this.t, d3_adderTemp); - d3_adderSum(d3_adderTemp.s, this.s, this); - if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; - }, - reset: function() { - this.s = this.t = 0; - }, - valueOf: function() { - return this.s; - } - }; - var d3_adderTemp = new d3_adder(); - function d3_adderSum(a, b, o) { - var x = o.s = a + b, bv = x - a, av = x - bv; - o.t = a - av + (b - bv); - } - d3.geo.stream = function(object, listener) { - if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { - d3_geo_streamObjectType[object.type](object, listener); - } else { - d3_geo_streamGeometry(object, listener); - } - }; - function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } - } - var d3_geo_streamObjectType = { - Feature: function(feature, listener) { - d3_geo_streamGeometry(feature.geometry, listener); - }, - FeatureCollection: function(object, listener) { - var features = object.features, i = -1, n = features.length; - while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); - } - }; - var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - object = object.coordinates; - listener.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); - }, - LineString: function(object, listener) { - d3_geo_streamLine(object.coordinates, listener, 0); - }, - MultiLineString: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); - }, - Polygon: function(object, listener) { - d3_geo_streamPolygon(object.coordinates, listener); - }, - MultiPolygon: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); - }, - GeometryCollection: function(object, listener) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) d3_geo_streamGeometry(geometries[i], listener); - } - }; - function d3_geo_streamLine(coordinates, listener, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - listener.lineStart(); - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); - listener.lineEnd(); - } - function d3_geo_streamPolygon(coordinates, listener) { - var i = -1, n = coordinates.length; - listener.polygonStart(); - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); - listener.polygonEnd(); - } - d3.geo.area = function(object) { - d3_geo_areaSum = 0; - d3.geo.stream(object, d3_geo_area); - return d3_geo_areaSum; - }; - var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); - var d3_geo_area = { - sphere: function() { - d3_geo_areaSum += 4 * π; - }, - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_areaRingSum.reset(); - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * d3_geo_areaRingSum; - d3_geo_areaSum += area < 0 ? 4 * π + area : area; - d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; - } - }; - function d3_geo_areaRingStart() { - var λ00, φ00, λ0, cosφ0, sinφ0; - d3_geo_area.point = function(λ, φ) { - d3_geo_area.point = nextPoint; - λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), - sinφ0 = Math.sin(φ); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - φ = φ * d3_radians / 2 + π / 4; - var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); - d3_geo_areaRingSum.add(Math.atan2(v, u)); - λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; - } - d3_geo_area.lineEnd = function() { - nextPoint(λ00, φ00); - }; - } - function d3_geo_cartesian(spherical) { - var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); - return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; - } - function d3_geo_cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - function d3_geo_cartesianCross(a, b) { - return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; - } - function d3_geo_cartesianAdd(a, b) { - a[0] += b[0]; - a[1] += b[1]; - a[2] += b[2]; - } - function d3_geo_cartesianScale(vector, k) { - return [ vector[0] * k, vector[1] * k, vector[2] * k ]; - } - function d3_geo_cartesianNormalize(d) { - var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l; - d[1] /= l; - d[2] /= l; - } - function d3_geo_spherical(cartesian) { - return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; - } - function d3_geo_sphericalEqual(a, b) { - return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; - } - d3.geo.bounds = function() { - var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; - var bound = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - bound.point = ringPoint; - bound.lineStart = ringStart; - bound.lineEnd = ringEnd; - dλSum = 0; - d3_geo_area.polygonStart(); - }, - polygonEnd: function() { - d3_geo_area.polygonEnd(); - bound.point = point; - bound.lineStart = lineStart; - bound.lineEnd = lineEnd; - if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; - range[0] = λ0, range[1] = λ1; - } - }; - function point(λ, φ) { - ranges.push(range = [ λ0 = λ, λ1 = λ ]); - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - function linePoint(λ, φ) { - var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); - if (p0) { - var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); - d3_geo_cartesianNormalize(inflection); - inflection = d3_geo_spherical(inflection); - var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; - if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = inflection[1] * d3_degrees; - if (φi > φ1) φ1 = φi; - } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = -inflection[1] * d3_degrees; - if (φi < φ0) φ0 = φi; - } else { - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - if (antimeridian) { - if (λ < λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } else { - if (λ1 >= λ0) { - if (λ < λ0) λ0 = λ; - if (λ > λ1) λ1 = λ; - } else { - if (λ > λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } - } - } else { - point(λ, φ); - } - p0 = p, λ_ = λ; - } - function lineStart() { - bound.point = linePoint; - } - function lineEnd() { - range[0] = λ0, range[1] = λ1; - bound.point = point; - p0 = null; - } - function ringPoint(λ, φ) { - if (p0) { - var dλ = λ - λ_; - dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; - } else λ__ = λ, φ__ = φ; - d3_geo_area.point(λ, φ); - linePoint(λ, φ); - } - function ringStart() { - d3_geo_area.lineStart(); - } - function ringEnd() { - ringPoint(λ__, φ__); - d3_geo_area.lineEnd(); - if (abs(dλSum) > ε) λ0 = -(λ1 = 180); - range[0] = λ0, range[1] = λ1; - p0 = null; - } - function angle(λ0, λ1) { - return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; - } - function compareRanges(a, b) { - return a[0] - b[0]; - } - function withinRange(x, range) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; - } - return function(feature) { - φ1 = λ1 = -(λ0 = φ0 = Infinity); - ranges = []; - d3.geo.stream(feature, bound); - var n = ranges.length; - if (n) { - ranges.sort(compareRanges); - for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { - b = ranges[i]; - if (withinRange(b[0], a) || withinRange(b[1], a)) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - var best = -Infinity, dλ; - for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { - b = merged[i]; - if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; - } - } - ranges = range = null; - return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; - }; - }(); - d3.geo.centroid = function(object) { - d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, d3_geo_centroid); - var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; - if (m < ε2) { - x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; - if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; - m = x * x + y * y + z * z; - if (m < ε2) return [ NaN, NaN ]; - } - return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; - }; - var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; - var d3_geo_centroid = { - sphere: d3_noop, - point: d3_geo_centroidPoint, - lineStart: d3_geo_centroidLineStart, - lineEnd: d3_geo_centroidLineEnd, - polygonStart: function() { - d3_geo_centroid.lineStart = d3_geo_centroidRingStart; - }, - polygonEnd: function() { - d3_geo_centroid.lineStart = d3_geo_centroidLineStart; - } - }; - function d3_geo_centroidPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); - } - function d3_geo_centroidPointXYZ(x, y, z) { - ++d3_geo_centroidW0; - d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; - d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; - d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; - } - function d3_geo_centroidLineStart() { - var x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroid.point = nextPoint; - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_geo_centroidLineEnd() { - d3_geo_centroid.point = d3_geo_centroidPoint; - } - function d3_geo_centroidRingStart() { - var λ00, φ00, x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ00 = λ, φ00 = φ; - d3_geo_centroid.point = nextPoint; - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - d3_geo_centroid.lineEnd = function() { - nextPoint(λ00, φ00); - d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; - d3_geo_centroid.point = d3_geo_centroidPoint; - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); - d3_geo_centroidX2 += v * cx; - d3_geo_centroidY2 += v * cy; - d3_geo_centroidZ2 += v * cz; - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_true() { - return true; - } - function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { - var subject = [], clip = []; - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n]; - if (d3_geo_sphericalEqual(p0, p1)) { - listener.lineStart(); - for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); - listener.lineEnd(); - return; - } - var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); - a.o = b; - subject.push(a); - clip.push(b); - a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); - b = new d3_geo_clipPolygonIntersection(p1, null, a, true); - a.o = b; - subject.push(a); - clip.push(b); - }); - clip.sort(compare); - d3_geo_clipPolygonLinkCircular(subject); - d3_geo_clipPolygonLinkCircular(clip); - if (!subject.length) return; - for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { - clip[i].e = entry = !entry; - } - var start = subject[0], points, point; - while (1) { - var current = start, isSubject = true; - while (current.v) if ((current = current.n) === start) return; - points = current.z; - listener.lineStart(); - do { - current.v = current.o.v = true; - if (current.e) { - if (isSubject) { - for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.n.x, 1, listener); - } - current = current.n; - } else { - if (isSubject) { - points = current.p.z; - for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.p.x, -1, listener); - } - current = current.p; - } - current = current.o; - points = current.z; - isSubject = !isSubject; - } while (!current.v); - listener.lineEnd(); - } - } - function d3_geo_clipPolygonLinkCircular(array) { - if (!(n = array.length)) return; - var n, i = 0, a = array[0], b; - while (++i < n) { - a.n = b = array[i]; - b.p = a; - a = b; - } - a.n = b = array[0]; - b.p = a; - } - function d3_geo_clipPolygonIntersection(point, points, other, entry) { - this.x = point; - this.z = points; - this.o = other; - this.e = entry; - this.v = false; - this.n = this.p = null; - } - function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { - return function(rotate, listener) { - var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - listener.polygonStart(); - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - segments = d3.merge(segments); - var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); - if (segments.length) { - d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); - } else if (clipStartInside) { - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - listener.polygonEnd(); - segments = polygon = null; - }, - sphere: function() { - listener.polygonStart(); - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - listener.polygonEnd(); - } - }; - function point(λ, φ) { - var point = rotate(λ, φ); - if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); - } - function pointLine(λ, φ) { - var point = rotate(λ, φ); - line.point(point[0], point[1]); - } - function lineStart() { - clip.point = pointLine; - line.lineStart(); - } - function lineEnd() { - clip.point = point; - line.lineEnd(); - } - var segments; - var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring; - function pointRing(λ, φ) { - ring.push([ λ, φ ]); - var point = rotate(λ, φ); - ringListener.point(point[0], point[1]); - } - function ringStart() { - ringListener.lineStart(); - ring = []; - } - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringListener.lineEnd(); - var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; - ring.pop(); - polygon.push(ring); - ring = null; - if (!n) return; - if (clean & 1) { - segment = ringSegments[0]; - var n = segment.length - 1, i = -1, point; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); - return; - } - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); - } - return clip; - }; - } - function d3_geo_clipSegmentLength1(segment) { - return segment.length > 1; - } - function d3_geo_clipBufferListener() { - var lines = [], line; - return { - lineStart: function() { - lines.push(line = []); - }, - point: function(λ, φ) { - line.push([ λ, φ ]); - }, - lineEnd: d3_noop, - buffer: function() { - var buffer = lines; - lines = []; - line = null; - return buffer; - }, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - } - }; - } - function d3_geo_clipSort(a, b) { - return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); - } - function d3_geo_pointInPolygon(point, polygon) { - var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; - d3_geo_areaRingSum.reset(); - for (var i = 0, n = polygon.length; i < n; ++i) { - var ring = polygon[i], m = ring.length; - if (!m) continue; - var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; - while (true) { - if (j === m) j = 0; - point = ring[j]; - var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); - polarAngle += antimeridian ? dλ + sdλ * τ : dλ; - if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { - var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); - d3_geo_cartesianNormalize(arc); - var intersection = d3_geo_cartesianCross(meridianNormal, arc); - d3_geo_cartesianNormalize(intersection); - var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); - if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { - winding += antimeridian ^ dλ >= 0 ? 1 : -1; - } - } - if (!j++) break; - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; - } - } - return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; - } - var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); - function d3_geo_clipAntimeridianLine(listener) { - var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; - return { - lineStart: function() { - listener.lineStart(); - clean = 1; - }, - point: function(λ1, φ1) { - var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); - if (abs(dλ - π) < ε) { - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - listener.point(λ1, φ0); - clean = 0; - } else if (sλ0 !== sλ1 && dλ >= π) { - if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - clean = 0; - } - listener.point(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - }, - lineEnd: function() { - listener.lineEnd(); - λ0 = φ0 = NaN; - }, - clean: function() { - return 2 - clean; - } - }; - } - function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { - var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); - return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; - } - function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { - var φ; - if (from == null) { - φ = direction * halfπ; - listener.point(-π, φ); - listener.point(0, φ); - listener.point(π, φ); - listener.point(π, 0); - listener.point(π, -φ); - listener.point(0, -φ); - listener.point(-π, -φ); - listener.point(-π, 0); - listener.point(-π, φ); - } else if (abs(from[0] - to[0]) > ε) { - var s = from[0] < to[0] ? π : -π; - φ = direction * s / 2; - listener.point(-s, φ); - listener.point(0, φ); - listener.point(s, φ); - } else { - listener.point(to[0], to[1]); - } - } - function d3_geo_clipCircle(radius) { - var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); - return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); - function visible(λ, φ) { - return Math.cos(λ) * Math.cos(φ) > cr; - } - function clipLine(listener) { - var point0, c0, v0, v00, clean; - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(λ, φ) { - var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; - if (!point0 && (v00 = v0 = v)) listener.lineStart(); - if (v !== v0) { - point2 = intersect(point0, point1); - if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { - point1[0] += ε; - point1[1] += ε; - v = visible(point1[0], point1[1]); - } - } - if (v !== v0) { - clean = 0; - if (v) { - listener.lineStart(); - point2 = intersect(point1, point0); - listener.point(point2[0], point2[1]); - } else { - point2 = intersect(point0, point1); - listener.point(point2[0], point2[1]); - listener.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - } else { - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - } - } - } - if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { - listener.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) listener.lineEnd(); - point0 = null; - }, - clean: function() { - return clean | (v00 && v0) << 1; - } - }; - } - function intersect(a, b, two) { - var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); - var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; - if (!determinant) return !two && a; - var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); - d3_geo_cartesianAdd(A, B); - var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); - if (t2 < 0) return; - var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); - d3_geo_cartesianAdd(q, A); - q = d3_geo_spherical(q); - if (!two) return q; - var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; - if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; - var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; - if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; - if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { - var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); - d3_geo_cartesianAdd(q1, A); - return [ q, d3_geo_spherical(q1) ]; - } - } - function code(λ, φ) { - var r = smallRadius ? radius : π - radius, code = 0; - if (λ < -r) code |= 1; else if (λ > r) code |= 2; - if (φ < -r) code |= 4; else if (φ > r) code |= 8; - return code; - } - } - function d3_geom_clipLine(x0, y0, x1, y1) { - return function(line) { - var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - if (t0 > 0) line.a = { - x: ax + t0 * dx, - y: ay + t0 * dy - }; - if (t1 < 1) line.b = { - x: ax + t1 * dx, - y: ay + t1 * dy - }; - return line; - }; - } - var d3_geo_clipExtentMAX = 1e9; - d3.geo.clipExtent = function() { - var x0, y0, x1, y1, stream, clip, clipExtent = { - stream: function(output) { - if (stream) stream.valid = false; - stream = clip(output); - stream.valid = true; - return stream; - }, - extent: function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); - if (stream) stream.valid = false, stream = null; - return clipExtent; - } - }; - return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); - }; - function d3_geo_clipExtent(x0, y0, x1, y1) { - return function(listener) { - var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - listener = bufferListener; - segments = []; - polygon = []; - clean = true; - }, - polygonEnd: function() { - listener = listener_; - segments = d3.merge(segments); - var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; - if (inside || visible) { - listener.polygonStart(); - if (inside) { - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - if (visible) { - d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); - } - listener.polygonEnd(); - } - segments = polygon = ring = null; - } - }; - function insidePolygon(p) { - var wn = 0, n = polygon.length, y = p[1]; - for (var i = 0; i < n; ++i) { - for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { - b = v[j]; - if (a[1] <= y) { - if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; - } else { - if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; - } - a = b; - } - } - return wn !== 0; - } - function interpolate(from, to, direction, listener) { - var a = 0, a1 = 0; - if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { - do { - listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - } while ((a = (a + direction + 4) % 4) !== a1); - } else { - listener.point(to[0], to[1]); - } - } - function pointVisible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - function point(x, y) { - if (pointVisible(x, y)) listener.point(x, y); - } - var x__, y__, v__, x_, y_, v_, first, clean; - function lineStart() { - clip.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - function lineEnd() { - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferListener.rejoin(); - segments.push(bufferListener.buffer()); - } - clip.point = point; - if (v_) listener.lineEnd(); - } - function linePoint(x, y) { - x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); - y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); - var v = pointVisible(x, y); - if (polygon) ring.push([ x, y ]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - listener.lineStart(); - listener.point(x, y); - } - } else { - if (v && v_) listener.point(x, y); else { - var l = { - a: { - x: x_, - y: y_ - }, - b: { - x: x, - y: y - } - }; - if (clipLine(l)) { - if (!v_) { - listener.lineStart(); - listener.point(l.a.x, l.a.y); - } - listener.point(l.b.x, l.b.y); - if (!v) listener.lineEnd(); - clean = false; - } else if (v) { - listener.lineStart(); - listener.point(x, y); - clean = false; - } - } - } - x_ = x, y_ = y, v_ = v; - } - return clip; - }; - function corner(p, direction) { - return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; - } - function compare(a, b) { - return comparePoints(a.x, b.x); - } - function comparePoints(a, b) { - var ca = corner(a, 1), cb = corner(b, 1); - return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; - } - } - function d3_geo_compose(a, b) { - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - return compose; - } - function d3_geo_conic(projectAt) { - var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); - p.parallels = function(_) { - if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; - return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); - }; - return p; - } - function d3_geo_conicEqualArea(φ0, φ1) { - var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; - function forward(λ, φ) { - var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; - return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = ρ0 - y; - return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; - }; - return forward; - } - (d3.geo.conicEqualArea = function() { - return d3_geo_conic(d3_geo_conicEqualArea); - }).raw = d3_geo_conicEqualArea; - d3.geo.albers = function() { - return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); - }; - d3.geo.albersUsa = function() { - var lower48 = d3.geo.albers(); - var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); - var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); - var point, pointStream = { - point: function(x, y) { - point = [ x, y ]; - } - }, lower48Point, alaskaPoint, hawaiiPoint; - function albersUsa(coordinates) { - var x = coordinates[0], y = coordinates[1]; - point = null; - (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); - return point; - } - albersUsa.invert = function(coordinates) { - var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; - return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); - }; - albersUsa.stream = function(stream) { - var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); - return { - point: function(x, y) { - lower48Stream.point(x, y); - alaskaStream.point(x, y); - hawaiiStream.point(x, y); - }, - sphere: function() { - lower48Stream.sphere(); - alaskaStream.sphere(); - hawaiiStream.sphere(); - }, - lineStart: function() { - lower48Stream.lineStart(); - alaskaStream.lineStart(); - hawaiiStream.lineStart(); - }, - lineEnd: function() { - lower48Stream.lineEnd(); - alaskaStream.lineEnd(); - hawaiiStream.lineEnd(); - }, - polygonStart: function() { - lower48Stream.polygonStart(); - alaskaStream.polygonStart(); - hawaiiStream.polygonStart(); - }, - polygonEnd: function() { - lower48Stream.polygonEnd(); - alaskaStream.polygonEnd(); - hawaiiStream.polygonEnd(); - } - }; - }; - albersUsa.precision = function(_) { - if (!arguments.length) return lower48.precision(); - lower48.precision(_); - alaska.precision(_); - hawaii.precision(_); - return albersUsa; - }; - albersUsa.scale = function(_) { - if (!arguments.length) return lower48.scale(); - lower48.scale(_); - alaska.scale(_ * .35); - hawaii.scale(_); - return albersUsa.translate(lower48.translate()); - }; - albersUsa.translate = function(_) { - if (!arguments.length) return lower48.translate(); - var k = lower48.scale(), x = +_[0], y = +_[1]; - lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; - alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - return albersUsa; - }; - return albersUsa.scale(1070); - }; - var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_pathAreaPolygon = 0; - d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; - }, - polygonEnd: function() { - d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; - d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); - } - }; - function d3_geo_pathAreaRingStart() { - var x00, y00, x0, y0; - d3_geo_pathArea.point = function(x, y) { - d3_geo_pathArea.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; - }; - function nextPoint(x, y) { - d3_geo_pathAreaPolygon += y0 * x - x0 * y; - x0 = x, y0 = y; - } - d3_geo_pathArea.lineEnd = function() { - nextPoint(x00, y00); - }; - } - var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; - var d3_geo_pathBounds = { - point: d3_geo_pathBoundsPoint, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_pathBoundsPoint(x, y) { - if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; - if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; - if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; - if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; - } - function d3_geo_pathBuffer() { - var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointCircle = d3_geo_pathBufferCircle(_); - return stream; - }, - result: function() { - if (buffer.length) { - var result = buffer.join(""); - buffer = []; - return result; - } - } - }; - function point(x, y) { - buffer.push("M", x, ",", y, pointCircle); - } - function pointLineStart(x, y) { - buffer.push("M", x, ",", y); - stream.point = pointLine; - } - function pointLine(x, y) { - buffer.push("L", x, ",", y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - buffer.push("Z"); - } - return stream; - } - function d3_geo_pathBufferCircle(radius) { - return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; - } - var d3_geo_pathCentroid = { - point: d3_geo_pathCentroidPoint, - lineStart: d3_geo_pathCentroidLineStart, - lineEnd: d3_geo_pathCentroidLineEnd, - polygonStart: function() { - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; - }, - polygonEnd: function() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; - d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; - } - }; - function d3_geo_pathCentroidPoint(x, y) { - d3_geo_centroidX0 += x; - d3_geo_centroidY0 += y; - ++d3_geo_centroidZ0; - } - function d3_geo_pathCentroidLineStart() { - var x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - } - function d3_geo_pathCentroidLineEnd() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - } - function d3_geo_pathCentroidRingStart() { - var x00, y00, x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - z = y0 * x - x0 * y; - d3_geo_centroidX2 += z * (x0 + x); - d3_geo_centroidY2 += z * (y0 + y); - d3_geo_centroidZ2 += z * 3; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - d3_geo_pathCentroid.lineEnd = function() { - nextPoint(x00, y00); - }; - } - function d3_geo_pathContext(context) { - var pointRadius = 4.5; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointRadius = _; - return stream; - }, - result: d3_noop - }; - function point(x, y) { - context.moveTo(x, y); - context.arc(x, y, pointRadius, 0, τ); - } - function pointLineStart(x, y) { - context.moveTo(x, y); - stream.point = pointLine; - } - function pointLine(x, y) { - context.lineTo(x, y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - context.closePath(); - } - return stream; - } - function d3_geo_resample(project) { - var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; - function resample(stream) { - return (maxDepth ? resampleRecursive : resampleNone)(stream); - } - function resampleNone(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - }); - } - function resampleRecursive(stream) { - var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; - var resample = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - stream.polygonStart(); - resample.lineStart = ringStart; - }, - polygonEnd: function() { - stream.polygonEnd(); - resample.lineStart = lineStart; - } - }; - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - function lineStart() { - x0 = NaN; - resample.point = linePoint; - stream.lineStart(); - } - function linePoint(λ, φ) { - var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); - resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - function lineEnd() { - resample.point = point; - stream.lineEnd(); - } - function ringStart() { - lineStart(); - resample.point = ringPoint; - resample.lineEnd = ringEnd; - } - function ringPoint(λ, φ) { - linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resample.point = linePoint; - } - function ringEnd() { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); - resample.lineEnd = lineEnd; - lineEnd(); - } - return resample; - } - function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; - if (d2 > 4 * δ2 && depth--) { - var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); - } - } - } - resample.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return resample; - }; - return resample; - } - d3.geo.path = function() { - var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); - d3.geo.stream(object, cacheStream); - } - return contextStream.result(); - } - path.area = function(object) { - d3_geo_pathAreaSum = 0; - d3.geo.stream(object, projectStream(d3_geo_pathArea)); - return d3_geo_pathAreaSum; - }; - path.centroid = function(object) { - d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); - return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; - }; - path.bounds = function(object) { - d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); - d3.geo.stream(object, projectStream(d3_geo_pathBounds)); - return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; - }; - path.projection = function(_) { - if (!arguments.length) return projection; - projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; - return reset(); - }; - path.context = function(_) { - if (!arguments.length) return context; - contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return reset(); - }; - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - function reset() { - cacheStream = null; - return path; - } - return path.projection(d3.geo.albersUsa()).context(null); - }; - function d3_geo_pathProjectStream(project) { - var resample = d3_geo_resample(function(x, y) { - return project([ x * d3_degrees, y * d3_degrees ]); - }); - return function(stream) { - return d3_geo_projectionRadians(resample(stream)); - }; - } - d3.geo.transform = function(methods) { - return { - stream: function(stream) { - var transform = new d3_geo_transform(stream); - for (var k in methods) transform[k] = methods[k]; - return transform; - } - }; - }; - function d3_geo_transform(stream) { - this.stream = stream; - } - d3_geo_transform.prototype = { - point: function(x, y) { - this.stream.point(x, y); - }, - sphere: function() { - this.stream.sphere(); - }, - lineStart: function() { - this.stream.lineStart(); - }, - lineEnd: function() { - this.stream.lineEnd(); - }, - polygonStart: function() { - this.stream.polygonStart(); - }, - polygonEnd: function() { - this.stream.polygonEnd(); - } - }; - function d3_geo_transformPoint(stream, point) { - return { - point: point, - sphere: function() { - stream.sphere(); - }, - lineStart: function() { - stream.lineStart(); - }, - lineEnd: function() { - stream.lineEnd(); - }, - polygonStart: function() { - stream.polygonStart(); - }, - polygonEnd: function() { - stream.polygonEnd(); - } - }; - } - d3.geo.projection = d3_geo_projection; - d3.geo.projectionMutator = d3_geo_projectionMutator; - function d3_geo_projection(project) { - return d3_geo_projectionMutator(function() { - return project; - })(); - } - function d3_geo_projectionMutator(projectAt) { - var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { - x = project(x, y); - return [ x[0] * k + δx, δy - x[1] * k ]; - }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; - function projection(point) { - point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); - return [ point[0] * k + δx, δy - point[1] * k ]; - } - function invert(point) { - point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); - return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; - } - projection.stream = function(output) { - if (stream) stream.valid = false; - stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); - stream.valid = true; - return stream; - }; - projection.clipAngle = function(_) { - if (!arguments.length) return clipAngle; - preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); - return invalidate(); - }; - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; - return invalidate(); - }; - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return reset(); - }; - projection.translate = function(_) { - if (!arguments.length) return [ x, y ]; - x = +_[0]; - y = +_[1]; - return reset(); - }; - projection.center = function(_) { - if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; - λ = _[0] % 360 * d3_radians; - φ = _[1] % 360 * d3_radians; - return reset(); - }; - projection.rotate = function(_) { - if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; - δλ = _[0] % 360 * d3_radians; - δφ = _[1] % 360 * d3_radians; - δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; - return reset(); - }; - d3.rebind(projection, projectResample, "precision"); - function reset() { - projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); - var center = project(λ, φ); - δx = x - center[0] * k; - δy = y + center[1] * k; - return invalidate(); - } - function invalidate() { - if (stream) stream.valid = false, stream = null; - return projection; - } - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return reset(); - }; - } - function d3_geo_projectionRadians(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - stream.point(x * d3_radians, y * d3_radians); - }); - } - function d3_geo_equirectangular(λ, φ) { - return [ λ, φ ]; - } - (d3.geo.equirectangular = function() { - return d3_geo_projection(d3_geo_equirectangular); - }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; - d3.geo.rotation = function(rotate) { - rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); - function forward(coordinates) { - coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - } - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - }; - return forward; - }; - function d3_geo_identityRotation(λ, φ) { - return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - } - d3_geo_identityRotation.invert = d3_geo_equirectangular; - function d3_geo_rotation(δλ, δφ, δγ) { - return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; - } - function d3_geo_forwardRotationλ(δλ) { - return function(λ, φ) { - return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - }; - } - function d3_geo_rotationλ(δλ) { - var rotation = d3_geo_forwardRotationλ(δλ); - rotation.invert = d3_geo_forwardRotationλ(-δλ); - return rotation; - } - function d3_geo_rotationφγ(δφ, δγ) { - var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); - function rotation(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; - return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; - } - rotation.invert = function(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; - return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; - }; - return rotation; - } - d3.geo.circle = function() { - var origin = [ 0, 0 ], angle, precision = 6, interpolate; - function circle() { - var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; - interpolate(null, null, 1, { - point: function(x, y) { - ring.push(x = rotate(x, y)); - x[0] *= d3_degrees, x[1] *= d3_degrees; - } - }); - return { - type: "Polygon", - coordinates: [ ring ] - }; - } - circle.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return circle; - }; - circle.angle = function(x) { - if (!arguments.length) return angle; - interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); - return circle; - }; - circle.precision = function(_) { - if (!arguments.length) return precision; - interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); - return circle; - }; - return circle.angle(90); - }; - function d3_geo_circleInterpolate(radius, precision) { - var cr = Math.cos(radius), sr = Math.sin(radius); - return function(from, to, direction, listener) { - var step = direction * precision; - if (from != null) { - from = d3_geo_circleAngle(cr, from); - to = d3_geo_circleAngle(cr, to); - if (direction > 0 ? from < to : from > to) from += direction * τ; - } else { - from = radius + direction * τ; - to = radius - .5 * step; - } - for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { - listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); - } - }; - } - function d3_geo_circleAngle(cr, point) { - var a = d3_geo_cartesian(point); - a[0] -= cr; - d3_geo_cartesianNormalize(a); - var angle = d3_acos(-a[1]); - return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); - } - d3.geo.distance = function(a, b) { - var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; - return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); - }; - d3.geo.graticule = function() { - var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; - function graticule() { - return { - type: "MultiLineString", - coordinates: lines() - }; - } - function lines() { - return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { - return abs(x % DX) > ε; - }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { - return abs(y % DY) > ε; - }).map(y)); - } - graticule.lines = function() { - return lines().map(function(coordinates) { - return { - type: "LineString", - coordinates: coordinates - }; - }); - }; - graticule.outline = function() { - return { - type: "Polygon", - coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] - }; - }; - graticule.extent = function(_) { - if (!arguments.length) return graticule.minorExtent(); - return graticule.majorExtent(_).minorExtent(_); - }; - graticule.majorExtent = function(_) { - if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; - X0 = +_[0][0], X1 = +_[1][0]; - Y0 = +_[0][1], Y1 = +_[1][1]; - if (X0 > X1) _ = X0, X0 = X1, X1 = _; - if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; - return graticule.precision(precision); - }; - graticule.minorExtent = function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - x0 = +_[0][0], x1 = +_[1][0]; - y0 = +_[0][1], y1 = +_[1][1]; - if (x0 > x1) _ = x0, x0 = x1, x1 = _; - if (y0 > y1) _ = y0, y0 = y1, y1 = _; - return graticule.precision(precision); - }; - graticule.step = function(_) { - if (!arguments.length) return graticule.minorStep(); - return graticule.majorStep(_).minorStep(_); - }; - graticule.majorStep = function(_) { - if (!arguments.length) return [ DX, DY ]; - DX = +_[0], DY = +_[1]; - return graticule; - }; - graticule.minorStep = function(_) { - if (!arguments.length) return [ dx, dy ]; - dx = +_[0], dy = +_[1]; - return graticule; - }; - graticule.precision = function(_) { - if (!arguments.length) return precision; - precision = +_; - x = d3_geo_graticuleX(y0, y1, 90); - y = d3_geo_graticuleY(x0, x1, precision); - X = d3_geo_graticuleX(Y0, Y1, 90); - Y = d3_geo_graticuleY(X0, X1, precision); - return graticule; - }; - return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); - }; - function d3_geo_graticuleX(y0, y1, dy) { - var y = d3.range(y0, y1 - ε, dy).concat(y1); - return function(x) { - return y.map(function(y) { - return [ x, y ]; - }); - }; - } - function d3_geo_graticuleY(x0, x1, dx) { - var x = d3.range(x0, x1 - ε, dx).concat(x1); - return function(y) { - return x.map(function(x) { - return [ x, y ]; - }); - }; - } - function d3_source(d) { - return d.source; - } - function d3_target(d) { - return d.target; - } - d3.geo.greatArc = function() { - var source = d3_source, source_, target = d3_target, target_; - function greatArc() { - return { - type: "LineString", - coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] - }; - } - greatArc.distance = function() { - return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); - }; - greatArc.source = function(_) { - if (!arguments.length) return source; - source = _, source_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.target = function(_) { - if (!arguments.length) return target; - target = _, target_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.precision = function() { - return arguments.length ? greatArc : 0; - }; - return greatArc; - }; - d3.geo.interpolate = function(source, target) { - return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); - }; - function d3_geo_interpolate(x0, y0, x1, y1) { - var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); - var interpolate = d ? function(t) { - var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; - return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; - } : function() { - return [ x0 * d3_degrees, y0 * d3_degrees ]; - }; - interpolate.distance = d; - return interpolate; - } - d3.geo.length = function(object) { - d3_geo_lengthSum = 0; - d3.geo.stream(object, d3_geo_length); - return d3_geo_lengthSum; - }; - var d3_geo_lengthSum; - var d3_geo_length = { - sphere: d3_noop, - point: d3_noop, - lineStart: d3_geo_lengthLineStart, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_lengthLineStart() { - var λ0, sinφ0, cosφ0; - d3_geo_length.point = function(λ, φ) { - λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); - d3_geo_length.point = nextPoint; - }; - d3_geo_length.lineEnd = function() { - d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; - }; - function nextPoint(λ, φ) { - var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); - d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; - } - } - function d3_geo_azimuthal(scale, angle) { - function azimuthal(λ, φ) { - var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); - return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; - } - azimuthal.invert = function(x, y) { - var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); - return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; - }; - return azimuthal; - } - var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { - return Math.sqrt(2 / (1 + cosλcosφ)); - }, function(ρ) { - return 2 * Math.asin(ρ / 2); - }); - (d3.geo.azimuthalEqualArea = function() { - return d3_geo_projection(d3_geo_azimuthalEqualArea); - }).raw = d3_geo_azimuthalEqualArea; - var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { - var c = Math.acos(cosλcosφ); - return c && c / Math.sin(c); - }, d3_identity); - (d3.geo.azimuthalEquidistant = function() { - return d3_geo_projection(d3_geo_azimuthalEquidistant); - }).raw = d3_geo_azimuthalEquidistant; - function d3_geo_conicConformal(φ0, φ1) { - var cosφ0 = Math.cos(φ0), t = function(φ) { - return Math.tan(π / 4 + φ / 2); - }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; - if (!n) return d3_geo_mercator; - function forward(λ, φ) { - if (F > 0) { - if (φ < -halfπ + ε) φ = -halfπ + ε; - } else { - if (φ > halfπ - ε) φ = halfπ - ε; - } - var ρ = F / Math.pow(t(φ), n); - return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); - return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; - }; - return forward; - } - (d3.geo.conicConformal = function() { - return d3_geo_conic(d3_geo_conicConformal); - }).raw = d3_geo_conicConformal; - function d3_geo_conicEquidistant(φ0, φ1) { - var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; - if (abs(n) < ε) return d3_geo_equirectangular; - function forward(λ, φ) { - var ρ = G - φ; - return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = G - y; - return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; - }; - return forward; - } - (d3.geo.conicEquidistant = function() { - return d3_geo_conic(d3_geo_conicEquidistant); - }).raw = d3_geo_conicEquidistant; - var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / cosλcosφ; - }, Math.atan); - (d3.geo.gnomonic = function() { - return d3_geo_projection(d3_geo_gnomonic); - }).raw = d3_geo_gnomonic; - function d3_geo_mercator(λ, φ) { - return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; - } - d3_geo_mercator.invert = function(x, y) { - return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; - }; - function d3_geo_mercatorProjection(project) { - var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; - m.scale = function() { - var v = scale.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.translate = function() { - var v = translate.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.clipExtent = function(_) { - var v = clipExtent.apply(m, arguments); - if (v === m) { - if (clipAuto = _ == null) { - var k = π * scale(), t = translate(); - clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); - } - } else if (clipAuto) { - v = null; - } - return v; - }; - return m.clipExtent(null); - } - (d3.geo.mercator = function() { - return d3_geo_mercatorProjection(d3_geo_mercator); - }).raw = d3_geo_mercator; - var d3_geo_orthographic = d3_geo_azimuthal(function() { - return 1; - }, Math.asin); - (d3.geo.orthographic = function() { - return d3_geo_projection(d3_geo_orthographic); - }).raw = d3_geo_orthographic; - var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / (1 + cosλcosφ); - }, function(ρ) { - return 2 * Math.atan(ρ); - }); - (d3.geo.stereographic = function() { - return d3_geo_projection(d3_geo_stereographic); - }).raw = d3_geo_stereographic; - function d3_geo_transverseMercator(λ, φ) { - return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; - } - d3_geo_transverseMercator.invert = function(x, y) { - return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; - }; - (d3.geo.transverseMercator = function() { - var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; - projection.center = function(_) { - return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]); - }; - projection.rotate = function(_) { - return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), - [ _[0], _[1], _[2] - 90 ]); - }; - return projection.rotate([ 0, 0 ]); - }).raw = d3_geo_transverseMercator; - d3.geom = {}; - function d3_geom_pointX(d) { - return d[0]; - } - function d3_geom_pointY(d) { - return d[1]; - } - d3.geom.hull = function(vertices) { - var x = d3_geom_pointX, y = d3_geom_pointY; - if (arguments.length) return hull(vertices); - function hull(data) { - if (data.length < 3) return []; - var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; - for (i = 0; i < n; i++) { - points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); - } - points.sort(d3_geom_hullOrder); - for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); - var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); - var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; - for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); - for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); - return polygon; - } - hull.x = function(_) { - return arguments.length ? (x = _, hull) : x; - }; - hull.y = function(_) { - return arguments.length ? (y = _, hull) : y; - }; - return hull; - }; - function d3_geom_hullUpper(points) { - var n = points.length, hull = [ 0, 1 ], hs = 2; - for (var i = 2; i < n; i++) { - while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; - hull[hs++] = i; - } - return hull.slice(0, hs); - } - function d3_geom_hullOrder(a, b) { - return a[0] - b[0] || a[1] - b[1]; - } - d3.geom.polygon = function(coordinates) { - d3_subclass(coordinates, d3_geom_polygonPrototype); - return coordinates; - }; - var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; - d3_geom_polygonPrototype.area = function() { - var i = -1, n = this.length, a, b = this[n - 1], area = 0; - while (++i < n) { - a = b; - b = this[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - return area * .5; - }; - d3_geom_polygonPrototype.centroid = function(k) { - var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; - if (!arguments.length) k = -1 / (6 * this.area()); - while (++i < n) { - a = b; - b = this[i]; - c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - return [ x * k, y * k ]; - }; - d3_geom_polygonPrototype.clip = function(subject) { - var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; - while (++i < n) { - input = subject.slice(); - subject.length = 0; - b = this[i]; - c = input[(m = input.length - closed) - 1]; - j = -1; - while (++j < m) { - d = input[j]; - if (d3_geom_polygonInside(d, a, b)) { - if (!d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - subject.push(d); - } else if (d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - c = d; - } - if (closed) subject.push(subject[0]); - a = b; - } - return subject; - }; - function d3_geom_polygonInside(p, a, b) { - return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); - } - function d3_geom_polygonIntersect(c, d, a, b) { - var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); - return [ x1 + ua * x21, y1 + ua * y21 ]; - } - function d3_geom_polygonClosed(coordinates) { - var a = coordinates[0], b = coordinates[coordinates.length - 1]; - return !(a[0] - b[0] || a[1] - b[1]); - } - var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; - function d3_geom_voronoiBeach() { - d3_geom_voronoiRedBlackNode(this); - this.edge = this.site = this.circle = null; - } - function d3_geom_voronoiCreateBeach(site) { - var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); - beach.site = site; - return beach; - } - function d3_geom_voronoiDetachBeach(beach) { - d3_geom_voronoiDetachCircle(beach); - d3_geom_voronoiBeaches.remove(beach); - d3_geom_voronoiBeachPool.push(beach); - d3_geom_voronoiRedBlackNode(beach); - } - function d3_geom_voronoiRemoveBeach(beach) { - var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { - x: x, - y: y - }, previous = beach.P, next = beach.N, disappearing = [ beach ]; - d3_geom_voronoiDetachBeach(beach); - var lArc = previous; - while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { - previous = lArc.P; - disappearing.unshift(lArc); - d3_geom_voronoiDetachBeach(lArc); - lArc = previous; - } - disappearing.unshift(lArc); - d3_geom_voronoiDetachCircle(lArc); - var rArc = next; - while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { - next = rArc.N; - disappearing.push(rArc); - d3_geom_voronoiDetachBeach(rArc); - rArc = next; - } - disappearing.push(rArc); - d3_geom_voronoiDetachCircle(rArc); - var nArcs = disappearing.length, iArc; - for (iArc = 1; iArc < nArcs; ++iArc) { - rArc = disappearing[iArc]; - lArc = disappearing[iArc - 1]; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); - } - lArc = disappearing[0]; - rArc = disappearing[nArcs - 1]; - rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiAddBeach(site) { - var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; - while (node) { - dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; - if (dxl > ε) node = node.L; else { - dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); - if (dxr > ε) { - if (!node.R) { - lArc = node; - break; - } - node = node.R; - } else { - if (dxl > -ε) { - lArc = node.P; - rArc = node; - } else if (dxr > -ε) { - lArc = node; - rArc = node.N; - } else { - lArc = rArc = node; - } - break; - } - } - } - var newArc = d3_geom_voronoiCreateBeach(site); - d3_geom_voronoiBeaches.insert(lArc, newArc); - if (!lArc && !rArc) return; - if (lArc === rArc) { - d3_geom_voronoiDetachCircle(lArc); - rArc = d3_geom_voronoiCreateBeach(lArc.site); - d3_geom_voronoiBeaches.insert(newArc, rArc); - newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - return; - } - if (!rArc) { - newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - return; - } - d3_geom_voronoiDetachCircle(lArc); - d3_geom_voronoiDetachCircle(rArc); - var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { - x: (cy * hb - by * hc) / d + ax, - y: (bx * hc - cx * hb) / d + ay - }; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); - newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); - rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiLeftBreakPoint(arc, directrix) { - var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; - if (!pby2) return rfocx; - var lArc = arc.P; - if (!lArc) return -Infinity; - site = lArc.site; - var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; - if (!plby2) return lfocx; - var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; - if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; - return (rfocx + lfocx) / 2; - } - function d3_geom_voronoiRightBreakPoint(arc, directrix) { - var rArc = arc.N; - if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); - var site = arc.site; - return site.y === directrix ? site.x : Infinity; - } - function d3_geom_voronoiCell(site) { - this.site = site; - this.edges = []; - } - d3_geom_voronoiCell.prototype.prepare = function() { - var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; - while (iHalfEdge--) { - edge = halfEdges[iHalfEdge].edge; - if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); - } - halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); - return halfEdges.length; - }; - function d3_geom_voronoiCloseCells(extent) { - var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; - while (iCell--) { - cell = cells[iCell]; - if (!cell || !cell.prepare()) continue; - halfEdges = cell.edges; - nHalfEdges = halfEdges.length; - iHalfEdge = 0; - while (iHalfEdge < nHalfEdges) { - end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; - start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; - if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { - halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { - x: x0, - y: abs(x2 - x0) < ε ? y2 : y1 - } : abs(y3 - y1) < ε && x1 - x3 > ε ? { - x: abs(y2 - y1) < ε ? x2 : x1, - y: y1 - } : abs(x3 - x1) < ε && y3 - y0 > ε ? { - x: x1, - y: abs(x2 - x1) < ε ? y2 : y0 - } : abs(y3 - y0) < ε && x3 - x0 > ε ? { - x: abs(y2 - y0) < ε ? x2 : x0, - y: y0 - } : null), cell.site, null)); - ++nHalfEdges; - } - } - } - } - function d3_geom_voronoiHalfEdgeOrder(a, b) { - return b.angle - a.angle; - } - function d3_geom_voronoiCircle() { - d3_geom_voronoiRedBlackNode(this); - this.x = this.y = this.arc = this.site = this.cy = null; - } - function d3_geom_voronoiAttachCircle(arc) { - var lArc = arc.P, rArc = arc.N; - if (!lArc || !rArc) return; - var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; - if (lSite === rSite) return; - var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; - var d = 2 * (ax * cy - ay * cx); - if (d >= -ε2) return; - var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; - var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); - circle.arc = arc; - circle.site = cSite; - circle.x = x + bx; - circle.y = cy + Math.sqrt(x * x + y * y); - circle.cy = cy; - arc.circle = circle; - var before = null, node = d3_geom_voronoiCircles._; - while (node) { - if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { - if (node.L) node = node.L; else { - before = node.P; - break; - } - } else { - if (node.R) node = node.R; else { - before = node; - break; - } - } - } - d3_geom_voronoiCircles.insert(before, circle); - if (!before) d3_geom_voronoiFirstCircle = circle; - } - function d3_geom_voronoiDetachCircle(arc) { - var circle = arc.circle; - if (circle) { - if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; - d3_geom_voronoiCircles.remove(circle); - d3_geom_voronoiCirclePool.push(circle); - d3_geom_voronoiRedBlackNode(circle); - arc.circle = null; - } - } - function d3_geom_voronoiClipEdges(extent) { - var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; - while (i--) { - e = edges[i]; - if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { - e.a = e.b = null; - edges.splice(i, 1); - } - } - } - function d3_geom_voronoiConnectEdge(edge, extent) { - var vb = edge.b; - if (vb) return true; - var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; - if (ry === ly) { - if (fx < x0 || fx >= x1) return; - if (lx > rx) { - if (!va) va = { - x: fx, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: fx, - y: y1 - }; - } else { - if (!va) va = { - x: fx, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: fx, - y: y0 - }; - } - } else { - fm = (lx - rx) / (ry - ly); - fb = fy - fm * fx; - if (fm < -1 || fm > 1) { - if (lx > rx) { - if (!va) va = { - x: (y0 - fb) / fm, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: (y1 - fb) / fm, - y: y1 - }; - } else { - if (!va) va = { - x: (y1 - fb) / fm, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: (y0 - fb) / fm, - y: y0 - }; - } - } else { - if (ly < ry) { - if (!va) va = { - x: x0, - y: fm * x0 + fb - }; else if (va.x >= x1) return; - vb = { - x: x1, - y: fm * x1 + fb - }; - } else { - if (!va) va = { - x: x1, - y: fm * x1 + fb - }; else if (va.x < x0) return; - vb = { - x: x0, - y: fm * x0 + fb - }; - } - } - } - edge.a = va; - edge.b = vb; - return true; - } - function d3_geom_voronoiEdge(lSite, rSite) { - this.l = lSite; - this.r = rSite; - this.a = this.b = null; - } - function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, rSite); - d3_geom_voronoiEdges.push(edge); - if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); - if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); - d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); - d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); - return edge; - } - function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, null); - edge.a = va; - edge.b = vb; - d3_geom_voronoiEdges.push(edge); - return edge; - } - function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { - if (!edge.a && !edge.b) { - edge.a = vertex; - edge.l = lSite; - edge.r = rSite; - } else if (edge.l === rSite) { - edge.b = vertex; - } else { - edge.a = vertex; - } - } - function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { - var va = edge.a, vb = edge.b; - this.edge = edge; - this.site = lSite; - this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); - } - d3_geom_voronoiHalfEdge.prototype = { - start: function() { - return this.edge.l === this.site ? this.edge.a : this.edge.b; - }, - end: function() { - return this.edge.l === this.site ? this.edge.b : this.edge.a; - } - }; - function d3_geom_voronoiRedBlackTree() { - this._ = null; - } - function d3_geom_voronoiRedBlackNode(node) { - node.U = node.C = node.L = node.R = node.P = node.N = null; - } - d3_geom_voronoiRedBlackTree.prototype = { - insert: function(after, node) { - var parent, grandpa, uncle; - if (after) { - node.P = after; - node.N = after.N; - if (after.N) after.N.P = node; - after.N = node; - if (after.R) { - after = after.R; - while (after.L) after = after.L; - after.L = node; - } else { - after.R = node; - } - parent = after; - } else if (this._) { - after = d3_geom_voronoiRedBlackFirst(this._); - node.P = null; - node.N = after; - after.P = after.L = node; - parent = after; - } else { - node.P = node.N = null; - this._ = node; - parent = null; - } - node.L = node.R = null; - node.U = parent; - node.C = true; - after = node; - while (parent && parent.C) { - grandpa = parent.U; - if (parent === grandpa.L) { - uncle = grandpa.R; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.R) { - d3_geom_voronoiRedBlackRotateLeft(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateRight(this, grandpa); - } - } else { - uncle = grandpa.L; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.L) { - d3_geom_voronoiRedBlackRotateRight(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, grandpa); - } - } - parent = after.U; - } - this._.C = false; - }, - remove: function(node) { - if (node.N) node.N.P = node.P; - if (node.P) node.P.N = node.N; - node.N = node.P = null; - var parent = node.U, sibling, left = node.L, right = node.R, next, red; - if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); - if (parent) { - if (parent.L === node) parent.L = next; else parent.R = next; - } else { - this._ = next; - } - if (left && right) { - red = next.C; - next.C = node.C; - next.L = left; - left.U = next; - if (next !== right) { - parent = next.U; - next.U = node.U; - node = next.R; - parent.L = node; - next.R = right; - right.U = next; - } else { - next.U = parent; - parent = next; - node = next.R; - } - } else { - red = node.C; - node = next; - } - if (node) node.U = parent; - if (red) return; - if (node && node.C) { - node.C = false; - return; - } - do { - if (node === this._) break; - if (node === parent.L) { - sibling = parent.R; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - sibling = parent.R; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.R || !sibling.R.C) { - sibling.L.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateRight(this, sibling); - sibling = parent.R; - } - sibling.C = parent.C; - parent.C = sibling.R.C = false; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - node = this._; - break; - } - } else { - sibling = parent.L; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateRight(this, parent); - sibling = parent.L; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.L || !sibling.L.C) { - sibling.R.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, sibling); - sibling = parent.L; - } - sibling.C = parent.C; - parent.C = sibling.L.C = false; - d3_geom_voronoiRedBlackRotateRight(this, parent); - node = this._; - break; - } - } - sibling.C = true; - node = parent; - parent = parent.U; - } while (!node.C); - if (node) node.C = false; - } - }; - function d3_geom_voronoiRedBlackRotateLeft(tree, node) { - var p = node, q = node.R, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.R = q.L; - if (p.R) p.R.U = p; - q.L = p; - } - function d3_geom_voronoiRedBlackRotateRight(tree, node) { - var p = node, q = node.L, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.L = q.R; - if (p.L) p.L.U = p; - q.R = p; - } - function d3_geom_voronoiRedBlackFirst(node) { - while (node.L) node = node.L; - return node; - } - function d3_geom_voronoi(sites, bbox) { - var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; - d3_geom_voronoiEdges = []; - d3_geom_voronoiCells = new Array(sites.length); - d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); - d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); - while (true) { - circle = d3_geom_voronoiFirstCircle; - if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { - if (site.x !== x0 || site.y !== y0) { - d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); - d3_geom_voronoiAddBeach(site); - x0 = site.x, y0 = site.y; - } - site = sites.pop(); - } else if (circle) { - d3_geom_voronoiRemoveBeach(circle.arc); - } else { - break; - } - } - if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); - var diagram = { - cells: d3_geom_voronoiCells, - edges: d3_geom_voronoiEdges - }; - d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; - return diagram; - } - function d3_geom_voronoiVertexOrder(a, b) { - return b.y - a.y || b.x - a.x; - } - d3.geom.voronoi = function(points) { - var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; - if (points) return voronoi(points); - function voronoi(data) { - var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; - d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { - var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { - var s = e.start(); - return [ s.x, s.y ]; - }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; - polygon.point = data[i]; - }); - return polygons; - } - function sites(data) { - return data.map(function(d, i) { - return { - x: Math.round(fx(d, i) / ε) * ε, - y: Math.round(fy(d, i) / ε) * ε, - i: i - }; - }); - } - voronoi.links = function(data) { - return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { - return edge.l && edge.r; - }).map(function(edge) { - return { - source: data[edge.l.i], - target: data[edge.r.i] - }; - }); - }; - voronoi.triangles = function(data) { - var triangles = []; - d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { - var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; - while (++j < m) { - e0 = e1; - s0 = s1; - e1 = edges[j].edge; - s1 = e1.l === site ? e1.r : e1.l; - if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { - triangles.push([ data[i], data[s0.i], data[s1.i] ]); - } - } - }); - return triangles; - }; - voronoi.x = function(_) { - return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; - }; - voronoi.y = function(_) { - return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; - }; - voronoi.clipExtent = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; - clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; - return voronoi; - }; - voronoi.size = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; - return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); - }; - return voronoi; - }; - var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; - function d3_geom_voronoiTriangleArea(a, b, c) { - return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); - } - d3.geom.delaunay = function(vertices) { - return d3.geom.voronoi().triangles(vertices); - }; - d3.geom.quadtree = function(points, x1, y1, x2, y2) { - var x = d3_geom_pointX, y = d3_geom_pointY, compat; - if (compat = arguments.length) { - x = d3_geom_quadtreeCompatX; - y = d3_geom_quadtreeCompatY; - if (compat === 3) { - y2 = y1; - x2 = x1; - y1 = x1 = 0; - } - return quadtree(points); - } - function quadtree(data) { - var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; - if (x1 != null) { - x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; - } else { - x2_ = y2_ = -(x1_ = y1_ = Infinity); - xs = [], ys = []; - n = data.length; - if (compat) for (i = 0; i < n; ++i) { - d = data[i]; - if (d.x < x1_) x1_ = d.x; - if (d.y < y1_) y1_ = d.y; - if (d.x > x2_) x2_ = d.x; - if (d.y > y2_) y2_ = d.y; - xs.push(d.x); - ys.push(d.y); - } else for (i = 0; i < n; ++i) { - var x_ = +fx(d = data[i], i), y_ = +fy(d, i); - if (x_ < x1_) x1_ = x_; - if (y_ < y1_) y1_ = y_; - if (x_ > x2_) x2_ = x_; - if (y_ > y2_) y2_ = y_; - xs.push(x_); - ys.push(y_); - } - } - var dx = x2_ - x1_, dy = y2_ - y1_; - if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; - function insert(n, d, x, y, x1, y1, x2, y2) { - if (isNaN(x) || isNaN(y)) return; - if (n.leaf) { - var nx = n.x, ny = n.y; - if (nx != null) { - if (abs(nx - x) + abs(ny - y) < .01) { - insertChild(n, d, x, y, x1, y1, x2, y2); - } else { - var nPoint = n.point; - n.x = n.y = n.point = null; - insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } else { - n.x = x, n.y = y, n.point = d; - } - } else { - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } - function insertChild(n, d, x, y, x1, y1, x2, y2) { - var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right; - n.leaf = false; - n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); - if (right) x1 = sx; else x2 = sx; - if (bottom) y1 = sy; else y2 = sy; - insert(n, d, x, y, x1, y1, x2, y2); - } - var root = d3_geom_quadtreeNode(); - root.add = function(d) { - insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); - }; - root.visit = function(f) { - d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); - }; - i = -1; - if (x1 == null) { - while (++i < n) { - insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); - } - --i; - } else data.forEach(root.add); - xs = ys = data = d = null; - return root; - } - quadtree.x = function(_) { - return arguments.length ? (x = _, quadtree) : x; - }; - quadtree.y = function(_) { - return arguments.length ? (y = _, quadtree) : y; - }; - quadtree.extent = function(_) { - if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], - y2 = +_[1][1]; - return quadtree; - }; - quadtree.size = function(_) { - if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; - return quadtree; - }; - return quadtree; - }; - function d3_geom_quadtreeCompatX(d) { - return d.x; - } - function d3_geom_quadtreeCompatY(d) { - return d.y; - } - function d3_geom_quadtreeNode() { - return { - leaf: true, - nodes: [], - point: null, - x: null, - y: null - }; - } - function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { - if (!f(node, x1, y1, x2, y2)) { - var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; - if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); - if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); - if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); - if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); - } - } - d3.interpolateRgb = d3_interpolateRgb; - function d3_interpolateRgb(a, b) { - a = d3.rgb(a); - b = d3.rgb(b); - var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; - return function(t) { - return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); - }; - } - d3.interpolateObject = d3_interpolateObject; - function d3_interpolateObject(a, b) { - var i = {}, c = {}, k; - for (k in a) { - if (k in b) { - i[k] = d3_interpolate(a[k], b[k]); - } else { - c[k] = a[k]; - } - } - for (k in b) { - if (!(k in a)) { - c[k] = b[k]; - } - } - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; - } - d3.interpolateNumber = d3_interpolateNumber; - function d3_interpolateNumber(a, b) { - b -= a = +a; - return function(t) { - return a + b * t; - }; - } - d3.interpolateString = d3_interpolateString; - function d3_interpolateString(a, b) { - var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o; - a = a + "", b = b + ""; - d3_interpolate_number.lastIndex = 0; - for (i = 0; m = d3_interpolate_number.exec(b); ++i) { - if (m.index) s.push(b.substring(s0, s1 = m.index)); - q.push({ - i: s.length, - x: m[0] - }); - s.push(null); - s0 = d3_interpolate_number.lastIndex; - } - if (s0 < b.length) s.push(b.substring(s0)); - for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { - o = q[i]; - if (o.x == m[0]) { - if (o.i) { - if (s[o.i + 1] == null) { - s[o.i - 1] += o.x; - s.splice(o.i, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } else { - s[o.i - 1] += o.x + s[o.i + 1]; - s.splice(o.i, 2); - for (j = i + 1; j < n; ++j) q[j].i -= 2; - } - } else { - if (s[o.i + 1] == null) { - s[o.i] = o.x; - } else { - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - for (j = i + 1; j < n; ++j) q[j].i--; - } - } - q.splice(i, 1); - n--; - i--; - } else { - o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); - } - } - while (i < n) { - o = q.pop(); - if (s[o.i + 1] == null) { - s[o.i] = o.x; - } else { - s[o.i] = o.x + s[o.i + 1]; - s.splice(o.i + 1, 1); - } - n--; - } - if (s.length === 1) { - return s[0] == null ? (o = q[0].x, function(t) { - return o(t) + ""; - }) : function() { - return b; - }; - } - return function(t) { - for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - } - var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; - d3.interpolate = d3_interpolate; - function d3_interpolate(a, b) { - var i = d3.interpolators.length, f; - while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; - return f; - } - d3.interpolators = [ function(a, b) { - var t = typeof b; - return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); - } ]; - d3.interpolateArray = d3_interpolateArray; - function d3_interpolateArray(a, b) { - var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; - for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); - for (;i < na; ++i) c[i] = a[i]; - for (;i < nb; ++i) c[i] = b[i]; - return function(t) { - for (i = 0; i < n0; ++i) c[i] = x[i](t); - return c; - }; - } - var d3_ease_default = function() { - return d3_identity; - }; - var d3_ease = d3.map({ - linear: d3_ease_default, - poly: d3_ease_poly, - quad: function() { - return d3_ease_quad; - }, - cubic: function() { - return d3_ease_cubic; - }, - sin: function() { - return d3_ease_sin; - }, - exp: function() { - return d3_ease_exp; - }, - circle: function() { - return d3_ease_circle; - }, - elastic: d3_ease_elastic, - back: d3_ease_back, - bounce: function() { - return d3_ease_bounce; - } - }); - var d3_ease_mode = d3.map({ - "in": d3_identity, - out: d3_ease_reverse, - "in-out": d3_ease_reflect, - "out-in": function(f) { - return d3_ease_reflect(d3_ease_reverse(f)); - } - }); - d3.ease = function(name) { - var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; - t = d3_ease.get(t) || d3_ease_default; - m = d3_ease_mode.get(m) || d3_identity; - return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); - }; - function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); - }; - } - function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); - }; - } - function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); - }; - } - function d3_ease_quad(t) { - return t * t; - } - function d3_ease_cubic(t) { - return t * t * t; - } - function d3_ease_cubicInOut(t) { - if (t <= 0) return 0; - if (t >= 1) return 1; - var t2 = t * t, t3 = t2 * t; - return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); - } - function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); - }; - } - function d3_ease_sin(t) { - return 1 - Math.cos(t * halfπ); - } - function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); - } - function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); - } - function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = .45; - if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; - return function(t) { - return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); - }; - } - function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); - }; - } - function d3_ease_bounce(t) { - return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; - } - d3.interpolateHcl = d3_interpolateHcl; - function d3_interpolateHcl(a, b) { - a = d3.hcl(a); - b = d3.hcl(b); - var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; - if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; - }; - } - d3.interpolateHsl = d3_interpolateHsl; - function d3_interpolateHsl(a, b) { - a = d3.hsl(a); - b = d3.hsl(b); - var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; - if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; - }; - } - d3.interpolateLab = d3_interpolateLab; - function d3_interpolateLab(a, b) { - a = d3.lab(a); - b = d3.lab(b); - var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; - return function(t) { - return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; - }; - } - d3.interpolateRound = d3_interpolateRound; - function d3_interpolateRound(a, b) { - b -= a; - return function(t) { - return Math.round(a + b * t); - }; - } - d3.transform = function(string) { - var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); - return (d3.transform = function(string) { - if (string != null) { - g.setAttribute("transform", string); - var t = g.transform.baseVal.consolidate(); - } - return new d3_transform(t ? t.matrix : d3_transformIdentity); - })(string); - }; - function d3_transform(m) { - var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; - if (r0[0] * r1[1] < r1[0] * r0[1]) { - r0[0] *= -1; - r0[1] *= -1; - kx *= -1; - kz *= -1; - } - this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; - this.translate = [ m.e, m.f ]; - this.scale = [ kx, ky ]; - this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; - } - d3_transform.prototype.toString = function() { - return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; - }; - function d3_transformDot(a, b) { - return a[0] * b[0] + a[1] * b[1]; - } - function d3_transformNormalize(a) { - var k = Math.sqrt(d3_transformDot(a, a)); - if (k) { - a[0] /= k; - a[1] /= k; - } - return k; - } - function d3_transformCombine(a, b, k) { - a[0] += k * b[0]; - a[1] += k * b[1]; - return a; - } - var d3_transformIdentity = { - a: 1, - b: 0, - c: 0, - d: 1, - e: 0, - f: 0 - }; - d3.interpolateTransform = d3_interpolateTransform; - function d3_interpolateTransform(a, b) { - var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; - if (ta[0] != tb[0] || ta[1] != tb[1]) { - s.push("translate(", null, ",", null, ")"); - q.push({ - i: 1, - x: d3_interpolateNumber(ta[0], tb[0]) - }, { - i: 3, - x: d3_interpolateNumber(ta[1], tb[1]) - }); - } else if (tb[0] || tb[1]) { - s.push("translate(" + tb + ")"); - } else { - s.push(""); - } - if (ra != rb) { - if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; - q.push({ - i: s.push(s.pop() + "rotate(", null, ")") - 2, - x: d3_interpolateNumber(ra, rb) - }); - } else if (rb) { - s.push(s.pop() + "rotate(" + rb + ")"); - } - if (wa != wb) { - q.push({ - i: s.push(s.pop() + "skewX(", null, ")") - 2, - x: d3_interpolateNumber(wa, wb) - }); - } else if (wb) { - s.push(s.pop() + "skewX(" + wb + ")"); - } - if (ka[0] != kb[0] || ka[1] != kb[1]) { - n = s.push(s.pop() + "scale(", null, ",", null, ")"); - q.push({ - i: n - 4, - x: d3_interpolateNumber(ka[0], kb[0]) - }, { - i: n - 2, - x: d3_interpolateNumber(ka[1], kb[1]) - }); - } else if (kb[0] != 1 || kb[1] != 1) { - s.push(s.pop() + "scale(" + kb + ")"); - } - n = q.length; - return function(t) { - var i = -1, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - } - function d3_uninterpolateNumber(a, b) { - b = b - (a = +a) ? 1 / (b - a) : 0; - return function(x) { - return (x - a) * b; - }; - } - function d3_uninterpolateClamp(a, b) { - b = b - (a = +a) ? 1 / (b - a) : 0; - return function(x) { - return Math.max(0, Math.min(1, (x - a) * b)); - }; - } - d3.layout = {}; - d3.layout.bundle = function() { - return function(links) { - var paths = [], i = -1, n = links.length; - while (++i < n) paths.push(d3_layout_bundlePath(links[i])); - return paths; - }; - }; - function d3_layout_bundlePath(link) { - var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; - while (start !== lca) { - start = start.parent; - points.push(start); - } - var k = points.length; - while (end !== lca) { - points.splice(k, 0, end); - end = end.parent; - } - return points; - } - function d3_layout_bundleAncestors(node) { - var ancestors = [], parent = node.parent; - while (parent != null) { - ancestors.push(node); - node = parent; - parent = parent.parent; - } - ancestors.push(node); - return ancestors; - } - function d3_layout_bundleLeastCommonAncestor(a, b) { - if (a === b) return a; - var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; - while (aNode === bNode) { - sharedNode = aNode; - aNode = aNodes.pop(); - bNode = bNodes.pop(); - } - return sharedNode; - } - d3.layout.chord = function() { - var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; - function relayout() { - var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; - chords = []; - groups = []; - k = 0, i = -1; - while (++i < n) { - x = 0, j = -1; - while (++j < n) { - x += matrix[i][j]; - } - groupSums.push(x); - subgroupIndex.push(d3.range(n)); - k += x; - } - if (sortGroups) { - groupIndex.sort(function(a, b) { - return sortGroups(groupSums[a], groupSums[b]); - }); - } - if (sortSubgroups) { - subgroupIndex.forEach(function(d, i) { - d.sort(function(a, b) { - return sortSubgroups(matrix[i][a], matrix[i][b]); - }); - }); - } - k = (τ - padding * n) / k; - x = 0, i = -1; - while (++i < n) { - x0 = x, j = -1; - while (++j < n) { - var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; - subgroups[di + "-" + dj] = { - index: di, - subindex: dj, - startAngle: a0, - endAngle: a1, - value: v - }; - } - groups[di] = { - index: di, - startAngle: x0, - endAngle: x, - value: (x - x0) / k - }; - x += padding; - } - i = -1; - while (++i < n) { - j = i - 1; - while (++j < n) { - var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; - if (source.value || target.value) { - chords.push(source.value < target.value ? { - source: target, - target: source - } : { - source: source, - target: target - }); - } - } - } - if (sortChords) resort(); - } - function resort() { - chords.sort(function(a, b) { - return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); - }); - } - chord.matrix = function(x) { - if (!arguments.length) return matrix; - n = (matrix = x) && matrix.length; - chords = groups = null; - return chord; - }; - chord.padding = function(x) { - if (!arguments.length) return padding; - padding = x; - chords = groups = null; - return chord; - }; - chord.sortGroups = function(x) { - if (!arguments.length) return sortGroups; - sortGroups = x; - chords = groups = null; - return chord; - }; - chord.sortSubgroups = function(x) { - if (!arguments.length) return sortSubgroups; - sortSubgroups = x; - chords = null; - return chord; - }; - chord.sortChords = function(x) { - if (!arguments.length) return sortChords; - sortChords = x; - if (chords) resort(); - return chord; - }; - chord.chords = function() { - if (!chords) relayout(); - return chords; - }; - chord.groups = function() { - if (!groups) relayout(); - return groups; - }; - return chord; - }; - d3.layout.force = function() { - var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; - function repulse(node) { - return function(quad, x1, _, x2) { - if (quad.point !== node) { - var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; - if (dw * dw / theta2 < dn) { - if (dn < chargeDistance2) { - var k = quad.charge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - return true; - } - if (quad.point && dn && dn < chargeDistance2) { - var k = quad.pointCharge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - } - return !quad.charge; - }; - } - force.tick = function() { - if ((alpha *= .99) < .005) { - event.end({ - type: "end", - alpha: alpha = 0 - }); - return true; - } - var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; - for (i = 0; i < m; ++i) { - o = links[i]; - s = o.source; - t = o.target; - x = t.x - s.x; - y = t.y - s.y; - if (l = x * x + y * y) { - l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; - x *= l; - y *= l; - t.x -= x * (k = s.weight / (t.weight + s.weight)); - t.y -= y * k; - s.x += x * (k = 1 - k); - s.y += y * k; - } - } - if (k = alpha * gravity) { - x = size[0] / 2; - y = size[1] / 2; - i = -1; - if (k) while (++i < n) { - o = nodes[i]; - o.x += (x - o.x) * k; - o.y += (y - o.y) * k; - } - } - if (charge) { - d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); - i = -1; - while (++i < n) { - if (!(o = nodes[i]).fixed) { - q.visit(repulse(o)); - } - } - } - i = -1; - while (++i < n) { - o = nodes[i]; - if (o.fixed) { - o.x = o.px; - o.y = o.py; - } else { - o.x -= (o.px - (o.px = o.x)) * friction; - o.y -= (o.py - (o.py = o.y)) * friction; - } - } - event.tick({ - type: "tick", - alpha: alpha - }); - }; - force.nodes = function(x) { - if (!arguments.length) return nodes; - nodes = x; - return force; - }; - force.links = function(x) { - if (!arguments.length) return links; - links = x; - return force; - }; - force.size = function(x) { - if (!arguments.length) return size; - size = x; - return force; - }; - force.linkDistance = function(x) { - if (!arguments.length) return linkDistance; - linkDistance = typeof x === "function" ? x : +x; - return force; - }; - force.distance = force.linkDistance; - force.linkStrength = function(x) { - if (!arguments.length) return linkStrength; - linkStrength = typeof x === "function" ? x : +x; - return force; - }; - force.friction = function(x) { - if (!arguments.length) return friction; - friction = +x; - return force; - }; - force.charge = function(x) { - if (!arguments.length) return charge; - charge = typeof x === "function" ? x : +x; - return force; - }; - force.chargeDistance = function(x) { - if (!arguments.length) return Math.sqrt(chargeDistance2); - chargeDistance2 = x * x; - return force; - }; - force.gravity = function(x) { - if (!arguments.length) return gravity; - gravity = +x; - return force; - }; - force.theta = function(x) { - if (!arguments.length) return Math.sqrt(theta2); - theta2 = x * x; - return force; - }; - force.alpha = function(x) { - if (!arguments.length) return alpha; - x = +x; - if (alpha) { - if (x > 0) alpha = x; else alpha = 0; - } else if (x > 0) { - event.start({ - type: "start", - alpha: alpha = x - }); - d3.timer(force.tick); - } - return force; - }; - force.start = function() { - var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; - for (i = 0; i < n; ++i) { - (o = nodes[i]).index = i; - o.weight = 0; - } - for (i = 0; i < m; ++i) { - o = links[i]; - if (typeof o.source == "number") o.source = nodes[o.source]; - if (typeof o.target == "number") o.target = nodes[o.target]; - ++o.source.weight; - ++o.target.weight; - } - for (i = 0; i < n; ++i) { - o = nodes[i]; - if (isNaN(o.x)) o.x = position("x", w); - if (isNaN(o.y)) o.y = position("y", h); - if (isNaN(o.px)) o.px = o.x; - if (isNaN(o.py)) o.py = o.y; - } - distances = []; - if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; - strengths = []; - if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; - charges = []; - if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; - function position(dimension, size) { - if (!neighbors) { - neighbors = new Array(n); - for (j = 0; j < n; ++j) { - neighbors[j] = []; - } - for (j = 0; j < m; ++j) { - var o = links[j]; - neighbors[o.source.index].push(o.target); - neighbors[o.target.index].push(o.source); - } - } - var candidates = neighbors[i], j = -1, m = candidates.length, x; - while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x; - return Math.random() * size; - } - return force.resume(); - }; - force.resume = function() { - return force.alpha(.1); - }; - force.stop = function() { - return force.alpha(0); - }; - force.drag = function() { - if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); - if (!arguments.length) return drag; - this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); - }; - function dragmove(d) { - d.px = d3.event.x, d.py = d3.event.y; - force.resume(); - } - return d3.rebind(force, event, "on"); - }; - function d3_layout_forceDragstart(d) { - d.fixed |= 2; - } - function d3_layout_forceDragend(d) { - d.fixed &= ~6; - } - function d3_layout_forceMouseover(d) { - d.fixed |= 4; - d.px = d.x, d.py = d.y; - } - function d3_layout_forceMouseout(d) { - d.fixed &= ~4; - } - function d3_layout_forceAccumulate(quad, alpha, charges) { - var cx = 0, cy = 0; - quad.charge = 0; - if (!quad.leaf) { - var nodes = quad.nodes, n = nodes.length, i = -1, c; - while (++i < n) { - c = nodes[i]; - if (c == null) continue; - d3_layout_forceAccumulate(c, alpha, charges); - quad.charge += c.charge; - cx += c.charge * c.cx; - cy += c.charge * c.cy; - } - } - if (quad.point) { - if (!quad.leaf) { - quad.point.x += Math.random() - .5; - quad.point.y += Math.random() - .5; - } - var k = alpha * charges[quad.point.index]; - quad.charge += quad.pointCharge = k; - cx += k * quad.point.x; - cy += k * quad.point.y; - } - quad.cx = cx / quad.charge; - quad.cy = cy / quad.charge; - } - var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; - d3.layout.hierarchy = function() { - var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; - function recurse(node, depth, nodes) { - var childs = children.call(hierarchy, node, depth); - node.depth = depth; - nodes.push(node); - if (childs && (n = childs.length)) { - var i = -1, n, c = node.children = new Array(n), v = 0, j = depth + 1, d; - while (++i < n) { - d = c[i] = recurse(childs[i], j, nodes); - d.parent = node; - v += d.value; - } - if (sort) c.sort(sort); - if (value) node.value = v; - } else { - delete node.children; - if (value) { - node.value = +value.call(hierarchy, node, depth) || 0; - } - } - return node; - } - function revalue(node, depth) { - var children = node.children, v = 0; - if (children && (n = children.length)) { - var i = -1, n, j = depth + 1; - while (++i < n) v += revalue(children[i], j); - } else if (value) { - v = +value.call(hierarchy, node, depth) || 0; - } - if (value) node.value = v; - return v; - } - function hierarchy(d) { - var nodes = []; - recurse(d, 0, nodes); - return nodes; - } - hierarchy.sort = function(x) { - if (!arguments.length) return sort; - sort = x; - return hierarchy; - }; - hierarchy.children = function(x) { - if (!arguments.length) return children; - children = x; - return hierarchy; - }; - hierarchy.value = function(x) { - if (!arguments.length) return value; - value = x; - return hierarchy; - }; - hierarchy.revalue = function(root) { - revalue(root, 0); - return root; - }; - return hierarchy; - }; - function d3_layout_hierarchyRebind(object, hierarchy) { - d3.rebind(object, hierarchy, "sort", "children", "value"); - object.nodes = object; - object.links = d3_layout_hierarchyLinks; - return object; - } - function d3_layout_hierarchyChildren(d) { - return d.children; - } - function d3_layout_hierarchyValue(d) { - return d.value; - } - function d3_layout_hierarchySort(a, b) { - return b.value - a.value; - } - function d3_layout_hierarchyLinks(nodes) { - return d3.merge(nodes.map(function(parent) { - return (parent.children || []).map(function(child) { - return { - source: parent, - target: child - }; - }); - })); - } - d3.layout.partition = function() { - var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; - function position(node, x, dx, dy) { - var children = node.children; - node.x = x; - node.y = node.depth * dy; - node.dx = dx; - node.dy = dy; - if (children && (n = children.length)) { - var i = -1, n, c, d; - dx = node.value ? dx / node.value : 0; - while (++i < n) { - position(c = children[i], x, d = c.value * dx, dy); - x += d; - } - } - } - function depth(node) { - var children = node.children, d = 0; - if (children && (n = children.length)) { - var i = -1, n; - while (++i < n) d = Math.max(d, depth(children[i])); - } - return 1 + d; - } - function partition(d, i) { - var nodes = hierarchy.call(this, d, i); - position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); - return nodes; - } - partition.size = function(x) { - if (!arguments.length) return size; - size = x; - return partition; - }; - return d3_layout_hierarchyRebind(partition, hierarchy); - }; - d3.layout.pie = function() { - var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ; - function pie(data) { - var values = data.map(function(d, i) { - return +value.call(pie, d, i); - }); - var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); - var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values); - var index = d3.range(data.length); - if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { - return values[j] - values[i]; - } : function(i, j) { - return sort(data[i], data[j]); - }); - var arcs = []; - index.forEach(function(i) { - var d; - arcs[i] = { - data: data[i], - value: d = values[i], - startAngle: a, - endAngle: a += d * k - }; - }); - return arcs; - } - pie.value = function(x) { - if (!arguments.length) return value; - value = x; - return pie; - }; - pie.sort = function(x) { - if (!arguments.length) return sort; - sort = x; - return pie; - }; - pie.startAngle = function(x) { - if (!arguments.length) return startAngle; - startAngle = x; - return pie; - }; - pie.endAngle = function(x) { - if (!arguments.length) return endAngle; - endAngle = x; - return pie; - }; - return pie; - }; - var d3_layout_pieSortByValue = {}; - d3.layout.stack = function() { - var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; - function stack(data, index) { - var series = data.map(function(d, i) { - return values.call(stack, d, i); - }); - var points = series.map(function(d) { - return d.map(function(v, i) { - return [ x.call(stack, v, i), y.call(stack, v, i) ]; - }); - }); - var orders = order.call(stack, points, index); - series = d3.permute(series, orders); - points = d3.permute(points, orders); - var offsets = offset.call(stack, points, index); - var n = series.length, m = series[0].length, i, j, o; - for (j = 0; j < m; ++j) { - out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); - for (i = 1; i < n; ++i) { - out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); - } - } - return data; - } - stack.values = function(x) { - if (!arguments.length) return values; - values = x; - return stack; - }; - stack.order = function(x) { - if (!arguments.length) return order; - order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; - return stack; - }; - stack.offset = function(x) { - if (!arguments.length) return offset; - offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; - return stack; - }; - stack.x = function(z) { - if (!arguments.length) return x; - x = z; - return stack; - }; - stack.y = function(z) { - if (!arguments.length) return y; - y = z; - return stack; - }; - stack.out = function(z) { - if (!arguments.length) return out; - out = z; - return stack; - }; - return stack; - }; - function d3_layout_stackX(d) { - return d.x; - } - function d3_layout_stackY(d) { - return d.y; - } - function d3_layout_stackOut(d, y0, y) { - d.y0 = y0; - d.y = y; - } - var d3_layout_stackOrders = d3.map({ - "inside-out": function(data) { - var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { - return max[a] - max[b]; - }), top = 0, bottom = 0, tops = [], bottoms = []; - for (i = 0; i < n; ++i) { - j = index[i]; - if (top < bottom) { - top += sums[j]; - tops.push(j); - } else { - bottom += sums[j]; - bottoms.push(j); - } - } - return bottoms.reverse().concat(tops); - }, - reverse: function(data) { - return d3.range(data.length).reverse(); - }, - "default": d3_layout_stackOrderDefault - }); - var d3_layout_stackOffsets = d3.map({ - silhouette: function(data) { - var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o > max) max = o; - sums.push(o); - } - for (j = 0; j < m; ++j) { - y0[j] = (max - sums[j]) / 2; - } - return y0; - }, - wiggle: function(data) { - var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; - y0[0] = o = o0 = 0; - for (j = 1; j < m; ++j) { - for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; - for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { - for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { - s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; - } - s2 += s3 * data[i][j][1]; - } - y0[j] = o -= s1 ? s2 / s1 * dx : 0; - if (o < o0) o0 = o; - } - for (j = 0; j < m; ++j) y0[j] -= o0; - return y0; - }, - expand: function(data) { - var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; - } - for (j = 0; j < m; ++j) y0[j] = 0; - return y0; - }, - zero: d3_layout_stackOffsetZero - }); - function d3_layout_stackOrderDefault(data) { - return d3.range(data.length); - } - function d3_layout_stackOffsetZero(data) { - var j = -1, m = data[0].length, y0 = []; - while (++j < m) y0[j] = 0; - return y0; - } - function d3_layout_stackMaxIndex(array) { - var i = 1, j = 0, v = array[0][1], k, n = array.length; - for (;i < n; ++i) { - if ((k = array[i][1]) > v) { - j = i; - v = k; - } - } - return j; - } - function d3_layout_stackReduceSum(d) { - return d.reduce(d3_layout_stackSum, 0); - } - function d3_layout_stackSum(p, d) { - return p + d[1]; - } - d3.layout.histogram = function() { - var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; - function histogram(data, i) { - var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; - while (++i < m) { - bin = bins[i] = []; - bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); - bin.y = 0; - } - if (m > 0) { - i = -1; - while (++i < n) { - x = values[i]; - if (x >= range[0] && x <= range[1]) { - bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; - bin.y += k; - bin.push(data[i]); - } - } - } - return bins; - } - histogram.value = function(x) { - if (!arguments.length) return valuer; - valuer = x; - return histogram; - }; - histogram.range = function(x) { - if (!arguments.length) return ranger; - ranger = d3_functor(x); - return histogram; - }; - histogram.bins = function(x) { - if (!arguments.length) return binner; - binner = typeof x === "number" ? function(range) { - return d3_layout_histogramBinFixed(range, x); - } : d3_functor(x); - return histogram; - }; - histogram.frequency = function(x) { - if (!arguments.length) return frequency; - frequency = !!x; - return histogram; - }; - return histogram; - }; - function d3_layout_histogramBinSturges(range, values) { - return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); - } - function d3_layout_histogramBinFixed(range, n) { - var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; - while (++x <= n) f[x] = m * x + b; - return f; - } - function d3_layout_histogramRange(values) { - return [ d3.min(values), d3.max(values) ]; - } - d3.layout.tree = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; - function tree(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0]; - function firstWalk(node, previousSibling) { - var children = node.children, layout = node._tree; - if (children && (n = children.length)) { - var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1; - while (++i < n) { - child = children[i]; - firstWalk(child, previousChild); - ancestor = apportion(child, previousChild, ancestor); - previousChild = child; - } - d3_layout_treeShift(node); - var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); - if (previousSibling) { - layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); - layout.mod = layout.prelim - midpoint; - } else { - layout.prelim = midpoint; - } - } else { - if (previousSibling) { - layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); - } - } - } - function secondWalk(node, x) { - node.x = node._tree.prelim + x; - var children = node.children; - if (children && (n = children.length)) { - var i = -1, n; - x += node._tree.mod; - while (++i < n) { - secondWalk(children[i], x); - } - } - } - function apportion(node, previousSibling, ancestor) { - if (previousSibling) { - var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift; - while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { - vom = d3_layout_treeLeft(vom); - vop = d3_layout_treeRight(vop); - vop._tree.ancestor = node; - shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); - if (shift > 0) { - d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); - sip += shift; - sop += shift; - } - sim += vim._tree.mod; - sip += vip._tree.mod; - som += vom._tree.mod; - sop += vop._tree.mod; - } - if (vim && !d3_layout_treeRight(vop)) { - vop._tree.thread = vim; - vop._tree.mod += sim - sop; - } - if (vip && !d3_layout_treeLeft(vom)) { - vom._tree.thread = vip; - vom._tree.mod += sip - som; - ancestor = node; - } - } - return ancestor; - } - d3_layout_treeVisitAfter(root, function(node, previousSibling) { - node._tree = { - ancestor: node, - prelim: 0, - mod: 0, - change: 0, - shift: 0, - number: previousSibling ? previousSibling._tree.number + 1 : 0 - }; - }); - firstWalk(root); - secondWalk(root, -root._tree.prelim); - var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1; - d3_layout_treeVisitAfter(root, nodeSize ? function(node) { - node.x *= size[0]; - node.y = node.depth * size[1]; - delete node._tree; - } : function(node) { - node.x = (node.x - x0) / (x1 - x0) * size[0]; - node.y = node.depth / y1 * size[1]; - delete node._tree; - }); - return nodes; - } - tree.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return tree; - }; - tree.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null; - return tree; - }; - tree.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) != null; - return tree; - }; - return d3_layout_hierarchyRebind(tree, hierarchy); - }; - function d3_layout_treeSeparation(a, b) { - return a.parent == b.parent ? 1 : 2; - } - function d3_layout_treeLeft(node) { - var children = node.children; - return children && children.length ? children[0] : node._tree.thread; - } - function d3_layout_treeRight(node) { - var children = node.children, n; - return children && (n = children.length) ? children[n - 1] : node._tree.thread; - } - function d3_layout_treeSearch(node, compare) { - var children = node.children; - if (children && (n = children.length)) { - var child, n, i = -1; - while (++i < n) { - if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { - node = child; - } - } - } - return node; - } - function d3_layout_treeRightmost(a, b) { - return a.x - b.x; - } - function d3_layout_treeLeftmost(a, b) { - return b.x - a.x; - } - function d3_layout_treeDeepest(a, b) { - return a.depth - b.depth; - } - function d3_layout_treeVisitAfter(node, callback) { - function visit(node, previousSibling) { - var children = node.children; - if (children && (n = children.length)) { - var child, previousChild = null, i = -1, n; - while (++i < n) { - child = children[i]; - visit(child, previousChild); - previousChild = child; - } - } - callback(node, previousSibling); - } - visit(node, null); - } - function d3_layout_treeShift(node) { - var shift = 0, change = 0, children = node.children, i = children.length, child; - while (--i >= 0) { - child = children[i]._tree; - child.prelim += shift; - child.mod += shift; - shift += child.shift + (change += child.change); - } - } - function d3_layout_treeMove(ancestor, node, shift) { - ancestor = ancestor._tree; - node = node._tree; - var change = shift / (node.number - ancestor.number); - ancestor.change += change; - node.change -= change; - node.shift += shift; - node.prelim += shift; - node.mod += shift; - } - function d3_layout_treeAncestor(vim, node, ancestor) { - return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor; - } - d3.layout.pack = function() { - var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; - function pack(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { - return radius; - }; - root.x = root.y = 0; - d3_layout_treeVisitAfter(root, function(d) { - d.r = +r(d.value); - }); - d3_layout_treeVisitAfter(root, d3_layout_packSiblings); - if (padding) { - var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; - d3_layout_treeVisitAfter(root, function(d) { - d.r += dr; - }); - d3_layout_treeVisitAfter(root, d3_layout_packSiblings); - d3_layout_treeVisitAfter(root, function(d) { - d.r -= dr; - }); - } - d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); - return nodes; - } - pack.size = function(_) { - if (!arguments.length) return size; - size = _; - return pack; - }; - pack.radius = function(_) { - if (!arguments.length) return radius; - radius = _ == null || typeof _ === "function" ? _ : +_; - return pack; - }; - pack.padding = function(_) { - if (!arguments.length) return padding; - padding = +_; - return pack; - }; - return d3_layout_hierarchyRebind(pack, hierarchy); - }; - function d3_layout_packSort(a, b) { - return a.value - b.value; - } - function d3_layout_packInsert(a, b) { - var c = a._pack_next; - a._pack_next = b; - b._pack_prev = a; - b._pack_next = c; - c._pack_prev = b; - } - function d3_layout_packSplice(a, b) { - a._pack_next = b; - b._pack_prev = a; - } - function d3_layout_packIntersects(a, b) { - var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; - return .999 * dr * dr > dx * dx + dy * dy; - } - function d3_layout_packSiblings(node) { - if (!(nodes = node.children) || !(n = nodes.length)) return; - var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; - function bound(node) { - xMin = Math.min(node.x - node.r, xMin); - xMax = Math.max(node.x + node.r, xMax); - yMin = Math.min(node.y - node.r, yMin); - yMax = Math.max(node.y + node.r, yMax); - } - nodes.forEach(d3_layout_packLink); - a = nodes[0]; - a.x = -a.r; - a.y = 0; - bound(a); - if (n > 1) { - b = nodes[1]; - b.x = b.r; - b.y = 0; - bound(b); - if (n > 2) { - c = nodes[2]; - d3_layout_packPlace(a, b, c); - bound(c); - d3_layout_packInsert(a, c); - a._pack_prev = c; - d3_layout_packInsert(c, b); - b = a._pack_next; - for (i = 3; i < n; i++) { - d3_layout_packPlace(a, b, c = nodes[i]); - var isect = 0, s1 = 1, s2 = 1; - for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { - if (d3_layout_packIntersects(j, c)) { - isect = 1; - break; - } - } - if (isect == 1) { - for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { - if (d3_layout_packIntersects(k, c)) { - break; - } - } - } - if (isect) { - if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); - i--; - } else { - d3_layout_packInsert(a, c); - b = c; - bound(c); - } - } - } - } - var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; - for (i = 0; i < n; i++) { - c = nodes[i]; - c.x -= cx; - c.y -= cy; - cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); - } - node.r = cr; - nodes.forEach(d3_layout_packUnlink); - } - function d3_layout_packLink(node) { - node._pack_next = node._pack_prev = node; - } - function d3_layout_packUnlink(node) { - delete node._pack_next; - delete node._pack_prev; - } - function d3_layout_packTransform(node, x, y, k) { - var children = node.children; - node.x = x += k * node.x; - node.y = y += k * node.y; - node.r *= k; - if (children) { - var i = -1, n = children.length; - while (++i < n) d3_layout_packTransform(children[i], x, y, k); - } - } - function d3_layout_packPlace(a, b, c) { - var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; - if (db && (dx || dy)) { - var da = b.r + c.r, dc = dx * dx + dy * dy; - da *= da; - db *= db; - var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); - c.x = a.x + x * dx + y * dy; - c.y = a.y + x * dy - y * dx; - } else { - c.x = a.x + db; - c.y = a.y; - } - } - d3.layout.cluster = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; - function cluster(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; - d3_layout_treeVisitAfter(root, function(node) { - var children = node.children; - if (children && children.length) { - node.x = d3_layout_clusterX(children); - node.y = d3_layout_clusterY(children); - } else { - node.x = previousNode ? x += separation(node, previousNode) : 0; - node.y = 0; - previousNode = node; - } - }); - var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; - d3_layout_treeVisitAfter(root, nodeSize ? function(node) { - node.x = (node.x - root.x) * size[0]; - node.y = (root.y - node.y) * size[1]; - } : function(node) { - node.x = (node.x - x0) / (x1 - x0) * size[0]; - node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; - }); - return nodes; - } - cluster.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return cluster; - }; - cluster.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null; - return cluster; - }; - cluster.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) != null; - return cluster; - }; - return d3_layout_hierarchyRebind(cluster, hierarchy); - }; - function d3_layout_clusterY(children) { - return 1 + d3.max(children, function(child) { - return child.y; - }); - } - function d3_layout_clusterX(children) { - return children.reduce(function(x, child) { - return x + child.x; - }, 0) / children.length; - } - function d3_layout_clusterLeft(node) { - var children = node.children; - return children && children.length ? d3_layout_clusterLeft(children[0]) : node; - } - function d3_layout_clusterRight(node) { - var children = node.children, n; - return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; - } - d3.layout.treemap = function() { - var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); - function scale(children, k) { - var i = -1, n = children.length, child, area; - while (++i < n) { - area = (child = children[i]).value * (k < 0 ? 0 : k); - child.area = isNaN(area) || area <= 0 ? 0 : area; - } - } - function squarify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while ((n = remaining.length) > 0) { - row.push(child = remaining[n - 1]); - row.area += child.area; - if (mode !== "squarify" || (score = worst(row, u)) <= best) { - remaining.pop(); - best = score; - } else { - row.area -= row.pop().area; - position(row, u, rect, false); - u = Math.min(rect.dx, rect.dy); - row.length = row.area = 0; - best = Infinity; - } - } - if (row.length) { - position(row, u, rect, true); - row.length = row.area = 0; - } - children.forEach(squarify); - } - } - function stickify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), remaining = children.slice(), child, row = []; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while (child = remaining.pop()) { - row.push(child); - row.area += child.area; - if (child.z != null) { - position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); - row.length = row.area = 0; - } - } - children.forEach(stickify); - } - } - function worst(row, u) { - var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; - while (++i < n) { - if (!(r = row[i].area)) continue; - if (r < rmin) rmin = r; - if (r > rmax) rmax = r; - } - s *= s; - u *= u; - return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; - } - function position(row, u, rect, flush) { - var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; - if (u == rect.dx) { - if (flush || v > rect.dy) v = rect.dy; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dy = v; - x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); - } - o.z = true; - o.dx += rect.x + rect.dx - x; - rect.y += v; - rect.dy -= v; - } else { - if (flush || v > rect.dx) v = rect.dx; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dx = v; - y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); - } - o.z = false; - o.dy += rect.y + rect.dy - y; - rect.x += v; - rect.dx -= v; - } - } - function treemap(d) { - var nodes = stickies || hierarchy(d), root = nodes[0]; - root.x = 0; - root.y = 0; - root.dx = size[0]; - root.dy = size[1]; - if (stickies) hierarchy.revalue(root); - scale([ root ], root.dx * root.dy / root.value); - (stickies ? stickify : squarify)(root); - if (sticky) stickies = nodes; - return nodes; - } - treemap.size = function(x) { - if (!arguments.length) return size; - size = x; - return treemap; - }; - treemap.padding = function(x) { - if (!arguments.length) return padding; - function padFunction(node) { - var p = x.call(treemap, node, node.depth); - return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); - } - function padConstant(node) { - return d3_layout_treemapPad(node, x); - } - var type; - pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], - padConstant) : padConstant; - return treemap; - }; - treemap.round = function(x) { - if (!arguments.length) return round != Number; - round = x ? Math.round : Number; - return treemap; - }; - treemap.sticky = function(x) { - if (!arguments.length) return sticky; - sticky = x; - stickies = null; - return treemap; - }; - treemap.ratio = function(x) { - if (!arguments.length) return ratio; - ratio = x; - return treemap; - }; - treemap.mode = function(x) { - if (!arguments.length) return mode; - mode = x + ""; - return treemap; - }; - return d3_layout_hierarchyRebind(treemap, hierarchy); - }; - function d3_layout_treemapPadNull(node) { - return { - x: node.x, - y: node.y, - dx: node.dx, - dy: node.dy - }; - } - function d3_layout_treemapPad(node, padding) { - var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; - if (dx < 0) { - x += dx / 2; - dx = 0; - } - if (dy < 0) { - y += dy / 2; - dy = 0; - } - return { - x: x, - y: y, - dx: dx, - dy: dy - }; - } - d3.random = { - normal: function(µ, σ) { - var n = arguments.length; - if (n < 2) σ = 1; - if (n < 1) µ = 0; - return function() { - var x, y, r; - do { - x = Math.random() * 2 - 1; - y = Math.random() * 2 - 1; - r = x * x + y * y; - } while (!r || r > 1); - return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); - }; - }, - logNormal: function() { - var random = d3.random.normal.apply(d3, arguments); - return function() { - return Math.exp(random()); - }; - }, - bates: function(m) { - var random = d3.random.irwinHall(m); - return function() { - return random() / m; - }; - }, - irwinHall: function(m) { - return function() { - for (var s = 0, j = 0; j < m; j++) s += Math.random(); - return s; - }; - } - }; - d3.scale = {}; - function d3_scaleExtent(domain) { - var start = domain[0], stop = domain[domain.length - 1]; - return start < stop ? [ start, stop ] : [ stop, start ]; - } - function d3_scaleRange(scale) { - return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); - } - function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { - var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); - return function(x) { - return i(u(x)); - }; - } - function d3_scale_nice(domain, nice) { - var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; - if (x1 < x0) { - dx = i0, i0 = i1, i1 = dx; - dx = x0, x0 = x1, x1 = dx; - } - domain[i0] = nice.floor(x0); - domain[i1] = nice.ceil(x1); - return domain; - } - function d3_scale_niceStep(step) { - return step ? { - floor: function(x) { - return Math.floor(x / step) * step; - }, - ceil: function(x) { - return Math.ceil(x / step) * step; - } - } : d3_scale_niceIdentity; - } - var d3_scale_niceIdentity = { - floor: d3_identity, - ceil: d3_identity - }; - function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { - var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; - if (domain[k] < domain[0]) { - domain = domain.slice().reverse(); - range = range.slice().reverse(); - } - while (++j <= k) { - u.push(uninterpolate(domain[j - 1], domain[j])); - i.push(interpolate(range[j - 1], range[j])); - } - return function(x) { - var j = d3.bisect(domain, x, 1, k) - 1; - return i[j](u[j](x)); - }; - } - d3.scale.linear = function() { - return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); - }; - function d3_scale_linear(domain, range, interpolate, clamp) { - var output, input; - function rescale() { - var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; - output = linear(domain, range, uninterpolate, interpolate); - input = linear(range, domain, uninterpolate, d3_interpolate); - return scale; - } - function scale(x) { - return output(x); - } - scale.invert = function(y) { - return input(y); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.map(Number); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.rangeRound = function(x) { - return scale.range(x).interpolate(d3_interpolateRound); - }; - scale.clamp = function(x) { - if (!arguments.length) return clamp; - clamp = x; - return rescale(); - }; - scale.interpolate = function(x) { - if (!arguments.length) return interpolate; - interpolate = x; - return rescale(); - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - d3_scale_linearNice(domain, m); - return rescale(); - }; - scale.copy = function() { - return d3_scale_linear(domain, range, interpolate, clamp); - }; - return rescale(); - } - function d3_scale_linearRebind(scale, linear) { - return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); - } - function d3_scale_linearNice(domain, m) { - return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); - } - function d3_scale_linearTickRange(domain, m) { - if (m == null) m = 10; - var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; - if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; - extent[0] = Math.ceil(extent[0] / step) * step; - extent[1] = Math.floor(extent[1] / step) * step + step * .5; - extent[2] = step; - return extent; - } - function d3_scale_linearTicks(domain, m) { - return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); - } - function d3_scale_linearTickFormat(domain, m, format) { - var range = d3_scale_linearTickRange(domain, m); - if (format) { - var match = d3_format_re.exec(format); - match.shift(); - if (match[8] === "s") { - var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); - if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); - match[8] = "f"; - format = d3.format(match.join("")); - return function(d) { - return format(prefix.scale(d)) + prefix.symbol; - }; - } - if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); - format = match.join(""); - } else { - format = ",." + d3_scale_linearPrecision(range[2]) + "f"; - } - return d3.format(format); - } - var d3_scale_linearFormatSignificant = { - s: 1, - g: 1, - p: 1, - r: 1, - e: 1 - }; - function d3_scale_linearPrecision(value) { - return -Math.floor(Math.log(value) / Math.LN10 + .01); - } - function d3_scale_linearFormatPrecision(type, range) { - var p = d3_scale_linearPrecision(range[2]); - return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; - } - d3.scale.log = function() { - return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); - }; - function d3_scale_log(linear, base, positive, domain) { - function log(x) { - return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); - } - function pow(x) { - return positive ? Math.pow(base, x) : -Math.pow(base, -x); - } - function scale(x) { - return linear(log(x)); - } - scale.invert = function(x) { - return pow(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - positive = x[0] >= 0; - linear.domain((domain = x.map(Number)).map(log)); - return scale; - }; - scale.base = function(_) { - if (!arguments.length) return base; - base = +_; - linear.domain(domain.map(log)); - return scale; - }; - scale.nice = function() { - var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); - linear.domain(niced); - domain = niced.map(pow); - return scale; - }; - scale.ticks = function() { - var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; - if (isFinite(j - i)) { - if (positive) { - for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); - ticks.push(pow(i)); - } else { - ticks.push(pow(i)); - for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); - } - for (i = 0; ticks[i] < u; i++) {} - for (j = ticks.length; ticks[j - 1] > v; j--) {} - ticks = ticks.slice(i, j); - } - return ticks; - }; - scale.tickFormat = function(n, format) { - if (!arguments.length) return d3_scale_logFormat; - if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); - var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, - Math.floor), e; - return function(d) { - return d / pow(f(log(d) + e)) <= k ? format(d) : ""; - }; - }; - scale.copy = function() { - return d3_scale_log(linear.copy(), base, positive, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { - floor: function(x) { - return -Math.ceil(-x); - }, - ceil: function(x) { - return -Math.floor(-x); - } - }; - d3.scale.pow = function() { - return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); - }; - function d3_scale_pow(linear, exponent, domain) { - var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); - function scale(x) { - return linear(powp(x)); - } - scale.invert = function(x) { - return powb(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - linear.domain((domain = x.map(Number)).map(powp)); - return scale; - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - return scale.domain(d3_scale_linearNice(domain, m)); - }; - scale.exponent = function(x) { - if (!arguments.length) return exponent; - powp = d3_scale_powPow(exponent = x); - powb = d3_scale_powPow(1 / exponent); - linear.domain(domain.map(powp)); - return scale; - }; - scale.copy = function() { - return d3_scale_pow(linear.copy(), exponent, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_scale_powPow(e) { - return function(x) { - return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); - }; - } - d3.scale.sqrt = function() { - return d3.scale.pow().exponent(.5); - }; - d3.scale.ordinal = function() { - return d3_scale_ordinal([], { - t: "range", - a: [ [] ] - }); - }; - function d3_scale_ordinal(domain, ranger) { - var index, range, rangeBand; - function scale(x) { - return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; - } - function steps(start, step) { - return d3.range(domain.length).map(function(i) { - return start + step * i; - }); - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = []; - index = new d3_Map(); - var i = -1, n = x.length, xi; - while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); - return scale[ranger.t].apply(scale, ranger.a); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - rangeBand = 0; - ranger = { - t: "range", - a: arguments - }; - return scale; - }; - scale.rangePoints = function(x, padding) { - if (arguments.length < 2) padding = 0; - var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); - range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); - rangeBand = 0; - ranger = { - t: "rangePoints", - a: arguments - }; - return scale; - }; - scale.rangeBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); - range = steps(start + step * outerPadding, step); - if (reverse) range.reverse(); - rangeBand = step * (1 - padding); - ranger = { - t: "rangeBands", - a: arguments - }; - return scale; - }; - scale.rangeRoundBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; - range = steps(start + Math.round(error / 2), step); - if (reverse) range.reverse(); - rangeBand = Math.round(step * (1 - padding)); - ranger = { - t: "rangeRoundBands", - a: arguments - }; - return scale; - }; - scale.rangeBand = function() { - return rangeBand; - }; - scale.rangeExtent = function() { - return d3_scaleExtent(ranger.a[0]); - }; - scale.copy = function() { - return d3_scale_ordinal(domain, ranger); - }; - return scale.domain(domain); - } - d3.scale.category10 = function() { - return d3.scale.ordinal().range(d3_category10); - }; - d3.scale.category20 = function() { - return d3.scale.ordinal().range(d3_category20); - }; - d3.scale.category20b = function() { - return d3.scale.ordinal().range(d3_category20b); - }; - d3.scale.category20c = function() { - return d3.scale.ordinal().range(d3_category20c); - }; - var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); - var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); - var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); - var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); - d3.scale.quantile = function() { - return d3_scale_quantile([], []); - }; - function d3_scale_quantile(domain, range) { - var thresholds; - function rescale() { - var k = 0, q = range.length; - thresholds = []; - while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); - return scale; - } - function scale(x) { - if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.filter(function(d) { - return !isNaN(d); - }).sort(d3_ascending); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.quantiles = function() { - return thresholds; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; - }; - scale.copy = function() { - return d3_scale_quantile(domain, range); - }; - return rescale(); - } - d3.scale.quantize = function() { - return d3_scale_quantize(0, 1, [ 0, 1 ]); - }; - function d3_scale_quantize(x0, x1, range) { - var kx, i; - function scale(x) { - return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; - } - function rescale() { - kx = range.length / (x1 - x0); - i = range.length - 1; - return scale; - } - scale.domain = function(x) { - if (!arguments.length) return [ x0, x1 ]; - x0 = +x[0]; - x1 = +x[x.length - 1]; - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - y = y < 0 ? NaN : y / kx + x0; - return [ y, y + 1 / kx ]; - }; - scale.copy = function() { - return d3_scale_quantize(x0, x1, range); - }; - return rescale(); - } - d3.scale.threshold = function() { - return d3_scale_threshold([ .5 ], [ 0, 1 ]); - }; - function d3_scale_threshold(domain, range) { - function scale(x) { - if (x <= x) return range[d3.bisect(domain, x)]; - } - scale.domain = function(_) { - if (!arguments.length) return domain; - domain = _; - return scale; - }; - scale.range = function(_) { - if (!arguments.length) return range; - range = _; - return scale; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return [ domain[y - 1], domain[y] ]; - }; - scale.copy = function() { - return d3_scale_threshold(domain, range); - }; - return scale; - } - d3.scale.identity = function() { - return d3_scale_identity([ 0, 1 ]); - }; - function d3_scale_identity(domain) { - function identity(x) { - return +x; - } - identity.invert = identity; - identity.domain = identity.range = function(x) { - if (!arguments.length) return domain; - domain = x.map(identity); - return identity; - }; - identity.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - identity.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - identity.copy = function() { - return d3_scale_identity(domain); - }; - return identity; - } - d3.svg = {}; - d3.svg.arc = function() { - var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; - function arc() { - var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, - a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); - return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; - } - arc.innerRadius = function(v) { - if (!arguments.length) return innerRadius; - innerRadius = d3_functor(v); - return arc; - }; - arc.outerRadius = function(v) { - if (!arguments.length) return outerRadius; - outerRadius = d3_functor(v); - return arc; - }; - arc.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return arc; - }; - arc.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return arc; - }; - arc.centroid = function() { - var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; - return [ Math.cos(a) * r, Math.sin(a) * r ]; - }; - return arc; - }; - var d3_svg_arcOffset = -halfπ, d3_svg_arcMax = τ - ε; - function d3_svg_arcInnerRadius(d) { - return d.innerRadius; - } - function d3_svg_arcOuterRadius(d) { - return d.outerRadius; - } - function d3_svg_arcStartAngle(d) { - return d.startAngle; - } - function d3_svg_arcEndAngle(d) { - return d.endAngle; - } - function d3_svg_line(projection) { - var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; - function line(data) { - var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); - function segment() { - segments.push("M", interpolate(projection(points), tension)); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); - } else if (points.length) { - segment(); - points = []; - } - } - if (points.length) segment(); - return segments.length ? segments.join("") : null; - } - line.x = function(_) { - if (!arguments.length) return x; - x = _; - return line; - }; - line.y = function(_) { - if (!arguments.length) return y; - y = _; - return line; - }; - line.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return line; - }; - line.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - return line; - }; - line.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return line; - }; - return line; - } - d3.svg.line = function() { - return d3_svg_line(d3_identity); - }; - var d3_svg_lineInterpolators = d3.map({ - linear: d3_svg_lineLinear, - "linear-closed": d3_svg_lineLinearClosed, - step: d3_svg_lineStep, - "step-before": d3_svg_lineStepBefore, - "step-after": d3_svg_lineStepAfter, - basis: d3_svg_lineBasis, - "basis-open": d3_svg_lineBasisOpen, - "basis-closed": d3_svg_lineBasisClosed, - bundle: d3_svg_lineBundle, - cardinal: d3_svg_lineCardinal, - "cardinal-open": d3_svg_lineCardinalOpen, - "cardinal-closed": d3_svg_lineCardinalClosed, - monotone: d3_svg_lineMonotone - }); - d3_svg_lineInterpolators.forEach(function(key, value) { - value.key = key; - value.closed = /-closed$/.test(key); - }); - function d3_svg_lineLinear(points) { - return points.join("L"); - } - function d3_svg_lineLinearClosed(points) { - return d3_svg_lineLinear(points) + "Z"; - } - function d3_svg_lineStep(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); - if (n > 1) path.push("H", p[0]); - return path.join(""); - } - function d3_svg_lineStepBefore(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); - return path.join(""); - } - function d3_svg_lineStepAfter(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); - return path.join(""); - } - function d3_svg_lineCardinalOpen(points, tension) { - return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineCardinalClosed(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), - points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); - } - function d3_svg_lineCardinal(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineHermite(points, tangents) { - if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { - return d3_svg_lineLinear(points); - } - var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; - if (quad) { - path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; - p0 = points[1]; - pi = 2; - } - if (tangents.length > 1) { - t = tangents[1]; - p = points[pi]; - pi++; - path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - for (var i = 2; i < tangents.length; i++, pi++) { - p = points[pi]; - t = tangents[i]; - path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - } - } - if (quad) { - var lp = points[pi]; - path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; - } - return path; - } - function d3_svg_lineCardinalTangents(points, tension) { - var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; - while (++i < n) { - p0 = p1; - p1 = p2; - p2 = points[i]; - tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); - } - return tangents; - } - function d3_svg_lineBasis(points) { - if (points.length < 3) return d3_svg_lineLinear(points); - var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - points.push(points[n - 1]); - while (++i <= n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - points.pop(); - path.push("L", pi); - return path.join(""); - } - function d3_svg_lineBasisOpen(points) { - if (points.length < 4) return d3_svg_lineLinear(points); - var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; - while (++i < 3) { - pi = points[i]; - px.push(pi[0]); - py.push(pi[1]); - } - path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); - --i; - while (++i < n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBasisClosed(points) { - var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; - while (++i < 4) { - pi = points[i % n]; - px.push(pi[0]); - py.push(pi[1]); - } - path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - --i; - while (++i < m) { - pi = points[i % n]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBundle(points, tension) { - var n = points.length - 1; - if (n) { - var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; - while (++i <= n) { - p = points[i]; - t = i / n; - p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); - p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); - } - } - return d3_svg_lineBasis(points); - } - function d3_svg_lineDot4(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; - } - var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; - function d3_svg_lineBasisBezier(path, x, y) { - path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); - } - function d3_svg_lineSlope(p0, p1) { - return (p1[1] - p0[1]) / (p1[0] - p0[0]); - } - function d3_svg_lineFiniteDifferences(points) { - var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); - while (++i < j) { - m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; - } - m[i] = d; - return m; - } - function d3_svg_lineMonotoneTangents(points) { - var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; - while (++i < j) { - d = d3_svg_lineSlope(points[i], points[i + 1]); - if (abs(d) < ε) { - m[i] = m[i + 1] = 0; - } else { - a = m[i] / d; - b = m[i + 1] / d; - s = a * a + b * b; - if (s > 9) { - s = d * 3 / Math.sqrt(s); - m[i] = s * a; - m[i + 1] = s * b; - } - } - } - i = -1; - while (++i <= j) { - s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); - tangents.push([ s || 0, m[i] * s || 0 ]); - } - return tangents; - } - function d3_svg_lineMonotone(points) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); - } - d3.svg.line.radial = function() { - var line = d3_svg_line(d3_svg_lineRadial); - line.radius = line.x, delete line.x; - line.angle = line.y, delete line.y; - return line; - }; - function d3_svg_lineRadial(points) { - var point, i = -1, n = points.length, r, a; - while (++i < n) { - point = points[i]; - r = point[0]; - a = point[1] + d3_svg_arcOffset; - point[0] = r * Math.cos(a); - point[1] = r * Math.sin(a); - } - return points; - } - function d3_svg_area(projection) { - var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; - function area(data) { - var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { - return x; - } : d3_functor(x1), fy1 = y0 === y1 ? function() { - return y; - } : d3_functor(y1), x, y; - function segment() { - segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); - points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); - } else if (points0.length) { - segment(); - points0 = []; - points1 = []; - } - } - if (points0.length) segment(); - return segments.length ? segments.join("") : null; - } - area.x = function(_) { - if (!arguments.length) return x1; - x0 = x1 = _; - return area; - }; - area.x0 = function(_) { - if (!arguments.length) return x0; - x0 = _; - return area; - }; - area.x1 = function(_) { - if (!arguments.length) return x1; - x1 = _; - return area; - }; - area.y = function(_) { - if (!arguments.length) return y1; - y0 = y1 = _; - return area; - }; - area.y0 = function(_) { - if (!arguments.length) return y0; - y0 = _; - return area; - }; - area.y1 = function(_) { - if (!arguments.length) return y1; - y1 = _; - return area; - }; - area.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return area; - }; - area.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - interpolateReverse = interpolate.reverse || interpolate; - L = interpolate.closed ? "M" : "L"; - return area; - }; - area.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return area; - }; - return area; - } - d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; - d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; - d3.svg.area = function() { - return d3_svg_area(d3_identity); - }; - d3.svg.area.radial = function() { - var area = d3_svg_area(d3_svg_lineRadial); - area.radius = area.x, delete area.x; - area.innerRadius = area.x0, delete area.x0; - area.outerRadius = area.x1, delete area.x1; - area.angle = area.y, delete area.y; - area.startAngle = area.y0, delete area.y0; - area.endAngle = area.y1, delete area.y1; - return area; - }; - d3.svg.chord = function() { - var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; - function chord(d, i) { - var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); - return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; - } - function subgroup(self, f, d, i) { - var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; - return { - r: r, - a0: a0, - a1: a1, - p0: [ r * Math.cos(a0), r * Math.sin(a0) ], - p1: [ r * Math.cos(a1), r * Math.sin(a1) ] - }; - } - function equals(a, b) { - return a.a0 == b.a0 && a.a1 == b.a1; - } - function arc(r, p, a) { - return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; - } - function curve(r0, p0, r1, p1) { - return "Q 0,0 " + p1; - } - chord.radius = function(v) { - if (!arguments.length) return radius; - radius = d3_functor(v); - return chord; - }; - chord.source = function(v) { - if (!arguments.length) return source; - source = d3_functor(v); - return chord; - }; - chord.target = function(v) { - if (!arguments.length) return target; - target = d3_functor(v); - return chord; - }; - chord.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return chord; - }; - chord.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return chord; - }; - return chord; - }; - function d3_svg_chordRadius(d) { - return d.radius; - } - d3.svg.diagonal = function() { - var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; - function diagonal(d, i) { - var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { - x: p0.x, - y: m - }, { - x: p3.x, - y: m - }, p3 ]; - p = p.map(projection); - return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; - } - diagonal.source = function(x) { - if (!arguments.length) return source; - source = d3_functor(x); - return diagonal; - }; - diagonal.target = function(x) { - if (!arguments.length) return target; - target = d3_functor(x); - return diagonal; - }; - diagonal.projection = function(x) { - if (!arguments.length) return projection; - projection = x; - return diagonal; - }; - return diagonal; - }; - function d3_svg_diagonalProjection(d) { - return [ d.x, d.y ]; - } - d3.svg.diagonal.radial = function() { - var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; - diagonal.projection = function(x) { - return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; - }; - return diagonal; - }; - function d3_svg_diagonalRadialProjection(projection) { - return function() { - var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; - return [ r * Math.cos(a), r * Math.sin(a) ]; - }; - } - d3.svg.symbol = function() { - var type = d3_svg_symbolType, size = d3_svg_symbolSize; - function symbol(d, i) { - return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); - } - symbol.type = function(x) { - if (!arguments.length) return type; - type = d3_functor(x); - return symbol; - }; - symbol.size = function(x) { - if (!arguments.length) return size; - size = d3_functor(x); - return symbol; - }; - return symbol; - }; - function d3_svg_symbolSize() { - return 64; - } - function d3_svg_symbolType() { - return "circle"; - } - function d3_svg_symbolCircle(size) { - var r = Math.sqrt(size / π); - return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; - } - var d3_svg_symbols = d3.map({ - circle: d3_svg_symbolCircle, - cross: function(size) { - var r = Math.sqrt(size / 5) / 2; - return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; - }, - diamond: function(size) { - var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; - return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; - }, - square: function(size) { - var r = Math.sqrt(size) / 2; - return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; - }, - "triangle-down": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; - }, - "triangle-up": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; - } - }); - d3.svg.symbolTypes = d3_svg_symbols.keys(); - var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); - function d3_transition(groups, id) { - d3_subclass(groups, d3_transitionPrototype); - groups.id = id; - return groups; - } - var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; - d3_transitionPrototype.call = d3_selectionPrototype.call; - d3_transitionPrototype.empty = d3_selectionPrototype.empty; - d3_transitionPrototype.node = d3_selectionPrototype.node; - d3_transitionPrototype.size = d3_selectionPrototype.size; - d3.transition = function(selection) { - return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition(); - }; - d3.transition.prototype = d3_transitionPrototype; - d3_transitionPrototype.select = function(selector) { - var id = this.id, subgroups = [], subgroup, subnode, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - d3_transitionNode(subnode, i, id, node.__transition__[id]); - subgroup.push(subnode); - } else { - subgroup.push(null); - } - } - } - return d3_transition(subgroups, id); - }; - d3_transitionPrototype.selectAll = function(selector) { - var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - transition = node.__transition__[id]; - subnodes = selector.call(node, node.__data__, i, j); - subgroups.push(subgroup = []); - for (var k = -1, o = subnodes.length; ++k < o; ) { - if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition); - subgroup.push(subnode); - } - } - } - } - return d3_transition(subgroups, id); - }; - d3_transitionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_transition(subgroups, this.id); - }; - d3_transitionPrototype.tween = function(name, tween) { - var id = this.id; - if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); - return d3_selection_each(this, tween == null ? function(node) { - node.__transition__[id].tween.remove(name); - } : function(node) { - node.__transition__[id].tween.set(name, tween); - }); - }; - function d3_transition_tween(groups, name, value, tween) { - var id = groups.id; - return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); - } : (value = tween(value), function(node) { - node.__transition__[id].tween.set(name, value); - })); - } - d3_transitionPrototype.attr = function(nameNS, value) { - if (arguments.length < 2) { - for (value in nameNS) this.attr(value, nameNS[value]); - return this; - } - var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrTween(b) { - return b == null ? attrNull : (b += "", function() { - var a = this.getAttribute(name), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttribute(name, i(t)); - }); - }); - } - function attrTweenNS(b) { - return b == null ? attrNullNS : (b += "", function() { - var a = this.getAttributeNS(name.space, name.local), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttributeNS(name.space, name.local, i(t)); - }); - }); - } - return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.attrTween = function(nameNS, tween) { - var name = d3.ns.qualify(nameNS); - function attrTween(d, i) { - var f = tween.call(this, d, i, this.getAttribute(name)); - return f && function(t) { - this.setAttribute(name, f(t)); - }; - } - function attrTweenNS(d, i) { - var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); - return f && function(t) { - this.setAttributeNS(name.space, name.local, f(t)); - }; - } - return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.style(priority, name[priority], value); - return this; - } - priority = ""; - } - function styleNull() { - this.style.removeProperty(name); - } - function styleString(b) { - return b == null ? styleNull : (b += "", function() { - var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; - return a !== b && (i = d3_interpolate(a, b), function(t) { - this.style.setProperty(name, i(t), priority); - }); - }); - } - return d3_transition_tween(this, "style." + name, value, styleString); - }; - d3_transitionPrototype.styleTween = function(name, tween, priority) { - if (arguments.length < 3) priority = ""; - function styleTween(d, i) { - var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); - return f && function(t) { - this.style.setProperty(name, f(t), priority); - }; - } - return this.tween("style." + name, styleTween); - }; - d3_transitionPrototype.text = function(value) { - return d3_transition_tween(this, "text", value, d3_transition_text); - }; - function d3_transition_text(b) { - if (b == null) b = ""; - return function() { - this.textContent = b; - }; - } - d3_transitionPrototype.remove = function() { - return this.each("end.transition", function() { - var p; - if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this); - }); - }; - d3_transitionPrototype.ease = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].ease; - if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { - node.__transition__[id].ease = value; - }); - }; - d3_transitionPrototype.delay = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].delay; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].delay = +value.call(node, node.__data__, i, j); - } : (value = +value, function(node) { - node.__transition__[id].delay = value; - })); - }; - d3_transitionPrototype.duration = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].duration; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); - } : (value = Math.max(1, value), function(node) { - node.__transition__[id].duration = value; - })); - }; - d3_transitionPrototype.each = function(type, listener) { - var id = this.id; - if (arguments.length < 2) { - var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; - d3_transitionInheritId = id; - d3_selection_each(this, function(node, i, j) { - d3_transitionInherit = node.__transition__[id]; - type.call(node, node.__data__, i, j); - }); - d3_transitionInherit = inherit; - d3_transitionInheritId = inheritId; - } else { - d3_selection_each(this, function(node) { - var transition = node.__transition__[id]; - (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener); - }); - } - return this; - }; - d3_transitionPrototype.transition = function() { - var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition; - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if (node = group[i]) { - transition = Object.create(node.__transition__[id0]); - transition.delay += transition.duration; - d3_transitionNode(node, i, id1, transition); - } - subgroup.push(node); - } - } - return d3_transition(subgroups, id1); - }; - function d3_transitionNode(node, i, id, inherit) { - var lock = node.__transition__ || (node.__transition__ = { - active: 0, - count: 0 - }), transition = lock[id]; - if (!transition) { - var time = inherit.time; - transition = lock[id] = { - tween: new d3_Map(), - time: time, - ease: inherit.ease, - delay: inherit.delay, - duration: inherit.duration - }; - ++lock.count; - d3.timer(function(elapsed) { - var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = []; - timer.t = delay + time; - if (delay <= elapsed) return start(elapsed - delay); - timer.c = start; - function start(elapsed) { - if (lock.active > id) return stop(); - lock.active = id; - transition.event && transition.event.start.call(node, d, i); - transition.tween.forEach(function(key, value) { - if (value = value.call(node, d, i)) { - tweened.push(value); - } - }); - d3.timer(function() { - timer.c = tick(elapsed || 1) ? d3_true : tick; - return 1; - }, 0, time); - } - function tick(elapsed) { - if (lock.active !== id) return stop(); - var t = elapsed / duration, e = ease(t), n = tweened.length; - while (n > 0) { - tweened[--n].call(node, e); - } - if (t >= 1) { - transition.event && transition.event.end.call(node, d, i); - return stop(); - } - } - function stop() { - if (--lock.count) delete lock[id]; else delete node.__transition__; - return 1; - } - }, 0, time); - } - } - d3.svg.axis = function() { - var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; - function axis(g) { - g.each(function() { - var g = d3.select(this); - var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); - var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickTransform; - var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), - d3.transition(path)); - tickEnter.append("line"); - tickEnter.append("text"); - var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); - switch (orient) { - case "bottom": - { - tickTransform = d3_svg_axisX; - lineEnter.attr("y2", innerTickSize); - textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding); - lineUpdate.attr("x2", 0).attr("y2", innerTickSize); - textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding); - text.attr("dy", ".71em").style("text-anchor", "middle"); - pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); - break; - } - - case "top": - { - tickTransform = d3_svg_axisX; - lineEnter.attr("y2", -innerTickSize); - textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); - lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); - textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); - text.attr("dy", "0em").style("text-anchor", "middle"); - pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); - break; - } - - case "left": - { - tickTransform = d3_svg_axisY; - lineEnter.attr("x2", -innerTickSize); - textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)); - lineUpdate.attr("x2", -innerTickSize).attr("y2", 0); - textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0); - text.attr("dy", ".32em").style("text-anchor", "end"); - pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); - break; - } - - case "right": - { - tickTransform = d3_svg_axisY; - lineEnter.attr("x2", innerTickSize); - textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding); - lineUpdate.attr("x2", innerTickSize).attr("y2", 0); - textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0); - text.attr("dy", ".32em").style("text-anchor", "start"); - pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); - break; - } - } - if (scale1.rangeBand) { - var x = scale1, dx = x.rangeBand() / 2; - scale0 = scale1 = function(d) { - return x(d) + dx; - }; - } else if (scale0.rangeBand) { - scale0 = scale1; - } else { - tickExit.call(tickTransform, scale1); - } - tickEnter.call(tickTransform, scale0); - tickUpdate.call(tickTransform, scale1); - }); - } - axis.scale = function(x) { - if (!arguments.length) return scale; - scale = x; - return axis; - }; - axis.orient = function(x) { - if (!arguments.length) return orient; - orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; - return axis; - }; - axis.ticks = function() { - if (!arguments.length) return tickArguments_; - tickArguments_ = arguments; - return axis; - }; - axis.tickValues = function(x) { - if (!arguments.length) return tickValues; - tickValues = x; - return axis; - }; - axis.tickFormat = function(x) { - if (!arguments.length) return tickFormat_; - tickFormat_ = x; - return axis; - }; - axis.tickSize = function(x) { - var n = arguments.length; - if (!n) return innerTickSize; - innerTickSize = +x; - outerTickSize = +arguments[n - 1]; - return axis; - }; - axis.innerTickSize = function(x) { - if (!arguments.length) return innerTickSize; - innerTickSize = +x; - return axis; - }; - axis.outerTickSize = function(x) { - if (!arguments.length) return outerTickSize; - outerTickSize = +x; - return axis; - }; - axis.tickPadding = function(x) { - if (!arguments.length) return tickPadding; - tickPadding = +x; - return axis; - }; - axis.tickSubdivide = function() { - return arguments.length && axis; - }; - return axis; - }; - var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { - top: 1, - right: 1, - bottom: 1, - left: 1 - }; - function d3_svg_axisX(selection, x) { - selection.attr("transform", function(d) { - return "translate(" + x(d) + ",0)"; - }); - } - function d3_svg_axisY(selection, y) { - selection.attr("transform", function(d) { - return "translate(0," + y(d) + ")"; - }); - } - d3.svg.brush = function() { - var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; - function brush(g) { - g.each(function() { - var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); - var background = g.selectAll(".background").data([ 0 ]); - background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); - g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); - var resize = g.selectAll(".resize").data(resizes, d3_identity); - resize.exit().remove(); - resize.enter().append("g").attr("class", function(d) { - return "resize " + d; - }).style("cursor", function(d) { - return d3_svg_brushCursor[d]; - }).append("rect").attr("x", function(d) { - return /[ew]$/.test(d) ? -3 : null; - }).attr("y", function(d) { - return /^[ns]/.test(d) ? -3 : null; - }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); - resize.style("display", brush.empty() ? "none" : null); - var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; - if (x) { - range = d3_scaleRange(x); - backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); - redrawX(gUpdate); - } - if (y) { - range = d3_scaleRange(y); - backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); - redrawY(gUpdate); - } - redraw(gUpdate); - }); - } - brush.event = function(g) { - g.each(function() { - var event_ = event.of(this, arguments), extent1 = { - x: xExtent, - y: yExtent, - i: xExtentDomain, - j: yExtentDomain - }, extent0 = this.__chart__ || extent1; - this.__chart__ = extent1; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.brush", function() { - xExtentDomain = extent0.i; - yExtentDomain = extent0.j; - xExtent = extent0.x; - yExtent = extent0.y; - event_({ - type: "brushstart" - }); - }).tween("brush:brush", function() { - var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); - xExtentDomain = yExtentDomain = null; - return function(t) { - xExtent = extent1.x = xi(t); - yExtent = extent1.y = yi(t); - event_({ - type: "brush", - mode: "resize" - }); - }; - }).each("end.brush", function() { - xExtentDomain = extent1.i; - yExtentDomain = extent1.j; - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - }); - } else { - event_({ - type: "brushstart" - }); - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - } - }); - }; - function redraw(g) { - g.selectAll(".resize").attr("transform", function(d) { - return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; - }); - } - function redrawX(g) { - g.select(".extent").attr("x", xExtent[0]); - g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); - } - function redrawY(g) { - g.select(".extent").attr("y", yExtent[0]); - g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); - } - function brushstart() { - var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset; - var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup); - if (d3.event.changedTouches) { - w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); - } else { - w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); - } - g.interrupt().selectAll("*").interrupt(); - if (dragging) { - origin[0] = xExtent[0] - origin[0]; - origin[1] = yExtent[0] - origin[1]; - } else if (resizing) { - var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); - offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; - origin[0] = xExtent[ex]; - origin[1] = yExtent[ey]; - } else if (d3.event.altKey) center = origin.slice(); - g.style("pointer-events", "none").selectAll(".resize").style("display", null); - d3.select("body").style("cursor", eventTarget.style("cursor")); - event_({ - type: "brushstart" - }); - brushmove(); - function keydown() { - if (d3.event.keyCode == 32) { - if (!dragging) { - center = null; - origin[0] -= xExtent[1]; - origin[1] -= yExtent[1]; - dragging = 2; - } - d3_eventPreventDefault(); - } - } - function keyup() { - if (d3.event.keyCode == 32 && dragging == 2) { - origin[0] += xExtent[1]; - origin[1] += yExtent[1]; - dragging = 0; - d3_eventPreventDefault(); - } - } - function brushmove() { - var point = d3.mouse(target), moved = false; - if (offset) { - point[0] += offset[0]; - point[1] += offset[1]; - } - if (!dragging) { - if (d3.event.altKey) { - if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; - origin[0] = xExtent[+(point[0] < center[0])]; - origin[1] = yExtent[+(point[1] < center[1])]; - } else center = null; - } - if (resizingX && move1(point, x, 0)) { - redrawX(g); - moved = true; - } - if (resizingY && move1(point, y, 1)) { - redrawY(g); - moved = true; - } - if (moved) { - redraw(g); - event_({ - type: "brush", - mode: dragging ? "move" : "resize" - }); - } - } - function move1(point, scale, i) { - var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; - if (dragging) { - r0 -= position; - r1 -= size + position; - } - min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; - if (dragging) { - max = (min += position) + size; - } else { - if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); - if (position < min) { - max = min; - min = position; - } else { - max = position; - } - } - if (extent[0] != min || extent[1] != max) { - if (i) yExtentDomain = null; else xExtentDomain = null; - extent[0] = min; - extent[1] = max; - return true; - } - } - function brushend() { - brushmove(); - g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); - d3.select("body").style("cursor", null); - w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); - dragRestore(); - event_({ - type: "brushend" - }); - } - } - brush.x = function(z) { - if (!arguments.length) return x; - x = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.y = function(z) { - if (!arguments.length) return y; - y = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.clamp = function(z) { - if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; - if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; - return brush; - }; - brush.extent = function(z) { - var x0, x1, y0, y1, t; - if (!arguments.length) { - if (x) { - if (xExtentDomain) { - x0 = xExtentDomain[0], x1 = xExtentDomain[1]; - } else { - x0 = xExtent[0], x1 = xExtent[1]; - if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - } - } - if (y) { - if (yExtentDomain) { - y0 = yExtentDomain[0], y1 = yExtentDomain[1]; - } else { - y0 = yExtent[0], y1 = yExtent[1]; - if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - } - } - return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; - } - if (x) { - x0 = z[0], x1 = z[1]; - if (y) x0 = x0[0], x1 = x1[0]; - xExtentDomain = [ x0, x1 ]; - if (x.invert) x0 = x(x0), x1 = x(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; - } - if (y) { - y0 = z[0], y1 = z[1]; - if (x) y0 = y0[1], y1 = y1[1]; - yExtentDomain = [ y0, y1 ]; - if (y.invert) y0 = y(y0), y1 = y(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; - } - return brush; - }; - brush.clear = function() { - if (!brush.empty()) { - xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; - xExtentDomain = yExtentDomain = null; - } - return brush; - }; - brush.empty = function() { - return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; - }; - return d3.rebind(brush, event, "on"); - }; - var d3_svg_brushCursor = { - n: "ns-resize", - e: "ew-resize", - s: "ns-resize", - w: "ew-resize", - nw: "nwse-resize", - ne: "nesw-resize", - se: "nwse-resize", - sw: "nesw-resize" - }; - var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; - var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; - var d3_time_formatUtc = d3_time_format.utc; - var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); - d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; - function d3_time_formatIsoNative(date) { - return date.toISOString(); - } - d3_time_formatIsoNative.parse = function(string) { - var date = new Date(string); - return isNaN(date) ? null : date; - }; - d3_time_formatIsoNative.toString = d3_time_formatIso.toString; - d3_time.second = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 1e3) * 1e3); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 1e3); - }, function(date) { - return date.getSeconds(); - }); - d3_time.seconds = d3_time.second.range; - d3_time.seconds.utc = d3_time.second.utc.range; - d3_time.minute = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 6e4) * 6e4); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 6e4); - }, function(date) { - return date.getMinutes(); - }); - d3_time.minutes = d3_time.minute.range; - d3_time.minutes.utc = d3_time.minute.utc.range; - d3_time.hour = d3_time_interval(function(date) { - var timezone = date.getTimezoneOffset() / 60; - return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 36e5); - }, function(date) { - return date.getHours(); - }); - d3_time.hours = d3_time.hour.range; - d3_time.hours.utc = d3_time.hour.utc.range; - d3_time.month = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setDate(1); - return date; - }, function(date, offset) { - date.setMonth(date.getMonth() + offset); - }, function(date) { - return date.getMonth(); - }); - d3_time.months = d3_time.month.range; - d3_time.months.utc = d3_time.month.utc.range; - function d3_time_scale(linear, methods, format) { - function scale(x) { - return linear(x); - } - scale.invert = function(x) { - return d3_time_scaleDate(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return linear.domain().map(d3_time_scaleDate); - linear.domain(x); - return scale; - }; - function tickMethod(extent, count) { - var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); - return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { - return d / 31536e6; - }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; - } - scale.nice = function(interval, skip) { - var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); - if (method) interval = method[0], skip = method[1]; - function skipped(date) { - return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; - } - return scale.domain(d3_scale_nice(domain, skip > 1 ? { - floor: function(date) { - while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); - return date; - }, - ceil: function(date) { - while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); - return date; - } - } : interval)); - }; - scale.ticks = function(interval, skip) { - var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { - range: interval - }, skip ]; - if (method) interval = method[0], skip = method[1]; - return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); - }; - scale.tickFormat = function() { - return format; - }; - scale.copy = function() { - return d3_time_scale(linear.copy(), methods, format); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_time_scaleDate(t) { - return new Date(t); - } - var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; - var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; - var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { - return d.getMilliseconds(); - } ], [ ":%S", function(d) { - return d.getSeconds(); - } ], [ "%I:%M", function(d) { - return d.getMinutes(); - } ], [ "%I %p", function(d) { - return d.getHours(); - } ], [ "%a %d", function(d) { - return d.getDay() && d.getDate() != 1; - } ], [ "%b %d", function(d) { - return d.getDate() != 1; - } ], [ "%B", function(d) { - return d.getMonth(); - } ], [ "%Y", d3_true ] ]); - var d3_time_scaleMilliseconds = { - range: function(start, stop, step) { - return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); - }, - floor: d3_identity, - ceil: d3_identity - }; - d3_time_scaleLocalMethods.year = d3_time.year; - d3_time.scale = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); - }; - var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { - return [ m[0].utc, m[1] ]; - }); - var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { - return d.getUTCMilliseconds(); - } ], [ ":%S", function(d) { - return d.getUTCSeconds(); - } ], [ "%I:%M", function(d) { - return d.getUTCMinutes(); - } ], [ "%I %p", function(d) { - return d.getUTCHours(); - } ], [ "%a %d", function(d) { - return d.getUTCDay() && d.getUTCDate() != 1; - } ], [ "%b %d", function(d) { - return d.getUTCDate() != 1; - } ], [ "%B", function(d) { - return d.getUTCMonth(); - } ], [ "%Y", d3_true ] ]); - d3_time_scaleUtcMethods.year = d3_time.year.utc; - d3_time.scale.utc = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); - }; - d3.text = d3_xhrType(function(request) { - return request.responseText; - }); - d3.json = function(url, callback) { - return d3_xhr(url, "application/json", d3_json, callback); - }; - function d3_json(request) { - return JSON.parse(request.responseText); - } - d3.html = function(url, callback) { - return d3_xhr(url, "text/html", d3_html, callback); - }; - function d3_html(request) { - var range = d3_document.createRange(); - range.selectNode(d3_document.body); - return range.createContextualFragment(request.responseText); - } - d3.xml = d3_xhrType(function(request) { - return request.responseXML; - }); - if (typeof define === "function" && define.amd) { - define(d3); - } else if (typeof module === "object" && module.exports) { - module.exports = d3; - } else { - this.d3 = d3; - } -}(); \ No newline at end of file diff --git a/presto-main/src/main/resources/webapp/vendor/dagre-d3/dagre-d3-0.3.2.js b/presto-main/src/main/resources/webapp/vendor/dagre-d3/dagre-d3-0.3.2.js deleted file mode 100644 index ae8d6944294fe..0000000000000 --- a/presto-main/src/main/resources/webapp/vendor/dagre-d3/dagre-d3-0.3.2.js +++ /dev/null @@ -1,11825 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.dagreD3=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0; -} - -},{}],14:[function(require,module,exports){ -module.exports = intersectNode; - -function intersectNode(node, point) { - return node.intersect(point); -} - -},{}],15:[function(require,module,exports){ -var intersectLine = require("./intersect-line"); - -module.exports = intersectPolygon; - -/* - * Returns the point ({x, y}) at which the point argument intersects with the - * node argument assuming that it has the shape specified by polygon. - */ -function intersectPolygon(node, polyPoints, point) { - var x1 = node.x; - var y1 = node.y; - - var intersections = []; - - var minX = Number.POSITIVE_INFINITY, - minY = Number.POSITIVE_INFINITY; - polyPoints.forEach(function(entry) { - minX = Math.min(minX, entry.x); - minY = Math.min(minY, entry.y); - }); - - var left = x1 - node.width / 2 - minX; - var top = y1 - node.height / 2 - minY; - - for (var i = 0; i < polyPoints.length; i++) { - var p1 = polyPoints[i]; - var p2 = polyPoints[i < polyPoints.length - 1 ? i + 1 : 0]; - var intersect = intersectLine(node, point, - {x: left + p1.x, y: top + p1.y}, {x: left + p2.x, y: top + p2.y}); - if (intersect) { - intersections.push(intersect); - } - } - - if (!intersections.length) { - console.log("NO INTERSECTION FOUND, RETURN NODE CENTER", node); - return node; - } - - if (intersections.length > 1) { - // More intersections, find the one nearest to edge end point - intersections.sort(function(p, q) { - var pdx = p.x - point.x, - pdy = p.y - point.y, - distp = Math.sqrt(pdx * pdx + pdy * pdy), - - qdx = q.x - point.x, - qdy = q.y - point.y, - distq = Math.sqrt(qdx * qdx + qdy * qdy); - - return (distp < distq) ? -1 : (distp === distq ? 0 : 1); - }); - } - return intersections[0]; -} - -},{"./intersect-line":13}],16:[function(require,module,exports){ -module.exports = intersectRect; - -function intersectRect(node, point) { - var x = node.x; - var y = node.y; - - // Rectangle intersection algorithm from: - // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes - var dx = point.x - x; - var dy = point.y - y; - var w = node.width / 2; - var h = node.height / 2; - - var sx, sy; - if (Math.abs(dy) * w > Math.abs(dx) * h) { - // Intersection is top or bottom of rect. - if (dy < 0) { - h = -h; - } - sx = dy === 0 ? 0 : h * dx / dy; - sy = h; - } else { - // Intersection is left or right of rect. - if (dx < 0) { - w = -w; - } - sx = w; - sy = dx === 0 ? 0 : w * dy / dx; - } - - return {x: x + sx, y: y + sy}; -} - -},{}],17:[function(require,module,exports){ -var util = require("../util"); - -module.exports = addHtmlLabel; - -function addHtmlLabel(root, node) { - var fo = root - .append("foreignObject") - .attr("width", "100000"); - - var div = fo - .append("xhtml:div"); - - var label = node.label; - switch(typeof label) { - case "function": - div.insert(label); - break; - case "object": - // Currently we assume this is a DOM object. - div.insert(function() { return label; }); - break; - default: div.html(label); - } - - util.applyStyle(div, node.labelStyle); - div.style("display", "inline-block"); - // Fix for firefox - div.style("white-space", "nowrap"); - - // TODO find a better way to get dimensions for foreignObjects... - var w, h; - div - .each(function() { - w = this.clientWidth; - h = this.clientHeight; - }); - - fo - .attr("width", w) - .attr("height", h); - - return fo; -} - -},{"../util":25}],18:[function(require,module,exports){ -var addTextLabel = require("./add-text-label"), - addHtmlLabel = require("./add-html-label"); - -module.exports = addLabel; - -function addLabel(root, node) { - var label = node.label; - var labelSvg = root.append("g"); - - // Allow the label to be a string, a function that returns a DOM element, or - // a DOM element itself. - if (typeof label !== "string" || node.labelType === "html") { - addHtmlLabel(labelSvg, node); - } else { - addTextLabel(labelSvg, node); - } - - var labelBBox = labelSvg.node().getBBox(); - labelSvg.attr("transform", - "translate(" + (-labelBBox.width / 2) + "," + (-labelBBox.height / 2) + ")"); - - return labelSvg; -} - -},{"./add-html-label":17,"./add-text-label":19}],19:[function(require,module,exports){ -var util = require("../util"); - -module.exports = addTextLabel; - -/* - * Attaches a text label to the specified root. Handles escape sequences. - */ -function addTextLabel(root, node) { - var domNode = root.append("text"); - - var lines = processEscapeSequences(node.label).split("\n"); - for (var i = 0; i < lines.length; i++) { - domNode - .append("tspan") - .attr("xml:space", "preserve") - .attr("dy", "1em") - .attr("x", "1") - .text(lines[i]); - } - - util.applyStyle(domNode, node.labelStyle); - - return domNode; -} - -function processEscapeSequences(text) { - var newText = "", - escaped = false, - ch; - for (var i = 0; i < text.length; ++i) { - ch = text[i]; - if (escaped) { - switch(ch) { - case "n": newText += "\n"; break; - default: newText += ch; - } - escaped = false; - } else if (ch === "\\") { - escaped = true; - } else { - newText += ch; - } - } - return newText; -} - -},{"../util":25}],20:[function(require,module,exports){ -/* global window */ - -var lodash; - -if (require) { - try { - lodash = require("lodash"); - } catch (e) {} -} - -if (!lodash) { - lodash = window._; -} - -module.exports = lodash; - -},{"lodash":77}],21:[function(require,module,exports){ -"use strict"; - -var util = require("./util"), - d3 = require("./d3"), - _ = require("./lodash"); - -module.exports = positionEdgeLabels; - -function positionEdgeLabels(selection, g) { - var created = selection.filter(function() { return !d3.select(this).classed("update"); }); - - function translate(e) { - var edge = g.edge(e); - return _.has(edge, "x") ? "translate(" + edge.x + "," + edge.y + ")" : ""; - } - - created.attr("transform", translate); - - util.applyTransition(selection, g) - .style("opacity", 1) - .attr("transform", translate); -} - -},{"./d3":7,"./lodash":20,"./util":25}],22:[function(require,module,exports){ -"use strict"; - -var util = require("./util"), - d3 = require("./d3"); - -module.exports = positionNodes; - -function positionNodes(selection, g) { - var created = selection.filter(function() { return !d3.select(this).classed("update"); }); - - function translate(v) { - var node = g.node(v); - return "translate(" + node.x + "," + node.y + ")"; - } - - created.attr("transform", translate); - - util.applyTransition(selection, g) - .style("opacity", 1) - .attr("transform", translate); -} - -},{"./d3":7,"./util":25}],23:[function(require,module,exports){ -var _ = require("./lodash"), - layout = require("./dagre").layout; - -module.exports = render; - -// This design is based on http://bost.ocks.org/mike/chart/. -function render() { - var createNodes = require("./create-nodes"), - createClusters = require("./create-clusters"), - createEdgeLabels = require("./create-edge-labels"), - createEdgePaths = require("./create-edge-paths"), - positionNodes = require("./position-nodes"), - positionEdgeLabels = require("./position-edge-labels"), - shapes = require("./shapes"), - arrows = require("./arrows"); - - var fn = function(svg, g) { - preProcessGraph(g); - - var outputGroup = createOrSelectGroup(svg, "output"), - clustersGroup = createOrSelectGroup(outputGroup, "clusters"), - edgePathsGroup = createOrSelectGroup(outputGroup, "edgePaths"), - edgeLabels = createEdgeLabels(createOrSelectGroup(outputGroup, "edgeLabels"), g), - nodes = createNodes(createOrSelectGroup(outputGroup, "nodes"), g, shapes); - - layout(g); - - positionNodes(nodes, g); - positionEdgeLabels(edgeLabels, g); - createEdgePaths(edgePathsGroup, g, arrows); - createClusters(clustersGroup, g); - - postProcessGraph(g); - }; - - fn.createNodes = function(value) { - if (!arguments.length) return createNodes; - createNodes = value; - return fn; - }; - - fn.createClusters = function(value) { - if (!arguments.length) return createClusters; - createClusters = value; - return fn; - }; - - fn.createEdgeLabels = function(value) { - if (!arguments.length) return createEdgeLabels; - createEdgeLabels = value; - return fn; - }; - - fn.createEdgePaths = function(value) { - if (!arguments.length) return createEdgePaths; - createEdgePaths = value; - return fn; - }; - - fn.shapes = function(value) { - if (!arguments.length) return shapes; - shapes = value; - return fn; - }; - - fn.arrows = function(value) { - if (!arguments.length) return arrows; - arrows = value; - return fn; - }; - - return fn; -} - -var NODE_DEFAULT_ATTRS = { - paddingLeft: 10, - paddingRight: 10, - paddingTop: 10, - paddingBottom: 10, - rx: 0, - ry: 0, - shape: "rect" -}; - -var EDGE_DEFAULT_ATTRS = { - arrowhead: "normal", - lineInterpolate: "linear" -}; - -function preProcessGraph(g) { - g.nodes().forEach(function(v) { - var node = g.node(v); - if (!_.has(node, "label")) { node.label = v; } - - if (_.has(node, "paddingX")) { - _.defaults(node, { - paddingLeft: node.paddingX, - paddingRight: node.paddingX - }); - } - - if (_.has(node, "paddingY")) { - _.defaults(node, { - paddingTop: node.paddingY, - paddingBottom: node.paddingY - }); - } - - if (_.has(node, "padding")) { - _.defaults(node, { - paddingLeft: node.padding, - paddingRight: node.padding, - paddingTop: node.padding, - paddingBottom: node.padding - }); - } - - _.defaults(node, NODE_DEFAULT_ATTRS); - - _.each(["paddingLeft", "paddingRight", "paddingTop", "paddingBottom"], function(k) { - node[k] = Number(node[k]); - }); - - // Save dimensions for restore during post-processing - if (_.has(node, "width")) { node._prevWidth = node.width; } - if (_.has(node, "height")) { node._prevHeight = node.height; } - }); - - g.edges().forEach(function(e) { - var edge = g.edge(e); - if (!_.has(edge, "label")) { edge.label = ""; } - _.defaults(edge, EDGE_DEFAULT_ATTRS); - }); -} - -function postProcessGraph(g) { - _.each(g.nodes(), function(v) { - var node = g.node(v); - - // Restore original dimensions - if (_.has(node, "_prevWidth")) { - node.width = node._prevWidth; - } else { - delete node.width; - } - - if (_.has(node, "_prevHeight")) { - node.height = node._prevHeight; - } else { - delete node.height; - } - - delete node._prevWidth; - delete node._prevHeight; - }); -} - -function createOrSelectGroup(root, name) { - var selection = root.select("g." + name); - if (selection.empty()) { - selection = root.append("g").attr("class", name); - } - return selection; -} - -},{"./arrows":2,"./create-clusters":3,"./create-edge-labels":4,"./create-edge-paths":5,"./create-nodes":6,"./dagre":8,"./lodash":20,"./position-edge-labels":21,"./position-nodes":22,"./shapes":24}],24:[function(require,module,exports){ -"use strict"; - -var intersectRect = require("./intersect/intersect-rect"), - intersectEllipse = require("./intersect/intersect-ellipse"), - intersectCircle = require("./intersect/intersect-circle"); - -module.exports = { - rect: rect, - ellipse: ellipse, - circle: circle -}; - -function rect(parent, bbox, node) { - var shapeSvg = parent.insert("rect", ":first-child") - .attr("rx", node.rx) - .attr("ry", node.ry) - .attr("x", -bbox.width / 2) - .attr("y", -bbox.height / 2) - .attr("width", bbox.width) - .attr("height", bbox.height); - - node.intersect = function(point) { - return intersectRect(node, point); - }; - - return shapeSvg; -} - -function ellipse(parent, bbox, node) { - var rx = bbox.width / 2, - ry = bbox.height / 2, - shapeSvg = parent.insert("ellipse", ":first-child") - .attr("x", -bbox.width / 2) - .attr("y", -bbox.height / 2) - .attr("rx", rx) - .attr("ry", ry); - - node.intersect = function(point) { - return intersectEllipse(node, rx, ry, point); - }; - - return shapeSvg; -} - -function circle(parent, bbox, node) { - var r = Math.max(bbox.width, bbox.height) / 2, - shapeSvg = parent.insert("circle", ":first-child") - .attr("x", -bbox.width / 2) - .attr("y", -bbox.height / 2) - .attr("r", r); - - node.intersect = function(point) { - return intersectCircle(node, r, point); - }; - - return shapeSvg; -} - -},{"./intersect/intersect-circle":11,"./intersect/intersect-ellipse":12,"./intersect/intersect-rect":16}],25:[function(require,module,exports){ -var _ = require("./lodash"); - -// Public utility functions -module.exports = { - isSubgraph: isSubgraph, - edgeToId: edgeToId, - applyStyle: applyStyle, - applyClass: applyClass, - applyTransition: applyTransition -}; - -/* - * Returns true if the specified node in the graph is a subgraph node. A - * subgraph node is one that contains other nodes. - */ -function isSubgraph(g, v) { - return !!g.children(v).length; -} - -function edgeToId(e) { - return escapeId(e.v) + ":" + escapeId(e.w) + ":" + escapeId(e.name); -} - -var ID_DELIM = /:/g; -function escapeId(str) { - return str ? String(str).replace(ID_DELIM, "\\:") : ""; -} - -function applyStyle(dom, styleFn) { - if (styleFn) { - dom.attr("style", styleFn); - } -} - -function applyClass(dom, classFn, otherClasses) { - if (classFn) { - dom - .attr("class", classFn) - .attr("class", otherClasses + " " + dom.attr("class")); - } -} - -function applyTransition(selection, g) { - var graph = g.graph(); - - if (_.isPlainObject(graph)) { - var transition = graph.transition; - if (_.isFunction(transition)) { - return transition(selection); - } - } - - return selection; -} - -},{"./lodash":20}],26:[function(require,module,exports){ -module.exports = "0.3.2"; - -},{}],27:[function(require,module,exports){ -/* -Copyright (c) 2012-2014 Chris Pettitt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -module.exports = { - graphlib: require("./lib/graphlib"), - - layout: require("./lib/layout"), - debug: require("./lib/debug"), - util: { - time: require("./lib/util").time, - notime: require("./lib/util").notime - }, - version: require("./lib/version") -}; - -},{"./lib/debug":32,"./lib/graphlib":33,"./lib/layout":35,"./lib/util":55,"./lib/version":56}],28:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - greedyFAS = require("./greedy-fas"); - -module.exports = { - run: run, - undo: undo -}; - -function run(g) { - var fas = (g.graph().acyclicer === "greedy" - ? greedyFAS(g, weightFn(g)) - : dfsFAS(g)); - _.each(fas, function(e) { - var label = g.edge(e); - g.removeEdge(e); - label.forwardName = e.name; - label.reversed = true; - g.setEdge(e.w, e.v, label, _.uniqueId("rev")); - }); - - function weightFn(g) { - return function(e) { - return g.edge(e).weight; - }; - } -} - -function dfsFAS(g) { - var fas = [], - stack = {}, - visited = {}; - - function dfs(v) { - if (_.has(visited, v)) { - return; - } - visited[v] = true; - stack[v] = true; - _.each(g.outEdges(v), function(e) { - if (_.has(stack, e.w)) { - fas.push(e); - } else { - dfs(e.w); - } - }); - delete stack[v]; - } - - _.each(g.nodes(), dfs); - return fas; -} - -function undo(g) { - _.each(g.edges(), function(e) { - var label = g.edge(e); - if (label.reversed) { - g.removeEdge(e); - - var forwardName = label.forwardName; - delete label.reversed; - delete label.forwardName; - g.setEdge(e.w, e.v, label, forwardName); - } - }); -} - -},{"./greedy-fas":34,"./lodash":36}],29:[function(require,module,exports){ -var _ = require("./lodash"), - util = require("./util"); - -module.exports = addBorderSegments; - -function addBorderSegments(g) { - function dfs(v) { - var children = g.children(v), - node = g.node(v); - if (children.length) { - _.each(children, dfs); - } - - if (_.has(node, "minRank")) { - node.borderLeft = []; - node.borderRight = []; - for (var rank = node.minRank, maxRank = node.maxRank + 1; - rank < maxRank; - ++rank) { - addBorderNode(g, "borderLeft", "_bl", v, node, rank); - addBorderNode(g, "borderRight", "_br", v, node, rank); - } - } - } - - _.each(g.children(), dfs); -} - -function addBorderNode(g, prop, prefix, sg, sgNode, rank) { - var label = { width: 0, height: 0, rank: rank }, - prev = sgNode[prop][rank - 1], - curr = util.addDummyNode(g, "border", label, prefix); - sgNode[prop][rank] = curr; - g.setParent(curr, sg); - if (prev) { - g.setEdge(prev, curr, { weight: 1 }); - } -} - -},{"./lodash":36,"./util":55}],30:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"); - -module.exports = { - adjust: adjust, - undo: undo -}; - -function adjust(g) { - var rankDir = g.graph().rankdir.toLowerCase(); - if (rankDir === "lr" || rankDir === "rl") { - swapWidthHeight(g); - } -} - -function undo(g) { - var rankDir = g.graph().rankdir.toLowerCase(); - if (rankDir === "bt" || rankDir === "rl") { - reverseY(g); - } - - if (rankDir === "lr" || rankDir === "rl") { - swapXY(g); - swapWidthHeight(g); - } -} - -function swapWidthHeight(g) { - _.each(g.nodes(), function(v) { swapWidthHeightOne(g.node(v)); }); - _.each(g.edges(), function(e) { swapWidthHeightOne(g.edge(e)); }); -} - -function swapWidthHeightOne(attrs) { - var w = attrs.width; - attrs.width = attrs.height; - attrs.height = w; -} - -function reverseY(g) { - _.each(g.nodes(), function(v) { reverseYOne(g.node(v)); }); - - _.each(g.edges(), function(e) { - var edge = g.edge(e); - _.each(edge.points, reverseYOne); - if (_.has(edge, "y")) { - reverseYOne(edge); - } - }); -} - -function reverseYOne(attrs) { - attrs.y = -attrs.y; -} - -function swapXY(g) { - _.each(g.nodes(), function(v) { swapXYOne(g.node(v)); }); - - _.each(g.edges(), function(e) { - var edge = g.edge(e); - _.each(edge.points, swapXYOne); - if (_.has(edge, "x")) { - swapXYOne(edge); - } - }); -} - -function swapXYOne(attrs) { - var x = attrs.x; - attrs.x = attrs.y; - attrs.y = x; -} - -},{"./lodash":36}],31:[function(require,module,exports){ -/* - * Simple doubly linked list implementation derived from Cormen, et al., - * "Introduction to Algorithms". - */ - -module.exports = List; - -function List() { - var sentinel = {}; - sentinel._next = sentinel._prev = sentinel; - this._sentinel = sentinel; -} - -List.prototype.dequeue = function() { - var sentinel = this._sentinel, - entry = sentinel._prev; - if (entry !== sentinel) { - unlink(entry); - return entry; - } -}; - -List.prototype.enqueue = function(entry) { - var sentinel = this._sentinel; - if (entry._prev && entry._next) { - unlink(entry); - } - entry._next = sentinel._next; - sentinel._next._prev = entry; - sentinel._next = entry; - entry._prev = sentinel; -}; - -List.prototype.toString = function() { - var strs = [], - sentinel = this._sentinel, - curr = sentinel._prev; - while (curr !== sentinel) { - strs.push(JSON.stringify(curr, filterOutLinks)); - curr = curr._prev; - } - return "[" + strs.join(", ") + "]"; -}; - -function unlink(entry) { - entry._prev._next = entry._next; - entry._next._prev = entry._prev; - delete entry._next; - delete entry._prev; -} - -function filterOutLinks(k, v) { - if (k !== "_next" && k !== "_prev") { - return v; - } -} - -},{}],32:[function(require,module,exports){ -var _ = require("./lodash"), - util = require("./util"), - Graph = require("./graphlib").Graph; - -module.exports = { - debugOrdering: debugOrdering -}; - -/* istanbul ignore next */ -function debugOrdering(g) { - var layerMatrix = util.buildLayerMatrix(g); - - var h = new Graph({ compound: true, multigraph: true }).setGraph({}); - - _.each(g.nodes(), function(v) { - h.setNode(v, { label: v }); - h.setParent(v, "layer" + g.node(v).rank); - }); - - _.each(g.edges(), function(e) { - h.setEdge(e.v, e.w, {}, e.name); - }); - - _.each(layerMatrix, function(layer, i) { - var layerV = "layer" + i; - h.setNode(layerV, { rank: "same" }); - _.reduce(layer, function(u, v) { - h.setEdge(u, v, { style: "invis" }); - return v; - }); - }); - - return h; -} - -},{"./graphlib":33,"./lodash":36,"./util":55}],33:[function(require,module,exports){ -module.exports=require(9) -},{"/Users/cpettitt/projects/dagre-d3/lib/graphlib.js":9,"graphlib":57}],34:[function(require,module,exports){ -var _ = require("./lodash"), - Graph = require("./graphlib").Graph, - List = require("./data/list"); - -/* - * A greedy heuristic for finding a feedback arc set for a graph. A feedback - * arc set is a set of edges that can be removed to make a graph acyclic. - * The algorithm comes from: P. Eades, X. Lin, and W. F. Smyth, "A fast and - * effective heuristic for the feedback arc set problem." This implementation - * adjusts that from the paper to allow for weighted edges. - */ -module.exports = greedyFAS; - -var DEFAULT_WEIGHT_FN = _.constant(1); - -function greedyFAS(g, weightFn) { - if (g.nodeCount() <= 1) { - return []; - } - var state = buildState(g, weightFn || DEFAULT_WEIGHT_FN); - var results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx); - - // Expand multi-edges - return _.flatten(_.map(results, function(e) { - return g.outEdges(e.v, e.w); - }), true); -} - -function doGreedyFAS(g, buckets, zeroIdx) { - var results = [], - sources = buckets[buckets.length - 1], - sinks = buckets[0]; - - var entry; - while (g.nodeCount()) { - while ((entry = sinks.dequeue())) { removeNode(g, buckets, zeroIdx, entry); } - while ((entry = sources.dequeue())) { removeNode(g, buckets, zeroIdx, entry); } - if (g.nodeCount()) { - for (var i = buckets.length - 2; i > 0; --i) { - entry = buckets[i].dequeue(); - if (entry) { - results = results.concat(removeNode(g, buckets, zeroIdx, entry, true)); - break; - } - } - } - } - - return results; -} - -function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) { - var results = collectPredecessors ? [] : undefined; - - _.each(g.inEdges(entry.v), function(edge) { - var weight = g.edge(edge), - uEntry = g.node(edge.v); - - if (collectPredecessors) { - results.push({ v: edge.v, w: edge.w }); - } - - uEntry.out -= weight; - assignBucket(buckets, zeroIdx, uEntry); - }); - - _.each(g.outEdges(entry.v), function(edge) { - var weight = g.edge(edge), - w = edge.w, - wEntry = g.node(w); - wEntry.in -= weight; - assignBucket(buckets, zeroIdx, wEntry); - }); - - g.removeNode(entry.v); - - return results; -} - -function buildState(g, weightFn) { - var fasGraph = new Graph(), - maxIn = 0, - maxOut = 0; - - _.each(g.nodes(), function(v) { - fasGraph.setNode(v, { v: v, in: 0, out: 0 }); - }); - - // Aggregate weights on nodes, but also sum the weights across multi-edges - // into a single edge for the fasGraph. - _.each(g.edges(), function(e) { - var prevWeight = fasGraph.edge(e.v, e.w) || 0, - weight = weightFn(e), - edgeWeight = prevWeight + weight; - fasGraph.setEdge(e.v, e.w, edgeWeight); - maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight); - maxIn = Math.max(maxIn, fasGraph.node(e.w).in += weight); - }); - - var buckets = _.range(maxOut + maxIn + 3).map(function() { return new List(); }); - var zeroIdx = maxIn + 1; - - _.each(fasGraph.nodes(), function(v) { - assignBucket(buckets, zeroIdx, fasGraph.node(v)); - }); - - return { graph: fasGraph, buckets: buckets, zeroIdx: zeroIdx }; -} - -function assignBucket(buckets, zeroIdx, entry) { - if (!entry.out) { - buckets[0].enqueue(entry); - } else if (!entry.in) { - buckets[buckets.length - 1].enqueue(entry); - } else { - buckets[entry.out - entry.in + zeroIdx].enqueue(entry); - } -} - -},{"./data/list":31,"./graphlib":33,"./lodash":36}],35:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - acyclic = require("./acyclic"), - normalize = require("./normalize"), - rank = require("./rank"), - normalizeRanks = require("./util").normalizeRanks, - parentDummyChains = require("./parent-dummy-chains"), - removeEmptyRanks = require("./util").removeEmptyRanks, - nestingGraph = require("./nesting-graph"), - addBorderSegments = require("./add-border-segments"), - coordinateSystem = require("./coordinate-system"), - order = require("./order"), - position = require("./position"), - util = require("./util"), - Graph = require("./graphlib").Graph; - -module.exports = layout; - -function layout(g, opts) { - var time = opts && opts.debugTiming ? util.time : util.notime; - time("layout", function() { - var layoutGraph = time(" buildLayoutGraph", - function() { return buildLayoutGraph(g); }); - time(" runLayout", function() { runLayout(layoutGraph, time); }); - time(" updateInputGraph", function() { updateInputGraph(g, layoutGraph); }); - }); -} - -function runLayout(g, time) { - time(" makeSpaceForEdgeLabels", function() { makeSpaceForEdgeLabels(g); }); - time(" removeSelfEdges", function() { removeSelfEdges(g); }); - time(" acyclic", function() { acyclic.run(g); }); - time(" nestingGraph.run", function() { nestingGraph.run(g); }); - time(" rank", function() { rank(util.asNonCompoundGraph(g)); }); - time(" injectEdgeLabelProxies", function() { injectEdgeLabelProxies(g); }); - time(" removeEmptyRanks", function() { removeEmptyRanks(g); }); - time(" nestingGraph.cleanup", function() { nestingGraph.cleanup(g); }); - time(" normalizeRanks", function() { normalizeRanks(g); }); - time(" assignRankMinMax", function() { assignRankMinMax(g); }); - time(" removeEdgeLabelProxies", function() { removeEdgeLabelProxies(g); }); - time(" normalize.run", function() { normalize.run(g); }); - time(" parentDummyChains", function() { parentDummyChains(g); }); - time(" addBorderSegments", function() { addBorderSegments(g); }); - time(" order", function() { order(g); }); - time(" insertSelfEdges", function() { insertSelfEdges(g); }); - time(" adjustCoordinateSystem", function() { coordinateSystem.adjust(g); }); - time(" position", function() { position(g); }); - time(" positionSelfEdges", function() { positionSelfEdges(g); }); - time(" removeBorderNodes", function() { removeBorderNodes(g); }); - time(" normalize.undo", function() { normalize.undo(g); }); - time(" fixupEdgeLabelCoords", function() { fixupEdgeLabelCoords(g); }); - time(" undoCoordinateSystem", function() { coordinateSystem.undo(g); }); - time(" translateGraph", function() { translateGraph(g); }); - time(" assignNodeIntersects", function() { assignNodeIntersects(g); }); - time(" reversePoints", function() { reversePointsForReversedEdges(g); }); - time(" acyclic.undo", function() { acyclic.undo(g); }); -} - -/* - * Copies final layout information from the layout graph back to the input - * graph. This process only copies whitelisted attributes from the layout graph - * to the input graph, so it serves as a good place to determine what - * attributes can influence layout. - */ -function updateInputGraph(inputGraph, layoutGraph) { - _.each(inputGraph.nodes(), function(v) { - var inputLabel = inputGraph.node(v), - layoutLabel = layoutGraph.node(v); - - if (inputLabel) { - inputLabel.x = layoutLabel.x; - inputLabel.y = layoutLabel.y; - - if (layoutGraph.children(v).length) { - inputLabel.width = layoutLabel.width; - inputLabel.height = layoutLabel.height; - } - } - }); - - _.each(inputGraph.edges(), function(e) { - var inputLabel = inputGraph.edge(e), - layoutLabel = layoutGraph.edge(e); - - inputLabel.points = layoutLabel.points; - if (_.has(layoutLabel, "x")) { - inputLabel.x = layoutLabel.x; - inputLabel.y = layoutLabel.y; - } - }); - - inputGraph.graph().width = layoutGraph.graph().width; - inputGraph.graph().height = layoutGraph.graph().height; -} - -var graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"], - graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" }, - graphAttrs = ["acyclicer", "ranker", "rankdir", "align"], - nodeNumAttrs = ["width", "height"], - nodeDefaults = { width: 0, height: 0 }, - edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"], - edgeDefaults = { - minlen: 1, weight: 1, width: 0, height: 0, - labeloffset: 10, labelpos: "r" - }, - edgeAttrs = ["labelpos"]; - -/* - * Constructs a new graph from the input graph, which can be used for layout. - * This process copies only whitelisted attributes from the input graph to the - * layout graph. Thus this function serves as a good place to determine what - * attributes can influence layout. - */ -function buildLayoutGraph(inputGraph) { - var g = new Graph({ multigraph: true, compound: true }), - graph = canonicalize(inputGraph.graph()); - - g.setGraph(_.merge({}, - graphDefaults, - selectNumberAttrs(graph, graphNumAttrs), - _.pick(graph, graphAttrs))); - - _.each(inputGraph.nodes(), function(v) { - var node = canonicalize(inputGraph.node(v)); - g.setNode(v, _.defaults(selectNumberAttrs(node, nodeNumAttrs), nodeDefaults)); - g.setParent(v, inputGraph.parent(v)); - }); - - _.each(inputGraph.edges(), function(e) { - var edge = canonicalize(inputGraph.edge(e)); - g.setEdge(e, _.merge({}, - edgeDefaults, - selectNumberAttrs(edge, edgeNumAttrs), - _.pick(edge, edgeAttrs))); - }); - - return g; -} - -/* - * This idea comes from the Gansner paper: to account for edge labels in our - * layout we split each rank in half by doubling minlen and halving ranksep. - * Then we can place labels at these mid-points between nodes. - * - * We also add some minimal padding to the width to push the label for the edge - * away from the edge itself a bit. - */ -function makeSpaceForEdgeLabels(g) { - var graph = g.graph(); - graph.ranksep /= 2; - _.each(g.edges(), function(e) { - var edge = g.edge(e); - edge.minlen *= 2; - if (edge.labelpos.toLowerCase() !== "c") { - if (graph.rankdir === "TB" || graph.rankdir === "BT") { - edge.width += edge.labeloffset; - } else { - edge.height += edge.labeloffset; - } - } - }); -} - -/* - * Creates temporary dummy nodes that capture the rank in which each edge's - * label is going to, if it has one of non-zero width and height. We do this - * so that we can safely remove empty ranks while preserving balance for the - * label's position. - */ -function injectEdgeLabelProxies(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.width && edge.height) { - var v = g.node(e.v), - w = g.node(e.w), - label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e }; - util.addDummyNode(g, "edge-proxy", label, "_ep"); - } - }); -} - -function assignRankMinMax(g) { - var maxRank = 0; - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.borderTop) { - node.minRank = g.node(node.borderTop).rank; - node.maxRank = g.node(node.borderBottom).rank; - maxRank = _.max(maxRank, node.maxRank); - } - }); - g.graph().maxRank = maxRank; -} - -function removeEdgeLabelProxies(g) { - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.dummy === "edge-proxy") { - g.edge(node.e).labelRank = node.rank; - g.removeNode(v); - } - }); -} - -function translateGraph(g) { - var minX = Number.POSITIVE_INFINITY, - maxX = 0, - minY = Number.POSITIVE_INFINITY, - maxY = 0, - graphLabel = g.graph(), - marginX = graphLabel.marginx || 0, - marginY = graphLabel.marginy || 0; - - function getExtremes(attrs) { - var x = attrs.x, - y = attrs.y, - w = attrs.width, - h = attrs.height; - minX = Math.min(minX, x - w / 2); - maxX = Math.max(maxX, x + w / 2); - minY = Math.min(minY, y - h / 2); - maxY = Math.max(maxY, y + h / 2); - } - - _.each(g.nodes(), function(v) { getExtremes(g.node(v)); }); - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (_.has(edge, "x")) { - getExtremes(edge); - } - }); - - minX -= marginX; - minY -= marginY; - - _.each(g.nodes(), function(v) { - var node = g.node(v); - node.x -= minX; - node.y -= minY; - }); - - _.each(g.edges(), function(e) { - var edge = g.edge(e); - _.each(edge.points, function(p) { - p.x -= minX; - p.y -= minY; - }); - if (_.has(edge, "x")) { edge.x -= minX; } - if (_.has(edge, "y")) { edge.y -= minY; } - }); - - graphLabel.width = maxX - minX + marginX; - graphLabel.height = maxY - minY + marginY; -} - -function assignNodeIntersects(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e), - nodeV = g.node(e.v), - nodeW = g.node(e.w), - p1, p2; - if (!edge.points) { - edge.points = []; - p1 = nodeW; - p2 = nodeV; - } else { - p1 = edge.points[0]; - p2 = edge.points[edge.points.length - 1]; - } - edge.points.unshift(util.intersectRect(nodeV, p1)); - edge.points.push(util.intersectRect(nodeW, p2)); - }); -} - -function fixupEdgeLabelCoords(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (_.has(edge, "x")) { - if (edge.labelpos === "l" || edge.labelpos === "r") { - edge.width -= edge.labeloffset; - } - switch (edge.labelpos) { - case "l": edge.x -= edge.width / 2 + edge.labeloffset; break; - case "r": edge.x += edge.width / 2 + edge.labeloffset; break; - } - } - }); -} - -function reversePointsForReversedEdges(g) { - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.reversed) { - edge.points.reverse(); - } - }); -} - -function removeBorderNodes(g) { - _.each(g.nodes(), function(v) { - if (g.children(v).length) { - var node = g.node(v), - t = g.node(node.borderTop), - b = g.node(node.borderBottom), - l = g.node(_.last(node.borderLeft)), - r = g.node(_.last(node.borderRight)); - - node.width = Math.abs(r.x - l.x); - node.height = Math.abs(b.y - t.y); - node.x = l.x + node.width / 2; - node.y = t.y + node.height / 2; - } - }); - - _.each(g.nodes(), function(v) { - if (g.node(v).dummy === "border") { - g.removeNode(v); - } - }); -} - -function removeSelfEdges(g) { - _.each(g.edges(), function(e) { - if (e.v === e.w) { - var node = g.node(e.v); - if (!node.selfEdges) { - node.selfEdges = []; - } - node.selfEdges.push({ e: e, label: g.edge(e) }); - g.removeEdge(e); - } - }); -} - -function insertSelfEdges(g) { - var layers = util.buildLayerMatrix(g); - _.each(layers, function(layer) { - var orderShift = 0; - _.each(layer, function(v, i) { - var node = g.node(v); - node.order = i + orderShift; - _.each(node.selfEdges, function(selfEdge) { - util.addDummyNode(g, "selfedge", { - width: selfEdge.label.width, - height: selfEdge.label.height, - rank: node.rank, - order: i + (++orderShift), - e: selfEdge.e, - label: selfEdge.label - }, "_se"); - }); - delete node.selfEdges; - }); - }); -} - -function positionSelfEdges(g) { - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (node.dummy === "selfedge") { - var selfNode = g.node(node.e.v), - x = selfNode.x + selfNode.width / 2, - y = selfNode.y, - dx = node.x - x, - dy = selfNode.height / 2; - g.setEdge(node.e, node.label); - g.removeNode(v); - node.label.points = [ - { x: x + 2 * dx / 3, y: y - dy }, - { x: x + 5 * dx / 6, y: y - dy }, - { x: x + dx , y: y }, - { x: x + 5 * dx / 6, y: y + dy }, - { x: x + 2 * dx / 3, y: y + dy }, - ]; - node.label.x = node.x; - node.label.y = node.y; - } - }); -} - -function selectNumberAttrs(obj, attrs) { - return _.mapValues(_.pick(obj, attrs), Number); -} - -function canonicalize(attrs) { - var newAttrs = {}; - _.each(attrs, function(v, k) { - newAttrs[k.toLowerCase()] = v; - }); - return newAttrs; -} - -},{"./acyclic":28,"./add-border-segments":29,"./coordinate-system":30,"./graphlib":33,"./lodash":36,"./nesting-graph":37,"./normalize":38,"./order":43,"./parent-dummy-chains":48,"./position":50,"./rank":52,"./util":55}],36:[function(require,module,exports){ -module.exports=require(20) -},{"/Users/cpettitt/projects/dagre-d3/lib/lodash.js":20,"lodash":77}],37:[function(require,module,exports){ -var _ = require("./lodash"), - util = require("./util"); - -module.exports = { - run: run, - cleanup: cleanup -}; - -/* - * A nesting graph creates dummy nodes for the tops and bottoms of subgraphs, - * adds appropriate edges to ensure that all cluster nodes are placed between - * these boundries, and ensures that the graph is connected. - * - * In addition we ensure, through the use of the minlen property, that nodes - * and subgraph border nodes to not end up on the same rank. - * - * Preconditions: - * - * 1. Input graph is a DAG - * 2. Nodes in the input graph has a minlen attribute - * - * Postconditions: - * - * 1. Input graph is connected. - * 2. Dummy nodes are added for the tops and bottoms of subgraphs. - * 3. The minlen attribute for nodes is adjusted to ensure nodes do not - * get placed on the same rank as subgraph border nodes. - * - * The nesting graph idea comes from Sander, "Layout of Compound Directed - * Graphs." - */ -function run(g) { - var root = util.addDummyNode(g, "root", {}, "_root"), - depths = treeDepths(g), - height = _.max(depths) - 1, - nodeSep = 2 * height + 1; - - g.graph().nestingRoot = root; - - // Multiply minlen by nodeSep to align nodes on non-border ranks. - _.each(g.edges(), function(e) { g.edge(e).minlen *= nodeSep; }); - - // Calculate a weight that is sufficient to keep subgraphs vertically compact - var weight = sumWeights(g) + 1; - - // Create border nodes and link them up - _.each(g.children(), function(child) { - dfs(g, root, nodeSep, weight, height, depths, child); - }); - - // Save the multiplier for node layers for later removal of empty border - // layers. - g.graph().nodeRankFactor = nodeSep; -} - -function dfs(g, root, nodeSep, weight, height, depths, v) { - var children = g.children(v); - if (!children.length) { - if (v !== root) { - g.setEdge(root, v, { weight: 0, minlen: nodeSep }); - } - return; - } - - var top = util.addBorderNode(g, "_bt"), - bottom = util.addBorderNode(g, "_bb"), - label = g.node(v); - - g.setParent(top, v); - label.borderTop = top; - g.setParent(bottom, v); - label.borderBottom = bottom; - - _.each(children, function(child) { - dfs(g, root, nodeSep, weight, height, depths, child); - - var childNode = g.node(child), - childTop = childNode.borderTop ? childNode.borderTop : child, - childBottom = childNode.borderBottom ? childNode.borderBottom : child, - thisWeight = childNode.borderTop ? weight : 2 * weight, - minlen = childTop !== childBottom ? 1 : height - depths[v] + 1; - - g.setEdge(top, childTop, { - weight: thisWeight, - minlen: minlen, - nestingEdge: true - }); - - g.setEdge(childBottom, bottom, { - weight: thisWeight, - minlen: minlen, - nestingEdge: true - }); - }); - - if (!g.parent(v)) { - g.setEdge(root, top, { weight: 0, minlen: height + depths[v] }); - } -} - -function treeDepths(g) { - var depths = {}; - function dfs(v, depth) { - var children = g.children(v); - if (children && children.length) { - _.each(children, function(child) { - dfs(child, depth + 1); - }); - } - depths[v] = depth; - } - _.each(g.children(), function(v) { dfs(v, 1); }); - return depths; -} - -function sumWeights(g) { - return _.reduce(g.edges(), function(acc, e) { - return acc + g.edge(e).weight; - }, 0); -} - -function cleanup(g) { - var graphLabel = g.graph(); - g.removeNode(graphLabel.nestingRoot); - delete graphLabel.nestingRoot; - _.each(g.edges(), function(e) { - var edge = g.edge(e); - if (edge.nestingEdge) { - g.removeEdge(e); - } - }); -} - -},{"./lodash":36,"./util":55}],38:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - util = require("./util"); - -module.exports = { - run: run, - undo: undo -}; - -/* - * Breaks any long edges in the graph into short segments that span 1 layer - * each. This operation is undoable with the denormalize function. - * - * Pre-conditions: - * - * 1. The input graph is a DAG. - * 2. Each node in the graph has a "rank" property. - * - * Post-condition: - * - * 1. All edges in the graph have a length of 1. - * 2. Dummy nodes are added where edges have been split into segments. - * 3. The graph is augmented with a "dummyChains" attribute which contains - * the first dummy in each chain of dummy nodes produced. - */ -function run(g) { - g.graph().dummyChains = []; - _.each(g.edges(), function(edge) { normalizeEdge(g, edge); }); -} - -function normalizeEdge(g, e) { - var v = e.v, - vRank = g.node(v).rank, - w = e.w, - wRank = g.node(w).rank, - name = e.name, - edgeLabel = g.edge(e), - labelRank = edgeLabel.labelRank; - - if (wRank === vRank + 1) return; - - g.removeEdge(e); - - var dummy, attrs, i; - for (i = 0, ++vRank; vRank < wRank; ++i, ++vRank) { - edgeLabel.points = []; - attrs = { - width: 0, height: 0, - edgeLabel: edgeLabel, edgeObj: e, - rank: vRank - }; - dummy = util.addDummyNode(g, "edge", attrs, "_d"); - if (vRank === labelRank) { - attrs.width = edgeLabel.width; - attrs.height = edgeLabel.height; - attrs.dummy = "edge-label"; - attrs.labelpos = edgeLabel.labelpos; - } - g.setEdge(v, dummy, { weight: edgeLabel.weight }, name); - if (i === 0) { - g.graph().dummyChains.push(dummy); - } - v = dummy; - } - - g.setEdge(v, w, { weight: edgeLabel.weight }, name); -} - -function undo(g) { - _.each(g.graph().dummyChains, function(v) { - var node = g.node(v), - origLabel = node.edgeLabel, - w; - g.setEdge(node.edgeObj, origLabel); - while (node.dummy) { - w = g.successors(v)[0]; - g.removeNode(v); - origLabel.points.push({ x: node.x, y: node.y }); - if (node.dummy === "edge-label") { - origLabel.x = node.x; - origLabel.y = node.y; - origLabel.width = node.width; - origLabel.height = node.height; - } - v = w; - node = g.node(v); - } - }); -} - -},{"./lodash":36,"./util":55}],39:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = addSubgraphConstraints; - -function addSubgraphConstraints(g, cg, vs) { - var prev = {}, - rootPrev; - - _.each(vs, function(v) { - var child = g.parent(v), - parent, - prevChild; - while (child) { - parent = g.parent(child); - if (parent) { - prevChild = prev[parent]; - prev[parent] = child; - } else { - prevChild = rootPrev; - rootPrev = child; - } - if (prevChild && prevChild !== child) { - cg.setEdge(prevChild, child); - return; - } - child = parent; - } - }); - - /* - function dfs(v) { - var children = v ? g.children(v) : g.children(); - if (children.length) { - var min = Number.POSITIVE_INFINITY, - subgraphs = []; - _.each(children, function(child) { - var childMin = dfs(child); - if (g.children(child).length) { - subgraphs.push({ v: child, order: childMin }); - } - min = Math.min(min, childMin); - }); - _.reduce(_.sortBy(subgraphs, "order"), function(prev, curr) { - cg.setEdge(prev.v, curr.v); - return curr; - }); - return min; - } - return g.node(v).order; - } - dfs(undefined); - */ -} - -},{"../lodash":36}],40:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = barycenter; - -function barycenter(g, movable) { - return _.map(movable, function(v) { - var inV = g.inEdges(v); - if (!inV.length) { - return { v: v }; - } else { - var result = _.reduce(inV, function(acc, e) { - var edge = g.edge(e), - nodeU = g.node(e.v); - return { - sum: acc.sum + (edge.weight * nodeU.order), - weight: acc.weight + edge.weight - }; - }, { sum: 0, weight: 0 }); - - return { - v: v, - barycenter: result.sum / result.weight, - weight: result.weight - }; - } - }); -} - - -},{"../lodash":36}],41:[function(require,module,exports){ -var _ = require("../lodash"), - Graph = require("../graphlib").Graph; - -module.exports = buildLayerGraph; - -/* - * Constructs a graph that can be used to sort a layer of nodes. The graph will - * contain all base and subgraph nodes from the request layer in their original - * hierarchy and any edges that are incident on these nodes and are of the type - * requested by the "relationship" parameter. - * - * Nodes from the requested rank that do not have parents are assigned a root - * node in the output graph, which is set in the root graph attribute. This - * makes it easy to walk the hierarchy of movable nodes during ordering. - * - * Pre-conditions: - * - * 1. Input graph is a DAG - * 2. Base nodes in the input graph have a rank attribute - * 3. Subgraph nodes in the input graph has minRank and maxRank attributes - * 4. Edges have an assigned weight - * - * Post-conditions: - * - * 1. Output graph has all nodes in the movable rank with preserved - * hierarchy. - * 2. Root nodes in the movable layer are made children of the node - * indicated by the root attribute of the graph. - * 3. Non-movable nodes incident on movable nodes, selected by the - * relationship parameter, are included in the graph (without hierarchy). - * 4. Edges incident on movable nodes, selected by the relationship - * parameter, are added to the output graph. - * 5. The weights for copied edges are aggregated as need, since the output - * graph is not a multi-graph. - */ -function buildLayerGraph(g, rank, relationship) { - var root = createRootNode(g), - result = new Graph({ compound: true }).setGraph({ root: root }) - .setDefaultNodeLabel(function(v) { return g.node(v); }); - - _.each(g.nodes(), function(v) { - var node = g.node(v), - parent = g.parent(v); - - if (node.rank === rank || node.minRank <= rank && rank <= node.maxRank) { - result.setNode(v); - result.setParent(v, parent || root); - - // This assumes we have only short edges! - _.each(g[relationship](v), function(e) { - var u = e.v === v ? e.w : e.v, - edge = result.edge(u, v), - weight = !_.isUndefined(edge) ? edge.weight : 0; - result.setEdge(u, v, { weight: g.edge(e).weight + weight }); - }); - - if (_.has(node, "minRank")) { - result.setNode(v, { - borderLeft: node.borderLeft[rank], - borderRight: node.borderRight[rank] - }); - } - } - }); - - return result; -} - -function createRootNode(g) { - var v; - while (g.hasNode((v = _.uniqueId("_root")))); - return v; -} - -},{"../graphlib":33,"../lodash":36}],42:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = crossCount; - -/* - * A function that takes a layering (an array of layers, each with an array of - * ordererd nodes) and a graph and returns a weighted crossing count. - * - * Pre-conditions: - * - * 1. Input graph must be simple (not a multigraph), directed, and include - * only simple edges. - * 2. Edges in the input graph must have assigned weights. - * - * Post-conditions: - * - * 1. The graph and layering matrix are left unchanged. - * - * This algorithm is derived from Barth, et al., "Bilayer Cross Counting." - */ -function crossCount(g, layering) { - var cc = 0; - for (var i = 1; i < layering.length; ++i) { - cc += twoLayerCrossCount(g, layering[i-1], layering[i]); - } - return cc; -} - -function twoLayerCrossCount(g, northLayer, southLayer) { - // Sort all of the edges between the north and south layers by their position - // in the north layer and then the south. Map these edges to the position of - // their head in the south layer. - var southPos = _.zipObject(southLayer, - _.map(southLayer, function (v, i) { return i; })); - var southEntries = _.flatten(_.map(northLayer, function(v) { - return _.chain(g.outEdges(v)) - .map(function(e) { - return { pos: southPos[e.w], weight: g.edge(e).weight }; - }) - .sortBy("pos") - .value(); - }), true); - - // Build the accumulator tree - var firstIndex = 1; - while (firstIndex < southLayer.length) firstIndex <<= 1; - var treeSize = 2 * firstIndex - 1; - firstIndex -= 1; - var tree = _.map(new Array(treeSize), function() { return 0; }); - - // Calculate the weighted crossings - var cc = 0; - _.each(southEntries.forEach(function(entry) { - var index = entry.pos + firstIndex; - tree[index] += entry.weight; - var weightSum = 0; - while (index > 0) { - if (index % 2) { - weightSum += tree[index + 1]; - } - index = (index - 1) >> 1; - tree[index] += entry.weight; - } - cc += entry.weight * weightSum; - })); - - return cc; -} - -},{"../lodash":36}],43:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - initOrder = require("./init-order"), - crossCount = require("./cross-count"), - sortSubgraph = require("./sort-subgraph"), - buildLayerGraph = require("./build-layer-graph"), - addSubgraphConstraints = require("./add-subgraph-constraints"), - Graph = require("../graphlib").Graph, - util = require("../util"); - -module.exports = order; - -/* - * Applies heuristics to minimize edge crossings in the graph and sets the best - * order solution as an order attribute on each node. - * - * Pre-conditions: - * - * 1. Graph must be DAG - * 2. Graph nodes must be objects with a "rank" attribute - * 3. Graph edges must have the "weight" attribute - * - * Post-conditions: - * - * 1. Graph nodes will have an "order" attribute based on the results of the - * algorithm. - */ -function order(g) { - var maxRank = util.maxRank(g), - downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), "inEdges"), - upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), "outEdges"); - - var layering = initOrder(g); - assignOrder(g, layering); - - var bestCC = Number.POSITIVE_INFINITY, - best; - - for (var i = 0, lastBest = 0; lastBest < 4; ++i, ++lastBest) { - sweepLayerGraphs(i % 2 ? downLayerGraphs : upLayerGraphs, i % 4 >= 2); - - layering = util.buildLayerMatrix(g); - var cc = crossCount(g, layering); - if (cc < bestCC) { - lastBest = 0; - best = _.cloneDeep(layering); - bestCC = cc; - } - } - - assignOrder(g, best); -} - -function buildLayerGraphs(g, ranks, relationship) { - return _.map(ranks, function(rank) { - return buildLayerGraph(g, rank, relationship); - }); -} - -function sweepLayerGraphs(layerGraphs, biasRight) { - var cg = new Graph(); - _.each(layerGraphs, function(lg) { - var root = lg.graph().root; - var sorted = sortSubgraph(lg, root, cg, biasRight); - _.each(sorted.vs, function(v, i) { - lg.node(v).order = i; - }); - addSubgraphConstraints(lg, cg, sorted.vs); - }); -} - -function assignOrder(g, layering) { - _.each(layering, function(layer) { - _.each(layer, function(v, i) { - g.node(v).order = i; - }); - }); -} - -},{"../graphlib":33,"../lodash":36,"../util":55,"./add-subgraph-constraints":39,"./build-layer-graph":41,"./cross-count":42,"./init-order":44,"./sort-subgraph":46}],44:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = initOrder; - -/* - * Assigns an initial order value for each node by performing a DFS search - * starting from nodes in the first rank. Nodes are assigned an order in their - * rank as they are first visited. - * - * This approach comes from Gansner, et al., "A Technique for Drawing Directed - * Graphs." - * - * Returns a layering matrix with an array per layer and each layer sorted by - * the order of its nodes. - */ -function initOrder(g) { - var visited = {}, - simpleNodes = _.filter(g.nodes(), function(v) { - return !g.children(v).length; - }), - maxRank = _.max(_.map(simpleNodes, function(v) { return g.node(v).rank; })), - layers = _.map(_.range(maxRank + 1), function() { return []; }); - - function dfs(v) { - if (_.has(visited, v)) return; - visited[v] = true; - var node = g.node(v); - layers[node.rank].push(v); - _.each(g.successors(v), dfs); - } - - var orderedVs = _.sortBy(simpleNodes, function(v) { return g.node(v).rank; }); - _.each(orderedVs, dfs); - - return layers; -} - -},{"../lodash":36}],45:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = resolveConflicts; - -/* - * Given a list of entries of the form {v, barycenter, weight} and a - * constraint graph this function will resolve any conflicts between the - * constraint graph and the barycenters for the entries. If the barycenters for - * an entry would violate a constraint in the constraint graph then we coalesce - * the nodes in the conflict into a new node that respects the contraint and - * aggregates barycenter and weight information. - * - * This implementation is based on the description in Forster, "A Fast and - * Simple Hueristic for Constrained Two-Level Crossing Reduction," thought it - * differs in some specific details. - * - * Pre-conditions: - * - * 1. Each entry has the form {v, barycenter, weight}, or if the node has - * no barycenter, then {v}. - * - * Returns: - * - * A new list of entries of the form {vs, i, barycenter, weight}. The list - * `vs` may either be a singleton or it may be an aggregation of nodes - * ordered such that they do not violate constraints from the constraint - * graph. The property `i` is the lowest original index of any of the - * elements in `vs`. - */ -function resolveConflicts(entries, cg) { - var mappedEntries = {}; - _.each(entries, function(entry, i) { - var tmp = mappedEntries[entry.v] = { - indegree: 0, - in: [], - out: [], - vs: [entry.v], - i: i - }; - if (!_.isUndefined(entry.barycenter)) { - tmp.barycenter = entry.barycenter; - tmp.weight = entry.weight; - } - }); - - _.each(cg.edges(), function(e) { - var entryV = mappedEntries[e.v], - entryW = mappedEntries[e.w]; - if (!_.isUndefined(entryV) && !_.isUndefined(entryW)) { - entryW.indegree++; - entryV.out.push(mappedEntries[e.w]); - } - }); - - var sourceSet = _.filter(mappedEntries, function(entry) { - return !entry.indegree; - }); - - return doResolveConflicts(sourceSet); -} - -function doResolveConflicts(sourceSet) { - var entries = []; - - function handleIn(vEntry) { - return function(uEntry) { - if (uEntry.merged) { - return; - } - if (_.isUndefined(uEntry.barycenter) || - _.isUndefined(vEntry.barycenter) || - uEntry.barycenter >= vEntry.barycenter) { - mergeEntries(vEntry, uEntry); - } - }; - } - - function handleOut(vEntry) { - return function(wEntry) { - wEntry.in.push(vEntry); - if (--wEntry.indegree === 0) { - sourceSet.push(wEntry); - } - }; - } - - while (sourceSet.length) { - var entry = sourceSet.pop(); - entries.push(entry); - _.each(entry.in.reverse(), handleIn(entry)); - _.each(entry.out, handleOut(entry)); - } - - return _.chain(entries) - .filter(function(entry) { return !entry.merged; }) - .map(function(entry) { - return _.pick(entry, ["vs", "i", "barycenter", "weight"]); - }) - .value(); -} - -function mergeEntries(target, source) { - var sum = 0, - weight = 0; - - if (target.weight) { - sum += target.barycenter * target.weight; - weight += target.weight; - } - - if (source.weight) { - sum += source.barycenter * source.weight; - weight += source.weight; - } - - target.vs = source.vs.concat(target.vs); - target.barycenter = sum / weight; - target.weight = weight; - target.i = Math.min(source.i, target.i); - source.merged = true; -} - -},{"../lodash":36}],46:[function(require,module,exports){ -var _ = require("../lodash"), - barycenter = require("./barycenter"), - resolveConflicts = require("./resolve-conflicts"), - sort = require("./sort"); - -module.exports = sortSubgraph; - -function sortSubgraph(g, v, cg, biasRight) { - var movable = g.children(v), - node = g.node(v), - bl = node ? node.borderLeft : undefined, - br = node ? node.borderRight: undefined, - subgraphs = {}; - - if (bl) { - movable = _.filter(movable, function(w) { - return w !== bl && w !== br; - }); - } - - var barycenters = barycenter(g, movable); - _.each(barycenters, function(entry) { - if (g.children(entry.v).length) { - var subgraphResult = sortSubgraph(g, entry.v, cg, biasRight); - subgraphs[entry.v] = subgraphResult; - if (_.has(subgraphResult, "barycenter")) { - mergeBarycenters(entry, subgraphResult); - } - } - }); - - var entries = resolveConflicts(barycenters, cg); - expandSubgraphs(entries, subgraphs); - - var result = sort(entries, biasRight); - - if (bl) { - result.vs = _.flatten([bl, result.vs, br], true); - if (g.predecessors(bl).length) { - var blPred = g.node(g.predecessors(bl)[0]), - brPred = g.node(g.predecessors(br)[0]); - if (!_.has(result, "barycenter")) { - result.barycenter = 0; - result.weight = 0; - } - result.barycenter = (result.barycenter * result.weight + - blPred.order + brPred.order) / (result.weight + 2); - result.weight += 2; - } - } - - return result; -} - -function expandSubgraphs(entries, subgraphs) { - _.each(entries, function(entry) { - entry.vs = _.flatten(entry.vs.map(function(v) { - if (subgraphs[v]) { - return subgraphs[v].vs; - } - return v; - }), true); - }); -} - -function mergeBarycenters(target, other) { - if (!_.isUndefined(target.barycenter)) { - target.barycenter = (target.barycenter * target.weight + - other.barycenter * other.weight) / - (target.weight + other.weight); - target.weight += other.weight; - } else { - target.barycenter = other.barycenter; - target.weight = other.weight; - } -} - -},{"../lodash":36,"./barycenter":40,"./resolve-conflicts":45,"./sort":47}],47:[function(require,module,exports){ -var _ = require("../lodash"), - util = require("../util"); - -module.exports = sort; - -function sort(entries, biasRight) { - var parts = util.partition(entries, function(entry) { - return _.has(entry, "barycenter"); - }); - var sortable = parts.lhs, - unsortable = _.sortBy(parts.rhs, function(entry) { return -entry.i; }), - vs = [], - sum = 0, - weight = 0, - vsIndex = 0; - - sortable.sort(compareWithBias(!!biasRight)); - - vsIndex = consumeUnsortable(vs, unsortable, vsIndex); - - _.each(sortable, function (entry) { - vsIndex += entry.vs.length; - vs.push(entry.vs); - sum += entry.barycenter * entry.weight; - weight += entry.weight; - vsIndex = consumeUnsortable(vs, unsortable, vsIndex); - }); - - var result = { vs: _.flatten(vs, true) }; - if (weight) { - result.barycenter = sum / weight; - result.weight = weight; - } - return result; -} - -function consumeUnsortable(vs, unsortable, index) { - var last; - while (unsortable.length && (last = _.last(unsortable)).i <= index) { - unsortable.pop(); - vs.push(last.vs); - index++; - } - return index; -} - -function compareWithBias(bias) { - return function(entryV, entryW) { - if (entryV.barycenter < entryW.barycenter) { - return -1; - } else if (entryV.barycenter > entryW.barycenter) { - return 1; - } - - return !bias ? entryV.i - entryW.i : entryW.i - entryV.i; - }; -} - -},{"../lodash":36,"../util":55}],48:[function(require,module,exports){ -var _ = require("./lodash"); - -module.exports = parentDummyChains; - -function parentDummyChains(g) { - var postorderNums = postorder(g); - - _.each(g.graph().dummyChains, function(v) { - var node = g.node(v), - edgeObj = node.edgeObj, - pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w), - path = pathData.path, - lca = pathData.lca, - pathIdx = 0, - pathV = path[pathIdx], - ascending = true; - - while (v !== edgeObj.w) { - node = g.node(v); - - if (ascending) { - while ((pathV = path[pathIdx]) !== lca && - g.node(pathV).maxRank < node.rank) { - pathIdx++; - } - - if (pathV === lca) { - ascending = false; - } - } - - if (!ascending) { - while (pathIdx < path.length - 1 && - g.node(pathV = path[pathIdx + 1]).minRank <= node.rank) { - pathIdx++; - } - pathV = path[pathIdx]; - } - - g.setParent(v, pathV); - v = g.successors(v)[0]; - } - }); -} - -// Find a path from v to w through the lowest common ancestor (LCA). Return the -// full path and the LCA. -function findPath(g, postorderNums, v, w) { - var vPath = [], - wPath = [], - low = Math.min(postorderNums[v].low, postorderNums[w].low), - lim = Math.max(postorderNums[v].lim, postorderNums[w].lim), - parent, - lca; - - // Traverse up from v to find the LCA - parent = v; - do { - parent = g.parent(parent); - vPath.push(parent); - } while (parent && - (postorderNums[parent].low > low || lim > postorderNums[parent].lim)); - lca = parent; - - // Traverse from w to LCA - parent = w; - while ((parent = g.parent(parent)) !== lca) { - wPath.push(parent); - } - - return { path: vPath.concat(wPath.reverse()), lca: lca }; -} - -function postorder(g) { - var result = {}, - lim = 0; - - function dfs(v) { - var low = lim; - _.each(g.children(v), dfs); - result[v] = { low: low, lim: lim++ }; - } - _.each(g.children(), dfs); - - return result; -} - -},{"./lodash":36}],49:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - util = require("../util"); - -/* - * This module provides coordinate assignment based on Brandes and Köpf, "Fast - * and Simple Horizontal Coordinate Assignment." - */ - -module.exports = { - positionX: positionX, - findType1Conflicts: findType1Conflicts, - findType2Conflicts: findType2Conflicts, - addConflict: addConflict, - hasConflict: hasConflict, - verticalAlignment: verticalAlignment, - horizontalCompaction: horizontalCompaction, - alignCoordinates: alignCoordinates, - findSmallestWidthAlignment: findSmallestWidthAlignment, - balance: balance -}; - -/* - * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" - * property. A type-1 conflict is one where a non-inner segment crosses an - * inner segment. An inner segment is an edge with both incident nodes marked - * with the "dummy" property. - * - * This algorithm scans layer by layer, starting with the second, for type-1 - * conflicts between the current layer and the previous layer. For each layer - * it scans the nodes from left to right until it reaches one that is incident - * on an inner segment. It then scans predecessors to determine if they have - * edges that cross that inner segment. At the end a final scan is done for all - * nodes on the current rank to see if they cross the last visited inner - * segment. - * - * This algorithm (safely) assumes that a dummy node will only be incident on a - * single node in the layers being scanned. - */ -function findType1Conflicts(g, layering) { - var conflicts = {}; - - function visitLayer(prevLayer, layer) { - var - // last visited node in the previous layer that is incident on an inner - // segment. - k0 = 0, - // Tracks the last node in this layer scanned for crossings with a type-1 - // segment. - scanPos = 0, - prevLayerLength = prevLayer.length, - lastNode = _.last(layer); - - _.each(layer, function(v, i) { - var w = findOtherInnerSegmentNode(g, v), - k1 = w ? g.node(w).order : prevLayerLength; - - if (w || v === lastNode) { - _.each(layer.slice(scanPos, i +1), function(scanNode) { - _.each(g.predecessors(scanNode), function(u) { - var uLabel = g.node(u), - uPos = uLabel.order; - if ((uPos < k0 || k1 < uPos) && - !(uLabel.dummy && g.node(scanNode).dummy)) { - addConflict(conflicts, u, scanNode); - } - }); - }); - scanPos = i + 1; - k0 = k1; - } - }); - - return layer; - } - - _.reduce(layering, visitLayer); - return conflicts; -} - -function findType2Conflicts(g, layering) { - var conflicts = {}; - - function scan(south, southPos, southEnd, prevNorthBorder, nextNorthBorder) { - var v; - _.each(_.range(southPos, southEnd), function(i) { - v = south[i]; - if (g.node(v).dummy) { - _.each(g.predecessors(v), function(u) { - var uNode = g.node(u); - if (uNode.dummy && - (uNode.order < prevNorthBorder || uNode.order > nextNorthBorder)) { - addConflict(conflicts, u, v); - } - }); - } - }); - } - - - function visitLayer(north, south) { - var prevNorthPos = -1, - nextNorthPos, - southPos = 0; - - _.each(south, function(v, southLookahead) { - if (g.node(v).dummy === "border") { - var predecessors = g.predecessors(v); - if (predecessors.length) { - nextNorthPos = g.node(predecessors[0]).order; - scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos); - southPos = southLookahead; - prevNorthPos = nextNorthPos; - } - } - scan(south, southPos, south.length, nextNorthPos, north.length); - }); - - return south; - } - - _.reduce(layering, visitLayer); - return conflicts; -} - -function findOtherInnerSegmentNode(g, v) { - if (g.node(v).dummy) { - return _.find(g.predecessors(v), function(u) { - return g.node(u).dummy; - }); - } -} - -function addConflict(conflicts, v, w) { - if (v > w) { - var tmp = v; - v = w; - w = tmp; - } - - var conflictsV = conflicts[v]; - if (!conflictsV) { - conflicts[v] = conflictsV = {}; - } - conflictsV[w] = true; -} - -function hasConflict(conflicts, v, w) { - if (v > w) { - var tmp = v; - v = w; - w = tmp; - } - return _.has(conflicts[v], w); -} - -/* - * Try to align nodes into vertical "blocks" where possible. This algorithm - * attempts to align a node with one of its median neighbors. If the edge - * connecting a neighbor is a type-1 conflict then we ignore that possibility. - * If a previous node has already formed a block with a node after the node - * we're trying to form a block with, we also ignore that possibility - our - * blocks would be split in that scenario. - */ -function verticalAlignment(g, layering, conflicts, neighborFn) { - var root = {}, - align = {}, - pos = {}; - - // We cache the position here based on the layering because the graph and - // layering may be out of sync. The layering matrix is manipulated to - // generate different extreme alignments. - _.each(layering, function(layer) { - _.each(layer, function(v, order) { - root[v] = v; - align[v] = v; - pos[v] = order; - }); - }); - - _.each(layering, function(layer) { - var prevIdx = -1; - _.each(layer, function(v) { - var ws = neighborFn(v); - if (ws.length) { - ws = _.sortBy(ws, function(w) { return pos[w]; }); - var mp = (ws.length - 1) / 2; - for (var i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) { - var w = ws[i]; - if (align[v] === v && - prevIdx < pos[w] && - !hasConflict(conflicts, v, w)) { - align[w] = v; - align[v] = root[v] = root[w]; - prevIdx = pos[w]; - } - } - } - }); - }); - - return { root: root, align: align }; -} - -function horizontalCompaction(g, layering, root, align, reverseSep) { - // We use local variables for these parameters instead of manipulating the - // graph because it becomes more verbose to access them in a chained manner. - var shift = {}, - sink = {}, - xs = {}, - pred = {}, - graphLabel = g.graph(), - sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); - - _.each(layering, function(layer) { - _.each(layer, function(v, order) { - sink[v] = v; - shift[v] = Number.POSITIVE_INFINITY; - pred[v] = layer[order - 1]; - }); - }); - - _.each(g.nodes(), function(v) { - if (root[v] === v) { - placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v); - } - }); - - _.each(layering, function(layer) { - _.each(layer, function(v) { - xs[v] = xs[root[v]]; - // This line differs from the source paper. See - // http://www.inf.uni-konstanz.de/~brandes/publications/ for details. - if (v === root[v] && shift[sink[root[v]]] < Number.POSITIVE_INFINITY) { - xs[v] += shift[sink[root[v]]]; - } - }); - }); - - return xs; -} - -function placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, v) { - if (_.has(xs, v)) return; - xs[v] = 0; - - var w = v, - u; - do { - if (pred[w]) { - u = root[pred[w]]; - placeBlock(g, layering, sepFn, root, align, shift, sink, pred, xs, u); - if (sink[v] === v) { - sink[v] = sink[u]; - } - - var delta = sepFn(g, w, pred[w]); - if (sink[v] !== sink[u]) { - shift[sink[u]] = Math.min(shift[sink[u]], xs[v] - xs[u] - delta); - } else { - xs[v] = Math.max(xs[v], xs[u] + delta); - } - } - w = align[w]; - } while (w !== v); -} - -/* - * Returns the alignment that has the smallest width of the given alignments. - */ -function findSmallestWidthAlignment(g, xss) { - return _.min(xss, function(xs) { - var min = _.min(xs, function(x, v) { return x - width(g, v) / 2; }), - max = _.max(xs, function(x, v) { return x + width(g, v) / 2; }); - return max - min; - }); -} - -/* - * Align the coordinates of each of the layout alignments such that - * left-biased alignments have their minimum coordinate at the same point as - * the minimum coordinate of the smallest width alignment and right-biased - * alignments have their maximum coordinate at the same point as the maximum - * coordinate of the smallest width alignment. - */ -function alignCoordinates(xss, alignTo) { - var alignToMin = _.min(alignTo), - alignToMax = _.max(alignTo); - - _.each(["u", "d"], function(vert) { - _.each(["l", "r"], function(horiz) { - var alignment = vert + horiz, - xs = xss[alignment], - delta; - if (xs === alignTo) return; - - delta = horiz === "l" ? alignToMin - _.min(xs) : alignToMax - _.max(xs); - - if (delta) { - xss[alignment] = _.mapValues(xs, function(x) { return x + delta; }); - } - }); - }); -} - -function balance(xss, align) { - return _.mapValues(xss.ul, function(ignore, v) { - if (align) { - return xss[align.toLowerCase()][v]; - } else { - var xs = _.sortBy(_.pluck(xss, v)); - return (xs[1] + xs[2]) / 2; - } - }); -} - -function positionX(g) { - var layering = util.buildLayerMatrix(g), - conflicts = _.merge(findType1Conflicts(g, layering), - findType2Conflicts(g, layering)); - - var xss = {}, - adjustedLayering; - _.each(["u", "d"], function(vert) { - adjustedLayering = vert === "u" ? layering : _.values(layering).reverse(); - _.each(["l", "r"], function(horiz) { - if (horiz === "r") { - adjustedLayering = _.map(adjustedLayering, function(inner) { - return _.values(inner).reverse(); - }); - } - - var neighborFn = _.bind(vert === "u" ? g.predecessors : g.successors, g); - var align = verticalAlignment(g, adjustedLayering, conflicts, neighborFn); - var xs = horizontalCompaction(g, adjustedLayering, - align.root, align.align, - horiz === "r"); - if (horiz === "r") { - xs = _.mapValues(xs, function(x) { return -x; }); - } - xss[vert + horiz] = xs; - }); - }); - - var smallestWidth = findSmallestWidthAlignment(g, xss); - alignCoordinates(xss, smallestWidth); - return balance(xss, g.graph().align); -} - -function sep(nodeSep, edgeSep, reverseSep) { - return function(g, v, w) { - var vLabel = g.node(v), - wLabel = g.node(w), - sum = 0, - delta; - - sum += vLabel.width / 2; - if (_.has(vLabel, "labelpos")) { - switch (vLabel.labelpos.toLowerCase()) { - case "l": delta = -vLabel.width / 2; break; - case "r": delta = vLabel.width / 2; break; - } - } - if (delta) { - sum += reverseSep ? delta : -delta; - } - delta = 0; - - sum += (vLabel.dummy ? edgeSep : nodeSep) / 2; - sum += (wLabel.dummy ? edgeSep : nodeSep) / 2; - - sum += wLabel.width / 2; - if (_.has(wLabel, "labelpos")) { - switch (wLabel.labelpos.toLowerCase()) { - case "l": delta = wLabel.width / 2; break; - case "r": delta = -wLabel.width / 2; break; - } - } - if (delta) { - sum += reverseSep ? delta : -delta; - } - delta = 0; - - return sum; - }; -} - -function width(g, v) { - return g.node(v).width; -} - -},{"../lodash":36,"../util":55}],50:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - util = require("../util"), - positionX = require("./bk").positionX; - -module.exports = position; - -function position(g) { - g = util.asNonCompoundGraph(g); - - positionY(g); - _.each(positionX(g), function(x, v) { - g.node(v).x = x; - }); -} - -function positionY(g) { - var layering = util.buildLayerMatrix(g), - rankSep = g.graph().ranksep, - prevY = 0; - _.each(layering, function(layer) { - var maxHeight = _.max(_.map(layer, function(v) { return g.node(v).height; })); - _.each(layer, function(v) { - g.node(v).y = prevY + maxHeight / 2; - }); - prevY += maxHeight + rankSep; - }); -} - - -},{"../lodash":36,"../util":55,"./bk":49}],51:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - Graph = require("../graphlib").Graph, - slack = require("./util").slack; - -module.exports = feasibleTree; - -/* - * Constructs a spanning tree with tight edges and adjusted the input node's - * ranks to achieve this. A tight edge is one that is has a length that matches - * its "minlen" attribute. - * - * The basic structure for this function is derived from Gansner, et al., "A - * Technique for Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a DAG. - * 2. Graph must be connected. - * 3. Graph must have at least one node. - * 5. Graph nodes must have been previously assigned a "rank" property that - * respects the "minlen" property of incident edges. - * 6. Graph edges must have a "minlen" property. - * - * Post-conditions: - * - * - Graph nodes will have their rank adjusted to ensure that all edges are - * tight. - * - * Returns a tree (undirected graph) that is constructed using only "tight" - * edges. - */ -function feasibleTree(g) { - var t = new Graph({ directed: false }); - - // Choose arbitrary node from which to start our tree - var start = g.nodes()[0], - size = g.nodeCount(); - t.setNode(start, {}); - - var edge, delta; - while (tightTree(t, g) < size) { - edge = findMinSlackEdge(t, g); - delta = t.hasNode(edge.v) ? slack(g, edge) : -slack(g, edge); - shiftRanks(t, g, delta); - } - - return t; -} - -/* - * Finds a maximal tree of tight edges and returns the number of nodes in the - * tree. - */ -function tightTree(t, g) { - function dfs(v) { - _.each(g.nodeEdges(v), function(e) { - var edgeV = e.v, - w = (v === edgeV) ? e.w : edgeV; - if (!t.hasNode(w) && !slack(g, e)) { - t.setNode(w, {}); - t.setEdge(v, w, {}); - dfs(w); - } - }); - } - - _.each(t.nodes(), dfs); - return t.nodeCount(); -} - -/* - * Finds the edge with the smallest slack that is incident on tree and returns - * it. - */ -function findMinSlackEdge(t, g) { - return _.min(g.edges(), function(e) { - if (t.hasNode(e.v) !== t.hasNode(e.w)) { - return slack(g, e); - } - }); -} - -function shiftRanks(t, g, delta) { - _.each(t.nodes(), function(v) { - g.node(v).rank += delta; - }); -} - -},{"../graphlib":33,"../lodash":36,"./util":54}],52:[function(require,module,exports){ -"use strict"; - -var rankUtil = require("./util"), - longestPath = rankUtil.longestPath, - feasibleTree = require("./feasible-tree"), - networkSimplex = require("./network-simplex"); - -module.exports = rank; - -/* - * Assigns a rank to each node in the input graph that respects the "minlen" - * constraint specified on edges between nodes. - * - * This basic structure is derived from Gansner, et al., "A Technique for - * Drawing Directed Graphs." - * - * Pre-conditions: - * - * 1. Graph must be a connected DAG - * 2. Graph nodes must be objects - * 3. Graph edges must have "weight" and "minlen" attributes - * - * Post-conditions: - * - * 1. Graph nodes will have a "rank" attribute based on the results of the - * algorithm. Ranks can start at any index (including negative), we'll - * fix them up later. - */ -function rank(g) { - switch(g.graph().ranker) { - case "network-simplex": networkSimplexRanker(g); break; - case "tight-tree": tightTreeRanker(g); break; - case "longest-path": longestPathRanker(g); break; - default: networkSimplexRanker(g); - } -} - -// A fast and simple ranker, but results are far from optimal. -var longestPathRanker = longestPath; - -function tightTreeRanker(g) { - longestPath(g); - feasibleTree(g); -} - -function networkSimplexRanker(g) { - networkSimplex(g); -} - -},{"./feasible-tree":51,"./network-simplex":53,"./util":54}],53:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"), - feasibleTree = require("./feasible-tree"), - slack = require("./util").slack, - initRank = require("./util").longestPath, - preorder = require("../graphlib").alg.preorder, - postorder = require("../graphlib").alg.postorder, - simplify = require("../util").simplify; - -module.exports = networkSimplex; - -// Expose some internals for testing purposes -networkSimplex.initLowLimValues = initLowLimValues; -networkSimplex.initCutValues = initCutValues; -networkSimplex.calcCutValue = calcCutValue; -networkSimplex.leaveEdge = leaveEdge; -networkSimplex.enterEdge = enterEdge; -networkSimplex.exchangeEdges = exchangeEdges; - -/* - * The network simplex algorithm assigns ranks to each node in the input graph - * and iteratively improves the ranking to reduce the length of edges. - * - * Preconditions: - * - * 1. The input graph must be a DAG. - * 2. All nodes in the graph must have an object value. - * 3. All edges in the graph must have "minlen" and "weight" attributes. - * - * Postconditions: - * - * 1. All nodes in the graph will have an assigned "rank" attribute that has - * been optimized by the network simplex algorithm. Ranks start at 0. - * - * - * A rough sketch of the algorithm is as follows: - * - * 1. Assign initial ranks to each node. We use the longest path algorithm, - * which assigns ranks to the lowest position possible. In general this - * leads to very wide bottom ranks and unnecessarily long edges. - * 2. Construct a feasible tight tree. A tight tree is one such that all - * edges in the tree have no slack (difference between length of edge - * and minlen for the edge). This by itself greatly improves the assigned - * rankings by shorting edges. - * 3. Iteratively find edges that have negative cut values. Generally a - * negative cut value indicates that the edge could be removed and a new - * tree edge could be added to produce a more compact graph. - * - * Much of the algorithms here are derived from Gansner, et al., "A Technique - * for Drawing Directed Graphs." The structure of the file roughly follows the - * structure of the overall algorithm. - */ -function networkSimplex(g) { - g = simplify(g); - initRank(g); - var t = feasibleTree(g); - initLowLimValues(t); - initCutValues(t, g); - - var e, f; - while ((e = leaveEdge(t))) { - f = enterEdge(t, g, e); - exchangeEdges(t, g, e, f); - } -} - -/* - * Initializes cut values for all edges in the tree. - */ -function initCutValues(t, g) { - var vs = postorder(t, t.nodes()); - vs = vs.slice(0, vs.length - 1); - _.each(vs, function(v) { - assignCutValue(t, g, v); - }); -} - -function assignCutValue(t, g, child) { - var childLab = t.node(child), - parent = childLab.parent; - t.edge(child, parent).cutvalue = calcCutValue(t, g, child); -} - -/* - * Given the tight tree, its graph, and a child in the graph calculate and - * return the cut value for the edge between the child and its parent. - */ -function calcCutValue(t, g, child) { - var childLab = t.node(child), - parent = childLab.parent, - // True if the child is on the tail end of the edge in the directed graph - childIsTail = true, - // The graph's view of the tree edge we're inspecting - graphEdge = g.edge(child, parent), - // The accumulated cut value for the edge between this node and its parent - cutValue = 0; - - if (!graphEdge) { - childIsTail = false; - graphEdge = g.edge(parent, child); - } - - cutValue = graphEdge.weight; - - _.each(g.nodeEdges(child), function(e) { - var isOutEdge = e.v === child, - other = isOutEdge ? e.w : e.v; - - if (other !== parent) { - var pointsToHead = isOutEdge === childIsTail, - otherWeight = g.edge(e).weight; - - cutValue += pointsToHead ? otherWeight : -otherWeight; - if (isTreeEdge(t, child, other)) { - var otherCutValue = t.edge(child, other).cutvalue; - cutValue += pointsToHead ? -otherCutValue : otherCutValue; - } - } - }); - - return cutValue; -} - -function initLowLimValues(tree, root) { - if (arguments.length < 2) { - root = tree.nodes()[0]; - } - dfsAssignLowLim(tree, {}, 1, root); -} - -function dfsAssignLowLim(tree, visited, nextLim, v, parent) { - var low = nextLim, - label = tree.node(v); - - visited[v] = true; - _.each(tree.neighbors(v), function(w) { - if (!_.has(visited, w)) { - nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v); - } - }); - - label.low = low; - label.lim = nextLim++; - if (parent) { - label.parent = parent; - } else { - // TODO should be able to remove this when we incrementally update low lim - delete label.parent; - } - - return nextLim; -} - -function leaveEdge(tree) { - return _.find(tree.edges(), function(e) { - return tree.edge(e).cutvalue < 0; - }); -} - -function enterEdge(t, g, edge) { - var v = edge.v, - w = edge.w; - - // For the rest of this function we assume that v is the tail and w is the - // head, so if we don't have this edge in the graph we should flip it to - // match the correct orientation. - if (!g.hasEdge(v, w)) { - v = edge.w; - w = edge.v; - } - - var vLabel = t.node(v), - wLabel = t.node(w), - tailLabel = vLabel, - flip = false; - - // If the root is in the tail of the edge then we need to flip the logic that - // checks for the head and tail nodes in the candidates function below. - if (vLabel.lim > wLabel.lim) { - tailLabel = wLabel; - flip = true; - } - - var candidates = _.filter(g.edges(), function(edge) { - return flip === isDescendant(t, t.node(edge.v), tailLabel) && - flip !== isDescendant(t, t.node(edge.w), tailLabel); - }); - - return _.min(candidates, function(edge) { return slack(g, edge); }); -} - -function exchangeEdges(t, g, e, f) { - var v = e.v, - w = e.w; - t.removeEdge(v, w); - t.setEdge(f.v, f.w, {}); - initLowLimValues(t); - initCutValues(t, g); - updateRanks(t, g); -} - -function updateRanks(t, g) { - var root = _.find(t.nodes(), function(v) { return !g.node(v).parent; }), - vs = preorder(t, root); - vs = vs.slice(1); - _.each(vs, function(v) { - var parent = t.node(v).parent, - edge = g.edge(v, parent), - flipped = false; - - if (!edge) { - edge = g.edge(parent, v); - flipped = true; - } - - g.node(v).rank = g.node(parent).rank + (flipped ? edge.minlen : -edge.minlen); - }); -} - -/* - * Returns true if the edge is in the tree. - */ -function isTreeEdge(tree, u, v) { - return tree.hasEdge(u, v); -} - -/* - * Returns true if the specified node is descendant of the root node per the - * assigned low and lim attributes in the tree. - */ -function isDescendant(tree, vLabel, rootLabel) { - return rootLabel.low <= vLabel.lim && vLabel.lim <= rootLabel.lim; -} - -},{"../graphlib":33,"../lodash":36,"../util":55,"./feasible-tree":51,"./util":54}],54:[function(require,module,exports){ -"use strict"; - -var _ = require("../lodash"); - -module.exports = { - longestPath: longestPath, - slack: slack -}; - -/* - * Initializes ranks for the input graph using the longest path algorithm. This - * algorithm scales well and is fast in practice, it yields rather poor - * solutions. Nodes are pushed to the lowest layer possible, leaving the bottom - * ranks wide and leaving edges longer than necessary. However, due to its - * speed, this algorithm is good for getting an initial ranking that can be fed - * into other algorithms. - * - * This algorithm does not normalize layers because it will be used by other - * algorithms in most cases. If using this algorithm directly, be sure to - * run normalize at the end. - * - * Pre-conditions: - * - * 1. Input graph is a DAG. - * 2. Input graph node labels can be assigned properties. - * - * Post-conditions: - * - * 1. Each node will be assign an (unnormalized) "rank" property. - */ -function longestPath(g) { - var visited = {}; - - function dfs(v) { - var label = g.node(v); - if (_.has(visited, v)) { - return label.rank; - } - visited[v] = true; - - var rank = _.min(_.map(g.outEdges(v), function(e) { - return dfs(e.w) - g.edge(e).minlen; - })); - - if (rank === Number.POSITIVE_INFINITY) { - rank = 0; - } - - return (label.rank = rank); - } - - _.each(g.sources(), dfs); -} - -/* - * Returns the amount of slack for the given edge. The slack is defined as the - * difference between the length of the edge and its minimum length. - */ -function slack(g, e) { - return g.node(e.w).rank - g.node(e.v).rank - g.edge(e).minlen; -} - -},{"../lodash":36}],55:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"), - Graph = require("./graphlib").Graph; - -module.exports = { - addDummyNode: addDummyNode, - simplify: simplify, - asNonCompoundGraph: asNonCompoundGraph, - successorWeights: successorWeights, - predecessorWeights: predecessorWeights, - intersectRect: intersectRect, - buildLayerMatrix: buildLayerMatrix, - normalizeRanks: normalizeRanks, - removeEmptyRanks: removeEmptyRanks, - addBorderNode: addBorderNode, - maxRank: maxRank, - partition: partition, - time: time, - notime: notime -}; - -/* - * Adds a dummy node to the graph and return v. - */ -function addDummyNode(g, type, attrs, name) { - var v; - do { - v = _.uniqueId(name); - } while (g.hasNode(v)); - - attrs.dummy = type; - g.setNode(v, attrs); - return v; -} - -/* - * Returns a new graph with only simple edges. Handles aggregation of data - * associated with multi-edges. - */ -function simplify(g) { - var simplified = new Graph().setGraph(g.graph()); - _.each(g.nodes(), function(v) { simplified.setNode(v, g.node(v)); }); - _.each(g.edges(), function(e) { - var simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 }, - label = g.edge(e); - simplified.setEdge(e.v, e.w, { - weight: simpleLabel.weight + label.weight, - minlen: Math.max(simpleLabel.minlen, label.minlen) - }); - }); - return simplified; -} - -function asNonCompoundGraph(g) { - var simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph()); - _.each(g.nodes(), function(v) { - if (!g.children(v).length) { - simplified.setNode(v, g.node(v)); - } - }); - _.each(g.edges(), function(e) { - simplified.setEdge(e, g.edge(e)); - }); - return simplified; -} - -function successorWeights(g) { - var weightMap = _.map(g.nodes(), function(v) { - var sucs = {}; - _.each(g.outEdges(v), function(e) { - sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight; - }); - return sucs; - }); - return _.zipObject(g.nodes(), weightMap); -} - -function predecessorWeights(g) { - var weightMap = _.map(g.nodes(), function(v) { - var preds = {}; - _.each(g.inEdges(v), function(e) { - preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight; - }); - return preds; - }); - return _.zipObject(g.nodes(), weightMap); -} - -/* - * Finds where a line starting at point ({x, y}) would intersect a rectangle - * ({x, y, width, height}) if it were pointing at the rectangle's center. - */ -function intersectRect(rect, point) { - var x = rect.x; - var y = rect.y; - - // Rectangle intersection algorithm from: - // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes - var dx = point.x - x; - var dy = point.y - y; - var w = rect.width / 2; - var h = rect.height / 2; - - if (!dx && !dy) { - throw new Error("Not possible to find intersection inside of the rectangle"); - } - - var sx, sy; - if (Math.abs(dy) * w > Math.abs(dx) * h) { - // Intersection is top or bottom of rect. - if (dy < 0) { - h = -h; - } - sx = h * dx / dy; - sy = h; - } else { - // Intersection is left or right of rect. - if (dx < 0) { - w = -w; - } - sx = w; - sy = w * dy / dx; - } - - return { x: x + sx, y: y + sy }; -} - -/* - * Given a DAG with each node assigned "rank" and "order" properties, this - * function will produce a matrix with the ids of each node. - */ -function buildLayerMatrix(g) { - var layering = _.map(_.range(maxRank(g) + 1), function() { return []; }); - _.each(g.nodes(), function(v) { - var node = g.node(v), - rank = node.rank; - if (!_.isUndefined(rank)) { - layering[rank][node.order] = v; - } - }); - return layering; -} - -/* - * Adjusts the ranks for all nodes in the graph such that all nodes v have - * rank(v) >= 0 and at least one node w has rank(w) = 0. - */ -function normalizeRanks(g) { - var min = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); - _.each(g.nodes(), function(v) { - var node = g.node(v); - if (_.has(node, "rank")) { - node.rank -= min; - } - }); -} - -function removeEmptyRanks(g) { - // Ranks may not start at 0, so we need to offset them - var offset = _.min(_.map(g.nodes(), function(v) { return g.node(v).rank; })); - - var layers = []; - _.each(g.nodes(), function(v) { - var rank = g.node(v).rank - offset; - if (!_.has(layers, rank)) { - layers[rank] = []; - } - layers[rank].push(v); - }); - - var delta = 0, - nodeRankFactor = g.graph().nodeRankFactor; - _.each(layers, function(vs, i) { - if (_.isUndefined(vs) && i % nodeRankFactor !== 0) { - --delta; - } else if (delta) { - _.each(vs, function(v) { g.node(v).rank += delta; }); - } - }); -} - -function addBorderNode(g, prefix, rank, order) { - var node = { - width: 0, - height: 0 - }; - if (arguments.length >= 4) { - node.rank = rank; - node.order = order; - } - return addDummyNode(g, "border", node, prefix); -} - -function maxRank(g) { - return _.max(_.map(g.nodes(), function(v) { - var rank = g.node(v).rank; - if (!_.isUndefined(rank)) { - return rank; - } - })); -} - -/* - * Partition a collection into two groups: `lhs` and `rhs`. If the supplied - * function returns true for an entry it goes into `lhs`. Otherwise it goes - * into `rhs. - */ -function partition(collection, fn) { - var result = { lhs: [], rhs: [] }; - _.each(collection, function(value) { - if (fn(value)) { - result.lhs.push(value); - } else { - result.rhs.push(value); - } - }); - return result; -} - -/* - * Returns a new function that wraps `fn` with a timer. The wrapper logs the - * time it takes to execute the function. - */ -function time(name, fn) { - var start = _.now(); - try { - return fn(); - } finally { - console.log(name + " time: " + (_.now() - start) + "ms"); - } -} - -function notime(name, fn) { - return fn(); -} - -},{"./graphlib":33,"./lodash":36}],56:[function(require,module,exports){ -module.exports = "0.6.1"; - -},{}],57:[function(require,module,exports){ -/** - * Copyright (c) 2014, Chris Pettitt - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -var lib = require("./lib"); - -module.exports = { - Graph: lib.Graph, - json: require("./lib/json"), - alg: require("./lib/alg"), - version: lib.version -}; - -},{"./lib":73,"./lib/alg":64,"./lib/json":74}],58:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = components; - -function components(g) { - var visited = {}, - cmpts = [], - cmpt; - - function dfs(v) { - if (_.has(visited, v)) return; - visited[v] = true; - cmpt.push(v); - _.each(g.successors(v), dfs); - _.each(g.predecessors(v), dfs); - } - - _.each(g.nodes(), function(v) { - cmpt = []; - dfs(v); - if (cmpt.length) { - cmpts.push(cmpt); - } - }); - - return cmpts; -} - -},{"../lodash":75}],59:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = dfs; - -/* - * A helper that preforms a pre- or post-order traversal on the input graph - * and returns the nodes in the order they were visited. This algorithm treats - * the input as undirected. - * - * Order must be one of "pre" or "post". - */ -function dfs(g, vs, order) { - if (!_.isArray(vs)) { - vs = [vs]; - } - - var acc = [], - visited = {}; - _.each(vs, function(v) { - if (!g.hasNode(v)) { - throw new Error("Graph does not have node: " + v); - } - - doDfs(g, v, order === "post", visited, acc); - }); - return acc; -} - -function doDfs(g, v, postorder, visited, acc) { - if (!_.has(visited, v)) { - visited[v] = true; - - if (!postorder) { acc.push(v); } - _.each(g.neighbors(v), function(w) { - doDfs(g, w, postorder, visited, acc); - }); - if (postorder) { acc.push(v); } - } -} - -},{"../lodash":75}],60:[function(require,module,exports){ -var dijkstra = require("./dijkstra"), - _ = require("../lodash"); - -module.exports = dijkstraAll; - -function dijkstraAll(g, weightFunc, edgeFunc) { - return _.transform(g.nodes(), function(acc, v) { - acc[v] = dijkstra(g, v, weightFunc, edgeFunc); - }, {}); -} - -},{"../lodash":75,"./dijkstra":61}],61:[function(require,module,exports){ -var _ = require("../lodash"), - PriorityQueue = require("../data/priority-queue"); - -module.exports = dijkstra; - -var DEFAULT_WEIGHT_FUNC = _.constant(1); - -function dijkstra(g, source, weightFn, edgeFn) { - return runDijkstra(g, String(source), - weightFn || DEFAULT_WEIGHT_FUNC, - edgeFn || function(v) { return g.outEdges(v); }); -} - -function runDijkstra(g, source, weightFn, edgeFn) { - var results = {}, - pq = new PriorityQueue(), - v, vEntry; - - var updateNeighbors = function(edge) { - var w = edge.v !== v ? edge.v : edge.w, - wEntry = results[w], - weight = weightFn(edge), - distance = vEntry.distance + weight; - - if (weight < 0) { - throw new Error("dijkstra does not allow negative edge weights. " + - "Bad edge: " + edge + " Weight: " + weight); - } - - if (distance < wEntry.distance) { - wEntry.distance = distance; - wEntry.predecessor = v; - pq.decrease(w, distance); - } - }; - - g.nodes().forEach(function(v) { - var distance = v === source ? 0 : Number.POSITIVE_INFINITY; - results[v] = { distance: distance }; - pq.add(v, distance); - }); - - while (pq.size() > 0) { - v = pq.removeMin(); - vEntry = results[v]; - if (vEntry.distance === Number.POSITIVE_INFINITY) { - break; - } - - edgeFn(v).forEach(updateNeighbors); - } - - return results; -} - -},{"../data/priority-queue":71,"../lodash":75}],62:[function(require,module,exports){ -var _ = require("../lodash"), - tarjan = require("./tarjan"); - -module.exports = findCycles; - -function findCycles(g) { - return _.filter(tarjan(g), function(cmpt) { return cmpt.length > 1; }); -} - -},{"../lodash":75,"./tarjan":69}],63:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = floydWarshall; - -var DEFAULT_WEIGHT_FUNC = _.constant(1); - -function floydWarshall(g, weightFn, edgeFn) { - return runFloydWarshall(g, - weightFn || DEFAULT_WEIGHT_FUNC, - edgeFn || function(v) { return g.outEdges(v); }); -} - -function runFloydWarshall(g, weightFn, edgeFn) { - var results = {}, - nodes = g.nodes(); - - nodes.forEach(function(v) { - results[v] = {}; - results[v][v] = { distance: 0 }; - nodes.forEach(function(w) { - if (v !== w) { - results[v][w] = { distance: Number.POSITIVE_INFINITY }; - } - }); - edgeFn(v).forEach(function(edge) { - var w = edge.v === v ? edge.w : edge.v, - d = weightFn(edge); - results[v][w] = { distance: d, predecessor: v }; - }); - }); - - nodes.forEach(function(k) { - var rowK = results[k]; - nodes.forEach(function(i) { - var rowI = results[i]; - nodes.forEach(function(j) { - var ik = rowI[k]; - var kj = rowK[j]; - var ij = rowI[j]; - var altDistance = ik.distance + kj.distance; - if (altDistance < ij.distance) { - ij.distance = altDistance; - ij.predecessor = kj.predecessor; - } - }); - }); - }); - - return results; -} - -},{"../lodash":75}],64:[function(require,module,exports){ -module.exports = { - components: require("./components"), - dijkstra: require("./dijkstra"), - dijkstraAll: require("./dijkstra-all"), - findCycles: require("./find-cycles"), - floydWarshall: require("./floyd-warshall"), - isAcyclic: require("./is-acyclic"), - postorder: require("./postorder"), - preorder: require("./preorder"), - prim: require("./prim"), - tarjan: require("./tarjan"), - topsort: require("./topsort") -}; - -},{"./components":58,"./dijkstra":61,"./dijkstra-all":60,"./find-cycles":62,"./floyd-warshall":63,"./is-acyclic":65,"./postorder":66,"./preorder":67,"./prim":68,"./tarjan":69,"./topsort":70}],65:[function(require,module,exports){ -var topsort = require("./topsort"); - -module.exports = isAcyclic; - -function isAcyclic(g) { - try { - topsort(g); - } catch (e) { - if (e instanceof topsort.CycleException) { - return false; - } - throw e; - } - return true; -} - -},{"./topsort":70}],66:[function(require,module,exports){ -var dfs = require("./dfs"); - -module.exports = postorder; - -function postorder(g, vs) { - return dfs(g, vs, "post"); -} - -},{"./dfs":59}],67:[function(require,module,exports){ -var dfs = require("./dfs"); - -module.exports = preorder; - -function preorder(g, vs) { - return dfs(g, vs, "pre"); -} - -},{"./dfs":59}],68:[function(require,module,exports){ -var _ = require("../lodash"), - Graph = require("../graph"), - PriorityQueue = require("../data/priority-queue"); - -module.exports = prim; - -function prim(g, weightFunc) { - var result = new Graph(), - parents = {}, - pq = new PriorityQueue(), - v; - - function updateNeighbors(edge) { - var w = edge.v === v ? edge.w : edge.v, - pri = pq.priority(w); - if (pri !== undefined) { - var edgeWeight = weightFunc(edge); - if (edgeWeight < pri) { - parents[w] = v; - pq.decrease(w, edgeWeight); - } - } - } - - if (g.nodeCount() === 0) { - return result; - } - - _.each(g.nodes(), function(v) { - pq.add(v, Number.POSITIVE_INFINITY); - result.setNode(v); - }); - - // Start from an arbitrary node - pq.decrease(g.nodes()[0], 0); - - var init = false; - while (pq.size() > 0) { - v = pq.removeMin(); - if (_.has(parents, v)) { - result.setEdge(v, parents[v]); - } else if (init) { - throw new Error("Input graph is not connected: " + g); - } else { - init = true; - } - - g.nodeEdges(v).forEach(updateNeighbors); - } - - return result; -} - -},{"../data/priority-queue":71,"../graph":72,"../lodash":75}],69:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = tarjan; - -function tarjan(g) { - var index = 0, - stack = [], - visited = {}, // node id -> { onStack, lowlink, index } - results = []; - - function dfs(v) { - var entry = visited[v] = { - onStack: true, - lowlink: index, - index: index++ - }; - stack.push(v); - - g.successors(v).forEach(function(w) { - if (!_.has(visited, w)) { - dfs(w); - entry.lowlink = Math.min(entry.lowlink, visited[w].lowlink); - } else if (visited[w].onStack) { - entry.lowlink = Math.min(entry.lowlink, visited[w].index); - } - }); - - if (entry.lowlink === entry.index) { - var cmpt = [], - w; - do { - w = stack.pop(); - visited[w].onStack = false; - cmpt.push(w); - } while (v !== w); - results.push(cmpt); - } - } - - g.nodes().forEach(function(v) { - if (!_.has(visited, v)) { - dfs(v); - } - }); - - return results; -} - -},{"../lodash":75}],70:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = topsort; -topsort.CycleException = CycleException; - -function topsort(g) { - var visited = {}, - stack = {}, - results = []; - - function visit(node) { - if (_.has(stack, node)) { - throw new CycleException(); - } - - if (!_.has(visited, node)) { - stack[node] = true; - visited[node] = true; - _.each(g.predecessors(node), visit); - delete stack[node]; - results.push(node); - } - } - - _.each(g.sinks(), visit); - - if (_.size(visited) !== g.nodeCount()) { - throw new CycleException(); - } - - return results; -} - -function CycleException() {} - -},{"../lodash":75}],71:[function(require,module,exports){ -var _ = require("../lodash"); - -module.exports = PriorityQueue; - -/** - * A min-priority queue data structure. This algorithm is derived from Cormen, - * et al., "Introduction to Algorithms". The basic idea of a min-priority - * queue is that you can efficiently (in O(1) time) get the smallest key in - * the queue. Adding and removing elements takes O(log n) time. A key can - * have its priority decreased in O(log n) time. - */ -function PriorityQueue() { - this._arr = []; - this._keyIndices = {}; -} - -/** - * Returns the number of elements in the queue. Takes `O(1)` time. - */ -PriorityQueue.prototype.size = function() { - return this._arr.length; -}; - -/** - * Returns the keys that are in the queue. Takes `O(n)` time. - */ -PriorityQueue.prototype.keys = function() { - return this._arr.map(function(x) { return x.key; }); -}; - -/** - * Returns `true` if **key** is in the queue and `false` if not. - */ -PriorityQueue.prototype.has = function(key) { - return _.has(this._keyIndices, key); -}; - -/** - * Returns the priority for **key**. If **key** is not present in the queue - * then this function returns `undefined`. Takes `O(1)` time. - * - * @param {Object} key - */ -PriorityQueue.prototype.priority = function(key) { - var index = this._keyIndices[key]; - if (index !== undefined) { - return this._arr[index].priority; - } -}; - -/** - * Returns the key for the minimum element in this queue. If the queue is - * empty this function throws an Error. Takes `O(1)` time. - */ -PriorityQueue.prototype.min = function() { - if (this.size() === 0) { - throw new Error("Queue underflow"); - } - return this._arr[0].key; -}; - -/** - * Inserts a new key into the priority queue. If the key already exists in - * the queue this function returns `false`; otherwise it will return `true`. - * Takes `O(n)` time. - * - * @param {Object} key the key to add - * @param {Number} priority the initial priority for the key - */ -PriorityQueue.prototype.add = function(key, priority) { - var keyIndices = this._keyIndices; - key = String(key); - if (!_.has(keyIndices, key)) { - var arr = this._arr; - var index = arr.length; - keyIndices[key] = index; - arr.push({key: key, priority: priority}); - this._decrease(index); - return true; - } - return false; -}; - -/** - * Removes and returns the smallest key in the queue. Takes `O(log n)` time. - */ -PriorityQueue.prototype.removeMin = function() { - this._swap(0, this._arr.length - 1); - var min = this._arr.pop(); - delete this._keyIndices[min.key]; - this._heapify(0); - return min.key; -}; - -/** - * Decreases the priority for **key** to **priority**. If the new priority is - * greater than the previous priority, this function will throw an Error. - * - * @param {Object} key the key for which to raise priority - * @param {Number} priority the new priority for the key - */ -PriorityQueue.prototype.decrease = function(key, priority) { - var index = this._keyIndices[key]; - if (priority > this._arr[index].priority) { - throw new Error("New priority is greater than current priority. " + - "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority); - } - this._arr[index].priority = priority; - this._decrease(index); -}; - -PriorityQueue.prototype._heapify = function(i) { - var arr = this._arr; - var l = 2 * i, - r = l + 1, - largest = i; - if (l < arr.length) { - largest = arr[l].priority < arr[largest].priority ? l : largest; - if (r < arr.length) { - largest = arr[r].priority < arr[largest].priority ? r : largest; - } - if (largest !== i) { - this._swap(i, largest); - this._heapify(largest); - } - } -}; - -PriorityQueue.prototype._decrease = function(index) { - var arr = this._arr; - var priority = arr[index].priority; - var parent; - while (index !== 0) { - parent = index >> 1; - if (arr[parent].priority < priority) { - break; - } - this._swap(index, parent); - index = parent; - } -}; - -PriorityQueue.prototype._swap = function(i, j) { - var arr = this._arr; - var keyIndices = this._keyIndices; - var origArrI = arr[i]; - var origArrJ = arr[j]; - arr[i] = origArrJ; - arr[j] = origArrI; - keyIndices[origArrJ.key] = i; - keyIndices[origArrI.key] = j; -}; - -},{"../lodash":75}],72:[function(require,module,exports){ -"use strict"; - -var _ = require("./lodash"); - -module.exports = Graph; - -var DEFAULT_EDGE_NAME = "\x00", - GRAPH_NODE = "\x00", - EDGE_KEY_DELIM = "\x01"; - -// Implementation notes: -// -// * Node id query functions should return string ids for the nodes -// * Edge id query functions should return an "edgeObj", edge object, that is -// composed of enough information to uniquely identify an edge: {v, w, name}. -// * Internally we use an "edgeId", a stringified form of the edgeObj, to -// reference edges. This is because we need a performant way to look these -// edges up and, object properties, which have string keys, are the closest -// we're going to get to a performant hashtable in JavaScript. - -function Graph(opts) { - this._isDirected = _.has(opts, "directed") ? opts.directed : true; - this._isMultigraph = _.has(opts, "multigraph") ? opts.multigraph : false; - this._isCompound = _.has(opts, "compound") ? opts.compound : false; - - // Label for the graph itself - this._label = undefined; - - // Defaults to be set when creating a new node - this._defaultNodeLabelFn = _.constant(undefined); - - // Defaults to be set when creating a new edge - this._defaultEdgeLabelFn = _.constant(undefined); - - // v -> label - this._nodes = {}; - - if (this._isCompound) { - // v -> parent - this._parent = {}; - - // v -> children - this._children = {}; - this._children[GRAPH_NODE] = {}; - } - - // v -> edgeObj - this._in = {}; - - // u -> v -> Number - this._preds = {}; - - // v -> edgeObj - this._out = {}; - - // v -> w -> Number - this._sucs = {}; - - // e -> edgeObj - this._edgeObjs = {}; - - // e -> label - this._edgeLabels = {}; -} - -/* Number of nodes in the graph. Should only be changed by the implementation. */ -Graph.prototype._nodeCount = 0; - -/* Number of edges in the graph. Should only be changed by the implementation. */ -Graph.prototype._edgeCount = 0; - - -/* === Graph functions ========= */ - -Graph.prototype.isDirected = function() { - return this._isDirected; -}; - -Graph.prototype.isMultigraph = function() { - return this._isMultigraph; -}; - -Graph.prototype.isCompound = function() { - return this._isCompound; -}; - -Graph.prototype.setGraph = function(label) { - this._label = label; - return this; -}; - -Graph.prototype.graph = function() { - return this._label; -}; - - -/* === Node functions ========== */ - -Graph.prototype.setDefaultNodeLabel = function(newDefault) { - if (!_.isFunction(newDefault)) { - newDefault = _.constant(newDefault); - } - this._defaultNodeLabelFn = newDefault; - return this; -}; - -Graph.prototype.nodeCount = function() { - return this._nodeCount; -}; - -Graph.prototype.nodes = function() { - return _.keys(this._nodes); -}; - -Graph.prototype.sources = function() { - return _.filter(this.nodes(), function(v) { - return _.isEmpty(this._in[v]); - }, this); -}; - -Graph.prototype.sinks = function() { - return _.filter(this.nodes(), function(v) { - return _.isEmpty(this._out[v]); - }, this); -}; - -Graph.prototype.setNodes = function(vs, value) { - var args = arguments; - _.each(vs, function(v) { - if (args.length > 1) { - this.setNode(v, value); - } else { - this.setNode(v); - } - }, this); - return this; -}; - -Graph.prototype.setNode = function(v, value) { - if (_.has(this._nodes, v)) { - if (arguments.length > 1) { - this._nodes[v] = value; - } - return this; - } - - this._nodes[v] = arguments.length > 1 ? value : this._defaultNodeLabelFn(v); - if (this._isCompound) { - this._parent[v] = GRAPH_NODE; - this._children[v] = {}; - this._children[GRAPH_NODE][v] = true; - } - this._in[v] = {}; - this._preds[v] = {}; - this._out[v] = {}; - this._sucs[v] = {}; - ++this._nodeCount; - return this; -}; - -Graph.prototype.node = function(v) { - return this._nodes[v]; -}; - -Graph.prototype.hasNode = function(v) { - return _.has(this._nodes, v); -}; - -Graph.prototype.removeNode = function(v) { - var self = this; - if (_.has(this._nodes, v)) { - var removeEdge = function(e) { self.removeEdge(self._edgeObjs[e]); }; - delete this._nodes[v]; - if (this._isCompound) { - this._removeFromParentsChildList(v); - delete this._parent[v]; - _.each(this.children(v), function(child) { - this.setParent(child); - }, this); - delete this._children[v]; - } - _.each(_.keys(this._in[v]), removeEdge); - delete this._in[v]; - delete this._preds[v]; - _.each(_.keys(this._out[v]), removeEdge); - delete this._out[v]; - delete this._sucs[v]; - --this._nodeCount; - } - return this; -}; - -Graph.prototype.setParent = function(v, parent) { - if (!this._isCompound) { - throw new Error("Cannot set parent in a non-compound graph"); - } - - if (_.isUndefined(parent)) { - parent = GRAPH_NODE; - } else { - for (var ancestor = parent; - !_.isUndefined(ancestor); - ancestor = this.parent(ancestor)) { - if (ancestor === v) { - throw new Error("Setting " + parent+ " as parent of " + v + - " would create create a cycle"); - } - } - - this.setNode(parent); - } - - this.setNode(v); - this._removeFromParentsChildList(v); - this._parent[v] = parent; - this._children[parent][v] = true; - return this; -}; - -Graph.prototype._removeFromParentsChildList = function(v) { - delete this._children[this._parent[v]][v]; -}; - -Graph.prototype.parent = function(v) { - if (this._isCompound) { - var parent = this._parent[v]; - if (parent !== GRAPH_NODE) { - return parent; - } - } -}; - -Graph.prototype.children = function(v) { - if (_.isUndefined(v)) { - v = GRAPH_NODE; - } - - if (this._isCompound) { - var children = this._children[v]; - if (children) { - return _.keys(children); - } - } else if (v === GRAPH_NODE) { - return this.nodes(); - } else if (this.hasNode(v)) { - return []; - } -}; - -Graph.prototype.predecessors = function(v) { - var predsV = this._preds[v]; - if (predsV) { - return _.keys(predsV); - } -}; - -Graph.prototype.successors = function(v) { - var sucsV = this._sucs[v]; - if (sucsV) { - return _.keys(sucsV); - } -}; - -Graph.prototype.neighbors = function(v) { - var preds = this.predecessors(v); - if (preds) { - return _.union(preds, this.successors(v)); - } -}; - -/* === Edge functions ========== */ - -Graph.prototype.setDefaultEdgeLabel = function(newDefault) { - if (!_.isFunction(newDefault)) { - newDefault = _.constant(newDefault); - } - this._defaultEdgeLabelFn = newDefault; - return this; -}; - -Graph.prototype.edgeCount = function() { - return this._edgeCount; -}; - -Graph.prototype.edges = function() { - return _.values(this._edgeObjs); -}; - -Graph.prototype.setPath = function(vs, value) { - var self = this, - args = arguments; - _.reduce(vs, function(v, w) { - if (args.length > 1) { - self.setEdge(v, w, value); - } else { - self.setEdge(v, w); - } - return w; - }); - return this; -}; - -/* - * setEdge(v, w, [value, [name]]) - * setEdge({ v, w, [name] }, [value]) - */ -Graph.prototype.setEdge = function(v, w, value, name) { - var valueSpecified = arguments.length > 2; - - v = String(v); - w = String(w); - if (!_.isUndefined(name)) { - name = String(name); - } - - if (_.isPlainObject(arguments[0])) { - v = arguments[0].v; - w = arguments[0].w; - name = arguments[0].name; - if (arguments.length === 2) { - value = arguments[1]; - valueSpecified = true; - } - } - - var e = edgeArgsToId(this._isDirected, v, w, name); - if (_.has(this._edgeLabels, e)) { - if (valueSpecified) { - this._edgeLabels[e] = value; - } - return this; - } - - if (!_.isUndefined(name) && !this._isMultigraph) { - throw new Error("Cannot set a named edge when isMultigraph = false"); - } - - // It didn't exist, so we need to create it. - // First ensure the nodes exist. - this.setNode(v); - this.setNode(w); - - this._edgeLabels[e] = valueSpecified ? value : this._defaultEdgeLabelFn(v, w, name); - - var edgeObj = edgeArgsToObj(this._isDirected, v, w, name); - // Ensure we add undirected edges in a consistent way. - v = edgeObj.v; - w = edgeObj.w; - - Object.freeze(edgeObj); - this._edgeObjs[e] = edgeObj; - incrementOrInitEntry(this._preds[w], v); - incrementOrInitEntry(this._sucs[v], w); - this._in[w][e] = edgeObj; - this._out[v][e] = edgeObj; - this._edgeCount++; - return this; -}; - -Graph.prototype.edge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)); - return this._edgeLabels[e]; -}; - -Graph.prototype.hasEdge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)); - return _.has(this._edgeLabels, e); -}; - -Graph.prototype.removeEdge = function(v, w, name) { - var e = (arguments.length === 1 - ? edgeObjToId(this._isDirected, arguments[0]) - : edgeArgsToId(this._isDirected, v, w, name)), - edge = this._edgeObjs[e]; - if (edge) { - v = edge.v; - w = edge.w; - delete this._edgeLabels[e]; - delete this._edgeObjs[e]; - decrementOrRemoveEntry(this._preds[w], v); - decrementOrRemoveEntry(this._sucs[v], w); - delete this._in[w][e]; - delete this._out[v][e]; - this._edgeCount--; - } - return this; -}; - -Graph.prototype.inEdges = function(v, u) { - var inV = this._in[v]; - if (inV) { - var edges = _.values(inV); - if (!u) { - return edges; - } - return _.filter(edges, function(edge) { return edge.v === u; }); - } -}; - -Graph.prototype.outEdges = function(v, w) { - var outV = this._out[v]; - if (outV) { - var edges = _.values(outV); - if (!w) { - return edges; - } - return _.filter(edges, function(edge) { return edge.w === w; }); - } -}; - -Graph.prototype.nodeEdges = function(v, w) { - var inEdges = this.inEdges(v, w); - if (inEdges) { - return inEdges.concat(this.outEdges(v, w)); - } -}; - -function incrementOrInitEntry(map, k) { - if (_.has(map, k)) { - map[k]++; - } else { - map[k] = 1; - } -} - -function decrementOrRemoveEntry(map, k) { - if (!--map[k]) { delete map[k]; } -} - -function edgeArgsToId(isDirected, v, w, name) { - if (!isDirected && v > w) { - var tmp = v; - v = w; - w = tmp; - } - return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + - (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name); -} - -function edgeArgsToObj(isDirected, v, w, name) { - if (!isDirected && v > w) { - var tmp = v; - v = w; - w = tmp; - } - var edgeObj = { v: v, w: w }; - if (name) { - edgeObj.name = name; - } - return edgeObj; -} - -function edgeObjToId(isDirected, edgeObj) { - return edgeArgsToId(isDirected, edgeObj.v, edgeObj.w, edgeObj.name); -} - -},{"./lodash":75}],73:[function(require,module,exports){ -// Includes only the "core" of graphlib -module.exports = { - Graph: require("./graph"), - version: require("./version") -}; - -},{"./graph":72,"./version":76}],74:[function(require,module,exports){ -var _ = require("./lodash"), - Graph = require("./graph"); - -module.exports = { - write: write, - read: read -}; - -function write(g) { - var json = { - options: { - directed: g.isDirected(), - multigraph: g.isMultigraph(), - compound: g.isCompound() - }, - nodes: writeNodes(g), - edges: writeEdges(g) - }; - if (!_.isUndefined(g.graph())) { - json.value = _.clone(g.graph()); - } - return json; -} - -function writeNodes(g) { - return _.map(g.nodes(), function(v) { - var nodeValue = g.node(v), - parent = g.parent(v), - node = { v: v }; - if (!_.isUndefined(nodeValue)) { - node.value = nodeValue; - } - if (!_.isUndefined(parent)) { - node.parent = parent; - } - return node; - }); -} - -function writeEdges(g) { - return _.map(g.edges(), function(e) { - var edgeValue = g.edge(e), - edge = { v: e.v, w: e.w }; - if (!_.isUndefined(e.name)) { - edge.name = e.name; - } - if (!_.isUndefined(edgeValue)) { - edge.value = edgeValue; - } - return edge; - }); -} - -function read(json) { - var g = new Graph(json.options).setGraph(json.value); - _.each(json.nodes, function(entry) { - g.setNode(entry.v, entry.value); - if (entry.parent) { - g.setParent(entry.v, entry.parent); - } - }); - _.each(json.edges, function(entry) { - g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value); - }); - return g; -} - -},{"./graph":72,"./lodash":75}],75:[function(require,module,exports){ -module.exports=require(20) -},{"/Users/cpettitt/projects/dagre-d3/lib/lodash.js":20,"lodash":77}],76:[function(require,module,exports){ -module.exports = '0.9.1'; - -},{}],77:[function(require,module,exports){ -(function (global){ -/** - * @license - * Lo-Dash 2.4.1 (Custom Build) - * Build: `lodash modern -o ./dist/lodash.js` - * Copyright 2012-2013 The Dojo Foundation - * Based on Underscore.js 1.5.2 - * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - * Available under MIT license - */ -;(function() { - - /** Used as a safe reference for `undefined` in pre ES5 environments */ - var undefined; - - /** Used to pool arrays and objects used internally */ - var arrayPool = [], - objectPool = []; - - /** Used to generate unique IDs */ - var idCounter = 0; - - /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */ - var keyPrefix = +new Date + ''; - - /** Used as the size when optimizations are enabled for large arrays */ - var largeArraySize = 75; - - /** Used as the max size of the `arrayPool` and `objectPool` */ - var maxPoolSize = 40; - - /** Used to detect and test whitespace */ - var whitespace = ( - // whitespace - ' \t\x0B\f\xA0\ufeff' + - - // line terminators - '\n\r\u2028\u2029' + - - // unicode category "Zs" space separators - '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' - ); - - /** Used to match empty string literals in compiled template source */ - var reEmptyStringLeading = /\b__p \+= '';/g, - reEmptyStringMiddle = /\b(__p \+=) '' \+/g, - reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; - - /** - * Used to match ES6 template delimiters - * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals - */ - var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; - - /** Used to match regexp flags from their coerced string values */ - var reFlags = /\w*$/; - - /** Used to detected named functions */ - var reFuncName = /^\s*function[ \n\r\t]+\w/; - - /** Used to match "interpolate" template delimiters */ - var reInterpolate = /<%=([\s\S]+?)%>/g; - - /** Used to match leading whitespace and zeros to be removed */ - var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)'); - - /** Used to ensure capturing order of template delimiters */ - var reNoMatch = /($^)/; - - /** Used to detect functions containing a `this` reference */ - var reThis = /\bthis\b/; - - /** Used to match unescaped characters in compiled string literals */ - var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; - - /** Used to assign default `context` object properties */ - var contextProps = [ - 'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object', - 'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN', - 'parseInt', 'setTimeout' - ]; - - /** Used to make template sourceURLs easier to identify */ - var templateCounter = 0; - - /** `Object#toString` result shortcuts */ - var argsClass = '[object Arguments]', - arrayClass = '[object Array]', - boolClass = '[object Boolean]', - dateClass = '[object Date]', - funcClass = '[object Function]', - numberClass = '[object Number]', - objectClass = '[object Object]', - regexpClass = '[object RegExp]', - stringClass = '[object String]'; - - /** Used to identify object classifications that `_.clone` supports */ - var cloneableClasses = {}; - cloneableClasses[funcClass] = false; - cloneableClasses[argsClass] = cloneableClasses[arrayClass] = - cloneableClasses[boolClass] = cloneableClasses[dateClass] = - cloneableClasses[numberClass] = cloneableClasses[objectClass] = - cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true; - - /** Used as an internal `_.debounce` options object */ - var debounceOptions = { - 'leading': false, - 'maxWait': 0, - 'trailing': false - }; - - /** Used as the property descriptor for `__bindData__` */ - var descriptor = { - 'configurable': false, - 'enumerable': false, - 'value': null, - 'writable': false - }; - - /** Used to determine if values are of the language type Object */ - var objectTypes = { - 'boolean': false, - 'function': true, - 'object': true, - 'number': false, - 'string': false, - 'undefined': false - }; - - /** Used to escape characters for inclusion in compiled string literals */ - var stringEscapes = { - '\\': '\\', - "'": "'", - '\n': 'n', - '\r': 'r', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - /** Used as a reference to the global object */ - var root = (objectTypes[typeof window] && window) || this; - - /** Detect free variable `exports` */ - var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; - - /** Detect free variable `module` */ - var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; - - /** Detect the popular CommonJS extension `module.exports` */ - var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; - - /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */ - var freeGlobal = objectTypes[typeof global] && global; - if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) { - root = freeGlobal; - } - - /*--------------------------------------------------------------------------*/ - - /** - * The base implementation of `_.indexOf` without support for binary searches - * or `fromIndex` constraints. - * - * @private - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the matched value or `-1`. - */ - function baseIndexOf(array, value, fromIndex) { - var index = (fromIndex || 0) - 1, - length = array ? array.length : 0; - - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * An implementation of `_.contains` for cache objects that mimics the return - * signature of `_.indexOf` by returning `0` if the value is found, else `-1`. - * - * @private - * @param {Object} cache The cache object to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns `0` if `value` is found, else `-1`. - */ - function cacheIndexOf(cache, value) { - var type = typeof value; - cache = cache.cache; - - if (type == 'boolean' || value == null) { - return cache[value] ? 0 : -1; - } - if (type != 'number' && type != 'string') { - type = 'object'; - } - var key = type == 'number' ? value : keyPrefix + value; - cache = (cache = cache[type]) && cache[key]; - - return type == 'object' - ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1) - : (cache ? 0 : -1); - } - - /** - * Adds a given value to the corresponding cache object. - * - * @private - * @param {*} value The value to add to the cache. - */ - function cachePush(value) { - var cache = this.cache, - type = typeof value; - - if (type == 'boolean' || value == null) { - cache[value] = true; - } else { - if (type != 'number' && type != 'string') { - type = 'object'; - } - var key = type == 'number' ? value : keyPrefix + value, - typeCache = cache[type] || (cache[type] = {}); - - if (type == 'object') { - (typeCache[key] || (typeCache[key] = [])).push(value); - } else { - typeCache[key] = true; - } - } - } - - /** - * Used by `_.max` and `_.min` as the default callback when a given - * collection is a string value. - * - * @private - * @param {string} value The character to inspect. - * @returns {number} Returns the code unit of given character. - */ - function charAtCallback(value) { - return value.charCodeAt(0); - } - - /** - * Used by `sortBy` to compare transformed `collection` elements, stable sorting - * them in ascending order. - * - * @private - * @param {Object} a The object to compare to `b`. - * @param {Object} b The object to compare to `a`. - * @returns {number} Returns the sort order indicator of `1` or `-1`. - */ - function compareAscending(a, b) { - var ac = a.criteria, - bc = b.criteria, - index = -1, - length = ac.length; - - while (++index < length) { - var value = ac[index], - other = bc[index]; - - if (value !== other) { - if (value > other || typeof value == 'undefined') { - return 1; - } - if (value < other || typeof other == 'undefined') { - return -1; - } - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to return the same value for - // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247 - // - // This also ensures a stable sort in V8 and other engines. - // See http://code.google.com/p/v8/issues/detail?id=90 - return a.index - b.index; - } - - /** - * Creates a cache object to optimize linear searches of large arrays. - * - * @private - * @param {Array} [array=[]] The array to search. - * @returns {null|Object} Returns the cache object or `null` if caching should not be used. - */ - function createCache(array) { - var index = -1, - length = array.length, - first = array[0], - mid = array[(length / 2) | 0], - last = array[length - 1]; - - if (first && typeof first == 'object' && - mid && typeof mid == 'object' && last && typeof last == 'object') { - return false; - } - var cache = getObject(); - cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false; - - var result = getObject(); - result.array = array; - result.cache = cache; - result.push = cachePush; - - while (++index < length) { - result.push(array[index]); - } - return result; - } - - /** - * Used by `template` to escape characters for inclusion in compiled - * string literals. - * - * @private - * @param {string} match The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeStringChar(match) { - return '\\' + stringEscapes[match]; - } - - /** - * Gets an array from the array pool or creates a new one if the pool is empty. - * - * @private - * @returns {Array} The array from the pool. - */ - function getArray() { - return arrayPool.pop() || []; - } - - /** - * Gets an object from the object pool or creates a new one if the pool is empty. - * - * @private - * @returns {Object} The object from the pool. - */ - function getObject() { - return objectPool.pop() || { - 'array': null, - 'cache': null, - 'criteria': null, - 'false': false, - 'index': 0, - 'null': false, - 'number': null, - 'object': null, - 'push': null, - 'string': null, - 'true': false, - 'undefined': false, - 'value': null - }; - } - - /** - * Releases the given array back to the array pool. - * - * @private - * @param {Array} [array] The array to release. - */ - function releaseArray(array) { - array.length = 0; - if (arrayPool.length < maxPoolSize) { - arrayPool.push(array); - } - } - - /** - * Releases the given object back to the object pool. - * - * @private - * @param {Object} [object] The object to release. - */ - function releaseObject(object) { - var cache = object.cache; - if (cache) { - releaseObject(cache); - } - object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null; - if (objectPool.length < maxPoolSize) { - objectPool.push(object); - } - } - - /** - * Slices the `collection` from the `start` index up to, but not including, - * the `end` index. - * - * Note: This function is used instead of `Array#slice` to support node lists - * in IE < 9 and to ensure dense arrays are returned. - * - * @private - * @param {Array|Object|string} collection The collection to slice. - * @param {number} start The start index. - * @param {number} end The end index. - * @returns {Array} Returns the new array. - */ - function slice(array, start, end) { - start || (start = 0); - if (typeof end == 'undefined') { - end = array ? array.length : 0; - } - var index = -1, - length = end - start || 0, - result = Array(length < 0 ? 0 : length); - - while (++index < length) { - result[index] = array[start + index]; - } - return result; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Create a new `lodash` function using the given context object. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Object} [context=root] The context object. - * @returns {Function} Returns the `lodash` function. - */ - function runInContext(context) { - // Avoid issues with some ES3 environments that attempt to use values, named - // after built-in constructors like `Object`, for the creation of literals. - // ES5 clears this up by stating that literals must use built-in constructors. - // See http://es5.github.io/#x11.1.5. - context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root; - - /** Native constructor references */ - var Array = context.Array, - Boolean = context.Boolean, - Date = context.Date, - Function = context.Function, - Math = context.Math, - Number = context.Number, - Object = context.Object, - RegExp = context.RegExp, - String = context.String, - TypeError = context.TypeError; - - /** - * Used for `Array` method references. - * - * Normally `Array.prototype` would suffice, however, using an array literal - * avoids issues in Narwhal. - */ - var arrayRef = []; - - /** Used for native method references */ - var objectProto = Object.prototype; - - /** Used to restore the original `_` reference in `noConflict` */ - var oldDash = context._; - - /** Used to resolve the internal [[Class]] of values */ - var toString = objectProto.toString; - - /** Used to detect if a method is native */ - var reNative = RegExp('^' + - String(toString) - .replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - .replace(/toString| for [^\]]+/g, '.*?') + '$' - ); - - /** Native method shortcuts */ - var ceil = Math.ceil, - clearTimeout = context.clearTimeout, - floor = Math.floor, - fnToString = Function.prototype.toString, - getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf, - hasOwnProperty = objectProto.hasOwnProperty, - push = arrayRef.push, - setTimeout = context.setTimeout, - splice = arrayRef.splice, - unshift = arrayRef.unshift; - - /** Used to set meta data on functions */ - var defineProperty = (function() { - // IE 8 only accepts DOM elements - try { - var o = {}, - func = isNative(func = Object.defineProperty) && func, - result = func(o, o, o) && func; - } catch(e) { } - return result; - }()); - - /* Native method shortcuts for methods with the same name as other `lodash` methods */ - var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate, - nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray, - nativeIsFinite = context.isFinite, - nativeIsNaN = context.isNaN, - nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys, - nativeMax = Math.max, - nativeMin = Math.min, - nativeParseInt = context.parseInt, - nativeRandom = Math.random; - - /** Used to lookup a built-in constructor by [[Class]] */ - var ctorByClass = {}; - ctorByClass[arrayClass] = Array; - ctorByClass[boolClass] = Boolean; - ctorByClass[dateClass] = Date; - ctorByClass[funcClass] = Function; - ctorByClass[objectClass] = Object; - ctorByClass[numberClass] = Number; - ctorByClass[regexpClass] = RegExp; - ctorByClass[stringClass] = String; - - /*--------------------------------------------------------------------------*/ - - /** - * Creates a `lodash` object which wraps the given value to enable intuitive - * method chaining. - * - * In addition to Lo-Dash methods, wrappers also have the following `Array` methods: - * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`, - * and `unshift` - * - * Chaining is supported in custom builds as long as the `value` method is - * implicitly or explicitly included in the build. - * - * The chainable wrapper functions are: - * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`, - * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`, - * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`, - * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, - * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, - * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`, - * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`, - * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, - * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, - * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, - * and `zip` - * - * The non-chainable wrapper functions are: - * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`, - * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`, - * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`, - * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`, - * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`, - * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`, - * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`, - * `template`, `unescape`, `uniqueId`, and `value` - * - * The wrapper functions `first` and `last` return wrapped values when `n` is - * provided, otherwise they return unwrapped values. - * - * Explicit chaining can be enabled by using the `_.chain` method. - * - * @name _ - * @constructor - * @category Chaining - * @param {*} value The value to wrap in a `lodash` instance. - * @returns {Object} Returns a `lodash` instance. - * @example - * - * var wrapped = _([1, 2, 3]); - * - * // returns an unwrapped value - * wrapped.reduce(function(sum, num) { - * return sum + num; - * }); - * // => 6 - * - * // returns a wrapped value - * var squares = wrapped.map(function(num) { - * return num * num; - * }); - * - * _.isArray(squares); - * // => false - * - * _.isArray(squares.value()); - * // => true - */ - function lodash(value) { - // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor - return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__')) - ? value - : new lodashWrapper(value); - } - - /** - * A fast path for creating `lodash` wrapper objects. - * - * @private - * @param {*} value The value to wrap in a `lodash` instance. - * @param {boolean} chainAll A flag to enable chaining for all methods - * @returns {Object} Returns a `lodash` instance. - */ - function lodashWrapper(value, chainAll) { - this.__chain__ = !!chainAll; - this.__wrapped__ = value; - } - // ensure `new lodashWrapper` is an instance of `lodash` - lodashWrapper.prototype = lodash.prototype; - - /** - * An object used to flag environments features. - * - * @static - * @memberOf _ - * @type Object - */ - var support = lodash.support = {}; - - /** - * Detect if functions can be decompiled by `Function#toString` - * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps). - * - * @memberOf _.support - * @type boolean - */ - support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext); - - /** - * Detect if `Function#name` is supported (all but IE). - * - * @memberOf _.support - * @type boolean - */ - support.funcNames = typeof Function.name == 'string'; - - /** - * By default, the template delimiters used by Lo-Dash are similar to those in - * embedded Ruby (ERB). Change the following template settings to use alternative - * delimiters. - * - * @static - * @memberOf _ - * @type Object - */ - lodash.templateSettings = { - - /** - * Used to detect `data` property values to be HTML-escaped. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'escape': /<%-([\s\S]+?)%>/g, - - /** - * Used to detect code to be evaluated. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'evaluate': /<%([\s\S]+?)%>/g, - - /** - * Used to detect `data` property values to inject. - * - * @memberOf _.templateSettings - * @type RegExp - */ - 'interpolate': reInterpolate, - - /** - * Used to reference the data object in the template text. - * - * @memberOf _.templateSettings - * @type string - */ - 'variable': '', - - /** - * Used to import variables into the compiled template. - * - * @memberOf _.templateSettings - * @type Object - */ - 'imports': { - - /** - * A reference to the `lodash` function. - * - * @memberOf _.templateSettings.imports - * @type Function - */ - '_': lodash - } - }; - - /*--------------------------------------------------------------------------*/ - - /** - * The base implementation of `_.bind` that creates the bound function and - * sets its meta data. - * - * @private - * @param {Array} bindData The bind data array. - * @returns {Function} Returns the new bound function. - */ - function baseBind(bindData) { - var func = bindData[0], - partialArgs = bindData[2], - thisArg = bindData[4]; - - function bound() { - // `Function#bind` spec - // http://es5.github.io/#x15.3.4.5 - if (partialArgs) { - // avoid `arguments` object deoptimizations by using `slice` instead - // of `Array.prototype.slice.call` and not assigning `arguments` to a - // variable as a ternary expression - var args = slice(partialArgs); - push.apply(args, arguments); - } - // mimic the constructor's `return` behavior - // http://es5.github.io/#x13.2.2 - if (this instanceof bound) { - // ensure `new bound` is an instance of `func` - var thisBinding = baseCreate(func.prototype), - result = func.apply(thisBinding, args || arguments); - return isObject(result) ? result : thisBinding; - } - return func.apply(thisArg, args || arguments); - } - setBindData(bound, bindData); - return bound; - } - - /** - * The base implementation of `_.clone` without argument juggling or support - * for `thisArg` binding. - * - * @private - * @param {*} value The value to clone. - * @param {boolean} [isDeep=false] Specify a deep clone. - * @param {Function} [callback] The function to customize cloning values. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates clones with source counterparts. - * @returns {*} Returns the cloned value. - */ - function baseClone(value, isDeep, callback, stackA, stackB) { - if (callback) { - var result = callback(value); - if (typeof result != 'undefined') { - return result; - } - } - // inspect [[Class]] - var isObj = isObject(value); - if (isObj) { - var className = toString.call(value); - if (!cloneableClasses[className]) { - return value; - } - var ctor = ctorByClass[className]; - switch (className) { - case boolClass: - case dateClass: - return new ctor(+value); - - case numberClass: - case stringClass: - return new ctor(value); - - case regexpClass: - result = ctor(value.source, reFlags.exec(value)); - result.lastIndex = value.lastIndex; - return result; - } - } else { - return value; - } - var isArr = isArray(value); - if (isDeep) { - // check for circular references and return corresponding clone - var initedStack = !stackA; - stackA || (stackA = getArray()); - stackB || (stackB = getArray()); - - var length = stackA.length; - while (length--) { - if (stackA[length] == value) { - return stackB[length]; - } - } - result = isArr ? ctor(value.length) : {}; - } - else { - result = isArr ? slice(value) : assign({}, value); - } - // add array properties assigned by `RegExp#exec` - if (isArr) { - if (hasOwnProperty.call(value, 'index')) { - result.index = value.index; - } - if (hasOwnProperty.call(value, 'input')) { - result.input = value.input; - } - } - // exit for shallow clone - if (!isDeep) { - return result; - } - // add the source value to the stack of traversed objects - // and associate it with its clone - stackA.push(value); - stackB.push(result); - - // recursively populate clone (susceptible to call stack limits) - (isArr ? forEach : forOwn)(value, function(objValue, key) { - result[key] = baseClone(objValue, isDeep, callback, stackA, stackB); - }); - - if (initedStack) { - releaseArray(stackA); - releaseArray(stackB); - } - return result; - } - - /** - * The base implementation of `_.create` without support for assigning - * properties to the created object. - * - * @private - * @param {Object} prototype The object to inherit from. - * @returns {Object} Returns the new object. - */ - function baseCreate(prototype, properties) { - return isObject(prototype) ? nativeCreate(prototype) : {}; - } - // fallback for browsers without `Object.create` - if (!nativeCreate) { - baseCreate = (function() { - function Object() {} - return function(prototype) { - if (isObject(prototype)) { - Object.prototype = prototype; - var result = new Object; - Object.prototype = null; - } - return result || context.Object(); - }; - }()); - } - - /** - * The base implementation of `_.createCallback` without support for creating - * "_.pluck" or "_.where" style callbacks. - * - * @private - * @param {*} [func=identity] The value to convert to a callback. - * @param {*} [thisArg] The `this` binding of the created callback. - * @param {number} [argCount] The number of arguments the callback accepts. - * @returns {Function} Returns a callback function. - */ - function baseCreateCallback(func, thisArg, argCount) { - if (typeof func != 'function') { - return identity; - } - // exit early for no `thisArg` or already bound by `Function#bind` - if (typeof thisArg == 'undefined' || !('prototype' in func)) { - return func; - } - var bindData = func.__bindData__; - if (typeof bindData == 'undefined') { - if (support.funcNames) { - bindData = !func.name; - } - bindData = bindData || !support.funcDecomp; - if (!bindData) { - var source = fnToString.call(func); - if (!support.funcNames) { - bindData = !reFuncName.test(source); - } - if (!bindData) { - // checks if `func` references the `this` keyword and stores the result - bindData = reThis.test(source); - setBindData(func, bindData); - } - } - } - // exit early if there are no `this` references or `func` is bound - if (bindData === false || (bindData !== true && bindData[1] & 1)) { - return func; - } - switch (argCount) { - case 1: return function(value) { - return func.call(thisArg, value); - }; - case 2: return function(a, b) { - return func.call(thisArg, a, b); - }; - case 3: return function(value, index, collection) { - return func.call(thisArg, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(thisArg, accumulator, value, index, collection); - }; - } - return bind(func, thisArg); - } - - /** - * The base implementation of `createWrapper` that creates the wrapper and - * sets its meta data. - * - * @private - * @param {Array} bindData The bind data array. - * @returns {Function} Returns the new function. - */ - function baseCreateWrapper(bindData) { - var func = bindData[0], - bitmask = bindData[1], - partialArgs = bindData[2], - partialRightArgs = bindData[3], - thisArg = bindData[4], - arity = bindData[5]; - - var isBind = bitmask & 1, - isBindKey = bitmask & 2, - isCurry = bitmask & 4, - isCurryBound = bitmask & 8, - key = func; - - function bound() { - var thisBinding = isBind ? thisArg : this; - if (partialArgs) { - var args = slice(partialArgs); - push.apply(args, arguments); - } - if (partialRightArgs || isCurry) { - args || (args = slice(arguments)); - if (partialRightArgs) { - push.apply(args, partialRightArgs); - } - if (isCurry && args.length < arity) { - bitmask |= 16 & ~32; - return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]); - } - } - args || (args = arguments); - if (isBindKey) { - func = thisBinding[key]; - } - if (this instanceof bound) { - thisBinding = baseCreate(func.prototype); - var result = func.apply(thisBinding, args); - return isObject(result) ? result : thisBinding; - } - return func.apply(thisBinding, args); - } - setBindData(bound, bindData); - return bound; - } - - /** - * The base implementation of `_.difference` that accepts a single array - * of values to exclude. - * - * @private - * @param {Array} array The array to process. - * @param {Array} [values] The array of values to exclude. - * @returns {Array} Returns a new array of filtered values. - */ - function baseDifference(array, values) { - var index = -1, - indexOf = getIndexOf(), - length = array ? array.length : 0, - isLarge = length >= largeArraySize && indexOf === baseIndexOf, - result = []; - - if (isLarge) { - var cache = createCache(values); - if (cache) { - indexOf = cacheIndexOf; - values = cache; - } else { - isLarge = false; - } - } - while (++index < length) { - var value = array[index]; - if (indexOf(values, value) < 0) { - result.push(value); - } - } - if (isLarge) { - releaseObject(values); - } - return result; - } - - /** - * The base implementation of `_.flatten` without support for callback - * shorthands or `thisArg` binding. - * - * @private - * @param {Array} array The array to flatten. - * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. - * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects. - * @param {number} [fromIndex=0] The index to start from. - * @returns {Array} Returns a new flattened array. - */ - function baseFlatten(array, isShallow, isStrict, fromIndex) { - var index = (fromIndex || 0) - 1, - length = array ? array.length : 0, - result = []; - - while (++index < length) { - var value = array[index]; - - if (value && typeof value == 'object' && typeof value.length == 'number' - && (isArray(value) || isArguments(value))) { - // recursively flatten arrays (susceptible to call stack limits) - if (!isShallow) { - value = baseFlatten(value, isShallow, isStrict); - } - var valIndex = -1, - valLength = value.length, - resIndex = result.length; - - result.length += valLength; - while (++valIndex < valLength) { - result[resIndex++] = value[valIndex]; - } - } else if (!isStrict) { - result.push(value); - } - } - return result; - } - - /** - * The base implementation of `_.isEqual`, without support for `thisArg` binding, - * that allows partial "_.where" style comparisons. - * - * @private - * @param {*} a The value to compare. - * @param {*} b The other value to compare. - * @param {Function} [callback] The function to customize comparing values. - * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons. - * @param {Array} [stackA=[]] Tracks traversed `a` objects. - * @param {Array} [stackB=[]] Tracks traversed `b` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(a, b, callback, isWhere, stackA, stackB) { - // used to indicate that when comparing objects, `a` has at least the properties of `b` - if (callback) { - var result = callback(a, b); - if (typeof result != 'undefined') { - return !!result; - } - } - // exit early for identical values - if (a === b) { - // treat `+0` vs. `-0` as not equal - return a !== 0 || (1 / a == 1 / b); - } - var type = typeof a, - otherType = typeof b; - - // exit early for unlike primitive values - if (a === a && - !(a && objectTypes[type]) && - !(b && objectTypes[otherType])) { - return false; - } - // exit early for `null` and `undefined` avoiding ES3's Function#call behavior - // http://es5.github.io/#x15.3.4.4 - if (a == null || b == null) { - return a === b; - } - // compare [[Class]] names - var className = toString.call(a), - otherClass = toString.call(b); - - if (className == argsClass) { - className = objectClass; - } - if (otherClass == argsClass) { - otherClass = objectClass; - } - if (className != otherClass) { - return false; - } - switch (className) { - case boolClass: - case dateClass: - // coerce dates and booleans to numbers, dates to milliseconds and booleans - // to `1` or `0` treating invalid dates coerced to `NaN` as not equal - return +a == +b; - - case numberClass: - // treat `NaN` vs. `NaN` as equal - return (a != +a) - ? b != +b - // but treat `+0` vs. `-0` as not equal - : (a == 0 ? (1 / a == 1 / b) : a == +b); - - case regexpClass: - case stringClass: - // coerce regexes to strings (http://es5.github.io/#x15.10.6.4) - // treat string primitives and their corresponding object instances as equal - return a == String(b); - } - var isArr = className == arrayClass; - if (!isArr) { - // unwrap any `lodash` wrapped values - var aWrapped = hasOwnProperty.call(a, '__wrapped__'), - bWrapped = hasOwnProperty.call(b, '__wrapped__'); - - if (aWrapped || bWrapped) { - return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB); - } - // exit for functions and DOM nodes - if (className != objectClass) { - return false; - } - // in older versions of Opera, `arguments` objects have `Array` constructors - var ctorA = a.constructor, - ctorB = b.constructor; - - // non `Object` object instances with different constructors are not equal - if (ctorA != ctorB && - !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) && - ('constructor' in a && 'constructor' in b) - ) { - return false; - } - } - // assume cyclic structures are equal - // the algorithm for detecting cyclic structures is adapted from ES 5.1 - // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3) - var initedStack = !stackA; - stackA || (stackA = getArray()); - stackB || (stackB = getArray()); - - var length = stackA.length; - while (length--) { - if (stackA[length] == a) { - return stackB[length] == b; - } - } - var size = 0; - result = true; - - // add `a` and `b` to the stack of traversed objects - stackA.push(a); - stackB.push(b); - - // recursively compare objects and arrays (susceptible to call stack limits) - if (isArr) { - // compare lengths to determine if a deep comparison is necessary - length = a.length; - size = b.length; - result = size == length; - - if (result || isWhere) { - // deep compare the contents, ignoring non-numeric properties - while (size--) { - var index = length, - value = b[size]; - - if (isWhere) { - while (index--) { - if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) { - break; - } - } - } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) { - break; - } - } - } - } - else { - // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys` - // which, in this case, is more costly - forIn(b, function(value, key, b) { - if (hasOwnProperty.call(b, key)) { - // count the number of properties. - size++; - // deep compare each property value. - return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB)); - } - }); - - if (result && !isWhere) { - // ensure both objects have the same number of properties - forIn(a, function(value, key, a) { - if (hasOwnProperty.call(a, key)) { - // `size` will be `-1` if `a` has more properties than `b` - return (result = --size > -1); - } - }); - } - } - stackA.pop(); - stackB.pop(); - - if (initedStack) { - releaseArray(stackA); - releaseArray(stackB); - } - return result; - } - - /** - * The base implementation of `_.merge` without argument juggling or support - * for `thisArg` binding. - * - * @private - * @param {Object} object The destination object. - * @param {Object} source The source object. - * @param {Function} [callback] The function to customize merging properties. - * @param {Array} [stackA=[]] Tracks traversed source objects. - * @param {Array} [stackB=[]] Associates values with source counterparts. - */ - function baseMerge(object, source, callback, stackA, stackB) { - (isArray(source) ? forEach : forOwn)(source, function(source, key) { - var found, - isArr, - result = source, - value = object[key]; - - if (source && ((isArr = isArray(source)) || isPlainObject(source))) { - // avoid merging previously merged cyclic sources - var stackLength = stackA.length; - while (stackLength--) { - if ((found = stackA[stackLength] == source)) { - value = stackB[stackLength]; - break; - } - } - if (!found) { - var isShallow; - if (callback) { - result = callback(value, source); - if ((isShallow = typeof result != 'undefined')) { - value = result; - } - } - if (!isShallow) { - value = isArr - ? (isArray(value) ? value : []) - : (isPlainObject(value) ? value : {}); - } - // add `source` and associated `value` to the stack of traversed objects - stackA.push(source); - stackB.push(value); - - // recursively merge objects and arrays (susceptible to call stack limits) - if (!isShallow) { - baseMerge(value, source, callback, stackA, stackB); - } - } - } - else { - if (callback) { - result = callback(value, source); - if (typeof result == 'undefined') { - result = source; - } - } - if (typeof result != 'undefined') { - value = result; - } - } - object[key] = value; - }); - } - - /** - * The base implementation of `_.random` without argument juggling or support - * for returning floating-point numbers. - * - * @private - * @param {number} min The minimum possible value. - * @param {number} max The maximum possible value. - * @returns {number} Returns a random number. - */ - function baseRandom(min, max) { - return min + floor(nativeRandom() * (max - min + 1)); - } - - /** - * The base implementation of `_.uniq` without support for callback shorthands - * or `thisArg` binding. - * - * @private - * @param {Array} array The array to process. - * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. - * @param {Function} [callback] The function called per iteration. - * @returns {Array} Returns a duplicate-value-free array. - */ - function baseUniq(array, isSorted, callback) { - var index = -1, - indexOf = getIndexOf(), - length = array ? array.length : 0, - result = []; - - var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf, - seen = (callback || isLarge) ? getArray() : result; - - if (isLarge) { - var cache = createCache(seen); - indexOf = cacheIndexOf; - seen = cache; - } - while (++index < length) { - var value = array[index], - computed = callback ? callback(value, index, array) : value; - - if (isSorted - ? !index || seen[seen.length - 1] !== computed - : indexOf(seen, computed) < 0 - ) { - if (callback || isLarge) { - seen.push(computed); - } - result.push(value); - } - } - if (isLarge) { - releaseArray(seen.array); - releaseObject(seen); - } else if (callback) { - releaseArray(seen); - } - return result; - } - - /** - * Creates a function that aggregates a collection, creating an object composed - * of keys generated from the results of running each element of the collection - * through a callback. The given `setter` function sets the keys and values - * of the composed object. - * - * @private - * @param {Function} setter The setter function. - * @returns {Function} Returns the new aggregator function. - */ - function createAggregator(setter) { - return function(collection, callback, thisArg) { - var result = {}; - callback = lodash.createCallback(callback, thisArg, 3); - - var index = -1, - length = collection ? collection.length : 0; - - if (typeof length == 'number') { - while (++index < length) { - var value = collection[index]; - setter(result, value, callback(value, index, collection), collection); - } - } else { - forOwn(collection, function(value, key, collection) { - setter(result, value, callback(value, key, collection), collection); - }); - } - return result; - }; - } - - /** - * Creates a function that, when called, either curries or invokes `func` - * with an optional `this` binding and partially applied arguments. - * - * @private - * @param {Function|string} func The function or method name to reference. - * @param {number} bitmask The bitmask of method flags to compose. - * The bitmask may be composed of the following flags: - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` - * 8 - `_.curry` (bound) - * 16 - `_.partial` - * 32 - `_.partialRight` - * @param {Array} [partialArgs] An array of arguments to prepend to those - * provided to the new function. - * @param {Array} [partialRightArgs] An array of arguments to append to those - * provided to the new function. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {number} [arity] The arity of `func`. - * @returns {Function} Returns the new function. - */ - function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { - var isBind = bitmask & 1, - isBindKey = bitmask & 2, - isCurry = bitmask & 4, - isCurryBound = bitmask & 8, - isPartial = bitmask & 16, - isPartialRight = bitmask & 32; - - if (!isBindKey && !isFunction(func)) { - throw new TypeError; - } - if (isPartial && !partialArgs.length) { - bitmask &= ~16; - isPartial = partialArgs = false; - } - if (isPartialRight && !partialRightArgs.length) { - bitmask &= ~32; - isPartialRight = partialRightArgs = false; - } - var bindData = func && func.__bindData__; - if (bindData && bindData !== true) { - // clone `bindData` - bindData = slice(bindData); - if (bindData[2]) { - bindData[2] = slice(bindData[2]); - } - if (bindData[3]) { - bindData[3] = slice(bindData[3]); - } - // set `thisBinding` is not previously bound - if (isBind && !(bindData[1] & 1)) { - bindData[4] = thisArg; - } - // set if previously bound but not currently (subsequent curried functions) - if (!isBind && bindData[1] & 1) { - bitmask |= 8; - } - // set curried arity if not yet set - if (isCurry && !(bindData[1] & 4)) { - bindData[5] = arity; - } - // append partial left arguments - if (isPartial) { - push.apply(bindData[2] || (bindData[2] = []), partialArgs); - } - // append partial right arguments - if (isPartialRight) { - unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); - } - // merge flags - bindData[1] |= bitmask; - return createWrapper.apply(null, bindData); - } - // fast path for `_.bind` - var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper; - return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); - } - - /** - * Used by `escape` to convert characters to HTML entities. - * - * @private - * @param {string} match The matched character to escape. - * @returns {string} Returns the escaped character. - */ - function escapeHtmlChar(match) { - return htmlEscapes[match]; - } - - /** - * Gets the appropriate "indexOf" function. If the `_.indexOf` method is - * customized, this method returns the custom method, otherwise it returns - * the `baseIndexOf` function. - * - * @private - * @returns {Function} Returns the "indexOf" function. - */ - function getIndexOf() { - var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result; - return result; - } - - /** - * Checks if `value` is a native function. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a native function, else `false`. - */ - function isNative(value) { - return typeof value == 'function' && reNative.test(value); - } - - /** - * Sets `this` binding data on a given function. - * - * @private - * @param {Function} func The function to set data on. - * @param {Array} value The data array to set. - */ - var setBindData = !defineProperty ? noop : function(func, value) { - descriptor.value = value; - defineProperty(func, '__bindData__', descriptor); - }; - - /** - * A fallback implementation of `isPlainObject` which checks if a given value - * is an object created by the `Object` constructor, assuming objects created - * by the `Object` constructor have no inherited enumerable properties and that - * there are no `Object.prototype` extensions. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - */ - function shimIsPlainObject(value) { - var ctor, - result; - - // avoid non Object objects, `arguments` objects, and DOM elements - if (!(value && toString.call(value) == objectClass) || - (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) { - return false; - } - // In most environments an object's own properties are iterated before - // its inherited properties. If the last iterated property is an object's - // own property then there are no inherited enumerable properties. - forIn(value, function(value, key) { - result = key; - }); - return typeof result == 'undefined' || hasOwnProperty.call(value, result); - } - - /** - * Used by `unescape` to convert HTML entities to characters. - * - * @private - * @param {string} match The matched character to unescape. - * @returns {string} Returns the unescaped character. - */ - function unescapeHtmlChar(match) { - return htmlUnescapes[match]; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Checks if `value` is an `arguments` object. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`. - * @example - * - * (function() { return _.isArguments(arguments); })(1, 2, 3); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - return value && typeof value == 'object' && typeof value.length == 'number' && - toString.call(value) == argsClass || false; - } - - /** - * Checks if `value` is an array. - * - * @static - * @memberOf _ - * @type Function - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is an array, else `false`. - * @example - * - * (function() { return _.isArray(arguments); })(); - * // => false - * - * _.isArray([1, 2, 3]); - * // => true - */ - var isArray = nativeIsArray || function(value) { - return value && typeof value == 'object' && typeof value.length == 'number' && - toString.call(value) == arrayClass || false; - }; - - /** - * A fallback implementation of `Object.keys` which produces an array of the - * given object's own enumerable property names. - * - * @private - * @type Function - * @param {Object} object The object to inspect. - * @returns {Array} Returns an array of property names. - */ - var shimKeys = function(object) { - var index, iterable = object, result = []; - if (!iterable) return result; - if (!(objectTypes[typeof object])) return result; - for (index in iterable) { - if (hasOwnProperty.call(iterable, index)) { - result.push(index); - } - } - return result - }; - - /** - * Creates an array composed of the own enumerable property names of an object. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns an array of property names. - * @example - * - * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); - * // => ['one', 'two', 'three'] (property order is not guaranteed across environments) - */ - var keys = !nativeKeys ? shimKeys : function(object) { - if (!isObject(object)) { - return []; - } - return nativeKeys(object); - }; - - /** - * Used to convert characters to HTML entities: - * - * Though the `>` character is escaped for symmetry, characters like `>` and `/` - * don't require escaping in HTML and have no special meaning unless they're part - * of a tag or an unquoted attribute value. - * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact") - */ - var htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - - /** Used to convert HTML entities to characters */ - var htmlUnescapes = invert(htmlEscapes); - - /** Used to match HTML entities and HTML characters */ - var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'), - reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g'); - - /*--------------------------------------------------------------------------*/ - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object. Subsequent sources will overwrite property assignments of previous - * sources. If a callback is provided it will be executed to produce the - * assigned values. The callback is bound to `thisArg` and invoked with two - * arguments; (objectValue, sourceValue). - * - * @static - * @memberOf _ - * @type Function - * @alias extend - * @category Objects - * @param {Object} object The destination object. - * @param {...Object} [source] The source objects. - * @param {Function} [callback] The function to customize assigning values. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the destination object. - * @example - * - * _.assign({ 'name': 'fred' }, { 'employer': 'slate' }); - * // => { 'name': 'fred', 'employer': 'slate' } - * - * var defaults = _.partialRight(_.assign, function(a, b) { - * return typeof a == 'undefined' ? b : a; - * }); - * - * var object = { 'name': 'barney' }; - * defaults(object, { 'name': 'fred', 'employer': 'slate' }); - * // => { 'name': 'barney', 'employer': 'slate' } - */ - var assign = function(object, source, guard) { - var index, iterable = object, result = iterable; - if (!iterable) return result; - var args = arguments, - argsIndex = 0, - argsLength = typeof guard == 'number' ? 2 : args.length; - if (argsLength > 3 && typeof args[argsLength - 2] == 'function') { - var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2); - } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') { - callback = args[--argsLength]; - } - while (++argsIndex < argsLength) { - iterable = args[argsIndex]; - if (iterable && objectTypes[typeof iterable]) { - var ownIndex = -1, - ownProps = objectTypes[typeof iterable] && keys(iterable), - length = ownProps ? ownProps.length : 0; - - while (++ownIndex < length) { - index = ownProps[ownIndex]; - result[index] = callback ? callback(result[index], iterable[index]) : iterable[index]; - } - } - } - return result - }; - - /** - * Creates a clone of `value`. If `isDeep` is `true` nested objects will also - * be cloned, otherwise they will be assigned by reference. If a callback - * is provided it will be executed to produce the cloned values. If the - * callback returns `undefined` cloning will be handled by the method instead. - * The callback is bound to `thisArg` and invoked with one argument; (value). - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to clone. - * @param {boolean} [isDeep=false] Specify a deep clone. - * @param {Function} [callback] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the cloned value. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * var shallow = _.clone(characters); - * shallow[0] === characters[0]; - * // => true - * - * var deep = _.clone(characters, true); - * deep[0] === characters[0]; - * // => false - * - * _.mixin({ - * 'clone': _.partialRight(_.clone, function(value) { - * return _.isElement(value) ? value.cloneNode(false) : undefined; - * }) - * }); - * - * var clone = _.clone(document.body); - * clone.childNodes.length; - * // => 0 - */ - function clone(value, isDeep, callback, thisArg) { - // allows working with "Collections" methods without using their `index` - // and `collection` arguments for `isDeep` and `callback` - if (typeof isDeep != 'boolean' && isDeep != null) { - thisArg = callback; - callback = isDeep; - isDeep = false; - } - return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); - } - - /** - * Creates a deep clone of `value`. If a callback is provided it will be - * executed to produce the cloned values. If the callback returns `undefined` - * cloning will be handled by the method instead. The callback is bound to - * `thisArg` and invoked with one argument; (value). - * - * Note: This method is loosely based on the structured clone algorithm. Functions - * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and - * objects created by constructors other than `Object` are cloned to plain `Object` objects. - * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to deep clone. - * @param {Function} [callback] The function to customize cloning values. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the deep cloned value. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * var deep = _.cloneDeep(characters); - * deep[0] === characters[0]; - * // => false - * - * var view = { - * 'label': 'docs', - * 'node': element - * }; - * - * var clone = _.cloneDeep(view, function(value) { - * return _.isElement(value) ? value.cloneNode(true) : undefined; - * }); - * - * clone.node == view.node; - * // => false - */ - function cloneDeep(value, callback, thisArg) { - return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1)); - } - - /** - * Creates an object that inherits from the given `prototype` object. If a - * `properties` object is provided its own enumerable properties are assigned - * to the created object. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * function Circle() { - * Shape.call(this); - * } - * - * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle }); - * - * var circle = new Circle; - * circle instanceof Circle; - * // => true - * - * circle instanceof Shape; - * // => true - */ - function create(prototype, properties) { - var result = baseCreate(prototype); - return properties ? assign(result, properties) : result; - } - - /** - * Assigns own enumerable properties of source object(s) to the destination - * object for all destination properties that resolve to `undefined`. Once a - * property is set, additional defaults of the same property will be ignored. - * - * @static - * @memberOf _ - * @type Function - * @category Objects - * @param {Object} object The destination object. - * @param {...Object} [source] The source objects. - * @param- {Object} [guard] Allows working with `_.reduce` without using its - * `key` and `object` arguments as sources. - * @returns {Object} Returns the destination object. - * @example - * - * var object = { 'name': 'barney' }; - * _.defaults(object, { 'name': 'fred', 'employer': 'slate' }); - * // => { 'name': 'barney', 'employer': 'slate' } - */ - var defaults = function(object, source, guard) { - var index, iterable = object, result = iterable; - if (!iterable) return result; - var args = arguments, - argsIndex = 0, - argsLength = typeof guard == 'number' ? 2 : args.length; - while (++argsIndex < argsLength) { - iterable = args[argsIndex]; - if (iterable && objectTypes[typeof iterable]) { - var ownIndex = -1, - ownProps = objectTypes[typeof iterable] && keys(iterable), - length = ownProps ? ownProps.length : 0; - - while (++ownIndex < length) { - index = ownProps[ownIndex]; - if (typeof result[index] == 'undefined') result[index] = iterable[index]; - } - } - } - return result - }; - - /** - * This method is like `_.findIndex` except that it returns the key of the - * first element that passes the callback check, instead of the element itself. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to search. - * @param {Function|Object|string} [callback=identity] The function called per - * iteration. If a property name or object is provided it will be used to - * create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {string|undefined} Returns the key of the found element, else `undefined`. - * @example - * - * var characters = { - * 'barney': { 'age': 36, 'blocked': false }, - * 'fred': { 'age': 40, 'blocked': true }, - * 'pebbles': { 'age': 1, 'blocked': false } - * }; - * - * _.findKey(characters, function(chr) { - * return chr.age < 40; - * }); - * // => 'barney' (property order is not guaranteed across environments) - * - * // using "_.where" callback shorthand - * _.findKey(characters, { 'age': 1 }); - * // => 'pebbles' - * - * // using "_.pluck" callback shorthand - * _.findKey(characters, 'blocked'); - * // => 'fred' - */ - function findKey(object, callback, thisArg) { - var result; - callback = lodash.createCallback(callback, thisArg, 3); - forOwn(object, function(value, key, object) { - if (callback(value, key, object)) { - result = key; - return false; - } - }); - return result; - } - - /** - * This method is like `_.findKey` except that it iterates over elements - * of a `collection` in the opposite order. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to search. - * @param {Function|Object|string} [callback=identity] The function called per - * iteration. If a property name or object is provided it will be used to - * create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {string|undefined} Returns the key of the found element, else `undefined`. - * @example - * - * var characters = { - * 'barney': { 'age': 36, 'blocked': true }, - * 'fred': { 'age': 40, 'blocked': false }, - * 'pebbles': { 'age': 1, 'blocked': true } - * }; - * - * _.findLastKey(characters, function(chr) { - * return chr.age < 40; - * }); - * // => returns `pebbles`, assuming `_.findKey` returns `barney` - * - * // using "_.where" callback shorthand - * _.findLastKey(characters, { 'age': 40 }); - * // => 'fred' - * - * // using "_.pluck" callback shorthand - * _.findLastKey(characters, 'blocked'); - * // => 'pebbles' - */ - function findLastKey(object, callback, thisArg) { - var result; - callback = lodash.createCallback(callback, thisArg, 3); - forOwnRight(object, function(value, key, object) { - if (callback(value, key, object)) { - result = key; - return false; - } - }); - return result; - } - - /** - * Iterates over own and inherited enumerable properties of an object, - * executing the callback for each property. The callback is bound to `thisArg` - * and invoked with three arguments; (value, key, object). Callbacks may exit - * iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * Shape.prototype.move = function(x, y) { - * this.x += x; - * this.y += y; - * }; - * - * _.forIn(new Shape, function(value, key) { - * console.log(key); - * }); - * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments) - */ - var forIn = function(collection, callback, thisArg) { - var index, iterable = collection, result = iterable; - if (!iterable) return result; - if (!objectTypes[typeof iterable]) return result; - callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); - for (index in iterable) { - if (callback(iterable[index], index, collection) === false) return result; - } - return result - }; - - /** - * This method is like `_.forIn` except that it iterates over elements - * of a `collection` in the opposite order. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * Shape.prototype.move = function(x, y) { - * this.x += x; - * this.y += y; - * }; - * - * _.forInRight(new Shape, function(value, key) { - * console.log(key); - * }); - * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move' - */ - function forInRight(object, callback, thisArg) { - var pairs = []; - - forIn(object, function(value, key) { - pairs.push(key, value); - }); - - var length = pairs.length; - callback = baseCreateCallback(callback, thisArg, 3); - while (length--) { - if (callback(pairs[length--], pairs[length], object) === false) { - break; - } - } - return object; - } - - /** - * Iterates over own enumerable properties of an object, executing the callback - * for each property. The callback is bound to `thisArg` and invoked with three - * arguments; (value, key, object). Callbacks may exit iteration early by - * explicitly returning `false`. - * - * @static - * @memberOf _ - * @type Function - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. - * @example - * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * console.log(key); - * }); - * // => logs '0', '1', and 'length' (property order is not guaranteed across environments) - */ - var forOwn = function(collection, callback, thisArg) { - var index, iterable = collection, result = iterable; - if (!iterable) return result; - if (!objectTypes[typeof iterable]) return result; - callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); - var ownIndex = -1, - ownProps = objectTypes[typeof iterable] && keys(iterable), - length = ownProps ? ownProps.length : 0; - - while (++ownIndex < length) { - index = ownProps[ownIndex]; - if (callback(iterable[index], index, collection) === false) return result; - } - return result - }; - - /** - * This method is like `_.forOwn` except that it iterates over elements - * of a `collection` in the opposite order. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns `object`. - * @example - * - * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * console.log(key); - * }); - * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' - */ - function forOwnRight(object, callback, thisArg) { - var props = keys(object), - length = props.length; - - callback = baseCreateCallback(callback, thisArg, 3); - while (length--) { - var key = props[length]; - if (callback(object[key], key, object) === false) { - break; - } - } - return object; - } - - /** - * Creates a sorted array of property names of all enumerable properties, - * own and inherited, of `object` that have function values. - * - * @static - * @memberOf _ - * @alias methods - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns an array of property names that have function values. - * @example - * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] - */ - function functions(object) { - var result = []; - forIn(object, function(value, key) { - if (isFunction(value)) { - result.push(key); - } - }); - return result.sort(); - } - - /** - * Checks if the specified property name exists as a direct property of `object`, - * instead of an inherited property. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @param {string} key The name of the property to check. - * @returns {boolean} Returns `true` if key is a direct property, else `false`. - * @example - * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); - * // => true - */ - function has(object, key) { - return object ? hasOwnProperty.call(object, key) : false; - } - - /** - * Creates an object composed of the inverted keys and values of the given object. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to invert. - * @returns {Object} Returns the created inverted object. - * @example - * - * _.invert({ 'first': 'fred', 'second': 'barney' }); - * // => { 'fred': 'first', 'barney': 'second' } - */ - function invert(object) { - var index = -1, - props = keys(object), - length = props.length, - result = {}; - - while (++index < length) { - var key = props[index]; - result[object[key]] = key; - } - return result; - } - - /** - * Checks if `value` is a boolean value. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`. - * @example - * - * _.isBoolean(null); - * // => false - */ - function isBoolean(value) { - return value === true || value === false || - value && typeof value == 'object' && toString.call(value) == boolClass || false; - } - - /** - * Checks if `value` is a date. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a date, else `false`. - * @example - * - * _.isDate(new Date); - * // => true - */ - function isDate(value) { - return value && typeof value == 'object' && toString.call(value) == dateClass || false; - } - - /** - * Checks if `value` is a DOM element. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`. - * @example - * - * _.isElement(document.body); - * // => true - */ - function isElement(value) { - return value && value.nodeType === 1 || false; - } - - /** - * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a - * length of `0` and objects with no own enumerable properties are considered - * "empty". - * - * @static - * @memberOf _ - * @category Objects - * @param {Array|Object|string} value The value to inspect. - * @returns {boolean} Returns `true` if the `value` is empty, else `false`. - * @example - * - * _.isEmpty([1, 2, 3]); - * // => false - * - * _.isEmpty({}); - * // => true - * - * _.isEmpty(''); - * // => true - */ - function isEmpty(value) { - var result = true; - if (!value) { - return result; - } - var className = toString.call(value), - length = value.length; - - if ((className == arrayClass || className == stringClass || className == argsClass ) || - (className == objectClass && typeof length == 'number' && isFunction(value.splice))) { - return !length; - } - forOwn(value, function() { - return (result = false); - }); - return result; - } - - /** - * Performs a deep comparison between two values to determine if they are - * equivalent to each other. If a callback is provided it will be executed - * to compare values. If the callback returns `undefined` comparisons will - * be handled by the method instead. The callback is bound to `thisArg` and - * invoked with two arguments; (a, b). - * - * @static - * @memberOf _ - * @category Objects - * @param {*} a The value to compare. - * @param {*} b The other value to compare. - * @param {Function} [callback] The function to customize comparing values. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'name': 'fred' }; - * var copy = { 'name': 'fred' }; - * - * object == copy; - * // => false - * - * _.isEqual(object, copy); - * // => true - * - * var words = ['hello', 'goodbye']; - * var otherWords = ['hi', 'goodbye']; - * - * _.isEqual(words, otherWords, function(a, b) { - * var reGreet = /^(?:hello|hi)$/i, - * aGreet = _.isString(a) && reGreet.test(a), - * bGreet = _.isString(b) && reGreet.test(b); - * - * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined; - * }); - * // => true - */ - function isEqual(a, b, callback, thisArg) { - return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2)); - } - - /** - * Checks if `value` is, or can be coerced to, a finite number. - * - * Note: This is not the same as native `isFinite` which will return true for - * booleans and empty strings. See http://es5.github.io/#x15.1.2.5. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is finite, else `false`. - * @example - * - * _.isFinite(-101); - * // => true - * - * _.isFinite('10'); - * // => true - * - * _.isFinite(true); - * // => false - * - * _.isFinite(''); - * // => false - * - * _.isFinite(Infinity); - * // => false - */ - function isFinite(value) { - return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value)); - } - - /** - * Checks if `value` is a function. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - */ - function isFunction(value) { - return typeof value == 'function'; - } - - /** - * Checks if `value` is the language type of Object. - * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(1); - * // => false - */ - function isObject(value) { - // check if the value is the ECMAScript language type of Object - // http://es5.github.io/#x8 - // and avoid a V8 bug - // http://code.google.com/p/v8/issues/detail?id=2291 - return !!(value && objectTypes[typeof value]); - } - - /** - * Checks if `value` is `NaN`. - * - * Note: This is not the same as native `isNaN` which will return `true` for - * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - function isNaN(value) { - // `NaN` as a primitive is the only value that is not equal to itself - // (perform the [[Class]] check first to avoid errors with some host objects in IE) - return isNumber(value) && value != +value; - } - - /** - * Checks if `value` is `null`. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is `null`, else `false`. - * @example - * - * _.isNull(null); - * // => true - * - * _.isNull(undefined); - * // => false - */ - function isNull(value) { - return value === null; - } - - /** - * Checks if `value` is a number. - * - * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a number, else `false`. - * @example - * - * _.isNumber(8.4 * 5); - * // => true - */ - function isNumber(value) { - return typeof value == 'number' || - value && typeof value == 'object' && toString.call(value) == numberClass || false; - } - - /** - * Checks if `value` is an object created by the `Object` constructor. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Shape() { - * this.x = 0; - * this.y = 0; - * } - * - * _.isPlainObject(new Shape); - * // => false - * - * _.isPlainObject([1, 2, 3]); - * // => false - * - * _.isPlainObject({ 'x': 0, 'y': 0 }); - * // => true - */ - var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { - if (!(value && toString.call(value) == objectClass)) { - return false; - } - var valueOf = value.valueOf, - objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); - - return objProto - ? (value == objProto || getPrototypeOf(value) == objProto) - : shimIsPlainObject(value); - }; - - /** - * Checks if `value` is a regular expression. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`. - * @example - * - * _.isRegExp(/fred/); - * // => true - */ - function isRegExp(value) { - return value && typeof value == 'object' && toString.call(value) == regexpClass || false; - } - - /** - * Checks if `value` is a string. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is a string, else `false`. - * @example - * - * _.isString('fred'); - * // => true - */ - function isString(value) { - return typeof value == 'string' || - value && typeof value == 'object' && toString.call(value) == stringClass || false; - } - - /** - * Checks if `value` is `undefined`. - * - * @static - * @memberOf _ - * @category Objects - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`. - * @example - * - * _.isUndefined(void 0); - * // => true - */ - function isUndefined(value) { - return typeof value == 'undefined'; - } - - /** - * Creates an object with the same keys as `object` and values generated by - * running each own enumerable property of `object` through the callback. - * The callback is bound to `thisArg` and invoked with three arguments; - * (value, key, object). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new object with values of the results of each `callback` execution. - * @example - * - * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; }); - * // => { 'a': 3, 'b': 6, 'c': 9 } - * - * var characters = { - * 'fred': { 'name': 'fred', 'age': 40 }, - * 'pebbles': { 'name': 'pebbles', 'age': 1 } - * }; - * - * // using "_.pluck" callback shorthand - * _.mapValues(characters, 'age'); - * // => { 'fred': 40, 'pebbles': 1 } - */ - function mapValues(object, callback, thisArg) { - var result = {}; - callback = lodash.createCallback(callback, thisArg, 3); - - forOwn(object, function(value, key, object) { - result[key] = callback(value, key, object); - }); - return result; - } - - /** - * Recursively merges own enumerable properties of the source object(s), that - * don't resolve to `undefined` into the destination object. Subsequent sources - * will overwrite property assignments of previous sources. If a callback is - * provided it will be executed to produce the merged values of the destination - * and source properties. If the callback returns `undefined` merging will - * be handled by the method instead. The callback is bound to `thisArg` and - * invoked with two arguments; (objectValue, sourceValue). - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The destination object. - * @param {...Object} [source] The source objects. - * @param {Function} [callback] The function to customize merging properties. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the destination object. - * @example - * - * var names = { - * 'characters': [ - * { 'name': 'barney' }, - * { 'name': 'fred' } - * ] - * }; - * - * var ages = { - * 'characters': [ - * { 'age': 36 }, - * { 'age': 40 } - * ] - * }; - * - * _.merge(names, ages); - * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } - * - * var food = { - * 'fruits': ['apple'], - * 'vegetables': ['beet'] - * }; - * - * var otherFood = { - * 'fruits': ['banana'], - * 'vegetables': ['carrot'] - * }; - * - * _.merge(food, otherFood, function(a, b) { - * return _.isArray(a) ? a.concat(b) : undefined; - * }); - * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] } - */ - function merge(object) { - var args = arguments, - length = 2; - - if (!isObject(object)) { - return object; - } - // allows working with `_.reduce` and `_.reduceRight` without using - // their `index` and `collection` arguments - if (typeof args[2] != 'number') { - length = args.length; - } - if (length > 3 && typeof args[length - 2] == 'function') { - var callback = baseCreateCallback(args[--length - 1], args[length--], 2); - } else if (length > 2 && typeof args[length - 1] == 'function') { - callback = args[--length]; - } - var sources = slice(arguments, 1, length), - index = -1, - stackA = getArray(), - stackB = getArray(); - - while (++index < length) { - baseMerge(object, sources[index], callback, stackA, stackB); - } - releaseArray(stackA); - releaseArray(stackB); - return object; - } - - /** - * Creates a shallow clone of `object` excluding the specified properties. - * Property names may be specified as individual arguments or as arrays of - * property names. If a callback is provided it will be executed for each - * property of `object` omitting the properties the callback returns truey - * for. The callback is bound to `thisArg` and invoked with three arguments; - * (value, key, object). - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The source object. - * @param {Function|...string|string[]} [callback] The properties to omit or the - * function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns an object without the omitted properties. - * @example - * - * _.omit({ 'name': 'fred', 'age': 40 }, 'age'); - * // => { 'name': 'fred' } - * - * _.omit({ 'name': 'fred', 'age': 40 }, function(value) { - * return typeof value == 'number'; - * }); - * // => { 'name': 'fred' } - */ - function omit(object, callback, thisArg) { - var result = {}; - if (typeof callback != 'function') { - var props = []; - forIn(object, function(value, key) { - props.push(key); - }); - props = baseDifference(props, baseFlatten(arguments, true, false, 1)); - - var index = -1, - length = props.length; - - while (++index < length) { - var key = props[index]; - result[key] = object[key]; - } - } else { - callback = lodash.createCallback(callback, thisArg, 3); - forIn(object, function(value, key, object) { - if (!callback(value, key, object)) { - result[key] = value; - } - }); - } - return result; - } - - /** - * Creates a two dimensional array of an object's key-value pairs, - * i.e. `[[key1, value1], [key2, value2]]`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns new array of key-value pairs. - * @example - * - * _.pairs({ 'barney': 36, 'fred': 40 }); - * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments) - */ - function pairs(object) { - var index = -1, - props = keys(object), - length = props.length, - result = Array(length); - - while (++index < length) { - var key = props[index]; - result[index] = [key, object[key]]; - } - return result; - } - - /** - * Creates a shallow clone of `object` composed of the specified properties. - * Property names may be specified as individual arguments or as arrays of - * property names. If a callback is provided it will be executed for each - * property of `object` picking the properties the callback returns truey - * for. The callback is bound to `thisArg` and invoked with three arguments; - * (value, key, object). - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The source object. - * @param {Function|...string|string[]} [callback] The function called per - * iteration or property names to pick, specified as individual property - * names or arrays of property names. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns an object composed of the picked properties. - * @example - * - * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name'); - * // => { 'name': 'fred' } - * - * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) { - * return key.charAt(0) != '_'; - * }); - * // => { 'name': 'fred' } - */ - function pick(object, callback, thisArg) { - var result = {}; - if (typeof callback != 'function') { - var index = -1, - props = baseFlatten(arguments, true, false, 1), - length = isObject(object) ? props.length : 0; - - while (++index < length) { - var key = props[index]; - if (key in object) { - result[key] = object[key]; - } - } - } else { - callback = lodash.createCallback(callback, thisArg, 3); - forIn(object, function(value, key, object) { - if (callback(value, key, object)) { - result[key] = value; - } - }); - } - return result; - } - - /** - * An alternative to `_.reduce` this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own - * enumerable properties through a callback, with each callback execution - * potentially mutating the `accumulator` object. The callback is bound to - * `thisArg` and invoked with four arguments; (accumulator, value, key, object). - * Callbacks may exit iteration early by explicitly returning `false`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Array|Object} object The object to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the accumulated value. - * @example - * - * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) { - * num *= num; - * if (num % 2) { - * return result.push(num) < 3; - * } - * }); - * // => [1, 9, 25] - * - * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { - * result[key] = num * 3; - * }); - * // => { 'a': 3, 'b': 6, 'c': 9 } - */ - function transform(object, callback, accumulator, thisArg) { - var isArr = isArray(object); - if (accumulator == null) { - if (isArr) { - accumulator = []; - } else { - var ctor = object && object.constructor, - proto = ctor && ctor.prototype; - - accumulator = baseCreate(proto); - } - } - if (callback) { - callback = lodash.createCallback(callback, thisArg, 4); - (isArr ? forEach : forOwn)(object, function(value, index, object) { - return callback(accumulator, value, index, object); - }); - } - return accumulator; - } - - /** - * Creates an array composed of the own enumerable property values of `object`. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns an array of property values. - * @example - * - * _.values({ 'one': 1, 'two': 2, 'three': 3 }); - * // => [1, 2, 3] (property order is not guaranteed across environments) - */ - function values(object) { - var index = -1, - props = keys(object), - length = props.length, - result = Array(length); - - while (++index < length) { - result[index] = object[props[index]]; - } - return result; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Creates an array of elements from the specified indexes, or keys, of the - * `collection`. Indexes may be specified as individual arguments or as arrays - * of indexes. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {...(number|number[]|string|string[])} [index] The indexes of `collection` - * to retrieve, specified as individual indexes or arrays of indexes. - * @returns {Array} Returns a new array of elements corresponding to the - * provided indexes. - * @example - * - * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]); - * // => ['a', 'c', 'e'] - * - * _.at(['fred', 'barney', 'pebbles'], 0, 2); - * // => ['fred', 'pebbles'] - */ - function at(collection) { - var args = arguments, - index = -1, - props = baseFlatten(args, true, false, 1), - length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length, - result = Array(length); - - while(++index < length) { - result[index] = collection[props[index]]; - } - return result; - } - - /** - * Checks if a given value is present in a collection using strict equality - * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the - * offset from the end of the collection. - * - * @static - * @memberOf _ - * @alias include - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {*} target The value to check for. - * @param {number} [fromIndex=0] The index to search from. - * @returns {boolean} Returns `true` if the `target` element is found, else `false`. - * @example - * - * _.contains([1, 2, 3], 1); - * // => true - * - * _.contains([1, 2, 3], 1, 2); - * // => false - * - * _.contains({ 'name': 'fred', 'age': 40 }, 'fred'); - * // => true - * - * _.contains('pebbles', 'eb'); - * // => true - */ - function contains(collection, target, fromIndex) { - var index = -1, - indexOf = getIndexOf(), - length = collection ? collection.length : 0, - result = false; - - fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0; - if (isArray(collection)) { - result = indexOf(collection, target, fromIndex) > -1; - } else if (typeof length == 'number') { - result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1; - } else { - forOwn(collection, function(value) { - if (++index >= fromIndex) { - return !(result = value === target); - } - }); - } - return result; - } - - /** - * Creates an object composed of keys generated from the results of running - * each element of `collection` through the callback. The corresponding value - * of each key is the number of times the key was returned by the callback. - * The callback is bound to `thisArg` and invoked with three arguments; - * (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); - * // => { '4': 1, '6': 2 } - * - * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); - * // => { '4': 1, '6': 2 } - * - * _.countBy(['one', 'two', 'three'], 'length'); - * // => { '3': 2, '5': 1 } - */ - var countBy = createAggregator(function(result, value, key) { - (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1); - }); - - /** - * Checks if the given callback returns truey value for **all** elements of - * a collection. The callback is bound to `thisArg` and invoked with three - * arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias all - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {boolean} Returns `true` if all elements passed the callback check, - * else `false`. - * @example - * - * _.every([true, 1, null, 'yes']); - * // => false - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * // using "_.pluck" callback shorthand - * _.every(characters, 'age'); - * // => true - * - * // using "_.where" callback shorthand - * _.every(characters, { 'age': 36 }); - * // => false - */ - function every(collection, callback, thisArg) { - var result = true; - callback = lodash.createCallback(callback, thisArg, 3); - - var index = -1, - length = collection ? collection.length : 0; - - if (typeof length == 'number') { - while (++index < length) { - if (!(result = !!callback(collection[index], index, collection))) { - break; - } - } - } else { - forOwn(collection, function(value, index, collection) { - return (result = !!callback(value, index, collection)); - }); - } - return result; - } - - /** - * Iterates over elements of a collection, returning an array of all elements - * the callback returns truey for. The callback is bound to `thisArg` and - * invoked with three arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias select - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of elements that passed the callback check. - * @example - * - * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [2, 4, 6] - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': false }, - * { 'name': 'fred', 'age': 40, 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.filter(characters, 'blocked'); - * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] - * - * // using "_.where" callback shorthand - * _.filter(characters, { 'age': 36 }); - * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] - */ - function filter(collection, callback, thisArg) { - var result = []; - callback = lodash.createCallback(callback, thisArg, 3); - - var index = -1, - length = collection ? collection.length : 0; - - if (typeof length == 'number') { - while (++index < length) { - var value = collection[index]; - if (callback(value, index, collection)) { - result.push(value); - } - } - } else { - forOwn(collection, function(value, index, collection) { - if (callback(value, index, collection)) { - result.push(value); - } - }); - } - return result; - } - - /** - * Iterates over elements of a collection, returning the first element that - * the callback returns truey for. The callback is bound to `thisArg` and - * invoked with three arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias detect, findWhere - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the found element, else `undefined`. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': false }, - * { 'name': 'fred', 'age': 40, 'blocked': true }, - * { 'name': 'pebbles', 'age': 1, 'blocked': false } - * ]; - * - * _.find(characters, function(chr) { - * return chr.age < 40; - * }); - * // => { 'name': 'barney', 'age': 36, 'blocked': false } - * - * // using "_.where" callback shorthand - * _.find(characters, { 'age': 1 }); - * // => { 'name': 'pebbles', 'age': 1, 'blocked': false } - * - * // using "_.pluck" callback shorthand - * _.find(characters, 'blocked'); - * // => { 'name': 'fred', 'age': 40, 'blocked': true } - */ - function find(collection, callback, thisArg) { - callback = lodash.createCallback(callback, thisArg, 3); - - var index = -1, - length = collection ? collection.length : 0; - - if (typeof length == 'number') { - while (++index < length) { - var value = collection[index]; - if (callback(value, index, collection)) { - return value; - } - } - } else { - var result; - forOwn(collection, function(value, index, collection) { - if (callback(value, index, collection)) { - result = value; - return false; - } - }); - return result; - } - } - - /** - * This method is like `_.find` except that it iterates over elements - * of a `collection` from right to left. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the found element, else `undefined`. - * @example - * - * _.findLast([1, 2, 3, 4], function(num) { - * return num % 2 == 1; - * }); - * // => 3 - */ - function findLast(collection, callback, thisArg) { - var result; - callback = lodash.createCallback(callback, thisArg, 3); - forEachRight(collection, function(value, index, collection) { - if (callback(value, index, collection)) { - result = value; - return false; - } - }); - return result; - } - - /** - * Iterates over elements of a collection, executing the callback for each - * element. The callback is bound to `thisArg` and invoked with three arguments; - * (value, index|key, collection). Callbacks may exit iteration early by - * explicitly returning `false`. - * - * Note: As with other "Collections" methods, objects with a `length` property - * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` - * may be used for object iteration. - * - * @static - * @memberOf _ - * @alias each - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); - * // => logs each number and returns '1,2,3' - * - * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); - * // => logs each number and returns the object (property order is not guaranteed across environments) - */ - function forEach(collection, callback, thisArg) { - var index = -1, - length = collection ? collection.length : 0; - - callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); - if (typeof length == 'number') { - while (++index < length) { - if (callback(collection[index], index, collection) === false) { - break; - } - } - } else { - forOwn(collection, callback); - } - return collection; - } - - /** - * This method is like `_.forEach` except that it iterates over elements - * of a `collection` from right to left. - * - * @static - * @memberOf _ - * @alias eachRight - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array|Object|string} Returns `collection`. - * @example - * - * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(','); - * // => logs each number from right to left and returns '3,2,1' - */ - function forEachRight(collection, callback, thisArg) { - var length = collection ? collection.length : 0; - callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3); - if (typeof length == 'number') { - while (length--) { - if (callback(collection[length], length, collection) === false) { - break; - } - } - } else { - var props = keys(collection); - length = props.length; - forOwn(collection, function(value, key, collection) { - key = props ? props[--length] : --length; - return callback(collection[key], key, collection); - }); - } - return collection; - } - - /** - * Creates an object composed of keys generated from the results of running - * each element of a collection through the callback. The corresponding value - * of each key is an array of the elements responsible for generating the key. - * The callback is bound to `thisArg` and invoked with three arguments; - * (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false` - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); - * // => { '4': [4.2], '6': [6.1, 6.4] } - * - * // using "_.pluck" callback shorthand - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - var groupBy = createAggregator(function(result, value, key) { - (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value); - }); - - /** - * Creates an object composed of keys generated from the results of running - * each element of the collection through the given callback. The corresponding - * value of each key is the last element responsible for generating the key. - * The callback is bound to `thisArg` and invoked with three arguments; - * (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * var keys = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ]; - * - * _.indexBy(keys, 'dir'); - * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); }); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - * - * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String); - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - */ - var indexBy = createAggregator(function(result, value, key) { - result[key] = value; - }); - - /** - * Invokes the method named by `methodName` on each element in the `collection` - * returning an array of the results of each invoked method. Additional arguments - * will be provided to each invoked method. If `methodName` is a function it - * will be invoked for, and `this` bound to, each element in the `collection`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|string} methodName The name of the method to invoke or - * the function invoked per iteration. - * @param {...*} [arg] Arguments to invoke the method with. - * @returns {Array} Returns a new array of the results of each invoked method. - * @example - * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - function invoke(collection, methodName) { - var args = slice(arguments, 2), - index = -1, - isFunc = typeof methodName == 'function', - length = collection ? collection.length : 0, - result = Array(typeof length == 'number' ? length : 0); - - forEach(collection, function(value) { - result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args); - }); - return result; - } - - /** - * Creates an array of values by running each element in the collection - * through the callback. The callback is bound to `thisArg` and invoked with - * three arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias collect - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of the results of each `callback` execution. - * @example - * - * _.map([1, 2, 3], function(num) { return num * 3; }); - * // => [3, 6, 9] - * - * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); - * // => [3, 6, 9] (property order is not guaranteed across environments) - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * // using "_.pluck" callback shorthand - * _.map(characters, 'name'); - * // => ['barney', 'fred'] - */ - function map(collection, callback, thisArg) { - var index = -1, - length = collection ? collection.length : 0; - - callback = lodash.createCallback(callback, thisArg, 3); - if (typeof length == 'number') { - var result = Array(length); - while (++index < length) { - result[index] = callback(collection[index], index, collection); - } - } else { - result = []; - forOwn(collection, function(value, key, collection) { - result[++index] = callback(value, key, collection); - }); - } - return result; - } - - /** - * Retrieves the maximum value of a collection. If the collection is empty or - * falsey `-Infinity` is returned. If a callback is provided it will be executed - * for each value in the collection to generate the criterion by which the value - * is ranked. The callback is bound to `thisArg` and invoked with three - * arguments; (value, index, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the maximum value. - * @example - * - * _.max([4, 2, 8, 6]); - * // => 8 - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * _.max(characters, function(chr) { return chr.age; }); - * // => { 'name': 'fred', 'age': 40 }; - * - * // using "_.pluck" callback shorthand - * _.max(characters, 'age'); - * // => { 'name': 'fred', 'age': 40 }; - */ - function max(collection, callback, thisArg) { - var computed = -Infinity, - result = computed; - - // allows working with functions like `_.map` without using - // their `index` argument as a callback - if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) { - callback = null; - } - if (callback == null && isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - var value = collection[index]; - if (value > result) { - result = value; - } - } - } else { - callback = (callback == null && isString(collection)) - ? charAtCallback - : lodash.createCallback(callback, thisArg, 3); - - forEach(collection, function(value, index, collection) { - var current = callback(value, index, collection); - if (current > computed) { - computed = current; - result = value; - } - }); - } - return result; - } - - /** - * Retrieves the minimum value of a collection. If the collection is empty or - * falsey `Infinity` is returned. If a callback is provided it will be executed - * for each value in the collection to generate the criterion by which the value - * is ranked. The callback is bound to `thisArg` and invoked with three - * arguments; (value, index, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the minimum value. - * @example - * - * _.min([4, 2, 8, 6]); - * // => 2 - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * _.min(characters, function(chr) { return chr.age; }); - * // => { 'name': 'barney', 'age': 36 }; - * - * // using "_.pluck" callback shorthand - * _.min(characters, 'age'); - * // => { 'name': 'barney', 'age': 36 }; - */ - function min(collection, callback, thisArg) { - var computed = Infinity, - result = computed; - - // allows working with functions like `_.map` without using - // their `index` argument as a callback - if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) { - callback = null; - } - if (callback == null && isArray(collection)) { - var index = -1, - length = collection.length; - - while (++index < length) { - var value = collection[index]; - if (value < result) { - result = value; - } - } - } else { - callback = (callback == null && isString(collection)) - ? charAtCallback - : lodash.createCallback(callback, thisArg, 3); - - forEach(collection, function(value, index, collection) { - var current = callback(value, index, collection); - if (current < computed) { - computed = current; - result = value; - } - }); - } - return result; - } - - /** - * Retrieves the value of a specified property from all elements in the collection. - * - * @static - * @memberOf _ - * @type Function - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {string} property The name of the property to pluck. - * @returns {Array} Returns a new array of property values. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * _.pluck(characters, 'name'); - * // => ['barney', 'fred'] - */ - var pluck = map; - - /** - * Reduces a collection to a value which is the accumulated result of running - * each element in the collection through the callback, where each successive - * callback execution consumes the return value of the previous execution. If - * `accumulator` is not provided the first element of the collection will be - * used as the initial `accumulator` value. The callback is bound to `thisArg` - * and invoked with four arguments; (accumulator, value, index|key, collection). - * - * @static - * @memberOf _ - * @alias foldl, inject - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [accumulator] Initial value of the accumulator. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the accumulated value. - * @example - * - * var sum = _.reduce([1, 2, 3], function(sum, num) { - * return sum + num; - * }); - * // => 6 - * - * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) { - * result[key] = num * 3; - * return result; - * }, {}); - * // => { 'a': 3, 'b': 6, 'c': 9 } - */ - function reduce(collection, callback, accumulator, thisArg) { - if (!collection) return accumulator; - var noaccum = arguments.length < 3; - callback = lodash.createCallback(callback, thisArg, 4); - - var index = -1, - length = collection.length; - - if (typeof length == 'number') { - if (noaccum) { - accumulator = collection[++index]; - } - while (++index < length) { - accumulator = callback(accumulator, collection[index], index, collection); - } - } else { - forOwn(collection, function(value, index, collection) { - accumulator = noaccum - ? (noaccum = false, value) - : callback(accumulator, value, index, collection) - }); - } - return accumulator; - } - - /** - * This method is like `_.reduce` except that it iterates over elements - * of a `collection` from right to left. - * - * @static - * @memberOf _ - * @alias foldr - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {*} [accumulator] Initial value of the accumulator. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the accumulated value. - * @example - * - * var list = [[0, 1], [2, 3], [4, 5]]; - * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); - * // => [4, 5, 2, 3, 0, 1] - */ - function reduceRight(collection, callback, accumulator, thisArg) { - var noaccum = arguments.length < 3; - callback = lodash.createCallback(callback, thisArg, 4); - forEachRight(collection, function(value, index, collection) { - accumulator = noaccum - ? (noaccum = false, value) - : callback(accumulator, value, index, collection); - }); - return accumulator; - } - - /** - * The opposite of `_.filter` this method returns the elements of a - * collection that the callback does **not** return truey for. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of elements that failed the callback check. - * @example - * - * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [1, 3, 5] - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': false }, - * { 'name': 'fred', 'age': 40, 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.reject(characters, 'blocked'); - * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }] - * - * // using "_.where" callback shorthand - * _.reject(characters, { 'age': 36 }); - * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }] - */ - function reject(collection, callback, thisArg) { - callback = lodash.createCallback(callback, thisArg, 3); - return filter(collection, function(value, index, collection) { - return !callback(value, index, collection); - }); - } - - /** - * Retrieves a random element or `n` random elements from a collection. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to sample. - * @param {number} [n] The number of elements to sample. - * @param- {Object} [guard] Allows working with functions like `_.map` - * without using their `index` arguments as `n`. - * @returns {Array} Returns the random sample(s) of `collection`. - * @example - * - * _.sample([1, 2, 3, 4]); - * // => 2 - * - * _.sample([1, 2, 3, 4], 2); - * // => [3, 1] - */ - function sample(collection, n, guard) { - if (collection && typeof collection.length != 'number') { - collection = values(collection); - } - if (n == null || guard) { - return collection ? collection[baseRandom(0, collection.length - 1)] : undefined; - } - var result = shuffle(collection); - result.length = nativeMin(nativeMax(0, n), result.length); - return result; - } - - /** - * Creates an array of shuffled values, using a version of the Fisher-Yates - * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to shuffle. - * @returns {Array} Returns a new shuffled collection. - * @example - * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] - */ - function shuffle(collection) { - var index = -1, - length = collection ? collection.length : 0, - result = Array(typeof length == 'number' ? length : 0); - - forEach(collection, function(value) { - var rand = baseRandom(0, ++index); - result[index] = result[rand]; - result[rand] = value; - }); - return result; - } - - /** - * Gets the size of the `collection` by returning `collection.length` for arrays - * and array-like objects or the number of own enumerable properties for objects. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns `collection.length` or number of own enumerable properties. - * @example - * - * _.size([1, 2]); - * // => 2 - * - * _.size({ 'one': 1, 'two': 2, 'three': 3 }); - * // => 3 - * - * _.size('pebbles'); - * // => 7 - */ - function size(collection) { - var length = collection ? collection.length : 0; - return typeof length == 'number' ? length : keys(collection).length; - } - - /** - * Checks if the callback returns a truey value for **any** element of a - * collection. The function returns as soon as it finds a passing value and - * does not iterate over the entire collection. The callback is bound to - * `thisArg` and invoked with three arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias any - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {boolean} Returns `true` if any element passed the callback check, - * else `false`. - * @example - * - * _.some([null, 0, 'yes', false], Boolean); - * // => true - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': false }, - * { 'name': 'fred', 'age': 40, 'blocked': true } - * ]; - * - * // using "_.pluck" callback shorthand - * _.some(characters, 'blocked'); - * // => true - * - * // using "_.where" callback shorthand - * _.some(characters, { 'age': 1 }); - * // => false - */ - function some(collection, callback, thisArg) { - var result; - callback = lodash.createCallback(callback, thisArg, 3); - - var index = -1, - length = collection ? collection.length : 0; - - if (typeof length == 'number') { - while (++index < length) { - if ((result = callback(collection[index], index, collection))) { - break; - } - } - } else { - forOwn(collection, function(value, index, collection) { - return !(result = callback(value, index, collection)); - }); - } - return !!result; - } - - /** - * Creates an array of elements, sorted in ascending order by the results of - * running each element in a collection through the callback. This method - * performs a stable sort, that is, it will preserve the original sort order - * of equal elements. The callback is bound to `thisArg` and invoked with - * three arguments; (value, index|key, collection). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an array of property names is provided for `callback` the collection - * will be sorted by each property value. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Array|Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of sorted elements. - * @example - * - * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); - * // => [3, 1, 2] - * - * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); - * // => [3, 1, 2] - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 }, - * { 'name': 'barney', 'age': 26 }, - * { 'name': 'fred', 'age': 30 } - * ]; - * - * // using "_.pluck" callback shorthand - * _.map(_.sortBy(characters, 'age'), _.values); - * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]] - * - * // sorting by multiple properties - * _.map(_.sortBy(characters, ['name', 'age']), _.values); - * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]] - */ - function sortBy(collection, callback, thisArg) { - var index = -1, - isArr = isArray(callback), - length = collection ? collection.length : 0, - result = Array(typeof length == 'number' ? length : 0); - - if (!isArr) { - callback = lodash.createCallback(callback, thisArg, 3); - } - forEach(collection, function(value, key, collection) { - var object = result[++index] = getObject(); - if (isArr) { - object.criteria = map(callback, function(key) { return value[key]; }); - } else { - (object.criteria = getArray())[0] = callback(value, key, collection); - } - object.index = index; - object.value = value; - }); - - length = result.length; - result.sort(compareAscending); - while (length--) { - var object = result[length]; - result[length] = object.value; - if (!isArr) { - releaseArray(object.criteria); - } - releaseObject(object); - } - return result; - } - - /** - * Converts the `collection` to an array. - * - * @static - * @memberOf _ - * @category Collections - * @param {Array|Object|string} collection The collection to convert. - * @returns {Array} Returns the new converted array. - * @example - * - * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); - * // => [2, 3, 4] - */ - function toArray(collection) { - if (collection && typeof collection.length == 'number') { - return slice(collection); - } - return values(collection); - } - - /** - * Performs a deep comparison of each element in a `collection` to the given - * `properties` object, returning an array of all elements that have equivalent - * property values. - * - * @static - * @memberOf _ - * @type Function - * @category Collections - * @param {Array|Object|string} collection The collection to iterate over. - * @param {Object} props The object of property values to filter by. - * @returns {Array} Returns a new array of elements that have the given properties. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }, - * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } - * ]; - * - * _.where(characters, { 'age': 36 }); - * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }] - * - * _.where(characters, { 'pets': ['dino'] }); - * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }] - */ - var where = filter; - - /*--------------------------------------------------------------------------*/ - - /** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are all falsey. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to compact. - * @returns {Array} Returns a new array of filtered values. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ - function compact(array) { - var index = -1, - length = array ? array.length : 0, - result = []; - - while (++index < length) { - var value = array[index]; - if (value) { - result.push(value); - } - } - return result; - } - - /** - * Creates an array excluding all values of the provided arrays using strict - * equality for comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to process. - * @param {...Array} [values] The arrays of values to exclude. - * @returns {Array} Returns a new array of filtered values. - * @example - * - * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); - * // => [1, 3, 4] - */ - function difference(array) { - return baseDifference(array, baseFlatten(arguments, true, true, 1)); - } - - /** - * This method is like `_.find` except that it returns the index of the first - * element that passes the callback check, instead of the element itself. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': false }, - * { 'name': 'fred', 'age': 40, 'blocked': true }, - * { 'name': 'pebbles', 'age': 1, 'blocked': false } - * ]; - * - * _.findIndex(characters, function(chr) { - * return chr.age < 20; - * }); - * // => 2 - * - * // using "_.where" callback shorthand - * _.findIndex(characters, { 'age': 36 }); - * // => 0 - * - * // using "_.pluck" callback shorthand - * _.findIndex(characters, 'blocked'); - * // => 1 - */ - function findIndex(array, callback, thisArg) { - var index = -1, - length = array ? array.length : 0; - - callback = lodash.createCallback(callback, thisArg, 3); - while (++index < length) { - if (callback(array[index], index, array)) { - return index; - } - } - return -1; - } - - /** - * This method is like `_.findIndex` except that it iterates over elements - * of a `collection` from right to left. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {number} Returns the index of the found element, else `-1`. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36, 'blocked': true }, - * { 'name': 'fred', 'age': 40, 'blocked': false }, - * { 'name': 'pebbles', 'age': 1, 'blocked': true } - * ]; - * - * _.findLastIndex(characters, function(chr) { - * return chr.age > 30; - * }); - * // => 1 - * - * // using "_.where" callback shorthand - * _.findLastIndex(characters, { 'age': 36 }); - * // => 0 - * - * // using "_.pluck" callback shorthand - * _.findLastIndex(characters, 'blocked'); - * // => 2 - */ - function findLastIndex(array, callback, thisArg) { - var length = array ? array.length : 0; - callback = lodash.createCallback(callback, thisArg, 3); - while (length--) { - if (callback(array[length], length, array)) { - return length; - } - } - return -1; - } - - /** - * Gets the first element or first `n` elements of an array. If a callback - * is provided elements at the beginning of the array are returned as long - * as the callback returns truey. The callback is bound to `thisArg` and - * invoked with three arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias head, take - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|number|string} [callback] The function called - * per element or the number of elements to return. If a property name or - * object is provided it will be used to create a "_.pluck" or "_.where" - * style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the first element(s) of `array`. - * @example - * - * _.first([1, 2, 3]); - * // => 1 - * - * _.first([1, 2, 3], 2); - * // => [1, 2] - * - * _.first([1, 2, 3], function(num) { - * return num < 3; - * }); - * // => [1, 2] - * - * var characters = [ - * { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, - * { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, - * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } - * ]; - * - * // using "_.pluck" callback shorthand - * _.first(characters, 'blocked'); - * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }] - * - * // using "_.where" callback shorthand - * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name'); - * // => ['barney', 'fred'] - */ - function first(array, callback, thisArg) { - var n = 0, - length = array ? array.length : 0; - - if (typeof callback != 'number' && callback != null) { - var index = -1; - callback = lodash.createCallback(callback, thisArg, 3); - while (++index < length && callback(array[index], index, array)) { - n++; - } - } else { - n = callback; - if (n == null || thisArg) { - return array ? array[0] : undefined; - } - } - return slice(array, 0, nativeMin(nativeMax(0, n), length)); - } - - /** - * Flattens a nested array (the nesting can be to any depth). If `isShallow` - * is truey, the array will only be flattened a single level. If a callback - * is provided each element of the array is passed through the callback before - * flattening. The callback is bound to `thisArg` and invoked with three - * arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to flatten. - * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new flattened array. - * @example - * - * _.flatten([1, [2], [3, [[4]]]]); - * // => [1, 2, 3, 4]; - * - * _.flatten([1, [2], [3, [[4]]]], true); - * // => [1, 2, 3, [[4]]]; - * - * var characters = [ - * { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] }, - * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] } - * ]; - * - * // using "_.pluck" callback shorthand - * _.flatten(characters, 'pets'); - * // => ['hoppy', 'baby puss', 'dino'] - */ - function flatten(array, isShallow, callback, thisArg) { - // juggle arguments - if (typeof isShallow != 'boolean' && isShallow != null) { - thisArg = callback; - callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow; - isShallow = false; - } - if (callback != null) { - array = map(array, callback, thisArg); - } - return baseFlatten(array, isShallow); - } - - /** - * Gets the index at which the first occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. If the array is already sorted - * providing `true` for `fromIndex` will run a faster binary search. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {boolean|number} [fromIndex=0] The index to search from or `true` - * to perform a binary search on a sorted array. - * @returns {number} Returns the index of the matched value or `-1`. - * @example - * - * _.indexOf([1, 2, 3, 1, 2, 3], 2); - * // => 1 - * - * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 4 - * - * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); - * // => 2 - */ - function indexOf(array, value, fromIndex) { - if (typeof fromIndex == 'number') { - var length = array ? array.length : 0; - fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0); - } else if (fromIndex) { - var index = sortedIndex(array, value); - return array[index] === value ? index : -1; - } - return baseIndexOf(array, value, fromIndex); - } - - /** - * Gets all but the last element or last `n` elements of an array. If a - * callback is provided elements at the end of the array are excluded from - * the result as long as the callback returns truey. The callback is bound - * to `thisArg` and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|number|string} [callback=1] The function called - * per element or the number of elements to exclude. If a property name or - * object is provided it will be used to create a "_.pluck" or "_.where" - * style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a slice of `array`. - * @example - * - * _.initial([1, 2, 3]); - * // => [1, 2] - * - * _.initial([1, 2, 3], 2); - * // => [1] - * - * _.initial([1, 2, 3], function(num) { - * return num > 1; - * }); - * // => [1] - * - * var characters = [ - * { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, - * { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, - * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } - * ]; - * - * // using "_.pluck" callback shorthand - * _.initial(characters, 'blocked'); - * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }] - * - * // using "_.where" callback shorthand - * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name'); - * // => ['barney', 'fred'] - */ - function initial(array, callback, thisArg) { - var n = 0, - length = array ? array.length : 0; - - if (typeof callback != 'number' && callback != null) { - var index = length; - callback = lodash.createCallback(callback, thisArg, 3); - while (index-- && callback(array[index], index, array)) { - n++; - } - } else { - n = (callback == null || thisArg) ? 1 : callback || n; - } - return slice(array, 0, nativeMin(nativeMax(0, length - n), length)); - } - - /** - * Creates an array of unique values present in all provided arrays using - * strict equality for comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {...Array} [array] The arrays to inspect. - * @returns {Array} Returns an array of shared values. - * @example - * - * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]); - * // => [1, 2] - */ - function intersection() { - var args = [], - argsIndex = -1, - argsLength = arguments.length, - caches = getArray(), - indexOf = getIndexOf(), - trustIndexOf = indexOf === baseIndexOf, - seen = getArray(); - - while (++argsIndex < argsLength) { - var value = arguments[argsIndex]; - if (isArray(value) || isArguments(value)) { - args.push(value); - caches.push(trustIndexOf && value.length >= largeArraySize && - createCache(argsIndex ? args[argsIndex] : seen)); - } - } - var array = args[0], - index = -1, - length = array ? array.length : 0, - result = []; - - outer: - while (++index < length) { - var cache = caches[0]; - value = array[index]; - - if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) { - argsIndex = argsLength; - (cache || seen).push(value); - while (--argsIndex) { - cache = caches[argsIndex]; - if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) { - continue outer; - } - } - result.push(value); - } - } - while (argsLength--) { - cache = caches[argsLength]; - if (cache) { - releaseObject(cache); - } - } - releaseArray(caches); - releaseArray(seen); - return result; - } - - /** - * Gets the last element or last `n` elements of an array. If a callback is - * provided elements at the end of the array are returned as long as the - * callback returns truey. The callback is bound to `thisArg` and invoked - * with three arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|number|string} [callback] The function called - * per element or the number of elements to return. If a property name or - * object is provided it will be used to create a "_.pluck" or "_.where" - * style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {*} Returns the last element(s) of `array`. - * @example - * - * _.last([1, 2, 3]); - * // => 3 - * - * _.last([1, 2, 3], 2); - * // => [2, 3] - * - * _.last([1, 2, 3], function(num) { - * return num > 1; - * }); - * // => [2, 3] - * - * var characters = [ - * { 'name': 'barney', 'blocked': false, 'employer': 'slate' }, - * { 'name': 'fred', 'blocked': true, 'employer': 'slate' }, - * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.last(characters, 'blocked'), 'name'); - * // => ['fred', 'pebbles'] - * - * // using "_.where" callback shorthand - * _.last(characters, { 'employer': 'na' }); - * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] - */ - function last(array, callback, thisArg) { - var n = 0, - length = array ? array.length : 0; - - if (typeof callback != 'number' && callback != null) { - var index = length; - callback = lodash.createCallback(callback, thisArg, 3); - while (index-- && callback(array[index], index, array)) { - n++; - } - } else { - n = callback; - if (n == null || thisArg) { - return array ? array[length - 1] : undefined; - } - } - return slice(array, nativeMax(0, length - n)); - } - - /** - * Gets the index at which the last occurrence of `value` is found using strict - * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used - * as the offset from the end of the collection. - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {*} value The value to search for. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the matched value or `-1`. - * @example - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); - * // => 4 - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 1 - */ - function lastIndexOf(array, value, fromIndex) { - var index = array ? array.length : 0; - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1; - } - while (index--) { - if (array[index] === value) { - return index; - } - } - return -1; - } - - /** - * Removes all provided values from the given array using strict equality for - * comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to modify. - * @param {...*} [value] The values to remove. - * @returns {Array} Returns `array`. - * @example - * - * var array = [1, 2, 3, 1, 2, 3]; - * _.pull(array, 2, 3); - * console.log(array); - * // => [1, 1] - */ - function pull(array) { - var args = arguments, - argsIndex = 0, - argsLength = args.length, - length = array ? array.length : 0; - - while (++argsIndex < argsLength) { - var index = -1, - value = args[argsIndex]; - while (++index < length) { - if (array[index] === value) { - splice.call(array, index--, 1); - length--; - } - } - } - return array; - } - - /** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to but not including `end`. If `start` is less than `stop` a - * zero-length range is created unless a negative `step` is specified. - * - * @static - * @memberOf _ - * @category Arrays - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns a new range array. - * @example - * - * _.range(4); - * // => [0, 1, 2, 3] - * - * _.range(1, 5); - * // => [1, 2, 3, 4] - * - * _.range(0, 20, 5); - * // => [0, 5, 10, 15] - * - * _.range(0, -4, -1); - * // => [0, -1, -2, -3] - * - * _.range(1, 4, 0); - * // => [1, 1, 1] - * - * _.range(0); - * // => [] - */ - function range(start, end, step) { - start = +start || 0; - step = typeof step == 'number' ? step : (+step || 1); - - if (end == null) { - end = start; - start = 0; - } - // use `Array(length)` so engines like Chakra and V8 avoid slower modes - // http://youtu.be/XAqIpGU8ZZk#t=17m25s - var index = -1, - length = nativeMax(0, ceil((end - start) / (step || 1))), - result = Array(length); - - while (++index < length) { - result[index] = start; - start += step; - } - return result; - } - - /** - * Removes all elements from an array that the callback returns truey for - * and returns an array of removed elements. The callback is bound to `thisArg` - * and invoked with three arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to modify. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of removed elements. - * @example - * - * var array = [1, 2, 3, 4, 5, 6]; - * var evens = _.remove(array, function(num) { return num % 2 == 0; }); - * - * console.log(array); - * // => [1, 3, 5] - * - * console.log(evens); - * // => [2, 4, 6] - */ - function remove(array, callback, thisArg) { - var index = -1, - length = array ? array.length : 0, - result = []; - - callback = lodash.createCallback(callback, thisArg, 3); - while (++index < length) { - var value = array[index]; - if (callback(value, index, array)) { - result.push(value); - splice.call(array, index--, 1); - length--; - } - } - return result; - } - - /** - * The opposite of `_.initial` this method gets all but the first element or - * first `n` elements of an array. If a callback function is provided elements - * at the beginning of the array are excluded from the result as long as the - * callback returns truey. The callback is bound to `thisArg` and invoked - * with three arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias drop, tail - * @category Arrays - * @param {Array} array The array to query. - * @param {Function|Object|number|string} [callback=1] The function called - * per element or the number of elements to exclude. If a property name or - * object is provided it will be used to create a "_.pluck" or "_.where" - * style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a slice of `array`. - * @example - * - * _.rest([1, 2, 3]); - * // => [2, 3] - * - * _.rest([1, 2, 3], 2); - * // => [3] - * - * _.rest([1, 2, 3], function(num) { - * return num < 3; - * }); - * // => [3] - * - * var characters = [ - * { 'name': 'barney', 'blocked': true, 'employer': 'slate' }, - * { 'name': 'fred', 'blocked': false, 'employer': 'slate' }, - * { 'name': 'pebbles', 'blocked': true, 'employer': 'na' } - * ]; - * - * // using "_.pluck" callback shorthand - * _.pluck(_.rest(characters, 'blocked'), 'name'); - * // => ['fred', 'pebbles'] - * - * // using "_.where" callback shorthand - * _.rest(characters, { 'employer': 'slate' }); - * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }] - */ - function rest(array, callback, thisArg) { - if (typeof callback != 'number' && callback != null) { - var n = 0, - index = -1, - length = array ? array.length : 0; - - callback = lodash.createCallback(callback, thisArg, 3); - while (++index < length && callback(array[index], index, array)) { - n++; - } - } else { - n = (callback == null || thisArg) ? 1 : nativeMax(0, callback); - } - return slice(array, n); - } - - /** - * Uses a binary search to determine the smallest index at which a value - * should be inserted into a given sorted array in order to maintain the sort - * order of the array. If a callback is provided it will be executed for - * `value` and each element of `array` to compute their sort ranking. The - * callback is bound to `thisArg` and invoked with one argument; (value). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to inspect. - * @param {*} value The value to evaluate. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * _.sortedIndex([20, 30, 50], 40); - * // => 2 - * - * // using "_.pluck" callback shorthand - * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x'); - * // => 2 - * - * var dict = { - * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 } - * }; - * - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { - * return dict.wordToNumber[word]; - * }); - * // => 2 - * - * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) { - * return this.wordToNumber[word]; - * }, dict); - * // => 2 - */ - function sortedIndex(array, value, callback, thisArg) { - var low = 0, - high = array ? array.length : low; - - // explicitly reference `identity` for better inlining in Firefox - callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity; - value = callback(value); - - while (low < high) { - var mid = (low + high) >>> 1; - (callback(array[mid]) < value) - ? low = mid + 1 - : high = mid; - } - return low; - } - - /** - * Creates an array of unique values, in order, of the provided arrays using - * strict equality for comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {...Array} [array] The arrays to inspect. - * @returns {Array} Returns an array of combined values. - * @example - * - * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]); - * // => [1, 2, 3, 5, 4] - */ - function union() { - return baseUniq(baseFlatten(arguments, true, true)); - } - - /** - * Creates a duplicate-value-free version of an array using strict equality - * for comparisons, i.e. `===`. If the array is sorted, providing - * `true` for `isSorted` will use a faster algorithm. If a callback is provided - * each element of `array` is passed through the callback before uniqueness - * is computed. The callback is bound to `thisArg` and invoked with three - * arguments; (value, index, array). - * - * If a property name is provided for `callback` the created "_.pluck" style - * callback will return the property value of the given element. - * - * If an object is provided for `callback` the created "_.where" style callback - * will return `true` for elements that have the properties of the given object, - * else `false`. - * - * @static - * @memberOf _ - * @alias unique - * @category Arrays - * @param {Array} array The array to process. - * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted. - * @param {Function|Object|string} [callback=identity] The function called - * per iteration. If a property name or object is provided it will be used - * to create a "_.pluck" or "_.where" style callback, respectively. - * @param {*} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a duplicate-value-free array. - * @example - * - * _.uniq([1, 2, 1, 3, 1]); - * // => [1, 2, 3] - * - * _.uniq([1, 1, 2, 2, 3], true); - * // => [1, 2, 3] - * - * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); }); - * // => ['A', 'b', 'C'] - * - * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math); - * // => [1, 2.5, 3] - * - * // using "_.pluck" callback shorthand - * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); - * // => [{ 'x': 1 }, { 'x': 2 }] - */ - function uniq(array, isSorted, callback, thisArg) { - // juggle arguments - if (typeof isSorted != 'boolean' && isSorted != null) { - thisArg = callback; - callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted; - isSorted = false; - } - if (callback != null) { - callback = lodash.createCallback(callback, thisArg, 3); - } - return baseUniq(array, isSorted, callback); - } - - /** - * Creates an array excluding all provided values using strict equality for - * comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to filter. - * @param {...*} [value] The values to exclude. - * @returns {Array} Returns a new array of filtered values. - * @example - * - * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); - * // => [2, 3, 4] - */ - function without(array) { - return baseDifference(array, slice(arguments, 1)); - } - - /** - * Creates an array that is the symmetric difference of the provided arrays. - * See http://en.wikipedia.org/wiki/Symmetric_difference. - * - * @static - * @memberOf _ - * @category Arrays - * @param {...Array} [array] The arrays to inspect. - * @returns {Array} Returns an array of values. - * @example - * - * _.xor([1, 2, 3], [5, 2, 1, 4]); - * // => [3, 5, 4] - * - * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]); - * // => [1, 4, 5] - */ - function xor() { - var index = -1, - length = arguments.length; - - while (++index < length) { - var array = arguments[index]; - if (isArray(array) || isArguments(array)) { - var result = result - ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result))) - : array; - } - } - return result || []; - } - - /** - * Creates an array of grouped elements, the first of which contains the first - * elements of the given arrays, the second of which contains the second - * elements of the given arrays, and so on. - * - * @static - * @memberOf _ - * @alias unzip - * @category Arrays - * @param {...Array} [array] Arrays to process. - * @returns {Array} Returns a new array of grouped elements. - * @example - * - * _.zip(['fred', 'barney'], [30, 40], [true, false]); - * // => [['fred', 30, true], ['barney', 40, false]] - */ - function zip() { - var array = arguments.length > 1 ? arguments : arguments[0], - index = -1, - length = array ? max(pluck(array, 'length')) : 0, - result = Array(length < 0 ? 0 : length); - - while (++index < length) { - result[index] = pluck(array, index); - } - return result; - } - - /** - * Creates an object composed from arrays of `keys` and `values`. Provide - * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]` - * or two arrays, one of `keys` and one of corresponding `values`. - * - * @static - * @memberOf _ - * @alias object - * @category Arrays - * @param {Array} keys The array of keys. - * @param {Array} [values=[]] The array of values. - * @returns {Object} Returns an object composed of the given keys and - * corresponding values. - * @example - * - * _.zipObject(['fred', 'barney'], [30, 40]); - * // => { 'fred': 30, 'barney': 40 } - */ - function zipObject(keys, values) { - var index = -1, - length = keys ? keys.length : 0, - result = {}; - - if (!values && length && !isArray(keys[0])) { - values = []; - } - while (++index < length) { - var key = keys[index]; - if (values) { - result[key] = values[index]; - } else if (key) { - result[key[0]] = key[1]; - } - } - return result; - } - - /*--------------------------------------------------------------------------*/ - - /** - * Creates a function that executes `func`, with the `this` binding and - * arguments of the created function, only after being called `n` times. - * - * @static - * @memberOf _ - * @category Functions - * @param {number} n The number of times the function must be called before - * `func` is executed. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var saves = ['profile', 'settings']; - * - * var done = _.after(saves.length, function() { - * console.log('Done saving!'); - * }); - * - * _.forEach(saves, function(type) { - * asyncSave({ 'type': type, 'complete': done }); - * }); - * // => logs 'Done saving!', after all saves have completed - */ - function after(n, func) { - if (!isFunction(func)) { - throw new TypeError; - } - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } - - /** - * Creates a function that, when called, invokes `func` with the `this` - * binding of `thisArg` and prepends any additional `bind` arguments to those - * provided to the bound function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to bind. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {...*} [arg] Arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var func = function(greeting) { - * return greeting + ' ' + this.name; - * }; - * - * func = _.bind(func, { 'name': 'fred' }, 'hi'); - * func(); - * // => 'hi fred' - */ - function bind(func, thisArg) { - return arguments.length > 2 - ? createWrapper(func, 17, slice(arguments, 2), null, thisArg) - : createWrapper(func, 1, null, null, thisArg); - } - - /** - * Binds methods of an object to the object itself, overwriting the existing - * method. Method names may be specified as individual arguments or as arrays - * of method names. If no method names are provided all the function properties - * of `object` will be bound. - * - * @static - * @memberOf _ - * @category Functions - * @param {Object} object The object to bind and assign the bound methods to. - * @param {...string} [methodName] The object method names to - * bind, specified as individual method names or arrays of method names. - * @returns {Object} Returns `object`. - * @example - * - * var view = { - * 'label': 'docs', - * 'onClick': function() { console.log('clicked ' + this.label); } - * }; - * - * _.bindAll(view); - * jQuery('#docs').on('click', view.onClick); - * // => logs 'clicked docs', when the button is clicked - */ - function bindAll(object) { - var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object), - index = -1, - length = funcs.length; - - while (++index < length) { - var key = funcs[index]; - object[key] = createWrapper(object[key], 1, null, null, object); - } - return object; - } - - /** - * Creates a function that, when called, invokes the method at `object[key]` - * and prepends any additional `bindKey` arguments to those provided to the bound - * function. This method differs from `_.bind` by allowing bound functions to - * reference methods that will be redefined or don't yet exist. - * See http://michaux.ca/articles/lazy-function-definition-pattern. - * - * @static - * @memberOf _ - * @category Functions - * @param {Object} object The object the method belongs to. - * @param {string} key The key of the method. - * @param {...*} [arg] Arguments to be partially applied. - * @returns {Function} Returns the new bound function. - * @example - * - * var object = { - * 'name': 'fred', - * 'greet': function(greeting) { - * return greeting + ' ' + this.name; - * } - * }; - * - * var func = _.bindKey(object, 'greet', 'hi'); - * func(); - * // => 'hi fred' - * - * object.greet = function(greeting) { - * return greeting + 'ya ' + this.name + '!'; - * }; - * - * func(); - * // => 'hiya fred!' - */ - function bindKey(object, key) { - return arguments.length > 2 - ? createWrapper(key, 19, slice(arguments, 2), null, object) - : createWrapper(key, 3, null, null, object); - } - - /** - * Creates a function that is the composition of the provided functions, - * where each function consumes the return value of the function that follows. - * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. - * Each function is executed with the `this` binding of the composed function. - * - * @static - * @memberOf _ - * @category Functions - * @param {...Function} [func] Functions to compose. - * @returns {Function} Returns the new composed function. - * @example - * - * var realNameMap = { - * 'pebbles': 'penelope' - * }; - * - * var format = function(name) { - * name = realNameMap[name.toLowerCase()] || name; - * return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase(); - * }; - * - * var greet = function(formatted) { - * return 'Hiya ' + formatted + '!'; - * }; - * - * var welcome = _.compose(greet, format); - * welcome('pebbles'); - * // => 'Hiya Penelope!' - */ - function compose() { - var funcs = arguments, - length = funcs.length; - - while (length--) { - if (!isFunction(funcs[length])) { - throw new TypeError; - } - } - return function() { - var args = arguments, - length = funcs.length; - - while (length--) { - args = [funcs[length].apply(this, args)]; - } - return args[0]; - }; - } - - /** - * Creates a function which accepts one or more arguments of `func` that when - * invoked either executes `func` returning its result, if all `func` arguments - * have been provided, or returns a function that accepts one or more of the - * remaining `func` arguments, and so on. The arity of `func` can be specified - * if `func.length` is not sufficient. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to curry. - * @param {number} [arity=func.length] The arity of `func`. - * @returns {Function} Returns the new curried function. - * @example - * - * var curried = _.curry(function(a, b, c) { - * console.log(a + b + c); - * }); - * - * curried(1)(2)(3); - * // => 6 - * - * curried(1, 2)(3); - * // => 6 - * - * curried(1, 2, 3); - * // => 6 - */ - function curry(func, arity) { - arity = typeof arity == 'number' ? arity : (+arity || func.length); - return createWrapper(func, 4, null, null, null, arity); - } - - /** - * Creates a function that will delay the execution of `func` until after - * `wait` milliseconds have elapsed since the last time it was invoked. - * Provide an options object to indicate that `func` should be invoked on - * the leading and/or trailing edge of the `wait` timeout. Subsequent calls - * to the debounced function will return the result of the last `func` call. - * - * Note: If `leading` and `trailing` options are `true` `func` will be called - * on the trailing edge of the timeout only if the the debounced function is - * invoked more than once during the `wait` timeout. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to debounce. - * @param {number} wait The number of milliseconds to delay. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout. - * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called. - * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // avoid costly calculations while the window size is in flux - * var lazyLayout = _.debounce(calculateLayout, 150); - * jQuery(window).on('resize', lazyLayout); - * - * // execute `sendMail` when the click event is fired, debouncing subsequent calls - * jQuery('#postbox').on('click', _.debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * }); - * - * // ensure `batchLog` is executed once after 1 second of debounced calls - * var source = new EventSource('/stream'); - * source.addEventListener('message', _.debounce(batchLog, 250, { - * 'maxWait': 1000 - * }, false); - */ - function debounce(func, wait, options) { - var args, - maxTimeoutId, - result, - stamp, - thisArg, - timeoutId, - trailingCall, - lastCalled = 0, - maxWait = false, - trailing = true; - - if (!isFunction(func)) { - throw new TypeError; - } - wait = nativeMax(0, wait) || 0; - if (options === true) { - var leading = true; - trailing = false; - } else if (isObject(options)) { - leading = options.leading; - maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0); - trailing = 'trailing' in options ? options.trailing : trailing; - } - var delayed = function() { - var remaining = wait - (now() - stamp); - if (remaining <= 0) { - if (maxTimeoutId) { - clearTimeout(maxTimeoutId); - } - var isCalled = trailingCall; - maxTimeoutId = timeoutId = trailingCall = undefined; - if (isCalled) { - lastCalled = now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - } else { - timeoutId = setTimeout(delayed, remaining); - } - }; - - var maxDelayed = function() { - if (timeoutId) { - clearTimeout(timeoutId); - } - maxTimeoutId = timeoutId = trailingCall = undefined; - if (trailing || (maxWait !== wait)) { - lastCalled = now(); - result = func.apply(thisArg, args); - if (!timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - } - }; - - return function() { - args = arguments; - stamp = now(); - thisArg = this; - trailingCall = trailing && (timeoutId || !leading); - - if (maxWait === false) { - var leadingCall = leading && !timeoutId; - } else { - if (!maxTimeoutId && !leading) { - lastCalled = stamp; - } - var remaining = maxWait - (stamp - lastCalled), - isCalled = remaining <= 0; - - if (isCalled) { - if (maxTimeoutId) { - maxTimeoutId = clearTimeout(maxTimeoutId); - } - lastCalled = stamp; - result = func.apply(thisArg, args); - } - else if (!maxTimeoutId) { - maxTimeoutId = setTimeout(maxDelayed, remaining); - } - } - if (isCalled && timeoutId) { - timeoutId = clearTimeout(timeoutId); - } - else if (!timeoutId && wait !== maxWait) { - timeoutId = setTimeout(delayed, wait); - } - if (leadingCall) { - isCalled = true; - result = func.apply(thisArg, args); - } - if (isCalled && !timeoutId && !maxTimeoutId) { - args = thisArg = null; - } - return result; - }; - } - - /** - * Defers executing the `func` function until the current call stack has cleared. - * Additional arguments will be provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to defer. - * @param {...*} [arg] Arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.defer(function(text) { console.log(text); }, 'deferred'); - * // logs 'deferred' after one or more milliseconds - */ - function defer(func) { - if (!isFunction(func)) { - throw new TypeError; - } - var args = slice(arguments, 1); - return setTimeout(function() { func.apply(undefined, args); }, 1); - } - - /** - * Executes the `func` function after `wait` milliseconds. Additional arguments - * will be provided to `func` when it is invoked. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay execution. - * @param {...*} [arg] Arguments to invoke the function with. - * @returns {number} Returns the timer id. - * @example - * - * _.delay(function(text) { console.log(text); }, 1000, 'later'); - * // => logs 'later' after one second - */ - function delay(func, wait) { - if (!isFunction(func)) { - throw new TypeError; - } - var args = slice(arguments, 2); - return setTimeout(function() { func.apply(undefined, args); }, wait); - } - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided it will be used to determine the cache key for storing the result - * based on the arguments provided to the memoized function. By default, the - * first argument provided to the memoized function is used as the cache key. - * The `func` is executed with the `this` binding of the memoized function. - * The result cache is exposed as the `cache` property on the memoized function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] A function used to resolve the cache key. - * @returns {Function} Returns the new memoizing function. - * @example - * - * var fibonacci = _.memoize(function(n) { - * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); - * }); - * - * fibonacci(9) - * // => 34 - * - * var data = { - * 'fred': { 'name': 'fred', 'age': 40 }, - * 'pebbles': { 'name': 'pebbles', 'age': 1 } - * }; - * - * // modifying the result cache - * var get = _.memoize(function(name) { return data[name]; }, _.identity); - * get('pebbles'); - * // => { 'name': 'pebbles', 'age': 1 } - * - * get.cache.pebbles.name = 'penelope'; - * get('pebbles'); - * // => { 'name': 'penelope', 'age': 1 } - */ - function memoize(func, resolver) { - if (!isFunction(func)) { - throw new TypeError; - } - var memoized = function() { - var cache = memoized.cache, - key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0]; - - return hasOwnProperty.call(cache, key) - ? cache[key] - : (cache[key] = func.apply(this, arguments)); - } - memoized.cache = {}; - return memoized; - } - - /** - * Creates a function that is restricted to execute `func` once. Repeat calls to - * the function will return the value of the first call. The `func` is executed - * with the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // `initialize` executes `createApplication` once - */ - function once(func) { - var ran, - result; - - if (!isFunction(func)) { - throw new TypeError; - } - return function() { - if (ran) { - return result; - } - ran = true; - result = func.apply(this, arguments); - - // clear the `func` variable so the function may be garbage collected - func = null; - return result; - }; - } - - /** - * Creates a function that, when called, invokes `func` with any additional - * `partial` arguments prepended to those provided to the new function. This - * method is similar to `_.bind` except it does **not** alter the `this` binding. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [arg] Arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var greet = function(greeting, name) { return greeting + ' ' + name; }; - * var hi = _.partial(greet, 'hi'); - * hi('fred'); - * // => 'hi fred' - */ - function partial(func) { - return createWrapper(func, 16, slice(arguments, 1)); - } - - /** - * This method is like `_.partial` except that `partial` arguments are - * appended to those provided to the new function. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to partially apply arguments to. - * @param {...*} [arg] Arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. - * @example - * - * var defaultsDeep = _.partialRight(_.merge, _.defaults); - * - * var options = { - * 'variable': 'data', - * 'imports': { 'jq': $ } - * }; - * - * defaultsDeep(options, _.templateSettings); - * - * options.variable - * // => 'data' - * - * options.imports - * // => { '_': _, 'jq': $ } - */ - function partialRight(func) { - return createWrapper(func, 32, null, slice(arguments, 1)); - } - - /** - * Creates a function that, when executed, will only call the `func` function - * at most once per every `wait` milliseconds. Provide an options object to - * indicate that `func` should be invoked on the leading and/or trailing edge - * of the `wait` timeout. Subsequent calls to the throttled function will - * return the result of the last `func` call. - * - * Note: If `leading` and `trailing` options are `true` `func` will be called - * on the trailing edge of the timeout only if the the throttled function is - * invoked more than once during the `wait` timeout. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to throttle. - * @param {number} wait The number of milliseconds to throttle executions to. - * @param {Object} [options] The options object. - * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // avoid excessively updating the position while scrolling - * var throttled = _.throttle(updatePosition, 100); - * jQuery(window).on('scroll', throttled); - * - * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes - * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, { - * 'trailing': false - * })); - */ - function throttle(func, wait, options) { - var leading = true, - trailing = true; - - if (!isFunction(func)) { - throw new TypeError; - } - if (options === false) { - leading = false; - } else if (isObject(options)) { - leading = 'leading' in options ? options.leading : leading; - trailing = 'trailing' in options ? options.trailing : trailing; - } - debounceOptions.leading = leading; - debounceOptions.maxWait = wait; - debounceOptions.trailing = trailing; - - return debounce(func, wait, debounceOptions); - } - - /** - * Creates a function that provides `value` to the wrapper function as its - * first argument. Additional arguments provided to the function are appended - * to those provided to the wrapper function. The wrapper is executed with - * the `this` binding of the created function. - * - * @static - * @memberOf _ - * @category Functions - * @param {*} value The value to wrap. - * @param {Function} wrapper The wrapper function. - * @returns {Function} Returns the new function. - * @example - * - * var p = _.wrap(_.escape, function(func, text) { - * return '

    ' + func(text) + '

    '; - * }); - * - * p('Fred, Wilma, & Pebbles'); - * // => '

    Fred, Wilma, & Pebbles

    ' - */ - function wrap(value, wrapper) { - return createWrapper(wrapper, 16, [value]); - } - - /*--------------------------------------------------------------------------*/ - - /** - * Creates a function that returns `value`. - * - * @static - * @memberOf _ - * @category Utilities - * @param {*} value The value to return from the new function. - * @returns {Function} Returns the new function. - * @example - * - * var object = { 'name': 'fred' }; - * var getter = _.constant(object); - * getter() === object; - * // => true - */ - function constant(value) { - return function() { - return value; - }; - } - - /** - * Produces a callback bound to an optional `thisArg`. If `func` is a property - * name the created callback will return the property value for a given element. - * If `func` is an object the created callback will return `true` for elements - * that contain the equivalent object properties, otherwise it will return `false`. - * - * @static - * @memberOf _ - * @category Utilities - * @param {*} [func=identity] The value to convert to a callback. - * @param {*} [thisArg] The `this` binding of the created callback. - * @param {number} [argCount] The number of arguments the callback accepts. - * @returns {Function} Returns a callback function. - * @example - * - * var characters = [ - * { 'name': 'barney', 'age': 36 }, - * { 'name': 'fred', 'age': 40 } - * ]; - * - * // wrap to create custom callback shorthands - * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) { - * var match = /^(.+?)__([gl]t)(.+)$/.exec(callback); - * return !match ? func(callback, thisArg) : function(object) { - * return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3]; - * }; - * }); - * - * _.filter(characters, 'age__gt38'); - * // => [{ 'name': 'fred', 'age': 40 }] - */ - function createCallback(func, thisArg, argCount) { - var type = typeof func; - if (func == null || type == 'function') { - return baseCreateCallback(func, thisArg, argCount); - } - // handle "_.pluck" style callback shorthands - if (type != 'object') { - return property(func); - } - var props = keys(func), - key = props[0], - a = func[key]; - - // handle "_.where" style callback shorthands - if (props.length == 1 && a === a && !isObject(a)) { - // fast path the common case of providing an object with a single - // property containing a primitive value - return function(object) { - var b = object[key]; - return a === b && (a !== 0 || (1 / a == 1 / b)); - }; - } - return function(object) { - var length = props.length, - result = false; - - while (length--) { - if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) { - break; - } - } - return result; - }; - } - - /** - * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their - * corresponding HTML entities. - * - * @static - * @memberOf _ - * @category Utilities - * @param {string} string The string to escape. - * @returns {string} Returns the escaped string. - * @example - * - * _.escape('Fred, Wilma, & Pebbles'); - * // => 'Fred, Wilma, & Pebbles' - */ - function escape(string) { - return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar); - } - - /** - * This method returns the first argument provided to it. - * - * @static - * @memberOf _ - * @category Utilities - * @param {*} value Any value. - * @returns {*} Returns `value`. - * @example - * - * var object = { 'name': 'fred' }; - * _.identity(object) === object; - * // => true - */ - function identity(value) { - return value; - } - - /** - * Adds function properties of a source object to the destination object. - * If `object` is a function methods will be added to its prototype as well. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Function|Object} [object=lodash] object The destination object. - * @param {Object} source The object of functions to add. - * @param {Object} [options] The options object. - * @param {boolean} [options.chain=true] Specify whether the functions added are chainable. - * @example - * - * function capitalize(string) { - * return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); - * } - * - * _.mixin({ 'capitalize': capitalize }); - * _.capitalize('fred'); - * // => 'Fred' - * - * _('fred').capitalize().value(); - * // => 'Fred' - * - * _.mixin({ 'capitalize': capitalize }, { 'chain': false }); - * _('fred').capitalize(); - * // => 'Fred' - */ - function mixin(object, source, options) { - var chain = true, - methodNames = source && functions(source); - - if (!source || (!options && !methodNames.length)) { - if (options == null) { - options = source; - } - ctor = lodashWrapper; - source = object; - object = lodash; - methodNames = functions(source); - } - if (options === false) { - chain = false; - } else if (isObject(options) && 'chain' in options) { - chain = options.chain; - } - var ctor = object, - isFunc = isFunction(ctor); - - forEach(methodNames, function(methodName) { - var func = object[methodName] = source[methodName]; - if (isFunc) { - ctor.prototype[methodName] = function() { - var chainAll = this.__chain__, - value = this.__wrapped__, - args = [value]; - - push.apply(args, arguments); - var result = func.apply(object, args); - if (chain || chainAll) { - if (value === result && isObject(result)) { - return this; - } - result = new ctor(result); - result.__chain__ = chainAll; - } - return result; - }; - } - }); - } - - /** - * Reverts the '_' variable to its previous value and returns a reference to - * the `lodash` function. - * - * @static - * @memberOf _ - * @category Utilities - * @returns {Function} Returns the `lodash` function. - * @example - * - * var lodash = _.noConflict(); - */ - function noConflict() { - context._ = oldDash; - return this; - } - - /** - * A no-operation function. - * - * @static - * @memberOf _ - * @category Utilities - * @example - * - * var object = { 'name': 'fred' }; - * _.noop(object) === undefined; - * // => true - */ - function noop() { - // no operation performed - } - - /** - * Gets the number of milliseconds that have elapsed since the Unix epoch - * (1 January 1970 00:00:00 UTC). - * - * @static - * @memberOf _ - * @category Utilities - * @example - * - * var stamp = _.now(); - * _.defer(function() { console.log(_.now() - stamp); }); - * // => logs the number of milliseconds it took for the deferred function to be called - */ - var now = isNative(now = Date.now) && now || function() { - return new Date().getTime(); - }; - - /** - * Converts the given value into an integer of the specified radix. - * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the - * `value` is a hexadecimal, in which case a `radix` of `16` is used. - * - * Note: This method avoids differences in native ES3 and ES5 `parseInt` - * implementations. See http://es5.github.io/#E. - * - * @static - * @memberOf _ - * @category Utilities - * @param {string} value The value to parse. - * @param {number} [radix] The radix used to interpret the value to parse. - * @returns {number} Returns the new integer value. - * @example - * - * _.parseInt('08'); - * // => 8 - */ - var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) { - // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt` - return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0); - }; - - /** - * Creates a "_.pluck" style function, which returns the `key` value of a - * given object. - * - * @static - * @memberOf _ - * @category Utilities - * @param {string} key The name of the property to retrieve. - * @returns {Function} Returns the new function. - * @example - * - * var characters = [ - * { 'name': 'fred', 'age': 40 }, - * { 'name': 'barney', 'age': 36 } - * ]; - * - * var getName = _.property('name'); - * - * _.map(characters, getName); - * // => ['barney', 'fred'] - * - * _.sortBy(characters, getName); - * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] - */ - function property(key) { - return function(object) { - return object[key]; - }; - } - - /** - * Produces a random number between `min` and `max` (inclusive). If only one - * argument is provided a number between `0` and the given number will be - * returned. If `floating` is truey or either `min` or `max` are floats a - * floating-point number will be returned instead of an integer. - * - * @static - * @memberOf _ - * @category Utilities - * @param {number} [min=0] The minimum possible value. - * @param {number} [max=1] The maximum possible value. - * @param {boolean} [floating=false] Specify returning a floating-point number. - * @returns {number} Returns a random number. - * @example - * - * _.random(0, 5); - * // => an integer between 0 and 5 - * - * _.random(5); - * // => also an integer between 0 and 5 - * - * _.random(5, true); - * // => a floating-point number between 0 and 5 - * - * _.random(1.2, 5.2); - * // => a floating-point number between 1.2 and 5.2 - */ - function random(min, max, floating) { - var noMin = min == null, - noMax = max == null; - - if (floating == null) { - if (typeof min == 'boolean' && noMax) { - floating = min; - min = 1; - } - else if (!noMax && typeof max == 'boolean') { - floating = max; - noMax = true; - } - } - if (noMin && noMax) { - max = 1; - } - min = +min || 0; - if (noMax) { - max = min; - min = 0; - } else { - max = +max || 0; - } - if (floating || min % 1 || max % 1) { - var rand = nativeRandom(); - return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max); - } - return baseRandom(min, max); - } - - /** - * Resolves the value of property `key` on `object`. If `key` is a function - * it will be invoked with the `this` binding of `object` and its result returned, - * else the property value is returned. If `object` is falsey then `undefined` - * is returned. - * - * @static - * @memberOf _ - * @category Utilities - * @param {Object} object The object to inspect. - * @param {string} key The name of the property to resolve. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { - * 'cheese': 'crumpets', - * 'stuff': function() { - * return 'nonsense'; - * } - * }; - * - * _.result(object, 'cheese'); - * // => 'crumpets' - * - * _.result(object, 'stuff'); - * // => 'nonsense' - */ - function result(object, key) { - if (object) { - var value = object[key]; - return isFunction(value) ? object[key]() : value; - } - } - - /** - * A micro-templating method that handles arbitrary delimiters, preserves - * whitespace, and correctly escapes quotes within interpolated code. - * - * Note: In the development build, `_.template` utilizes sourceURLs for easier - * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl - * - * For more information on precompiling templates see: - * http://lodash.com/custom-builds - * - * For more information on Chrome extension sandboxes see: - * http://developer.chrome.com/stable/extensions/sandboxingEval.html - * - * @static - * @memberOf _ - * @category Utilities - * @param {string} text The template text. - * @param {Object} data The data object used to populate the text. - * @param {Object} [options] The options object. - * @param {RegExp} [options.escape] The "escape" delimiter. - * @param {RegExp} [options.evaluate] The "evaluate" delimiter. - * @param {Object} [options.imports] An object to import into the template as local variables. - * @param {RegExp} [options.interpolate] The "interpolate" delimiter. - * @param {string} [sourceURL] The sourceURL of the template's compiled source. - * @param {string} [variable] The data object variable name. - * @returns {Function|string} Returns a compiled function when no `data` object - * is given, else it returns the interpolated text. - * @example - * - * // using the "interpolate" delimiter to create a compiled template - * var compiled = _.template('hello <%= name %>'); - * compiled({ 'name': 'fred' }); - * // => 'hello fred' - * - * // using the "escape" delimiter to escape HTML in data property values - * _.template('<%- value %>', { 'value': ' - - - - - - - - - - @@ -45,52 +35,12 @@ - - -
    - +
    Loading...
    @@ -100,13 +50,7 @@
    - - - - - + diff --git a/presto-main/src/test/java/com/facebook/presto/BenchmarkBoxedBoolean.java b/presto-main/src/test/java/com/facebook/presto/BenchmarkBoxedBoolean.java new file mode 100644 index 0000000000000..9485ba755a3a1 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/BenchmarkBoxedBoolean.java @@ -0,0 +1,159 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static java.lang.Boolean.TRUE; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.openjdk.jmh.annotations.Level.Iteration; + +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 3) +@Warmup(iterations = 10, time = 1, timeUnit = SECONDS) +@Measurement(iterations = 10, time = 1, timeUnit = SECONDS) +public class BenchmarkBoxedBoolean +{ + private static final int ARRAY_SIZE = 100; + + @Benchmark + public void primitive(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (data.primitives[i]) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @Benchmark + public void unboxing(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (data.boxed[i]) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @Benchmark + public void identity(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (TRUE == data.constants[i]) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @Benchmark + public void booleanEquals(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (TRUE.equals(data.constants[i])) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @Benchmark + public void object(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (TRUE.equals(data.objects[i])) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @Benchmark + public void booleanEqualsNotNull(BenchmarkData data, Blackhole blackhole) + { + for (int i = 0; i < ARRAY_SIZE; i++) { + if (TRUE.equals(data.boxed[i])) { + blackhole.consume(0xDEADBEAF); + } + else { + blackhole.consume(0xBEAFDEAD); + } + } + } + + @State(Scope.Thread) + public static class BenchmarkData + { + public boolean[] primitives = new boolean[ARRAY_SIZE]; + public Boolean[] boxed = new Boolean[ARRAY_SIZE]; + public Boolean[] constants = new Boolean[ARRAY_SIZE]; + public Boolean[] objects = new Boolean[ARRAY_SIZE]; + + @Setup(Iteration) + public void setup() + { + for (int i = 0; i < ARRAY_SIZE; i++) { + boolean value = ThreadLocalRandom.current().nextBoolean(); + boolean isNull = ThreadLocalRandom.current().nextBoolean(); + + primitives[i] = value; + boxed[i] = value; + + constants[i] = isNull ? null : value; + objects[i] = isNull ? null : new Boolean(value); + } + } + } + + public static void main(String[] args) + throws RunnerException + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkBoxedBoolean.class.getSimpleName() + ".*") + .addProfiler("perfasm") + .build(); + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/TestHiddenColumns.java b/presto-main/src/test/java/com/facebook/presto/TestHiddenColumns.java index 1e692614b6dc8..5bc9cc6b0065d 100644 --- a/presto-main/src/test/java/com/facebook/presto/TestHiddenColumns.java +++ b/presto-main/src/test/java/com/facebook/presto/TestHiddenColumns.java @@ -65,4 +65,14 @@ public void testSimpleSelect() assertEquals(runner.execute("SELECT *, row_number, * from REGION"), runner.execute("SELECT regionkey, name, comment, row_number, regionkey, name, comment from REGION")); assertEquals(runner.execute("SELECT row_number, x.row_number from REGION x"), runner.execute("SELECT row_number, row_number from REGION")); } + + @Test + public void testAliasedTableColumns() + { + // https://github.com/prestodb/presto/issues/11385 + // TPCH tables have a hidden "row_number" column, which triggers this bug. + assertEquals( + runner.execute("SELECT * FROM orders AS t (a, b, c, d, e, f, g, h, i)"), + runner.execute("SELECT * FROM orders")); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/block/AbstractTestBlock.java b/presto-main/src/test/java/com/facebook/presto/block/AbstractTestBlock.java index 7381740711ffe..63978bd55e7e6 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/AbstractTestBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/AbstractTestBlock.java @@ -34,6 +34,7 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -48,6 +49,7 @@ import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.toIntExact; import static java.lang.String.format; +import static java.util.Arrays.fill; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotSame; @@ -60,6 +62,7 @@ public abstract class AbstractTestBlock { private static final TypeManager TYPE_MANAGER = new TypeRegistry(); private static final BlockEncodingSerde BLOCK_ENCODING_SERDE = new BlockEncodingManager(TYPE_MANAGER); + static { // associate TYPE_MANAGER with a function registry new FunctionRegistry(TYPE_MANAGER, new BlockEncodingManager(TYPE_MANAGER), new FeaturesConfig()); @@ -223,6 +226,14 @@ private void assertBlockSize(Block block) long expectedSecondHalfSize = copyBlockViaBlockSerde(secondHalf).getSizeInBytes(); assertEquals(secondHalf.getSizeInBytes(), expectedSecondHalfSize); assertEquals(block.getRegionSizeInBytes(firstHalf.getPositionCount(), secondHalf.getPositionCount()), expectedSecondHalfSize); + + boolean[] positions = new boolean[block.getPositionCount()]; + fill(positions, 0, firstHalf.getPositionCount(), true); + assertEquals(block.getPositionsSizeInBytes(positions), expectedFirstHalfSize); + fill(positions, true); + assertEquals(block.getPositionsSizeInBytes(positions), expectedBlockSize); + fill(positions, 0, firstHalf.getPositionCount(), false); + assertEquals(block.getPositionsSizeInBytes(positions), expectedSecondHalfSize); } // expectedValueType is required since otherwise the expected value type is unknown when expectedValue is null. @@ -448,9 +459,9 @@ protected static Slice createExpectedValue(int length) return dynamicSliceOutput.slice(); } - protected static Object[] alternatingNullValues(Object[] objects) + protected static T[] alternatingNullValues(T[] objects) { - Object[] objectsWithNulls = (Object[]) Array.newInstance(objects.getClass().getComponentType(), objects.length * 2 + 1); + T[] objectsWithNulls = Arrays.copyOf(objects, objects.length * 2 + 1); for (int i = 0; i < objects.length; i++) { objectsWithNulls[i * 2] = null; objectsWithNulls[i * 2 + 1] = objects[i]; diff --git a/presto-main/src/test/java/com/facebook/presto/block/ColumnarTestUtils.java b/presto-main/src/test/java/com/facebook/presto/block/ColumnarTestUtils.java index db9ffc80a7840..f3d1fbca3a541 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/ColumnarTestUtils.java +++ b/presto-main/src/test/java/com/facebook/presto/block/ColumnarTestUtils.java @@ -35,6 +35,7 @@ final class ColumnarTestUtils { private static final TypeManager TYPE_MANAGER = new TypeRegistry(); private static final BlockEncodingSerde BLOCK_ENCODING_SERDE = new BlockEncodingManager(TYPE_MANAGER); + static { // associate TYPE_MANAGER with a function registry new FunctionRegistry(TYPE_MANAGER, new BlockEncodingManager(TYPE_MANAGER), new FeaturesConfig()); diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestArrayBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestArrayBlock.java index c42b8a60e644a..aad293d70a895 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestArrayBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestArrayBlock.java @@ -50,7 +50,7 @@ public void testWithFixedWidthBlock() assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 1, 3, 4, 7); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 2, 3, 5, 6); - long[][] expectedValuesWithNull = (long[][]) alternatingNullValues(expectedValues); + long[][] expectedValuesWithNull = alternatingNullValues(expectedValues); BlockBuilder blockBuilderWithNull = createBlockBuilderWithValues(expectedValuesWithNull); assertBlock(blockBuilderWithNull, () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); assertBlock(blockBuilderWithNull.build(), () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); @@ -76,7 +76,7 @@ public void testWithVariableWidthBlock() assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 1, 3, 4, 7); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 2, 3, 5, 6); - Slice[][] expectedValuesWithNull = (Slice[][]) alternatingNullValues(expectedValues); + Slice[][] expectedValuesWithNull = alternatingNullValues(expectedValues); BlockBuilder blockBuilderWithNull = createBlockBuilderWithValues(expectedValuesWithNull); assertBlock(blockBuilderWithNull, () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); assertBlock(blockBuilderWithNull.build(), () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); @@ -96,7 +96,7 @@ public void testWithArrayBlock() assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 1, 3, 4, 7); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 2, 3, 5, 6); - long[][][] expectedValuesWithNull = (long[][][]) alternatingNullValues(expectedValues); + long[][][] expectedValuesWithNull = alternatingNullValues(expectedValues); BlockBuilder blockBuilderWithNull = createBlockBuilderWithValues(expectedValuesWithNull); assertBlock(blockBuilderWithNull, () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); assertBlock(blockBuilderWithNull.build(), () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); @@ -147,7 +147,7 @@ public void testLazyBlockBuilderInitialization() @Test public void testEstimatedDataSizeForStats() { - long[][][] expectedValues = (long[][][]) alternatingNullValues(createExpectedValues()); + long[][][] expectedValues = alternatingNullValues(createExpectedValues()); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); Block block = blockBuilder.build(); assertEquals(block.getPositionCount(), expectedValues.length); @@ -180,11 +180,11 @@ public void testCompactBlock() int[] offsets = {0, 1, 1, 2, 4, 8, 16}; boolean[] valueIsNull = {false, true, false, false, false, false}; - testCompactBlock(fromElementBlock(0, new boolean[0], new int[1], emptyValueBlock)); - testCompactBlock(fromElementBlock(valueIsNull.length, valueIsNull, offsets, compactValueBlock)); - testIncompactBlock(fromElementBlock(valueIsNull.length - 1, valueIsNull, offsets, compactValueBlock)); + testCompactBlock(fromElementBlock(0, Optional.empty(), new int[1], emptyValueBlock)); + testCompactBlock(fromElementBlock(valueIsNull.length, Optional.of(valueIsNull), offsets, compactValueBlock)); + testIncompactBlock(fromElementBlock(valueIsNull.length - 1, Optional.of(valueIsNull), offsets, compactValueBlock)); // underlying value block is not compact - testIncompactBlock(fromElementBlock(valueIsNull.length, valueIsNull, offsets, inCompactValueBlock)); + testIncompactBlock(fromElementBlock(valueIsNull.length, Optional.of(valueIsNull), offsets, inCompactValueBlock)); } private static BlockBuilder createBlockBuilderWithValues(long[][][] expectedValues) diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestByteArrayBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestByteArrayBlock.java index f6a64c5cc698f..58db495606a4d 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestByteArrayBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestByteArrayBlock.java @@ -33,13 +33,13 @@ public void test() { Slice[] expectedValues = createTestValue(17); assertFixedWithValues(expectedValues); - assertFixedWithValues((Slice[]) alternatingNullValues(expectedValues)); + assertFixedWithValues(alternatingNullValues(expectedValues)); } @Test public void testCopyPositions() { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createTestValue(17)); + Slice[] expectedValues = alternatingNullValues(createTestValue(17)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestDictionaryBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestDictionaryBlock.java index 35d37e4475e28..397222e6394b0 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestDictionaryBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestDictionaryBlock.java @@ -41,6 +41,31 @@ public void testSizeInBytes() assertEquals(dictionaryBlock.getSizeInBytes(), dictionaryBlock.getDictionary().getSizeInBytes() + (100 * SIZE_OF_INT)); } + @Test + public void testLogicalSizeInBytes() + { + // The 10 Slices in the array will be of lengths 0 to 9. + Slice[] expectedValues = createExpectedValues(10); + + // The dictionary within the dictionary block is expected to be a VariableWidthBlock of size 95 bytes. + // 45 bytes for the expectedValues Slices (sum of seq(0,9)) and 50 bytes for the position and isNull array (total 10 positions). + DictionaryBlock dictionaryBlock = createDictionaryBlock(expectedValues, 100); + assertEquals(dictionaryBlock.getDictionary().getLogicalSizeInBytes(), 95); + + // The 100 positions in the dictionary block index to 10 positions in the underlying dictionary (10 each). + // Logical size calculation accounts for 4 bytes of offset and 1 byte of isNull. Therefore the expected unoptimized + // size is 10 times the size of the underlying dictionary (VariableWidthBlock). + assertEquals(dictionaryBlock.getLogicalSizeInBytes(), 95 * 10); + + // With alternating nulls, we have 21 positions, with the same size calculation as above. + dictionaryBlock = createDictionaryBlock(alternatingNullValues(expectedValues), 210); + assertEquals(dictionaryBlock.getDictionary().getPositionCount(), 21); + assertEquals(dictionaryBlock.getDictionary().getLogicalSizeInBytes(), 150); + + // The null positions should be included in the logical size. + assertEquals(dictionaryBlock.getLogicalSizeInBytes(), 150 * 10); + } + @Test public void testCopyRegionCreatesCompactBlock() { diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestFixedWidthBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestFixedWidthBlock.java index b2e751502b322..91eb07409a015 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestFixedWidthBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestFixedWidthBlock.java @@ -35,7 +35,7 @@ public void test() for (int fixedSize = 0; fixedSize < 20; fixedSize++) { Slice[] expectedValues = createExpectedValues(17, fixedSize); assertFixedWithValues(expectedValues, fixedSize); - assertFixedWithValues((Slice[]) alternatingNullValues(expectedValues), fixedSize); + assertFixedWithValues(alternatingNullValues(expectedValues), fixedSize); } } @@ -43,7 +43,7 @@ public void test() public void testCopyPositions() { for (int fixedSize = 0; fixedSize < 20; fixedSize++) { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createExpectedValues(17, fixedSize)); + Slice[] expectedValues = alternatingNullValues(createExpectedValues(17, fixedSize)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues, fixedSize); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } @@ -53,7 +53,7 @@ public void testCopyPositions() public void testLazyBlockBuilderInitialization() { for (int fixedSize = 0; fixedSize < 20; fixedSize++) { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createExpectedValues(17, fixedSize)); + Slice[] expectedValues = alternatingNullValues(createExpectedValues(17, fixedSize)); BlockBuilder emptyBlockBuilder = new FixedWidthBlockBuilder(fixedSize, null, 0); BlockBuilder blockBuilder = new FixedWidthBlockBuilder(fixedSize, null, expectedValues.length); @@ -74,7 +74,7 @@ public void testLazyBlockBuilderInitialization() public void testEstimatedDataSizeForStats() { for (int fixedSize = 0; fixedSize < 20; fixedSize++) { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createExpectedValues(17, fixedSize)); + Slice[] expectedValues = alternatingNullValues(createExpectedValues(17, fixedSize)); BlockBuilder blockBuilder = new FixedWidthBlockBuilder(fixedSize, null, expectedValues.length); writeValues(expectedValues, blockBuilder); assertEstimatedDataSizeForStats(blockBuilder, expectedValues); diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestIntArrayBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestIntArrayBlock.java index fe4ade054f81b..20d7337ae4b9e 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestIntArrayBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestIntArrayBlock.java @@ -33,13 +33,13 @@ public void test() { Slice[] expectedValues = createTestValue(17); assertFixedWithValues(expectedValues); - assertFixedWithValues((Slice[]) alternatingNullValues(expectedValues)); + assertFixedWithValues(alternatingNullValues(expectedValues)); } @Test public void testCopyPositions() { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createTestValue(17)); + Slice[] expectedValues = alternatingNullValues(createTestValue(17)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestLongArrayBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestLongArrayBlock.java index 2995b77cf76f1..dce2a94ed9a45 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestLongArrayBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestLongArrayBlock.java @@ -34,13 +34,13 @@ public void test() { Slice[] expectedValues = createTestValue(17); assertFixedWithValues(expectedValues); - assertFixedWithValues((Slice[]) alternatingNullValues(expectedValues)); + assertFixedWithValues(alternatingNullValues(expectedValues)); } @Test public void testCopyPositions() { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createTestValue(17)); + Slice[] expectedValues = alternatingNullValues(createTestValue(17)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestMapBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestMapBlock.java index ac0d91c711946..87239562767ca 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestMapBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestMapBlock.java @@ -72,12 +72,12 @@ public void testCompactBlock() int[] offsets = {0, 1, 1, 2, 4, 8, 16}; boolean[] mapIsNull = {false, true, false, false, false, false}; - testCompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(new boolean[0], new int[1], emptyBlock, emptyBlock)); - testCompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(mapIsNull, offsets, compactKeyBlock, compactValueBlock)); + testCompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(Optional.empty(), new int[1], emptyBlock, emptyBlock)); + testCompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(Optional.of(mapIsNull), offsets, compactKeyBlock, compactValueBlock)); // TODO: Add test case for a sliced MapBlock // underlying key/value block is not compact - testIncompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(mapIsNull, offsets, inCompactKeyBlock, inCompactValueBlock)); + testIncompactBlock(mapType(TINYINT, TINYINT).createBlockFromKeyValue(Optional.of(mapIsNull), offsets, inCompactKeyBlock, inCompactValueBlock)); } private Map[] createTestMap(int... entryCounts) @@ -111,7 +111,7 @@ private void testWith(Map[] expectedValues) assertBlockFilteredPositions(expectedValues, block, () -> blockBuilder.newBlockBuilderLike(null), 0, 1, 3, 4, 7); assertBlockFilteredPositions(expectedValues, block, () -> blockBuilder.newBlockBuilderLike(null), 2, 3, 5, 6); - Map[] expectedValuesWithNull = (Map[]) alternatingNullValues(expectedValues); + Map[] expectedValuesWithNull = alternatingNullValues(expectedValues); BlockBuilder blockBuilderWithNull = createBlockBuilderWithValues(expectedValuesWithNull); assertBlock(blockBuilderWithNull, () -> blockBuilder.newBlockBuilderLike(null), expectedValuesWithNull); @@ -158,7 +158,7 @@ private Block createBlockWithValuesFromKeyValueBlock(Map[] maps) offsets[i + 1] = offsets[i] + map.size(); } } - return mapType(VARCHAR, BIGINT).createBlockFromKeyValue(mapIsNull, offsets, createStringsBlock(keys), createLongsBlock(values)); + return mapType(VARCHAR, BIGINT).createBlockFromKeyValue(Optional.of(mapIsNull), offsets, createStringsBlock(keys), createLongsBlock(values)); } private void createBlockBuilderWithValues(Map map, BlockBuilder mapBlockBuilder) @@ -262,7 +262,7 @@ public void testCloseEntryStrict() @Test public void testEstimatedDataSizeForStats() { - Map[] expectedValues = (Map[]) alternatingNullValues(createTestMap(9, 3, 4, 0, 8, 0, 6, 5)); + Map[] expectedValues = alternatingNullValues(createTestMap(9, 3, 4, 0, 8, 0, 6, 5)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); Block block = blockBuilder.build(); assertEquals(block.getPositionCount(), expectedValues.length); diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestRowBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestRowBlock.java index d23ad77873c52..7707409e4e99d 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestRowBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestRowBlock.java @@ -49,14 +49,14 @@ void testWithVarcharBigint() List[] testRows = generateTestRows(fieldTypes, 100); testWith(fieldTypes, testRows); - testWith(fieldTypes, (List[]) alternatingNullValues(testRows)); + testWith(fieldTypes, alternatingNullValues(testRows)); } @Test public void testEstimatedDataSizeForStats() { List fieldTypes = ImmutableList.of(VARCHAR, BIGINT); - List[] expectedValues = (List[]) alternatingNullValues(generateTestRows(fieldTypes, 100)); + List[] expectedValues = alternatingNullValues(generateTestRows(fieldTypes, 100)); BlockBuilder blockBuilder = createBlockBuilderWithValues(fieldTypes, expectedValues); Block block = blockBuilder.build(); assertEquals(block.getPositionCount(), expectedValues.length); @@ -88,13 +88,13 @@ public void testCompactBlock() Block incompactFiledBlock2 = new ByteArrayBlock(5, Optional.empty(), createExpectedValue(6).getBytes()); boolean[] rowIsNull = {false, true, false, false, false, false}; - assertCompact(fromFieldBlocks(new boolean[0], new Block[] {emptyBlock, emptyBlock})); - assertCompact(fromFieldBlocks(rowIsNull, new Block[] {compactFieldBlock1, compactFieldBlock2})); + assertCompact(fromFieldBlocks(0, Optional.empty(), new Block[] {emptyBlock, emptyBlock})); + assertCompact(fromFieldBlocks(rowIsNull.length, Optional.of(rowIsNull), new Block[] {compactFieldBlock1, compactFieldBlock2})); // TODO: add test case for a sliced RowBlock // underlying field blocks are not compact - testIncompactBlock(fromFieldBlocks(rowIsNull, new Block[] {incompactFiledBlock1, incompactFiledBlock2})); - testIncompactBlock(fromFieldBlocks(rowIsNull, new Block[] {incompactFiledBlock1, incompactFiledBlock2})); + testIncompactBlock(fromFieldBlocks(rowIsNull.length, Optional.of(rowIsNull), new Block[] {incompactFiledBlock1, incompactFiledBlock2})); + testIncompactBlock(fromFieldBlocks(rowIsNull.length, Optional.of(rowIsNull), new Block[] {incompactFiledBlock1, incompactFiledBlock2})); } private void testWith(List fieldTypes, List[] expectedValues) diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestShortArrayBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestShortArrayBlock.java index 6cc6ea66940ab..cb8b145e04e7e 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestShortArrayBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestShortArrayBlock.java @@ -33,13 +33,13 @@ public void test() { Slice[] expectedValues = createTestValue(17); assertFixedWithValues(expectedValues); - assertFixedWithValues((Slice[]) alternatingNullValues(expectedValues)); + assertFixedWithValues(alternatingNullValues(expectedValues)); } @Test public void testCopyPositions() { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createTestValue(17)); + Slice[] expectedValues = alternatingNullValues(createTestValue(17)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } diff --git a/presto-main/src/test/java/com/facebook/presto/block/TestVariableWidthBlock.java b/presto-main/src/test/java/com/facebook/presto/block/TestVariableWidthBlock.java index 3fd52d8e3e5fd..4e8f3f334bc0d 100644 --- a/presto-main/src/test/java/com/facebook/presto/block/TestVariableWidthBlock.java +++ b/presto-main/src/test/java/com/facebook/presto/block/TestVariableWidthBlock.java @@ -41,7 +41,7 @@ public void test() { Slice[] expectedValues = createExpectedValues(100); assertVariableWithValues(expectedValues); - assertVariableWithValues((Slice[]) alternatingNullValues(expectedValues)); + assertVariableWithValues(alternatingNullValues(expectedValues)); } @Test @@ -58,7 +58,7 @@ public void testCopyRegion() @Test public void testCopyPositions() { - Slice[] expectedValues = (Slice[]) alternatingNullValues(createExpectedValues(100)); + Slice[] expectedValues = alternatingNullValues(createExpectedValues(100)); BlockBuilder blockBuilder = createBlockBuilderWithValues(expectedValues); assertBlockFilteredPositions(expectedValues, blockBuilder.build(), () -> blockBuilder.newBlockBuilderLike(null), 0, 2, 4, 6, 7, 9, 10, 16); } diff --git a/presto-main/src/test/java/com/facebook/presto/connector/MockConnectorFactory.java b/presto-main/src/test/java/com/facebook/presto/connector/MockConnectorFactory.java index 11a2887de45a6..3dba03c08a61a 100644 --- a/presto-main/src/test/java/com/facebook/presto/connector/MockConnectorFactory.java +++ b/presto-main/src/test/java/com/facebook/presto/connector/MockConnectorFactory.java @@ -87,7 +87,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { return new MockConnector(context, listSchemaNames, listTables, getViews, getColumnHandles); } diff --git a/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorAssertion.java b/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorAssertion.java index 8f6f89ccd167e..1455302b94eba 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorAssertion.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorAssertion.java @@ -26,7 +26,6 @@ import java.util.Optional; import java.util.function.Consumer; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -49,7 +48,7 @@ public StatsCalculatorAssertion(StatsCalculator statsCalculator, Session session this.types = requireNonNull(types, "types is null"); sourcesStats = new HashMap<>(); - planNode.getSources().forEach(child -> sourcesStats.put(child, UNKNOWN_STATS)); + planNode.getSources().forEach(child -> sourcesStats.put(child, PlanNodeStatsEstimate.unknown())); } public StatsCalculatorAssertion withSourceStats(PlanNodeStatsEstimate sourceStats) @@ -72,6 +71,12 @@ public StatsCalculatorAssertion withSourceStats(PlanNodeId planNodeId, PlanNodeS return this; } + public StatsCalculatorAssertion withSourceStats(Map stats) + { + sourcesStats.putAll(stats); + return this; + } + public StatsCalculatorAssertion check(Consumer statisticsAssertionConsumer) { PlanNodeStatsEstimate statsEstimate = statsCalculator.calculateStats(planNode, this::getSourceStats, noLookup(), session, types); diff --git a/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorTester.java b/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorTester.java index 84fdc6458fc18..3bc0e89582928 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorTester.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/StatsCalculatorTester.java @@ -36,7 +36,12 @@ public class StatsCalculatorTester public StatsCalculatorTester() { - this(createQueryRunner()); + this(testSessionBuilder().build()); + } + + public StatsCalculatorTester(Session session) + { + this(createQueryRunner(session)); } private StatsCalculatorTester(LocalQueryRunner queryRunner) @@ -47,10 +52,8 @@ private StatsCalculatorTester(LocalQueryRunner queryRunner) this.queryRunner = queryRunner; } - private static LocalQueryRunner createQueryRunner() + private static LocalQueryRunner createQueryRunner(Session session) { - Session session = testSessionBuilder().build(); - LocalQueryRunner queryRunner = new LocalQueryRunner(session); queryRunner.createCatalog(session.getCatalog().get(), new TpchConnectorFactory(1), diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestAggregationStatsRule.java b/presto-main/src/test/java/com/facebook/presto/cost/TestAggregationStatsRule.java index 132ab2d722c5c..042ccb2231bbb 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestAggregationStatsRule.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestAggregationStatsRule.java @@ -34,7 +34,12 @@ public void testAggregationWhenAllStatisticsAreKnown() .lowValue(10) .highValue(15) .distinctValuesCount(4) - .nullsFraction(0.2)); + .nullsFraction(0.2)) + .symbolStats("y", symbolStatsAssertion -> symbolStatsAssertion + .lowValue(0) + .highValue(3) + .distinctValuesCount(3) + .nullsFraction(0)); testAggregation( SymbolStatsEstimate.builder() @@ -56,10 +61,13 @@ public void testAggregationWhenAllStatisticsAreKnown() Consumer outputRowsCountAndZStatsAreNotFullyCalculated = check -> check .outputRowsCountUnknown() .symbolStats("z", symbolStatsAssertion -> symbolStatsAssertion - .lowValue(10) - .highValue(15) + .unknownRange() .distinctValuesCountUnknown() - .nullsFractionUnknown()); + .nullsFractionUnknown()) + .symbolStats("y", symbolStatsAssertion -> symbolStatsAssertion + .unknownRange() + .nullsFractionUnknown() + .distinctValuesCountUnknown()); testAggregation( SymbolStatsEstimate.builder() @@ -122,12 +130,7 @@ private StatsCalculatorAssertion testAggregation(SymbolStatsEstimate zStats) .lowValueUnknown() .highValueUnknown() .distinctValuesCountUnknown() - .nullsFractionUnknown()) - .symbolStats("y", symbolStatsAssertion -> symbolStatsAssertion - .lowValue(0) - .highValue(3) - .distinctValuesCount(3) - .nullsFraction(0))); + .nullsFractionUnknown())); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestComparisonStatsCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestComparisonStatsCalculator.java index cad46b31f3638..d0586f1390fa7 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestComparisonStatsCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestComparisonStatsCalculator.java @@ -581,61 +581,61 @@ public void symbolToSymbolEqualStats() .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // One symbol's range is within the other's - rowCount = 4.6875; + rowCount = 9.375; assertCalculate(new ComparisonExpression(EQUAL, new SymbolReference("x"), new SymbolReference("y"))) .outputRowsCount(rowCount) .symbolStats("x", symbolAssert -> { symbolAssert.averageRowSize(4) .lowValue(0) .highValue(5) - .distinctValuesCount(4.6875 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(9.375 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("y", symbolAssert -> { symbolAssert.averageRowSize(4) .lowValue(0) .highValue(5) - .distinctValuesCount(4.6875 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(9.375 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // Partially overlapping ranges - rowCount = 8.4375; + rowCount = 16.875; assertCalculate(new ComparisonExpression(EQUAL, new SymbolReference("x"), new SymbolReference("w"))) .outputRowsCount(rowCount) .symbolStats("x", symbolAssert -> { symbolAssert.averageRowSize(6) .lowValue(0) .highValue(10) - .distinctValuesCount(8.4375 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(16.875 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("w", symbolAssert -> { symbolAssert.averageRowSize(6) .lowValue(0) .highValue(10) - .distinctValuesCount(8.4375 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(16.875 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // None of the ranges is included in the other, and one symbol has much higher cardinality, so that it has bigger NDV in intersect than the other in total - rowCount = 1.125; + rowCount = 2.25; assertCalculate(new ComparisonExpression(EQUAL, new SymbolReference("x"), new SymbolReference("u"))) .outputRowsCount(rowCount) .symbolStats("x", symbolAssert -> { symbolAssert.averageRowSize(6) .lowValue(0) .highValue(10) - .distinctValuesCount(1.125 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(2.25 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("u", symbolAssert -> { symbolAssert.averageRowSize(6) .lowValue(0) .highValue(10) - .distinctValuesCount(1.125 /* min(rowCount, ndv in intersection */) + .distinctValuesCount(2.25 /* min(rowCount, ndv in intersection */) .nullsFraction(0); }) .symbolStats("z", equalTo(capNDV(zStats, rowCount))); @@ -653,7 +653,7 @@ public void symbolToSymbolNotEqual() .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // One symbol's range is within the other's - rowCount = 370.3125; + rowCount = 365.625; assertCalculate(new ComparisonExpression(NOT_EQUAL, new SymbolReference("x"), new SymbolReference("y"))) .outputRowsCount(rowCount) .symbolStats("x", equalTo(capNDV(zeroNullsFraction(xStats), rowCount))) @@ -661,7 +661,7 @@ public void symbolToSymbolNotEqual() .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // Partially overlapping ranges - rowCount = 666.5625; + rowCount = 658.125; assertCalculate(new ComparisonExpression(NOT_EQUAL, new SymbolReference("x"), new SymbolReference("w"))) .outputRowsCount(rowCount) .symbolStats("x", equalTo(capNDV(zeroNullsFraction(xStats), rowCount))) @@ -669,7 +669,7 @@ public void symbolToSymbolNotEqual() .symbolStats("z", equalTo(capNDV(zStats, rowCount))); // None of the ranges is included in the other, and one symbol has much higher cardinality, so that it has bigger NDV in intersect than the other in total - rowCount = 673.875; + rowCount = 672.75; assertCalculate(new ComparisonExpression(NOT_EQUAL, new SymbolReference("x"), new SymbolReference("u"))) .outputRowsCount(rowCount) .symbolStats("x", equalTo(capNDV(zeroNullsFraction(xStats), rowCount))) diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestCostCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestCostCalculator.java index 572e42918fed3..ccf24f235e73b 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestCostCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestCostCalculator.java @@ -15,21 +15,38 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.NodeTaskMap; +import com.facebook.presto.execution.QueryManagerConfig; +import com.facebook.presto.execution.scheduler.LegacyNetworkTopology; +import com.facebook.presto.execution.scheduler.NodeScheduler; +import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; +import com.facebook.presto.metadata.CatalogManager; +import com.facebook.presto.metadata.InMemoryNodeManager; +import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.TableHandle; +import com.facebook.presto.metadata.TableLayoutHandle; +import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.analyzer.FeaturesConfig; +import com.facebook.presto.sql.planner.NodePartitioningManager; +import com.facebook.presto.sql.planner.Plan; +import com.facebook.presto.sql.planner.PlanFragmenter; +import com.facebook.presto.sql.planner.SubPlan; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.plan.AggregationNode; import com.facebook.presto.sql.planner.plan.Assignments; +import com.facebook.presto.sql.planner.plan.ExchangeNode; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.planner.plan.TableScanNode; +import com.facebook.presto.sql.planner.plan.UnionNode; import com.facebook.presto.sql.tree.Cast; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; @@ -37,9 +54,15 @@ import com.facebook.presto.sql.tree.SymbolReference; import com.facebook.presto.tpch.TpchColumnHandle; import com.facebook.presto.tpch.TpchTableHandle; +import com.facebook.presto.tpch.TpchTableLayoutHandle; +import com.facebook.presto.transaction.TransactionManager; +import com.facebook.presto.util.FinalizerService; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.Arrays; @@ -49,19 +72,25 @@ import java.util.Optional; import java.util.function.Function; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; -import static com.facebook.presto.cost.PlanNodeCostEstimate.ZERO_COST; import static com.facebook.presto.cost.PlanNodeCostEstimate.cpuCost; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; +import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; -import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.facebook.presto.sql.planner.plan.AggregationNode.singleGroupingSet; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.LOCAL; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.REMOTE; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.partitionedExchange; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.replicatedExchange; +import static com.facebook.presto.testing.TestingSession.createBogusTestingCatalog; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.facebook.presto.tpch.TpchTransactionHandle.INSTANCE; +import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; +import static com.facebook.presto.transaction.TransactionBuilder.transaction; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -72,9 +101,56 @@ public class TestCostCalculator private static final double AVERAGE_ROW_SIZE = 8.; private static final double IS_NULL_OVERHEAD = 9. / AVERAGE_ROW_SIZE; private static final double OFFSET_AND_IS_NULL_OVERHEAD = 13. / AVERAGE_ROW_SIZE; - private final CostCalculator costCalculatorUsingExchanges = new CostCalculatorUsingExchanges(() -> NUMBER_OF_NODES); - private final CostCalculator costCalculatorWithEstimatedExchanges = new CostCalculatorWithEstimatedExchanges(costCalculatorUsingExchanges, () -> NUMBER_OF_NODES); - private Session session = testSessionBuilder().build(); + private CostCalculator costCalculatorUsingExchanges; + private CostCalculator costCalculatorWithEstimatedExchanges; + private PlanFragmenter planFragmenter; + private Session session; + private MetadataManager metadata; + private TransactionManager transactionManager; + private FinalizerService finalizerService; + private NodeScheduler nodeScheduler; + private NodePartitioningManager nodePartitioningManager; + + @BeforeClass + public void setUp() + { + TaskCountEstimator taskCountEstimator = new TaskCountEstimator(() -> NUMBER_OF_NODES); + costCalculatorUsingExchanges = new CostCalculatorUsingExchanges(taskCountEstimator); + costCalculatorWithEstimatedExchanges = new CostCalculatorWithEstimatedExchanges(costCalculatorUsingExchanges, taskCountEstimator); + + session = testSessionBuilder().setCatalog("tpch").build(); + + CatalogManager catalogManager = new CatalogManager(); + catalogManager.registerCatalog(createBogusTestingCatalog("tpch")); + transactionManager = createTestTransactionManager(catalogManager); + metadata = createTestMetadataManager(transactionManager, new FeaturesConfig()); + + finalizerService = new FinalizerService(); + finalizerService.start(); + nodeScheduler = new NodeScheduler( + new LegacyNetworkTopology(), + new InMemoryNodeManager(), + new NodeSchedulerConfig().setIncludeCoordinator(true), + new NodeTaskMap(finalizerService)); + nodePartitioningManager = new NodePartitioningManager(nodeScheduler); + planFragmenter = new PlanFragmenter(metadata, nodePartitioningManager, new QueryManagerConfig()); + } + + @AfterClass(alwaysRun = true) + public void tearDown() + { + costCalculatorUsingExchanges = null; + costCalculatorWithEstimatedExchanges = null; + planFragmenter = null; + session = null; + transactionManager = null; + metadata = null; + finalizerService.destroy(); + finalizerService = null; + nodeScheduler.stop(); + nodeScheduler = null; + nodePartitioningManager = null; + } @Test public void testTableScan() @@ -92,6 +168,11 @@ public void testTableScan() .memory(0) .network(0); + assertCostFragmentedPlan(tableScan, ImmutableMap.of(), ImmutableMap.of("ts", statsEstimate(tableScan, 1000)), types) + .cpu(1000 * IS_NULL_OVERHEAD) + .memory(0) + .network(0); + assertCostHasUnknownComponentsForUnknownStats(tableScan, types); } @@ -118,6 +199,11 @@ public void testProject() .memory(0) .network(0); + assertCostFragmentedPlan(project, costs, stats, types) + .cpu(1000 + 4000 * OFFSET_AND_IS_NULL_OVERHEAD) + .memory(0) + .network(0); + assertCostHasUnknownComponentsForUnknownStats(project, types); } @@ -155,6 +241,11 @@ public void testRepartitionedJoin() .memory(1000 * IS_NULL_OVERHEAD) .network((6000 + 1000) * IS_NULL_OVERHEAD); + assertCostFragmentedPlan(join, costs, stats, types) + .cpu(6000 + 1000 + (12000 + 6000 + 1000) * IS_NULL_OVERHEAD) + .memory(1000 * IS_NULL_OVERHEAD) + .network(0); + assertCostHasUnknownComponentsForUnknownStats(join, types); } @@ -193,6 +284,11 @@ public void testReplicatedJoin() .memory(1000 * NUMBER_OF_NODES * IS_NULL_OVERHEAD) .network(1000 * NUMBER_OF_NODES * IS_NULL_OVERHEAD); + assertCostFragmentedPlan(join, costs, stats, types) + .cpu(1000 + 6000 + (12000 + 6000 + 10000 + 1000 * (NUMBER_OF_NODES - 1)) * IS_NULL_OVERHEAD) + .memory(1000 * NUMBER_OF_NODES * IS_NULL_OVERHEAD) + .network(0); + assertCostHasUnknownComponentsForUnknownStats(join, types); } @@ -220,9 +316,104 @@ public void testAggregation() .memory(13 * IS_NULL_OVERHEAD) .network(6000 * IS_NULL_OVERHEAD); + assertCostFragmentedPlan(aggregation, costs, stats, types) + .cpu(6000 + 6000 * IS_NULL_OVERHEAD) + .memory(13 * IS_NULL_OVERHEAD) + .network(0 * IS_NULL_OVERHEAD); + assertCostHasUnknownComponentsForUnknownStats(aggregation, types); } + @Test + public void testRepartitionedJoinWithExchange() + { + TableScanNode ts1 = tableScan("ts1", "orderkey"); + TableScanNode ts2 = tableScan("ts2", "orderkey_0"); + ExchangeNode remoteExchange1 = partitionedExchange(new PlanNodeId("re1"), REMOTE, ts1, ImmutableList.of(new Symbol("orderkey")), Optional.empty()); + ExchangeNode remoteExchange2 = partitionedExchange(new PlanNodeId("re2"), REMOTE, ts2, ImmutableList.of(new Symbol("orderkey_0")), Optional.empty()); + ExchangeNode localExchange = partitionedExchange(new PlanNodeId("le"), LOCAL, remoteExchange2, ImmutableList.of(new Symbol("orderkey_0")), Optional.empty()); + + JoinNode join = join("join", + remoteExchange1, + localExchange, + JoinNode.DistributionType.PARTITIONED, + "orderkey", + "orderkey_0"); + + Map stats = ImmutableMap.builder() + .put("join", statsEstimate(join, 12000)) + .put("re1", statsEstimate(remoteExchange1, 10000)) + .put("re2", statsEstimate(remoteExchange2, 10000)) + .put("le", statsEstimate(localExchange, 6000)) + .put("ts1", statsEstimate(ts1, 6000)) + .put("ts2", statsEstimate(ts2, 1000)) + .build(); + Map types = ImmutableMap.of( + "orderkey", BIGINT, + "orderkey_0", BIGINT); + + assertFragmentedEqualsUnfragmented(join, stats, types); + } + + @Test + public void testReplicatedJoinWithExchange() + { + TableScanNode ts1 = tableScan("ts1", "orderkey"); + TableScanNode ts2 = tableScan("ts2", "orderkey_0"); + ExchangeNode remoteExchange2 = replicatedExchange(new PlanNodeId("re2"), REMOTE, ts2); + ExchangeNode localExchange = partitionedExchange(new PlanNodeId("le"), LOCAL, remoteExchange2, ImmutableList.of(new Symbol("orderkey_0")), Optional.empty()); + + JoinNode join = join("join", + ts1, + localExchange, + JoinNode.DistributionType.REPLICATED, + "orderkey", + "orderkey_0"); + + Map stats = ImmutableMap.builder() + .put("join", statsEstimate(join, 12000)) + .put("re2", statsEstimate(remoteExchange2, 10000)) + .put("le", statsEstimate(localExchange, 6000)) + .put("ts1", statsEstimate(ts1, 6000)) + .put("ts2", statsEstimate(ts2, 1000)) + .build(); + Map types = ImmutableMap.of( + "orderkey", BIGINT, + "orderkey_0", BIGINT); + + assertFragmentedEqualsUnfragmented(join, stats, types); + } + + @Test + public void testUnion() + { + TableScanNode ts1 = tableScan("ts1", "orderkey"); + TableScanNode ts2 = tableScan("ts2", "orderkey_0"); + ImmutableListMultimap.Builder outputMappings = ImmutableListMultimap.builder(); + outputMappings.put(new Symbol("orderkey_1"), new Symbol("orderkey")); + outputMappings.put(new Symbol("orderkey_1"), new Symbol("orderkey_0")); + UnionNode union = new UnionNode(new PlanNodeId("union"), ImmutableList.of(ts1, ts2), outputMappings.build(), ImmutableList.of(new Symbol("orderkey_1"))); + Map stats = ImmutableMap.of( + "ts1", statsEstimate(ts1, 4000), + "ts2", statsEstimate(ts2, 1000), + "union", statsEstimate(ts1, 5000)); + Map costs = ImmutableMap.of( + "ts1", cpuCost(1000), + "ts2", cpuCost(1000)); + Map types = ImmutableMap.of( + "orderkey", BIGINT, + "orderkey_0", BIGINT, + "orderkey_1", BIGINT); + assertCost(union, costs, stats, types) + .cpu(2000) + .memory(0) + .network(0); + assertCostEstimatedExchanges(union, costs, stats, types) + .cpu(2000) + .memory(0) + .network(5000 * IS_NULL_OVERHEAD); + } + private CostAssertionBuilder assertCost( PlanNode node, Map costs, @@ -241,6 +432,57 @@ private CostAssertionBuilder assertCostEstimatedExchanges( return assertCost(costCalculatorWithEstimatedExchanges, node, costs, stats, types); } + private CostAssertionBuilder assertCostFragmentedPlan( + PlanNode node, + Map costs, + Map stats, + Map types) + { + TypeProvider typeProvider = TypeProvider.copyOf(types.entrySet().stream() + .collect(ImmutableMap.toImmutableMap(entry -> new Symbol(entry.getKey()), Map.Entry::getValue))); + StatsProvider statsProvider = new CachingStatsProvider(statsCalculator(stats), session, typeProvider); + CostProvider costProvider = new TestingCostProvider(costs, costCalculatorUsingExchanges, statsProvider, session, typeProvider); + SubPlan subPlan = fragment(new Plan(node, typeProvider, StatsAndCosts.create(node, statsProvider, costProvider))); + return new CostAssertionBuilder(subPlan.getFragment().getStatsAndCosts().getCosts().getOrDefault(node.getId(), PlanNodeCostEstimate.unknown())); + } + + private static class TestingCostProvider + implements CostProvider + { + private final Map costs; + private final CostCalculator costCalculator; + private final StatsProvider statsProvider; + private final Session session; + private final TypeProvider types; + + private TestingCostProvider(Map costs, CostCalculator costCalculator, StatsProvider statsProvider, Session session, TypeProvider types) + { + this.costs = ImmutableMap.copyOf(requireNonNull(costs, "costs is null")); + this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); + this.statsProvider = requireNonNull(statsProvider, "statsProvider is null"); + this.session = requireNonNull(session, "session is null"); + this.types = requireNonNull(types, "types is null"); + } + + @Override + public PlanNodeCostEstimate getCumulativeCost(PlanNode node) + { + if (costs.containsKey(node.getId().toString())) { + return costs.get(node.getId().toString()); + } + return calculateCumulativeCost(node); + } + + private PlanNodeCostEstimate calculateCumulativeCost(PlanNode node) + { + PlanNodeCostEstimate sourcesCost = node.getSources().stream() + .map(this::getCumulativeCost) + .reduce(PlanNodeCostEstimate.zero(), PlanNodeCostEstimate::add); + + return costCalculator.calculateCost(node, statsProvider, session, types).add(sourcesCost); + } + } + private CostAssertionBuilder assertCost( CostCalculator costCalculator, PlanNode node, @@ -262,19 +504,33 @@ private void assertCostHasUnknownComponentsForUnknownStats(PlanNode node, Map UNKNOWN_COST, - planNode -> UNKNOWN_STATS, + planNode -> PlanNodeCostEstimate.unknown(), + planNode -> PlanNodeStatsEstimate.unknown(), types)) .hasUnknownComponents(); new CostAssertionBuilder(calculateCumulativeCost( costCalculatorWithEstimatedExchanges, node, - planNode -> UNKNOWN_COST, - planNode -> UNKNOWN_STATS, + planNode -> PlanNodeCostEstimate.unknown(), + planNode -> PlanNodeStatsEstimate.unknown(), types)) .hasUnknownComponents(); } + private void assertFragmentedEqualsUnfragmented(PlanNode node, Map stats, Map types) + { + StatsCalculator statsCalculator = statsCalculator(stats); + PlanNodeCostEstimate costWithExchanges = calculateCumulativeCost(node, costCalculatorUsingExchanges, statsCalculator, types); + PlanNodeCostEstimate costWithFragments = calculateCumulativeCostFragmentedPlan(node, statsCalculator, types); + assertEquals(costWithExchanges, costWithFragments); + } + + private StatsCalculator statsCalculator(Map stats) + { + return (node, sourceStats, lookup, session, types) -> + requireNonNull(stats.get(node.getId().toString()), "no stats for node"); + } + private PlanNodeCostEstimate calculateCumulativeCost( CostCalculator costCalculator, PlanNode node, @@ -285,17 +541,35 @@ private PlanNodeCostEstimate calculateCumulativeCost( PlanNodeCostEstimate localCost = costCalculator.calculateCost( node, planNode -> requireNonNull(stats.apply(planNode), "no stats for node"), - noLookup(), session, TypeProvider.copyOf(types.entrySet().stream() .collect(ImmutableMap.toImmutableMap(entry -> new Symbol(entry.getKey()), Map.Entry::getValue)))); PlanNodeCostEstimate sourcesCost = node.getSources().stream() - .map(source -> requireNonNull(costs.apply(source), "no cost for source")) - .reduce(ZERO_COST, PlanNodeCostEstimate::add); + .map(source -> requireNonNull(costs.apply(source), format("no cost for source: %s", source.getId()))) + .reduce(PlanNodeCostEstimate.zero(), PlanNodeCostEstimate::add); return sourcesCost.add(localCost); } + private PlanNodeCostEstimate calculateCumulativeCost(PlanNode node, CostCalculator costCalculator, StatsCalculator statsCalculator, Map types) + { + TypeProvider typeProvider = TypeProvider.copyOf(types.entrySet().stream() + .collect(ImmutableMap.toImmutableMap(entry -> new Symbol(entry.getKey()), Map.Entry::getValue))); + StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, typeProvider); + CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.empty(), session, typeProvider); + return costProvider.getCumulativeCost(node); + } + + private PlanNodeCostEstimate calculateCumulativeCostFragmentedPlan(PlanNode node, StatsCalculator statsCalculator, Map types) + { + TypeProvider typeProvider = TypeProvider.copyOf(types.entrySet().stream() + .collect(ImmutableMap.toImmutableMap(entry -> new Symbol(entry.getKey()), Map.Entry::getValue))); + StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, typeProvider); + CostProvider costProvider = new CachingCostProvider(costCalculatorUsingExchanges, statsProvider, Optional.empty(), session, typeProvider); + SubPlan subPlan = fragment(new Plan(node, typeProvider, StatsAndCosts.create(node, statsProvider, costProvider))); + return subPlan.getFragment().getStatsAndCosts().getCosts().getOrDefault(node.getId(), PlanNodeCostEstimate.unknown()); + } + private static class CostAssertionBuilder { private final PlanNodeCostEstimate actual; @@ -364,14 +638,15 @@ private TableScanNode tableScan(String id, String... symbols) assignments.put(symbol, new TpchColumnHandle("orderkey", BIGINT)); } + TpchTableHandle tableHandle = new TpchTableHandle("orders", 1.0); return new TableScanNode( new PlanNodeId(id), - new TableHandle(new ConnectorId("tpch"), new TpchTableHandle("local", "orders", 1.0)), + new TableHandle(new ConnectorId("tpch"), new TpchTableHandle("orders", 1.0)), symbolsList, assignments.build(), - Optional.empty(), + Optional.of(new TableLayoutHandle(new ConnectorId("tpch"), INSTANCE, new TpchTableLayoutHandle(tableHandle, TupleDomain.all()))), TupleDomain.all(), - null); + TupleDomain.all()); } private PlanNode project(String id, PlanNode source, String symbol, Expression expression) @@ -428,4 +703,20 @@ private JoinNode join(String planNodeId, PlanNode left, PlanNode right, JoinNode Optional.empty(), Optional.of(distributionType)); } + + private SubPlan fragment(Plan plan) + { + return inTransaction(session -> planFragmenter.createSubPlans(session, plan, false)); + } + + private T inTransaction(Function transactionSessionConsumer) + { + return transaction(transactionManager, new AllowAllAccessControl()) + .singleStatement() + .execute(session, session -> { + // metadata.getCatalogHandle() registers the catalog for the transaction + session.getCatalog().ifPresent(catalog -> metadata.getCatalogHandle(session, catalog)); + return transactionSessionConsumer.apply(session); + }); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestCostComparator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestCostComparator.java index 3ddf807c22069..a8c961c3924bc 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestCostComparator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestCostComparator.java @@ -16,8 +16,6 @@ import com.facebook.presto.Session; import org.testng.annotations.Test; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; -import static com.facebook.presto.cost.PlanNodeCostEstimate.ZERO_COST; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.google.common.base.Preconditions.checkState; import static org.testng.Assert.assertThrows; @@ -71,9 +69,9 @@ public void testUnknownCost() { CostComparator costComparator = new CostComparator(1.0, 1.0, 1.0); Session session = testSessionBuilder().build(); - assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, ZERO_COST, UNKNOWN_COST)); - assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, UNKNOWN_COST, ZERO_COST)); - assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, UNKNOWN_COST, UNKNOWN_COST)); + assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanNodeCostEstimate.zero(), PlanNodeCostEstimate.unknown())); + assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanNodeCostEstimate.unknown(), PlanNodeCostEstimate.zero())); + assertThrows(IllegalArgumentException.class, () -> costComparator.compare(session, PlanNodeCostEstimate.unknown(), PlanNodeCostEstimate.unknown())); } private static class CostComparisonAssertion diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestExchangeStatsRule.java b/presto-main/src/test/java/com/facebook/presto/cost/TestExchangeStatsRule.java index cd451c7961722..9dbc189f723fc 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestExchangeStatsRule.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestExchangeStatsRule.java @@ -97,17 +97,17 @@ public void testExchange() .symbolStats("o1", assertion -> assertion .lowValue(1) .highValue(20) - .distinctValuesCount(5) + .distinctValuesCountUnknown() .nullsFraction(0.3666666)) .symbolStats("o2", assertion -> assertion .lowValue(0) .highValue(7) - .distinctValuesCount(3) + .distinctValuesCount(4) .nullsFractionUnknown()) .symbolStats("o3", assertion -> assertion .lowValueUnknown() .highValueUnknown() - .distinctValuesCount(4.0) + .distinctValuesCount(6) .nullsFraction(0.1666667)) .symbolStats("o4", assertion -> assertion .lowValue(10) diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsCalculator.java index 57391697b84ef..6284bb8a5c85c 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsCalculator.java @@ -37,6 +37,14 @@ public class TestFilterStatsCalculator { private static final VarcharType MEDIUM_VARCHAR_TYPE = VarcharType.createVarcharType(100); + private SymbolStatsEstimate xStats; + private SymbolStatsEstimate yStats; + private SymbolStatsEstimate zStats; + private SymbolStatsEstimate leftOpenStats; + private SymbolStatsEstimate rightOpenStats; + private SymbolStatsEstimate unknownRangeStats; + private SymbolStatsEstimate emptyRangeStats; + private SymbolStatsEstimate mediumVarcharStats; private FilterStatsCalculator statsCalculator; private PlanNodeStatsEstimate standardInputStatistics; private TypeProvider standardTypes; @@ -46,56 +54,56 @@ public class TestFilterStatsCalculator public void setUp() throws Exception { - SymbolStatsEstimate xStats = SymbolStatsEstimate.builder() + xStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(40.0) .setLowValue(-10.0) .setHighValue(10.0) .setNullsFraction(0.25) .build(); - SymbolStatsEstimate yStats = SymbolStatsEstimate.builder() + yStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(20.0) .setLowValue(0.0) .setHighValue(5.0) .setNullsFraction(0.5) .build(); - SymbolStatsEstimate zStats = SymbolStatsEstimate.builder() + zStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(5.0) .setLowValue(-100.0) .setHighValue(100.0) .setNullsFraction(0.1) .build(); - SymbolStatsEstimate leftOpenStats = SymbolStatsEstimate.builder() + leftOpenStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(50.0) .setLowValue(NEGATIVE_INFINITY) .setHighValue(15.0) .setNullsFraction(0.1) .build(); - SymbolStatsEstimate rightOpenStats = SymbolStatsEstimate.builder() + rightOpenStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(50.0) .setLowValue(-15.0) .setHighValue(POSITIVE_INFINITY) .setNullsFraction(0.1) .build(); - SymbolStatsEstimate unknownRangeStats = SymbolStatsEstimate.builder() + unknownRangeStats = SymbolStatsEstimate.builder() .setAverageRowSize(4.0) .setDistinctValuesCount(50.0) .setLowValue(NEGATIVE_INFINITY) .setHighValue(POSITIVE_INFINITY) .setNullsFraction(0.1) .build(); - SymbolStatsEstimate emptyRangeStats = SymbolStatsEstimate.builder() + emptyRangeStats = SymbolStatsEstimate.builder() .setAverageRowSize(0.0) .setDistinctValuesCount(0.0) .setLowValue(NaN) .setHighValue(NaN) .setNullsFraction(NaN) .build(); - SymbolStatsEstimate mediumVarcharStats = SymbolStatsEstimate.builder() + mediumVarcharStats = SymbolStatsEstimate.builder() .setAverageRowSize(85.0) .setDistinctValuesCount(165) .setLowValue(NEGATIVE_INFINITY) @@ -163,26 +171,28 @@ public void testComparison() assertExpression("-x > -3e0") .outputRowsCount(lessThan3Rows); - for (String minusThree : ImmutableList.of("DECIMAL '-3'", "-3e0", "(4e0-7e0)", "CAST(-3 AS DECIMAL)")) { - System.out.println(minusThree); - - assertExpression("x = " + minusThree) - .outputRowsCount(18.75) - .symbolStats(new Symbol("x"), symbolAssert -> - symbolAssert.averageRowSize(4.0) - .lowValue(-3) - .highValue(-3) - .distinctValuesCount(1) - .nullsFraction(0.0)); - - assertExpression("x < " + minusThree) - .outputRowsCount(262.5) - .symbolStats(new Symbol("x"), symbolAssert -> - symbolAssert.averageRowSize(4.0) - .lowValue(-10) - .highValue(-3) - .distinctValuesCount(14) - .nullsFraction(0.0)); + for (String minusThree : ImmutableList.of("DECIMAL '-3'", "-3e0", "(4e0-7e0)", "CAST(-3 AS DECIMAL(7,3))"/*, "CAST('1' AS BIGINT) - 4"*/)) { + for (String xEquals : ImmutableList.of("x = %s", "%s = x", "COALESCE(x * CAST(NULL AS BIGINT), x) = %s", "%s = CAST(x AS DOUBLE)")) { + assertExpression(format(xEquals, minusThree)) + .outputRowsCount(18.75) + .symbolStats(new Symbol("x"), symbolAssert -> + symbolAssert.averageRowSize(4.0) + .lowValue(-3) + .highValue(-3) + .distinctValuesCount(1) + .nullsFraction(0.0)); + } + + for (String xLessThan : ImmutableList.of("x < %s", "%s > x", "%s > CAST(x AS DOUBLE)")) { + assertExpression(format(xLessThan, minusThree)) + .outputRowsCount(262.5) + .symbolStats(new Symbol("x"), symbolAssert -> + symbolAssert.averageRowSize(4.0) + .lowValue(-10) + .highValue(-3) + .distinctValuesCount(14) + .nullsFraction(0.0)); + } } } @@ -215,15 +225,27 @@ public void testOrStats() .highValue(3) .distinctValuesCount(2) .nullsFraction(0)); + + assertExpression("x = 1e0 OR 'a' = 'b' OR x = 3e0") + .outputRowsCount(37.5) + .symbolStats(new Symbol("x"), symbolAssert -> + symbolAssert.averageRowSize(4.0) + .lowValue(1) + .highValue(3) + .distinctValuesCount(2) + .nullsFraction(0)); + + assertExpression("x = 1e0 OR (CAST('b' AS VARCHAR(3)) IN (CAST('a' AS VARCHAR(3)), CAST('b' AS VARCHAR(3)))) OR x = 3e0") + .equalTo(standardInputStatistics); } @Test public void testUnsupportedExpression() { assertExpression("sin(x)") - .outputRowsCount(900); + .outputRowsCountUnknown(); assertExpression("x = sin(x)") - .outputRowsCount(900); + .outputRowsCountUnknown(); } @Test @@ -264,12 +286,10 @@ public void testAndStats() // both arguments unknown assertExpression("json_array_contains(JSON '[11]', x) AND json_array_contains(JSON '[13]', x)") - .outputRowsCount(900) - .symbolStats(new Symbol("x"), symbolAssert -> - symbolAssert.lowValue(-10) - .highValue(10) - .distinctValuesCount(40) - .nullsFraction(0.25)); + .outputRowsCountUnknown(); + + assertExpression("'a' IN ('b', 'c') AND unknownRange = 3e0") + .outputRowsCount(0); } @Test @@ -277,21 +297,13 @@ public void testNotStats() { assertExpression("NOT(x < 0e0)") .outputRowsCount(625) // FIXME - nulls shouldn't be restored - .symbolStats(new Symbol("x"), symbolAssert -> - symbolAssert.averageRowSize(4.0) - .lowValue(0.0) - .highValue(10.0) - .distinctValuesCount(20.0) - .nullsFraction(0.4)); // FIXME - nulls shouldn't be restored - - assertExpression("NOT(json_array_contains(JSON '[]', x))") - .outputRowsCount(900) .symbolStats(new Symbol("x"), symbolAssert -> symbolAssert.averageRowSize(4.0) .lowValue(-10.0) .highValue(10.0) - .distinctValuesCount(40.0) - .nullsFraction(0.25)); + .distinctValuesCount(20.0) + .nullsFraction(0.4)) // FIXME - nulls shouldn't be restored + .symbolStats(new Symbol("y"), symbolAssert -> symbolAssert.isEqualTo(yStats)); assertExpression("NOT(x IS NULL)") .outputRowsCount(750) @@ -300,7 +312,11 @@ public void testNotStats() .lowValue(-10.0) .highValue(10.0) .distinctValuesCount(40.0) - .nullsFraction(0)); + .nullsFraction(0)) + .symbolStats(new Symbol("y"), symbolAssert -> symbolAssert.isEqualTo(yStats)); + + assertExpression("NOT(json_array_contains(JSON '[]', x))") + .outputRowsCountUnknown(); } @Test @@ -308,11 +324,10 @@ public void testIsNullFilter() { assertExpression("x IS NULL") .outputRowsCount(250.0) - .symbolStats(new Symbol("x"), symbolStats -> { - symbolStats.distinctValuesCount(0) - .emptyRange() - .nullsFraction(1.0); - }); + .symbolStats(new Symbol("x"), symbolStats -> + symbolStats.distinctValuesCount(0) + .emptyRange() + .nullsFraction(1.0)); assertExpression("emptyRange IS NULL") .outputRowsCount(1000.0) @@ -324,12 +339,11 @@ public void testIsNotNullFilter() { assertExpression("x IS NOT NULL") .outputRowsCount(750.0) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(40.0) - .lowValue(-10.0) - .highValue(10.0) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(40.0) + .lowValue(-10.0) + .highValue(10.0) + .nullsFraction(0.0)); assertExpression("emptyRange IS NOT NULL") .outputRowsCount(0.0) @@ -342,70 +356,63 @@ public void testBetweenOperatorFilter() // Only right side cut assertExpression("x BETWEEN 7.5e0 AND 12e0") .outputRowsCount(93.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(7.5) - .highValue(10.0) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(7.5) + .highValue(10.0) + .nullsFraction(0.0)); // Only left side cut assertExpression("x BETWEEN DOUBLE '-12' AND DOUBLE '-7.5'") .outputRowsCount(93.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(-10) - .highValue(-7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(-10) + .highValue(-7.5) + .nullsFraction(0.0)); assertExpression("x BETWEEN -12e0 AND -7.5e0") .outputRowsCount(93.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(-10) - .highValue(-7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(-10) + .highValue(-7.5) + .nullsFraction(0.0)); // Both sides cut assertExpression("x BETWEEN DOUBLE '-2.5' AND 2.5e0") .outputRowsCount(187.5) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(10.0) - .lowValue(-2.5) - .highValue(2.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(10.0) + .lowValue(-2.5) + .highValue(2.5) + .nullsFraction(0.0)); // Both sides cut unknownRange assertExpression("unknownRange BETWEEN 2.72e0 AND 3.14e0") .outputRowsCount(112.5) - .symbolStats("unknownRange", symbolStats -> { - symbolStats.distinctValuesCount(6.25) - .lowValue(2.72) - .highValue(3.14) - .nullsFraction(0.0); - }); + .symbolStats("unknownRange", symbolStats -> + symbolStats.distinctValuesCount(6.25) + .lowValue(2.72) + .highValue(3.14) + .nullsFraction(0.0)); // Left side open, cut on open side assertExpression("leftOpen BETWEEN DOUBLE '-10' AND 10e0") .outputRowsCount(180.0) - .symbolStats("leftOpen", symbolStats -> { - symbolStats.distinctValuesCount(10.0) - .lowValue(-10.0) - .highValue(10.0) - .nullsFraction(0.0); - }); + .symbolStats("leftOpen", symbolStats -> + symbolStats.distinctValuesCount(10.0) + .lowValue(-10.0) + .highValue(10.0) + .nullsFraction(0.0)); // Right side open, cut on open side assertExpression("rightOpen BETWEEN DOUBLE '-10' AND 10e0") .outputRowsCount(180.0) - .symbolStats("rightOpen", symbolStats -> { - symbolStats.distinctValuesCount(10.0) - .lowValue(-10.0) - .highValue(10.0) - .nullsFraction(0.0); - }); + .symbolStats("rightOpen", symbolStats -> + symbolStats.distinctValuesCount(10.0) + .lowValue(-10.0) + .highValue(10.0) + .nullsFraction(0.0)); // Filter all assertExpression("y BETWEEN 27.5e0 AND 107e0") @@ -415,22 +422,25 @@ public void testBetweenOperatorFilter() // Filter nothing assertExpression("y BETWEEN DOUBLE '-100' AND 100e0") .outputRowsCount(500.0) - .symbolStats("y", symbolStats -> { - symbolStats.distinctValuesCount(20.0) - .lowValue(0.0) - .highValue(5.0) - .nullsFraction(0.0); - }); + .symbolStats("y", symbolStats -> + symbolStats.distinctValuesCount(20.0) + .lowValue(0.0) + .highValue(5.0) + .nullsFraction(0.0)); // Filter non exact match assertExpression("z BETWEEN DOUBLE '-100' AND 100e0") .outputRowsCount(900.0) - .symbolStats("z", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(-100.0) - .highValue(100.0) - .nullsFraction(0.0); - }); + .symbolStats("z", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(-100.0) + .highValue(100.0) + .nullsFraction(0.0)); + + assertExpression("'a' IN ('a', 'b')").equalTo(standardInputStatistics); + assertExpression("'a' IN ('b', 'c')").outputRowsCount(0); + assertExpression("CAST('b' AS VARCHAR(3)) IN (CAST('a' AS VARCHAR(3)), CAST('b' AS VARCHAR(3)))").equalTo(standardInputStatistics); + assertExpression("CAST('c' AS VARCHAR(3)) IN (CAST('a' AS VARCHAR(3)), CAST('b' AS VARCHAR(3)))").outputRowsCount(0); } @Test @@ -438,14 +448,13 @@ public void testSymbolEqualsSameSymbolFilter() { assertExpression("x = x") .outputRowsCount(750) - .symbolStats("x", symbolStats -> { - SymbolStatsEstimate.builder() - .setAverageRowSize(4.0) - .setDistinctValuesCount(40.0) - .setLowValue(-10.0) - .setHighValue(10.0) - .build(); - }); + .symbolStats("x", symbolStats -> + SymbolStatsEstimate.builder() + .setAverageRowSize(4.0) + .setDistinctValuesCount(40.0) + .setLowValue(-10.0) + .setHighValue(10.0) + .build()); } @Test @@ -454,88 +463,78 @@ public void testInPredicateFilter() // One value in range assertExpression("x IN (7.5e0)") .outputRowsCount(18.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(1.0) - .lowValue(7.5) - .highValue(7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(1.0) + .lowValue(7.5) + .highValue(7.5) + .nullsFraction(0.0)); assertExpression("x IN (DOUBLE '-7.5')") .outputRowsCount(18.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(1.0) - .lowValue(-7.5) - .highValue(-7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(1.0) + .lowValue(-7.5) + .highValue(-7.5) + .nullsFraction(0.0)); assertExpression("x IN (BIGINT '2' + 5.5e0)") .outputRowsCount(18.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(1.0) - .lowValue(7.5) - .highValue(7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(1.0) + .lowValue(7.5) + .highValue(7.5) + .nullsFraction(0.0)); assertExpression("x IN (-7.5e0)") .outputRowsCount(18.75) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(1.0) - .lowValue(-7.5) - .highValue(-7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(1.0) + .lowValue(-7.5) + .highValue(-7.5) + .nullsFraction(0.0)); // Multiple values in range assertExpression("x IN (1.5e0, 2.5e0, 7.5e0)") .outputRowsCount(56.25) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(3.0) - .lowValue(1.5) - .highValue(7.5) - .nullsFraction(0.0); - }) - .symbolStats("y", symbolStats -> { - // Symbol not involved in the comparison should have stats basically unchanged - symbolStats.distinctValuesCount(20.0) - .lowValue(0.0) - .highValue(5) - .nullsFraction(0.5); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(3.0) + .lowValue(1.5) + .highValue(7.5) + .nullsFraction(0.0)) + .symbolStats("y", symbolStats -> + // Symbol not involved in the comparison should have stats basically unchanged + symbolStats.distinctValuesCount(20.0) + .lowValue(0.0) + .highValue(5) + .nullsFraction(0.5)); // Multiple values some in some out of range assertExpression("x IN (DOUBLE '-42', 1.5e0, 2.5e0, 7.5e0, 314e0)") .outputRowsCount(56.25) - .symbolStats("x", symbolStats -> { - symbolStats.distinctValuesCount(3.0) - .lowValue(1.5) - .highValue(7.5) - .nullsFraction(0.0); - }); + .symbolStats("x", symbolStats -> + symbolStats.distinctValuesCount(3.0) + .lowValue(1.5) + .highValue(7.5) + .nullsFraction(0.0)); // Multiple values in unknown range assertExpression("unknownRange IN (DOUBLE '-42', 1.5e0, 2.5e0, 7.5e0, 314e0)") .outputRowsCount(90.0) - .symbolStats("unknownRange", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(-42.0) - .highValue(314.0) - .nullsFraction(0.0); - }); + .symbolStats("unknownRange", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(-42.0) + .highValue(314.0) + .nullsFraction(0.0)); // Casted literals as value assertExpression(format("mediumVarchar IN (CAST('abc' AS %s))", MEDIUM_VARCHAR_TYPE.toString())) .outputRowsCount(4) - .symbolStats("mediumVarchar", symbolStats -> { - symbolStats.distinctValuesCount(1) - .nullsFraction(0.0); - }); + .symbolStats("mediumVarchar", symbolStats -> + symbolStats.distinctValuesCount(1) + .nullsFraction(0.0)); assertExpression(format("mediumVarchar IN (CAST('abc' AS %1$s), CAST('def' AS %1$s))", MEDIUM_VARCHAR_TYPE.toString())) .outputRowsCount(8) - .symbolStats("mediumVarchar", symbolStats -> { - symbolStats.distinctValuesCount(2) - .nullsFraction(0.0); - }); + .symbolStats("mediumVarchar", symbolStats -> + symbolStats.distinctValuesCount(2) + .nullsFraction(0.0)); // No value in range assertExpression("y IN (DOUBLE '-42', 6e0, 31.1341e0, DOUBLE '-0.000000002', 314e0)") @@ -545,22 +544,20 @@ public void testInPredicateFilter() // More values in range than distinct values assertExpression("z IN (DOUBLE '-1', 3.14e0, 0e0, 1e0, 2e0, 3e0, 4e0, 5e0, 6e0, 7e0, 8e0, DOUBLE '-2')") .outputRowsCount(900.0) - .symbolStats("z", symbolStats -> { - symbolStats.distinctValuesCount(5.0) - .lowValue(-2.0) - .highValue(8.0) - .nullsFraction(0.0); - }); + .symbolStats("z", symbolStats -> + symbolStats.distinctValuesCount(5.0) + .lowValue(-2.0) + .highValue(8.0) + .nullsFraction(0.0)); // Values in weird order assertExpression("z IN (DOUBLE '-1', 1e0, 0e0)") .outputRowsCount(540.0) - .symbolStats("z", symbolStats -> { - symbolStats.distinctValuesCount(3.0) - .lowValue(-1.0) - .highValue(1.0) - .nullsFraction(0.0); - }); + .symbolStats("z", symbolStats -> + symbolStats.distinctValuesCount(3.0) + .lowValue(-1.0) + .highValue(1.0) + .nullsFraction(0.0)); } private PlanNodeStatsAssertion assertExpression(String expression) diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsRule.java b/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsRule.java new file mode 100644 index 0000000000000..c790c7f96c9f1 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestFilterStatsRule.java @@ -0,0 +1,217 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.cost; + +import com.facebook.presto.sql.planner.Symbol; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder.expression; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; + +public class TestFilterStatsRule + extends BaseStatsCalculatorTest +{ + public StatsCalculatorTester defaultFilterTester; + + @BeforeClass + public void setupClass() + { + defaultFilterTester = new StatsCalculatorTester( + testSessionBuilder() + .setSystemProperty("default_filter_factor_enabled", "true") + .build()); + } + + @AfterClass(alwaysRun = true) + public void tearDownClass() + { + defaultFilterTester.close(); + defaultFilterTester = null; + } + + @Test + public void testEstimatableFilter() + { + tester().assertStatsFor(pb -> pb + .filter(expression("i1 = 5"), + pb.values(pb.symbol("i1"), pb.symbol("i2"), pb.symbol("i3")))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("i1"), SymbolStatsEstimate.builder() + .setLowValue(1) + .setHighValue(10) + .setDistinctValuesCount(5) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i2"), SymbolStatsEstimate.builder() + .setLowValue(0) + .setHighValue(3) + .setDistinctValuesCount(4) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i3"), SymbolStatsEstimate.builder() + .setLowValue(10) + .setHighValue(15) + .setDistinctValuesCount(4) + .setNullsFraction(0.1) + .build()) + .build()) + .check(check -> check + .outputRowsCount(2) + .symbolStats("i1", assertion -> assertion + .lowValue(5) + .highValue(5) + .distinctValuesCount(1) + .dataSizeUnknown() + .nullsFraction(0)) + .symbolStats("i2", assertion -> assertion + .lowValue(0) + .highValue(3) + .dataSizeUnknown() + .distinctValuesCount(2) + .nullsFraction(0)) + .symbolStats("i3", assertion -> assertion + .lowValue(10) + .highValue(15) + .dataSizeUnknown() + .distinctValuesCount(1.9) + .nullsFraction(0.05))); + + defaultFilterTester.assertStatsFor(pb -> pb + .filter(expression("i1 = 5"), + pb.values(pb.symbol("i1"), pb.symbol("i2"), pb.symbol("i3")))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("i1"), SymbolStatsEstimate.builder() + .setLowValue(1) + .setHighValue(10) + .setDistinctValuesCount(5) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i2"), SymbolStatsEstimate.builder() + .setLowValue(0) + .setHighValue(3) + .setDistinctValuesCount(4) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i3"), SymbolStatsEstimate.builder() + .setLowValue(10) + .setHighValue(15) + .setDistinctValuesCount(4) + .setNullsFraction(0.1) + .build()) + .build()) + .check(check -> check + .outputRowsCount(2) + .symbolStats("i1", assertion -> assertion + .lowValue(5) + .highValue(5) + .distinctValuesCount(1) + .dataSizeUnknown() + .nullsFraction(0)) + .symbolStats("i2", assertion -> assertion + .lowValue(0) + .highValue(3) + .dataSizeUnknown() + .distinctValuesCount(2) + .nullsFraction(0)) + .symbolStats("i3", assertion -> assertion + .lowValue(10) + .highValue(15) + .dataSizeUnknown() + .distinctValuesCount(1.9) + .nullsFraction(0.05))); + } + + @Test + public void testUnestimatableFunction() + { + // can't estimate function and default filter factor is turned off + tester() + .assertStatsFor(pb -> pb + .filter(expression("sin(i1) = 1"), + pb.values(pb.symbol("i1"), pb.symbol("i2"), pb.symbol("i3")))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("i1"), SymbolStatsEstimate.builder() + .setLowValue(1) + .setHighValue(10) + .setDistinctValuesCount(5) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i2"), SymbolStatsEstimate.builder() + .setLowValue(0) + .setHighValue(3) + .setDistinctValuesCount(4) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i3"), SymbolStatsEstimate.builder() + .setLowValue(10) + .setHighValue(15) + .setDistinctValuesCount(4) + .setNullsFraction(0.1) + .build()) + .build()) + .check(check -> check.outputRowsCountUnknown()); + + // can't estimate function, but default filter factor is turned on + defaultFilterTester.assertStatsFor(pb -> pb + .filter(expression("sin(i1) = 1"), + pb.values(pb.symbol("i1"), pb.symbol("i2"), pb.symbol("i3")))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("i1"), SymbolStatsEstimate.builder() + .setLowValue(1) + .setHighValue(10) + .setDistinctValuesCount(5) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i2"), SymbolStatsEstimate.builder() + .setLowValue(0) + .setHighValue(3) + .setDistinctValuesCount(4) + .setNullsFraction(0) + .build()) + .addSymbolStatistics(new Symbol("i3"), SymbolStatsEstimate.builder() + .setLowValue(10) + .setHighValue(15) + .setDistinctValuesCount(4) + .setNullsFraction(0.1) + .build()) + .build()) + .check(check -> check + .outputRowsCount(9) + .symbolStats("i1", assertion -> assertion + .lowValue(1) + .highValue(10) + .dataSizeUnknown() + .distinctValuesCount(5) + .nullsFraction(0)) + .symbolStats("i2", assertion -> assertion + .lowValue(0) + .highValue(3) + .dataSizeUnknown() + .distinctValuesCount(4) + .nullsFraction(0)) + .symbolStats("i3", assertion -> assertion + .lowValue(10) + .highValue(15) + .dataSizeUnknown() + .distinctValuesCount(4) + .nullsFraction(0.1))); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestJoinStatsRule.java b/presto-main/src/test/java/com/facebook/presto/cost/TestJoinStatsRule.java index 674800177db7c..80a002ae9d345 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestJoinStatsRule.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestJoinStatsRule.java @@ -14,12 +14,15 @@ package com.facebook.presto.cost; import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.JoinNode.EquiJoinClause; import com.facebook.presto.sql.tree.ComparisonExpression; import com.facebook.presto.sql.tree.LongLiteral; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import org.testng.annotations.Test; import java.util.Optional; @@ -34,6 +37,7 @@ import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.RIGHT; import static java.lang.Double.NaN; +import static org.testng.Assert.assertEquals; public class TestJoinStatsRule extends BaseStatsCalculatorTest @@ -86,6 +90,14 @@ public class TestJoinStatsRule new FilterStatsCalculator(METADATA, new ScalarStatsCalculator(METADATA), NORMALIZER), NORMALIZER, 1.0); + private static final TypeProvider TYPES = TypeProvider.copyOf(ImmutableMap.builder() + .put(new Symbol(LEFT_JOIN_COLUMN), BIGINT) + .put(new Symbol(LEFT_JOIN_COLUMN_2), DOUBLE) + .put(new Symbol(RIGHT_JOIN_COLUMN), BIGINT) + .put(new Symbol(RIGHT_JOIN_COLUMN_2), DOUBLE) + .put(new Symbol(LEFT_OTHER_COLUMN), DOUBLE) + .put(new Symbol(RIGHT_OTHER_COLUMN), BIGINT) + .build()); @Test public void testStatsForInnerJoin() @@ -182,49 +194,64 @@ public void testStatsForInnerJoinWithTwoEquiClausesAndNonEqualityFunction() @Test public void testJoinComplementStats() { - PlanNodeStatsEstimate joinComplementStats = planNodeStats(LEFT_ROWS_COUNT * (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), + PlanNodeStatsEstimate expected = planNodeStats(LEFT_ROWS_COUNT * (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), symbolStatistics(LEFT_JOIN_COLUMN, 0.0, 20.0, LEFT_JOIN_COLUMN_NULLS / (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), 5), LEFT_OTHER_COLUMN_STATS); - - assertThat(JOIN_STATS_RULE.calculateJoinComplementStats( + PlanNodeStatsEstimate actual = JOIN_STATS_RULE.calculateJoinComplementStats( Optional.empty(), - ImmutableList.of(new JoinNode.EquiJoinClause(new Symbol(LEFT_JOIN_COLUMN), new Symbol(RIGHT_JOIN_COLUMN))), - LEFT_STATS, RIGHT_STATS)).equalTo(joinComplementStats); + ImmutableList.of(new EquiJoinClause(new Symbol(LEFT_JOIN_COLUMN), new Symbol(RIGHT_JOIN_COLUMN))), + LEFT_STATS, + RIGHT_STATS, + TYPES); + assertEquals(actual, expected); } @Test public void testRightJoinComplementStats() { - PlanNodeStatsEstimate joinComplementStats = planNodeStats(RIGHT_ROWS_COUNT * RIGHT_JOIN_COLUMN_NULLS, - symbolStatistics(RIGHT_JOIN_COLUMN, NaN, NaN, 1.0, 0), - RIGHT_OTHER_COLUMN_STATS); - - assertThat(JOIN_STATS_RULE.calculateJoinComplementStats( + PlanNodeStatsEstimate expected = NORMALIZER.normalize( + planNodeStats( + RIGHT_ROWS_COUNT * RIGHT_JOIN_COLUMN_NULLS, + symbolStatistics(RIGHT_JOIN_COLUMN, NaN, NaN, 1.0, 0), + RIGHT_OTHER_COLUMN_STATS), + TYPES); + PlanNodeStatsEstimate actual = JOIN_STATS_RULE.calculateJoinComplementStats( Optional.empty(), - ImmutableList.of(new JoinNode.EquiJoinClause(new Symbol(RIGHT_JOIN_COLUMN), new Symbol(LEFT_JOIN_COLUMN))), - RIGHT_STATS, LEFT_STATS)).equalTo(joinComplementStats); + ImmutableList.of(new EquiJoinClause(new Symbol(RIGHT_JOIN_COLUMN), new Symbol(LEFT_JOIN_COLUMN))), + RIGHT_STATS, + LEFT_STATS, + TYPES); + assertEquals(actual, expected); } @Test public void testLeftJoinComplementStatsWithNoClauses() { - assertThat(JOIN_STATS_RULE.calculateJoinComplementStats( + PlanNodeStatsEstimate expected = NORMALIZER.normalize(LEFT_STATS.mapOutputRowCount(rowCount -> 0.0), TYPES); + PlanNodeStatsEstimate actual = JOIN_STATS_RULE.calculateJoinComplementStats( Optional.empty(), ImmutableList.of(), - LEFT_STATS, RIGHT_STATS)).equalTo(LEFT_STATS.mapOutputRowCount(rowCount -> 0.0)); + LEFT_STATS, + RIGHT_STATS, + TYPES); + assertEquals(actual, expected); } @Test public void testLeftJoinComplementStatsWithMultipleClauses() { - PlanNodeStatsEstimate joinComplementStats = planNodeStats(LEFT_ROWS_COUNT * (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), + PlanNodeStatsEstimate expected = planNodeStats( + LEFT_ROWS_COUNT * (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), symbolStatistics(LEFT_JOIN_COLUMN, 0.0, 20.0, LEFT_JOIN_COLUMN_NULLS / (LEFT_JOIN_COLUMN_NULLS + LEFT_JOIN_COLUMN_NON_NULLS / 4), 5), - LEFT_OTHER_COLUMN_STATS).mapOutputRowCount(rowCount -> rowCount / UNKNOWN_FILTER_COEFFICIENT); - - assertThat(JOIN_STATS_RULE.calculateJoinComplementStats( + LEFT_OTHER_COLUMN_STATS) + .mapOutputRowCount(rowCount -> rowCount / UNKNOWN_FILTER_COEFFICIENT); + PlanNodeStatsEstimate actual = JOIN_STATS_RULE.calculateJoinComplementStats( Optional.empty(), - ImmutableList.of(new JoinNode.EquiJoinClause(new Symbol(LEFT_JOIN_COLUMN), new Symbol(RIGHT_JOIN_COLUMN)), new JoinNode.EquiJoinClause(new Symbol(LEFT_OTHER_COLUMN), new Symbol(RIGHT_OTHER_COLUMN))), - LEFT_STATS, RIGHT_STATS)).equalTo(joinComplementStats); + ImmutableList.of(new EquiJoinClause(new Symbol(LEFT_JOIN_COLUMN), new Symbol(RIGHT_JOIN_COLUMN)), new EquiJoinClause(new Symbol(LEFT_OTHER_COLUMN), new Symbol(RIGHT_OTHER_COLUMN))), + LEFT_STATS, + RIGHT_STATS, + TYPES); + assertEquals(actual, expected); } @Test @@ -246,6 +273,20 @@ public void testStatsForLeftAndRightJoin() assertJoinStats(RIGHT, RIGHT_JOIN_COLUMN, RIGHT_OTHER_COLUMN, LEFT_JOIN_COLUMN, LEFT_OTHER_COLUMN, RIGHT_STATS, LEFT_STATS, leftJoinStats); } + @Test + public void testLeftJoinMissingStats() + { + PlanNodeStatsEstimate leftStats = planNodeStats( + 1, + new SymbolStatistics(LEFT_JOIN_COLUMN, SymbolStatsEstimate.unknown()), + new SymbolStatistics(LEFT_OTHER_COLUMN, SymbolStatsEstimate.unknown())); + PlanNodeStatsEstimate rightStats = planNodeStats( + 1, + new SymbolStatistics(RIGHT_JOIN_COLUMN, SymbolStatsEstimate.unknown()), + new SymbolStatistics(RIGHT_OTHER_COLUMN, SymbolStatsEstimate.unknown())); + assertJoinStats(LEFT, leftStats, rightStats, PlanNodeStatsEstimate.unknown()); + } + @Test public void testStatsForFullJoin() { @@ -332,6 +373,11 @@ private static class SymbolStatistics final Symbol symbol; final SymbolStatsEstimate estimate; + SymbolStatistics(String symbolName, SymbolStatsEstimate estimate) + { + this(new Symbol(symbolName), estimate); + } + SymbolStatistics(Symbol symbol, SymbolStatsEstimate estimate) { this.symbol = symbol; diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestPlanNodeStatsEstimateMath.java b/presto-main/src/test/java/com/facebook/presto/cost/TestPlanNodeStatsEstimateMath.java new file mode 100644 index 0000000000000..2e2ae96f144dc --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestPlanNodeStatsEstimateMath.java @@ -0,0 +1,359 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cost; + +import com.facebook.presto.sql.planner.Symbol; +import org.testng.annotations.Test; + +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.addStatsAndMaxDistinctValues; +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.addStatsAndSumDistinctValues; +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.capStats; +import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.subtractSubsetStats; +import static com.facebook.presto.testing.assertions.Assert.assertEquals; +import static java.lang.Double.NEGATIVE_INFINITY; +import static java.lang.Double.NaN; +import static java.lang.Double.POSITIVE_INFINITY; + +public class TestPlanNodeStatsEstimateMath +{ + private static final Symbol SYMBOL = new Symbol("symbol"); + private static final StatisticRange NON_EMPTY_RANGE = openRange(1); + + @Test + public void testAddRowCount() + { + PlanNodeStatsEstimate unknownStats = statistics(NaN, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate first = statistics(10, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate second = statistics(20, NaN, NaN, StatisticRange.empty()); + + assertEquals(addStatsAndSumDistinctValues(unknownStats, unknownStats), PlanNodeStatsEstimate.unknown()); + assertEquals(addStatsAndSumDistinctValues(first, unknownStats), PlanNodeStatsEstimate.unknown()); + assertEquals(addStatsAndSumDistinctValues(unknownStats, second), PlanNodeStatsEstimate.unknown()); + assertEquals(addStatsAndSumDistinctValues(first, second).getOutputRowCount(), 30.0); + } + + @Test + public void testAddNullsFraction() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownNullsFraction = statistics(10, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(10, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(20, 0.2, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountFirst = statistics(0.1, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountSecond = statistics(0.2, 0.3, NaN, NON_EMPTY_RANGE); + + assertAddNullsFraction(unknownRowCount, unknownRowCount, NaN); + assertAddNullsFraction(unknownNullsFraction, unknownNullsFraction, NaN); + assertAddNullsFraction(unknownRowCount, unknownNullsFraction, NaN); + assertAddNullsFraction(first, unknownNullsFraction, NaN); + assertAddNullsFraction(unknownRowCount, second, NaN); + assertAddNullsFraction(first, second, 0.16666666666666666); + assertAddNullsFraction(fractionalRowCountFirst, fractionalRowCountSecond, 0.2333333333333333); + } + + private static void assertAddNullsFraction(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(addStatsAndSumDistinctValues(first, second).getSymbolStatistics(SYMBOL).getNullsFraction(), expected); + } + + @Test + public void testAddAverageRowSize() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, 0.1, 10, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownNullsFraction = statistics(10, NaN, 10, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownAverageRowSize = statistics(10, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(10, 0.1, 15, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(20, 0.2, 20, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountFirst = statistics(0.1, 0.1, 0.3, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountSecond = statistics(0.2, 0.3, 0.4, NON_EMPTY_RANGE); + + assertAddAverageRowSize(unknownRowCount, unknownRowCount, NaN); + assertAddAverageRowSize(unknownNullsFraction, unknownNullsFraction, NaN); + assertAddAverageRowSize(unknownAverageRowSize, unknownAverageRowSize, NaN); + assertAddAverageRowSize(first, unknownRowCount, NaN); + assertAddAverageRowSize(unknownNullsFraction, second, NaN); + assertAddAverageRowSize(first, unknownAverageRowSize, NaN); + assertAddAverageRowSize(first, second, 18.2); + assertAddAverageRowSize(fractionalRowCountFirst, fractionalRowCountSecond, 0.3608695652173913); + } + + private static void assertAddAverageRowSize(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(addStatsAndSumDistinctValues(first, second).getSymbolStatistics(SYMBOL).getAverageRowSize(), expected); + } + + @Test + public void testSumNumberOfDistinctValues() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate emptyRange = statistics(10, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate unknownRange = statistics(10, NaN, NaN, openRange(NaN)); + PlanNodeStatsEstimate first = statistics(10, NaN, NaN, openRange(2)); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, openRange(3)); + + assertSumNumberOfDistinctValues(unknownRowCount, unknownRowCount, NaN); + assertSumNumberOfDistinctValues(unknownRowCount, second, NaN); + assertSumNumberOfDistinctValues(first, emptyRange, 2); + assertSumNumberOfDistinctValues(first, unknownRange, NaN); + assertSumNumberOfDistinctValues(first, second, 5); + } + + private static void assertSumNumberOfDistinctValues(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(addStatsAndSumDistinctValues(first, second).getSymbolStatistics(SYMBOL).getDistinctValuesCount(), expected); + } + + @Test + public void testMaxNumberOfDistinctValues() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate emptyRange = statistics(10, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate unknownRange = statistics(10, NaN, NaN, openRange(NaN)); + PlanNodeStatsEstimate first = statistics(10, NaN, NaN, openRange(2)); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, openRange(3)); + + assertMaxNumberOfDistinctValues(unknownRowCount, unknownRowCount, NaN); + assertMaxNumberOfDistinctValues(unknownRowCount, second, NaN); + assertMaxNumberOfDistinctValues(first, emptyRange, 2); + assertMaxNumberOfDistinctValues(first, unknownRange, NaN); + assertMaxNumberOfDistinctValues(first, second, 3); + } + + private static void assertMaxNumberOfDistinctValues(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(addStatsAndMaxDistinctValues(first, second).getSymbolStatistics(SYMBOL).getDistinctValuesCount(), expected); + } + + @Test + public void testAddRange() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate emptyRange = statistics(10, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate unknownRange = statistics(10, NaN, NaN, openRange(NaN)); + PlanNodeStatsEstimate first = statistics(10, NaN, NaN, new StatisticRange(12, 100, 2)); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, new StatisticRange(101, 200, 3)); + + assertAddRange(unknownRange, unknownRange, NEGATIVE_INFINITY, POSITIVE_INFINITY); + assertAddRange(unknownRowCount, second, NEGATIVE_INFINITY, POSITIVE_INFINITY); + assertAddRange(unknownRange, second, NEGATIVE_INFINITY, POSITIVE_INFINITY); + assertAddRange(emptyRange, second, 101, 200); + assertAddRange(first, second, 12, 200); + } + + private static void assertAddRange(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expectedLow, double expectedHigh) + { + SymbolStatsEstimate statistics = addStatsAndMaxDistinctValues(first, second).getSymbolStatistics(SYMBOL); + assertEquals(statistics.getLowValue(), expectedLow); + assertEquals(statistics.getHighValue(), expectedHigh); + } + + @Test + public void testSubtractRowCount() + { + PlanNodeStatsEstimate unknownStats = statistics(NaN, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate first = statistics(40, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, StatisticRange.empty()); + + assertEquals(subtractSubsetStats(unknownStats, unknownStats), PlanNodeStatsEstimate.unknown()); + assertEquals(subtractSubsetStats(first, unknownStats), PlanNodeStatsEstimate.unknown()); + assertEquals(subtractSubsetStats(unknownStats, second), PlanNodeStatsEstimate.unknown()); + assertEquals(subtractSubsetStats(first, second).getOutputRowCount(), 30.0); + } + + @Test + public void testSubtractNullsFraction() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownNullsFraction = statistics(10, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(50, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(20, 0.2, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountFirst = statistics(0.7, 0.1, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate fractionalRowCountSecond = statistics(0.2, 0.3, NaN, NON_EMPTY_RANGE); + + assertSubtractNullsFraction(unknownRowCount, unknownRowCount, NaN); + assertSubtractNullsFraction(unknownRowCount, unknownNullsFraction, NaN); + assertSubtractNullsFraction(first, unknownNullsFraction, NaN); + assertSubtractNullsFraction(unknownRowCount, second, NaN); + assertSubtractNullsFraction(first, second, 0.03333333333333333); + assertSubtractNullsFraction(fractionalRowCountFirst, fractionalRowCountSecond, 0.019999999999999993); + } + + private static void assertSubtractNullsFraction(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(subtractSubsetStats(first, second).getSymbolStatistics(SYMBOL).getNullsFraction(), expected); + } + + @Test + public void testSubtractNumberOfDistinctValues() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownDistinctValues = statistics(100, 0.1, NaN, openRange(NaN)); + PlanNodeStatsEstimate zero = statistics(0, 0.1, NaN, openRange(0)); + PlanNodeStatsEstimate first = statistics(30, 0.1, NaN, openRange(10)); + PlanNodeStatsEstimate second = statistics(20, 0.1, NaN, openRange(5)); + PlanNodeStatsEstimate third = statistics(10, 0.1, NaN, openRange(3)); + + assertSubtractNumberOfDistinctValues(unknownRowCount, unknownRowCount, NaN); + assertSubtractNumberOfDistinctValues(unknownRowCount, second, NaN); + assertSubtractNumberOfDistinctValues(unknownDistinctValues, second, NaN); + assertSubtractNumberOfDistinctValues(first, zero, 10); + assertSubtractNumberOfDistinctValues(zero, zero, 0); + assertSubtractNumberOfDistinctValues(first, second, 5); + assertSubtractNumberOfDistinctValues(second, third, 5); + } + + private static void assertSubtractNumberOfDistinctValues(PlanNodeStatsEstimate first, PlanNodeStatsEstimate second, double expected) + { + assertEquals(subtractSubsetStats(first, second).getSymbolStatistics(SYMBOL).getDistinctValuesCount(), expected); + } + + @Test + public void testSubtractRange() + { + assertSubtractRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, NEGATIVE_INFINITY, POSITIVE_INFINITY, NEGATIVE_INFINITY, POSITIVE_INFINITY); + assertSubtractRange(0, 1, NEGATIVE_INFINITY, POSITIVE_INFINITY, 0, 1); + assertSubtractRange(NaN, NaN, 0, 1, NaN, NaN); + assertSubtractRange(0, 1, NaN, NaN, 0, 1); + assertSubtractRange(0, 2, 0, 1, 0, 2); + assertSubtractRange(0, 2, 1, 2, 0, 2); + assertSubtractRange(0, 2, 0.5, 1, 0, 2); + } + + private static void assertSubtractRange(double supersetLow, double supersetHigh, double subsetLow, double subsetHigh, double expectedLow, double expectedHigh) + { + PlanNodeStatsEstimate first = statistics(30, NaN, NaN, new StatisticRange(supersetLow, supersetHigh, 10)); + PlanNodeStatsEstimate second = statistics(20, NaN, NaN, new StatisticRange(subsetLow, subsetHigh, 5)); + SymbolStatsEstimate statistics = subtractSubsetStats(first, second).getSymbolStatistics(SYMBOL); + assertEquals(statistics.getLowValue(), expectedLow); + assertEquals(statistics.getHighValue(), expectedHigh); + } + + @Test + public void testCapRowCount() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(20, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, NON_EMPTY_RANGE); + + assertEquals(capStats(unknownRowCount, unknownRowCount).getOutputRowCount(), NaN); + assertEquals(capStats(first, unknownRowCount).getOutputRowCount(), NaN); + assertEquals(capStats(unknownRowCount, second).getOutputRowCount(), NaN); + assertEquals(capStats(first, second).getOutputRowCount(), 10.0); + assertEquals(capStats(second, first).getOutputRowCount(), 10.0); + } + + @Test + public void testCapAverageRowSize() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownAverageRowSize = statistics(20, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(20, NaN, 10, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(10, NaN, 5, NON_EMPTY_RANGE); + + assertCapAverageRowSize(unknownRowCount, unknownRowCount, NaN); + assertCapAverageRowSize(unknownAverageRowSize, unknownAverageRowSize, NaN); + // average row size should be preserved + assertCapAverageRowSize(first, unknownAverageRowSize, 10); + assertCapAverageRowSize(unknownAverageRowSize, second, NaN); + // average row size should be preserved + assertCapAverageRowSize(first, second, 10); + } + + private static void assertCapAverageRowSize(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap, double expected) + { + assertEquals(capStats(stats, cap).getSymbolStatistics(SYMBOL).getAverageRowSize(), expected); + } + + @Test + public void testCapNumberOfDistinctValues() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownNumberOfDistinctValues = statistics(20, NaN, NaN, openRange(NaN)); + PlanNodeStatsEstimate first = statistics(20, NaN, NaN, openRange(10)); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, openRange(5)); + + assertCapNumberOfDistinctValues(unknownRowCount, unknownRowCount, NaN); + assertCapNumberOfDistinctValues(unknownNumberOfDistinctValues, unknownNumberOfDistinctValues, NaN); + assertCapNumberOfDistinctValues(first, unknownRowCount, NaN); + assertCapNumberOfDistinctValues(unknownNumberOfDistinctValues, second, NaN); + assertCapNumberOfDistinctValues(first, second, 5); + } + + private static void assertCapNumberOfDistinctValues(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap, double expected) + { + assertEquals(capStats(stats, cap).getSymbolStatistics(SYMBOL).getDistinctValuesCount(), expected); + } + + @Test + public void testCapRange() + { + PlanNodeStatsEstimate emptyRange = statistics(10, NaN, NaN, StatisticRange.empty()); + PlanNodeStatsEstimate openRange = statistics(10, NaN, NaN, openRange(NaN)); + PlanNodeStatsEstimate first = statistics(10, NaN, NaN, new StatisticRange(12, 100, NaN)); + PlanNodeStatsEstimate second = statistics(10, NaN, NaN, new StatisticRange(13, 99, NaN)); + + assertCapRange(emptyRange, emptyRange, NaN, NaN); + assertCapRange(emptyRange, openRange, NaN, NaN); + assertCapRange(openRange, emptyRange, NaN, NaN); + assertCapRange(first, openRange, 12, 100); + assertCapRange(openRange, second, 13, 99); + assertCapRange(first, second, 13, 99); + } + + private static void assertCapRange(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap, double expectedLow, double expectedHigh) + { + SymbolStatsEstimate symbolStats = capStats(stats, cap).getSymbolStatistics(SYMBOL); + assertEquals(symbolStats.getLowValue(), expectedLow); + assertEquals(symbolStats.getHighValue(), expectedHigh); + } + + @Test + public void testCapNullsFraction() + { + PlanNodeStatsEstimate unknownRowCount = statistics(NaN, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate unknownNullsFraction = statistics(10, NaN, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate first = statistics(20, 0.25, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate second = statistics(10, 0.6, NaN, NON_EMPTY_RANGE); + PlanNodeStatsEstimate third = statistics(0, 0.6, NaN, NON_EMPTY_RANGE); + + assertCapNullsFraction(unknownRowCount, unknownRowCount, NaN); + assertCapNullsFraction(unknownNullsFraction, unknownNullsFraction, NaN); + assertCapNullsFraction(first, unknownNullsFraction, NaN); + assertCapNullsFraction(unknownNullsFraction, second, NaN); + assertCapNullsFraction(first, second, 0.5); + assertCapNullsFraction(first, third, 1); + } + + private static void assertCapNullsFraction(PlanNodeStatsEstimate stats, PlanNodeStatsEstimate cap, double expected) + { + assertEquals(capStats(stats, cap).getSymbolStatistics(SYMBOL).getNullsFraction(), expected); + } + + private static PlanNodeStatsEstimate statistics(double rowCount, double nullsFraction, double averageRowSize, StatisticRange range) + { + return PlanNodeStatsEstimate.builder() + .setOutputRowCount(rowCount) + .addSymbolStatistics(SYMBOL, SymbolStatsEstimate.builder() + .setNullsFraction(nullsFraction) + .setAverageRowSize(averageRowSize) + .setStatisticsRange(range) + .build()) + .build(); + } + + private static StatisticRange openRange(double distinctValues) + { + return new StatisticRange(NEGATIVE_INFINITY, POSITIVE_INFINITY, distinctValues); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestRowNumberStatsRule.java b/presto-main/src/test/java/com/facebook/presto/cost/TestRowNumberStatsRule.java new file mode 100644 index 0000000000000..8f00684a5ce2c --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestRowNumberStatsRule.java @@ -0,0 +1,172 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.cost; + +import com.facebook.presto.sql.planner.Symbol; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.Test; + +import java.util.Optional; + +import static com.facebook.presto.spi.type.BigintType.BIGINT; + +public class TestRowNumberStatsRule + extends BaseStatsCalculatorTest +{ + private SymbolStatsEstimate xStats = SymbolStatsEstimate.builder() + .setDistinctValuesCount(5.0) + .setNullsFraction(0) + .build(); + private SymbolStatsEstimate yStats = SymbolStatsEstimate.builder() + .setDistinctValuesCount(5.0) + .setNullsFraction(0.5) + .build(); + + @Test + public void testSingleGroupingKey() + { + // grouping on a key with 0 nulls fraction without max rows per partition limit + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT)), + Optional.empty(), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(check -> check + .outputRowsCount(10) + .symbolStats("x", assertion -> assertion.isEqualTo(xStats)) + .symbolStats("y", assertion -> assertion.isEqualTo(yStats)) + .symbolStats("z", assertion -> assertion + .lowValue(1) + .distinctValuesCount(2) + .nullsFraction(0) + .averageRowSize(BIGINT.getFixedSize()))); + + // grouping on a key with 0 nulls fraction with max rows per partition limit + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT)), + Optional.of(1), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(10) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(check -> check + .outputRowsCount(5) + .symbolStats("z", assertion -> assertion + .lowValue(1) + .distinctValuesCount(1) + .nullsFraction(0) + .averageRowSize(BIGINT.getFixedSize()))); + + // grouping on a key with non zero nulls fraction + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("y", BIGINT)), + Optional.empty(), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(60) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(check -> check + .outputRowsCount(60) + .symbolStats("z", assertion -> assertion + .lowValue(1) + .distinctValuesCount(10) + .nullsFraction(0) + .averageRowSize(BIGINT.getFixedSize()))); + + // unknown input row count + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT)), + Optional.of(1), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(PlanNodeStatsAssertion::outputRowsCountUnknown); + } + + @Test + public void testMultipleGroupingKeys() + { + // grouping on multiple keys with the number of estimated groups less than the row count + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)), + Optional.empty(), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(60) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(check -> check + .outputRowsCount(60) + .symbolStats("z", assertion -> assertion + .lowValue(1) + .distinctValuesCount(2) + .nullsFraction(0) + .averageRowSize(BIGINT.getFixedSize()))); + + // grouping on multiple keys with the number of estimated groups greater than the row count + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)), + Optional.empty(), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(20) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), yStats) + .build()) + .check(check -> check + .outputRowsCount(20) + .symbolStats("z", assertion -> assertion + .lowValue(1) + .distinctValuesCount(1) + .nullsFraction(0) + .averageRowSize(BIGINT.getFixedSize()))); + + // grouping on multiple keys with stats for one of the keys are unknown + tester().assertStatsFor(pb -> pb + .rowNumber( + ImmutableList.of(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)), + Optional.empty(), + pb.symbol("z", BIGINT), + pb.values(pb.symbol("x", BIGINT), pb.symbol("y", BIGINT)))) + .withSourceStats(0, PlanNodeStatsEstimate.builder() + .setOutputRowCount(20) + .addSymbolStatistics(new Symbol("x"), xStats) + .addSymbolStatistics(new Symbol("y"), SymbolStatsEstimate.unknown()) + .build()) + .check(PlanNodeStatsAssertion::outputRowsCountUnknown); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestScalarStatsCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestScalarStatsCalculator.java index e17f9b04d93ae..5f65c483cbb91 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestScalarStatsCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestScalarStatsCalculator.java @@ -16,18 +16,28 @@ import com.facebook.presto.Session; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.sql.parser.SqlParser; +import com.facebook.presto.sql.planner.LiteralEncoder; import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.tree.Cast; import com.facebook.presto.sql.tree.DecimalLiteral; import com.facebook.presto.sql.tree.DoubleLiteral; import com.facebook.presto.sql.tree.Expression; +import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.GenericLiteral; import com.facebook.presto.sql.tree.NullLiteral; +import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.StringLiteral; import com.facebook.presto.sql.tree.SymbolReference; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.airlift.slice.Slices; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import static com.facebook.presto.cost.PlanNodeStatsEstimate.UNKNOWN_STATS; +import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; +import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; +import static com.facebook.presto.spi.type.VarcharType.createVarcharType; import static com.facebook.presto.sql.ExpressionUtils.rewriteIdentifiersToSymbolReferences; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static java.lang.Double.NEGATIVE_INFINITY; @@ -49,6 +59,30 @@ public void setUp() @Test public void testLiteral() { + assertCalculate(new GenericLiteral("TINYINT", "7")) + .distinctValuesCount(1.0) + .lowValue(7) + .highValue(7) + .nullsFraction(0.0); + + assertCalculate(new GenericLiteral("SMALLINT", "8")) + .distinctValuesCount(1.0) + .lowValue(8) + .highValue(8) + .nullsFraction(0.0); + + assertCalculate(new GenericLiteral("INTEGER", "9")) + .distinctValuesCount(1.0) + .lowValue(9) + .highValue(9) + .nullsFraction(0.0); + + assertCalculate(new GenericLiteral("BIGINT", Long.toString(Long.MAX_VALUE))) + .distinctValuesCount(1.0) + .lowValue(Long.MAX_VALUE) + .highValue(Long.MAX_VALUE) + .nullsFraction(0.0); + assertCalculate(new DoubleLiteral("7.5")) .distinctValuesCount(1.0) .lowValue(7.5) @@ -74,6 +108,44 @@ public void testLiteral() .nullsFraction(1.0); } + @Test + public void testFunctionCall() + { + assertCalculate( + new FunctionCall( + QualifiedName.of("length"), + ImmutableList.of(new Cast(new NullLiteral(), "VARCHAR(10)")))) + .distinctValuesCount(0.0) + .lowValueUnknown() + .highValueUnknown() + .nullsFraction(1.0); + + assertCalculate( + new FunctionCall( + QualifiedName.of("length"), + ImmutableList.of(new SymbolReference("x"))), + PlanNodeStatsEstimate.unknown(), + TypeProvider.viewOf(ImmutableMap.of(new Symbol("x"), createVarcharType(2)))) + .distinctValuesCountUnknown() + .lowValueUnknown() + .highValueUnknown() + .nullsFractionUnknown(); + } + + @Test + public void testVarbinaryConstant() + { + MetadataManager metadata = createTestMetadataManager(); + LiteralEncoder literalEncoder = new LiteralEncoder(metadata.getBlockEncodingSerde()); + Expression expression = literalEncoder.toExpression(Slices.utf8Slice("ala ma kota"), VARBINARY); + + assertCalculate(expression) + .distinctValuesCount(1.0) + .lowValueUnknown() + .highValueUnknown() + .nullsFraction(0.0); + } + @Test public void testSymbolReference() { @@ -89,7 +161,7 @@ public void testSymbolReference() .build(); assertCalculate(expression("x"), inputStatistics).isEqualTo(xStats); - assertCalculate(expression("y"), inputStatistics).isEqualTo(SymbolStatsEstimate.UNKNOWN_STATS); + assertCalculate(expression("y"), inputStatistics).isEqualTo(SymbolStatsEstimate.unknown()); } @Test @@ -178,7 +250,7 @@ public void testCastBigintToDouble() @Test public void testCastUnknown() { - assertCalculate(new Cast(new SymbolReference("a"), "bigint"), UNKNOWN_STATS) + assertCalculate(new Cast(new SymbolReference("a"), "bigint"), PlanNodeStatsEstimate.unknown()) .lowValueUnknown() .highValueUnknown() .distinctValuesCountUnknown() @@ -188,12 +260,17 @@ public void testCastUnknown() private SymbolStatsAssertion assertCalculate(Expression scalarExpression) { - return assertCalculate(scalarExpression, UNKNOWN_STATS); + return assertCalculate(scalarExpression, PlanNodeStatsEstimate.unknown()); } private SymbolStatsAssertion assertCalculate(Expression scalarExpression, PlanNodeStatsEstimate inputStatistics) { - return SymbolStatsAssertion.assertThat(calculator.calculate(scalarExpression, inputStatistics, session)); + return assertCalculate(scalarExpression, inputStatistics, TypeProvider.empty()); + } + + private SymbolStatsAssertion assertCalculate(Expression scalarExpression, PlanNodeStatsEstimate inputStatistics, TypeProvider types) + { + return SymbolStatsAssertion.assertThat(calculator.calculate(scalarExpression, inputStatistics, session, types)); } @Test @@ -239,6 +316,40 @@ public void testNonDivideArithmeticBinaryExpression() .averageRowSize(2.0); } + @Test + public void testArithmeticBinaryWithAllNullsSymbol() + { + SymbolStatsEstimate allNullStats = SymbolStatsEstimate.zero(); + PlanNodeStatsEstimate relationStats = PlanNodeStatsEstimate.builder() + .addSymbolStatistics(new Symbol("x"), SymbolStatsEstimate.builder() + .setLowValue(-1) + .setHighValue(10) + .setDistinctValuesCount(4) + .setNullsFraction(0.1) + .setAverageRowSize(0) + .build()) + .addSymbolStatistics(new Symbol("all_null"), allNullStats) + .setOutputRowCount(10) + .build(); + + assertCalculate(expression("x + all_null"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("x - all_null"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("all_null - x"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("all_null * x"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("x % all_null"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("all_null % x"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("x / all_null"), relationStats) + .isEqualTo(allNullStats); + assertCalculate(expression("all_null / x"), relationStats) + .isEqualTo(allNullStats); + } + @Test public void testDivideArithmeticBinaryExpression() { diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestSemiJoinStatsCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestSemiJoinStatsCalculator.java index 35a10a4f1826f..d077654c7539f 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestSemiJoinStatsCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestSemiJoinStatsCalculator.java @@ -21,7 +21,6 @@ import static com.facebook.presto.cost.PlanNodeStatsAssertion.assertThat; import static com.facebook.presto.cost.SemiJoinStatsCalculator.computeAntiJoin; import static com.facebook.presto.cost.SemiJoinStatsCalculator.computeSemiJoin; -import static com.facebook.presto.cost.SymbolStatsEstimate.UNKNOWN_STATS; import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.NaN; import static java.lang.Double.POSITIVE_INFINITY; @@ -38,6 +37,7 @@ public class TestSemiJoinStatsCalculator private SymbolStatsEstimate rightOpenStats; private SymbolStatsEstimate unknownRangeStats; private SymbolStatsEstimate emptyRangeStats; + private SymbolStatsEstimate fractionalNdvStats; private Symbol u = new Symbol("u"); private Symbol w = new Symbol("w"); @@ -49,6 +49,7 @@ public class TestSemiJoinStatsCalculator private Symbol unknownRange = new Symbol("unknownRange"); private Symbol emptyRange = new Symbol("emptyRange"); private Symbol unknown = new Symbol("unknown"); + private Symbol fractionalNdv = new Symbol("fractionalNdv"); @BeforeMethod public void setUp() @@ -117,6 +118,11 @@ public void setUp() .setHighValue(NaN) .setNullsFraction(NaN) .build(); + fractionalNdvStats = SymbolStatsEstimate.builder() + .setAverageRowSize(NaN) + .setDistinctValuesCount(0.1) + .setNullsFraction(0) + .build(); inputStatistics = PlanNodeStatsEstimate.builder() .addSymbolStatistics(u, uStats) .addSymbolStatistics(w, wStats) @@ -127,7 +133,8 @@ public void setUp() .addSymbolStatistics(rightOpen, rightOpenStats) .addSymbolStatistics(unknownRange, unknownRangeStats) .addSymbolStatistics(emptyRange, emptyRangeStats) - .addSymbolStatistics(unknown, UNKNOWN_STATS) + .addSymbolStatistics(unknown, SymbolStatsEstimate.unknown()) + .addSymbolStatistics(fractionalNdv, fractionalNdvStats) .setOutputRowCount(1000.0) .build(); } @@ -177,6 +184,17 @@ public void testSemiJoin() .symbolStatsUnknown(unknown) .symbolStats(z, stats -> stats.isEqualTo(zStats)) .outputRowsCountUnknown(); + + // zero distinct values + assertThat(computeSemiJoin(inputStatistics, inputStatistics, emptyRange, emptyRange)) + .outputRowsCount(0); + + // fractional distinct values + assertThat(computeSemiJoin(inputStatistics, inputStatistics, fractionalNdv, fractionalNdv)) + .outputRowsCount(1000) + .symbolStats(fractionalNdv, stats -> stats + .nullsFraction(0) + .distinctValuesCount(0.1)); } @Test @@ -205,7 +223,7 @@ public void testAntiJoin() .outputRowsCount(inputStatistics.getOutputRowCount() * xStats.getValuesFraction() * 0.5); // source stats are unknown - assertThat(computeSemiJoin(inputStatistics, inputStatistics, unknown, u)) + assertThat(computeAntiJoin(inputStatistics, inputStatistics, unknown, u)) .symbolStats(unknown, stats -> stats .nullsFraction(0) .distinctValuesCountUnknown() @@ -215,7 +233,7 @@ public void testAntiJoin() .outputRowsCountUnknown(); // filtering stats are unknown - assertThat(computeSemiJoin(inputStatistics, inputStatistics, x, unknown)) + assertThat(computeAntiJoin(inputStatistics, inputStatistics, x, unknown)) .symbolStats(x, stats -> stats .nullsFraction(0) .lowValue(xStats.getLowValue()) @@ -224,5 +242,16 @@ public void testAntiJoin() .symbolStatsUnknown(unknown) .symbolStats(z, stats -> stats.isEqualTo(zStats)) .outputRowsCountUnknown(); + + // zero distinct values + assertThat(computeAntiJoin(inputStatistics, inputStatistics, emptyRange, emptyRange)) + .outputRowsCount(0); + + // fractional distinct values + assertThat(computeAntiJoin(inputStatistics, inputStatistics, fractionalNdv, fractionalNdv)) + .outputRowsCount(500) + .symbolStats(fractionalNdv, stats -> stats + .nullsFraction(0) + .distinctValuesCount(0.05)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestStatisticRange.java b/presto-main/src/test/java/com/facebook/presto/cost/TestStatisticRange.java index 212f0a0240353..d88780ecf8ccc 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestStatisticRange.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestStatisticRange.java @@ -17,6 +17,7 @@ import static com.facebook.presto.cost.EstimateAssertion.assertEstimateEquals; import static java.lang.Double.NEGATIVE_INFINITY; +import static java.lang.Double.NaN; import static java.lang.Double.POSITIVE_INFINITY; import static org.testng.Assert.assertEquals; @@ -52,6 +53,10 @@ public void testOverlapPercentWith() // Unbounded (infinite), NDV-based assertOverlap(unboundedRange(10), unboundedRange(20), 1); assertOverlap(unboundedRange(20), unboundedRange(10), 0.5); + + assertOverlap(unboundedRange(0.1), unboundedRange(1), 1); + assertOverlap(unboundedRange(0.0), unboundedRange(1), 0); + assertOverlap(unboundedRange(0.0), unboundedRange(0), 0); } @Test @@ -62,6 +67,43 @@ public void testIntersect() assertEquals(zeroToTen.intersect(fiveToFifteen), range(5, 10, 10)); } + @Test + public void testAddAndSumDistinctValues() + { + assertEquals(unboundedRange(NaN).addAndSumDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(NaN).addAndSumDistinctValues(unboundedRange(1)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndSumDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndSumDistinctValues(unboundedRange(2)), unboundedRange(3)); + assertEquals(StatisticRange.empty().addAndSumDistinctValues(StatisticRange.empty()), StatisticRange.empty()); + assertEquals(range(0, 1, 1).addAndSumDistinctValues(StatisticRange.empty()), range(0, 1, 1)); + assertEquals(range(0, 1, 1).addAndSumDistinctValues(range(1, 2, 1)), range(0, 2, 2)); + } + + @Test + public void testAddAndMaxDistinctValues() + { + assertEquals(unboundedRange(NaN).addAndMaxDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(NaN).addAndMaxDistinctValues(unboundedRange(1)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndMaxDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndMaxDistinctValues(unboundedRange(2)), unboundedRange(2)); + assertEquals(StatisticRange.empty().addAndMaxDistinctValues(StatisticRange.empty()), StatisticRange.empty()); + assertEquals(range(0, 1, 1).addAndMaxDistinctValues(StatisticRange.empty()), range(0, 1, 1)); + assertEquals(range(0, 1, 1).addAndMaxDistinctValues(range(1, 2, 1)), range(0, 2, 1)); + } + + @Test + public void testAddAndCollapseDistinctValues() + { + assertEquals(unboundedRange(NaN).addAndCollapseDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(NaN).addAndCollapseDistinctValues(unboundedRange(1)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndCollapseDistinctValues(unboundedRange(NaN)), unboundedRange(NaN)); + assertEquals(unboundedRange(1).addAndCollapseDistinctValues(unboundedRange(2)), unboundedRange(2)); + assertEquals(StatisticRange.empty().addAndCollapseDistinctValues(StatisticRange.empty()), StatisticRange.empty()); + assertEquals(range(0, 1, 1).addAndCollapseDistinctValues(StatisticRange.empty()), range(0, 1, 1)); + assertEquals(range(0, 1, 1).addAndCollapseDistinctValues(range(1, 2, 1)), range(0, 2, 1)); + assertEquals(range(0, 3, 3).addAndCollapseDistinctValues(range(2, 6, 4)), range(0, 6, 6)); + } + private static StatisticRange range(double low, double high, double distinctValues) { return new StatisticRange(low, high, distinctValues); diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestStatsCalculator.java b/presto-main/src/test/java/com/facebook/presto/cost/TestStatsCalculator.java index d2b3d8a9f8303..0ebb04820d9ef 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestStatsCalculator.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestStatsCalculator.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.cost; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.LogicalPlanner; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.assertions.PlanAssert; @@ -63,7 +64,7 @@ private void assertPlan(String sql, PlanMatchPattern pattern) private void assertPlan(String sql, LogicalPlanner.Stage stage, PlanMatchPattern pattern) { queryRunner.inTransaction(transactionSession -> { - Plan actualPlan = queryRunner.createPlan(transactionSession, sql, stage); + Plan actualPlan = queryRunner.createPlan(transactionSession, sql, stage, WarningCollector.NOOP); PlanAssert.assertPlan(transactionSession, queryRunner.getMetadata(), queryRunner.getStatsCalculator(), actualPlan, pattern); return null; }); diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestStatsNormalizer.java b/presto-main/src/test/java/com/facebook/presto/cost/TestStatsNormalizer.java index 493fc89ff6beb..848af21c257a2 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestStatsNormalizer.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestStatsNormalizer.java @@ -30,7 +30,6 @@ import java.time.LocalDate; import static com.facebook.presto.cost.StatsUtil.toStatsRepresentation; -import static com.facebook.presto.cost.SymbolStatsEstimate.UNKNOWN_STATS; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.spi.type.DateType.DATE; @@ -57,6 +56,7 @@ public void testNoCapping() { Symbol a = new Symbol("a"); PlanNodeStatsEstimate estimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(30) .addSymbolStatistics(a, SymbolStatsEstimate.builder().setDistinctValuesCount(20).build()) .build(); @@ -71,9 +71,10 @@ public void testDropNonOutputSymbols() Symbol b = new Symbol("b"); Symbol c = new Symbol("c"); PlanNodeStatsEstimate estimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(40) .addSymbolStatistics(a, SymbolStatsEstimate.builder().setDistinctValuesCount(20).build()) .addSymbolStatistics(b, SymbolStatsEstimate.builder().setDistinctValuesCount(30).build()) - .addSymbolStatistics(c, SymbolStatsEstimate.buildFrom(UNKNOWN_STATS).build()) + .addSymbolStatistics(c, SymbolStatsEstimate.unknown()) .build(); PlanNodeStatsAssertion.assertThat(normalizer.normalize(estimate, ImmutableList.of(b, c), TypeProvider.copyOf(ImmutableMap.of(b, BIGINT, c, BIGINT)))) @@ -90,7 +91,7 @@ public void tesCapDistinctValuesByOutputRowCount() PlanNodeStatsEstimate estimate = PlanNodeStatsEstimate.builder() .addSymbolStatistics(a, SymbolStatsEstimate.builder().setNullsFraction(0).setDistinctValuesCount(20).build()) .addSymbolStatistics(b, SymbolStatsEstimate.builder().setNullsFraction(0.4).setDistinctValuesCount(20).build()) - .addSymbolStatistics(c, SymbolStatsEstimate.builder().build()) + .addSymbolStatistics(c, SymbolStatsEstimate.unknown()) .setOutputRowCount(10) .build(); @@ -139,7 +140,9 @@ private void testCapDistinctValuesByToDomainRangeLength(Type type, double ndv, O .setLowValue(asStatsValue(low, type)) .setHighValue(asStatsValue(high, type)) .build(); - PlanNodeStatsEstimate estimate = PlanNodeStatsEstimate.builder().addSymbolStatistics(symbol, symbolStats).build(); + PlanNodeStatsEstimate estimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(10000000000L) + .addSymbolStatistics(symbol, symbolStats).build(); assertNormalized(estimate, TypeProvider.copyOf(ImmutableMap.of(symbol, type))) .symbolStats(symbol, symbolAssert -> symbolAssert.distinctValuesCount(expectedNormalizedNdv)); diff --git a/presto-main/src/test/java/com/facebook/presto/cost/TestValuesNodeStats.java b/presto-main/src/test/java/com/facebook/presto/cost/TestValuesNodeStats.java index 9629dad08fb5b..d7322db9b5e83 100644 --- a/presto-main/src/test/java/com/facebook/presto/cost/TestValuesNodeStats.java +++ b/presto-main/src/test/java/com/facebook/presto/cost/TestValuesNodeStats.java @@ -81,7 +81,7 @@ public void testStatsForValuesNodeWithJustNulls() { PlanNodeStatsEstimate nullAStats = PlanNodeStatsEstimate.builder() .setOutputRowCount(1) - .addSymbolStatistics(new Symbol("a"), SymbolStatsEstimate.ZERO_STATS) + .addSymbolStatistics(new Symbol("a"), SymbolStatsEstimate.zero()) .build(); tester().assertStatsFor(pb -> pb @@ -112,7 +112,7 @@ public void testStatsForEmptyValues() .check(outputStats -> outputStats.equalTo( PlanNodeStatsEstimate.builder() .setOutputRowCount(0) - .addSymbolStatistics(new Symbol("a"), SymbolStatsEstimate.ZERO_STATS) + .addSymbolStatistics(new Symbol("a"), SymbolStatsEstimate.zero()) .build())); } } diff --git a/presto-main/src/test/java/com/facebook/presto/event/query/TestQueryMonitorConfig.java b/presto-main/src/test/java/com/facebook/presto/event/TestQueryMonitorConfig.java similarity index 97% rename from presto-main/src/test/java/com/facebook/presto/event/query/TestQueryMonitorConfig.java rename to presto-main/src/test/java/com/facebook/presto/event/TestQueryMonitorConfig.java index 235930ffc94ad..963b191c52128 100644 --- a/presto-main/src/test/java/com/facebook/presto/event/query/TestQueryMonitorConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/event/TestQueryMonitorConfig.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.event.query; +package com.facebook.presto.event; import com.google.common.collect.ImmutableMap; import io.airlift.units.DataSize; diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java index d88d5d271efa1..bab7ae62e5a15 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java @@ -16,6 +16,8 @@ import com.facebook.presto.Session; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.memory.VersionedMemoryPoolId; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.memory.MemoryPoolId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -42,7 +44,7 @@ import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.google.common.util.concurrent.Futures.immediateFuture; import static io.airlift.units.DataSize.Unit.BYTE; -import static java.util.Objects.requireNonNull; +import static io.airlift.units.DataSize.succinctBytes; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -50,7 +52,7 @@ public class MockQueryExecution implements QueryExecution { private final List> listeners = new ArrayList<>(); - private final long memoryUsage; + private final DataSize memoryUsage; private final Duration cpuUsage; private final Session session; private final QueryId queryId; @@ -70,7 +72,7 @@ public MockQueryExecution(long memoryUsage, String queryId, int priority) public MockQueryExecution(long memoryUsage, String queryId, int priority, Duration cpuUsage) { - this.memoryUsage = memoryUsage; + this.memoryUsage = succinctBytes(memoryUsage); this.cpuUsage = cpuUsage; this.session = testSessionBuilder() .setSystemProperty(QUERY_PRIORITY, String.valueOf(priority)) @@ -111,6 +113,7 @@ public QueryInfo getQueryInfo() new Duration(6, NANOSECONDS), new Duration(5, NANOSECONDS), new Duration(31, NANOSECONDS), + new Duration(41, NANOSECONDS), new Duration(7, NANOSECONDS), new Duration(8, NANOSECONDS), @@ -137,7 +140,6 @@ public QueryInfo getQueryInfo() true, new Duration(20, NANOSECONDS), new Duration(21, NANOSECONDS), - new Duration(22, NANOSECONDS), new Duration(23, NANOSECONDS), false, ImmutableSet.of(), @@ -168,6 +170,7 @@ public QueryInfo getQueryInfo() Optional.empty(), null, null, + ImmutableList.of(), ImmutableSet.of(), Optional.empty(), state.isDone(), @@ -186,7 +189,7 @@ public Plan getQueryPlan() throw new UnsupportedOperationException(); } - public Throwable getFailureCause() + public Throwable getThrowable() { return failureCause; } @@ -216,39 +219,63 @@ public void setMemoryPool(VersionedMemoryPoolId poolId) } @Override - public long getUserMemoryReservation() + public Session getSession() { - return memoryUsage; + return session; } @Override - public long getTotalMemoryReservation() + public DateTime getCreateTime() { - return memoryUsage; + return getQueryInfo().getQueryStats().getCreateTime(); } @Override - public Duration getTotalCpuTime() + public Optional getExecutionStartTime() { - return cpuUsage; + return Optional.ofNullable(getQueryInfo().getQueryStats().getExecutionStartTime()); } @Override - public Session getSession() + public DateTime getLastHeartbeat() { - return session; + return getQueryInfo().getQueryStats().getLastHeartbeat(); } @Override - public Optional getResourceGroup() + public Optional getEndTime() { - return this.resourceGroupId; + return Optional.ofNullable(getQueryInfo().getQueryStats().getEndTime()); } @Override - public void setResourceGroup(ResourceGroupId resourceGroupId) + public Optional getErrorCode() { - this.resourceGroupId = Optional.of(requireNonNull(resourceGroupId, "resourceGroupId is null")); + return Optional.ofNullable(getQueryInfo().getFailureInfo()).map(ExecutionFailureInfo::getErrorCode); + } + + @Override + public BasicQueryInfo getBasicQueryInfo() + { + return new BasicQueryInfo(getQueryInfo()); + } + + @Override + public DataSize getUserMemoryReservation() + { + return memoryUsage; + } + + @Override + public DataSize getTotalMemoryReservation() + { + return memoryUsage; + } + + @Override + public Duration getTotalCpuTime() + { + return cpuUsage; } @Override @@ -266,6 +293,12 @@ public void fail(Throwable cause) fireStateChange(); } + @Override + public boolean isDone() + { + return getState().isDone(); + } + @Override public void cancelQuery() { diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockRemoteTaskFactory.java b/presto-main/src/test/java/com/facebook/presto/execution/MockRemoteTaskFactory.java index ce916abfbe3fa..7c831719f352c 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/MockRemoteTaskFactory.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/MockRemoteTaskFactory.java @@ -16,6 +16,7 @@ import com.facebook.presto.OutputBuffers; import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.execution.NodeTaskMap.PartitionedSplitCountTracker; import com.facebook.presto.execution.buffer.LazyOutputBuffer; import com.facebook.presto.execution.buffer.OutputBuffer; @@ -29,7 +30,6 @@ import com.facebook.presto.operator.TaskStats; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.memory.MemoryPoolId; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spiller.SpillSpaceTracker; import com.facebook.presto.sql.planner.Partitioning; import com.facebook.presto.sql.planner.PartitioningScheme; @@ -64,11 +64,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; @@ -111,15 +111,13 @@ public MockRemoteTask createTableScanTask(TaskId taskId, Node newNode, List initialSplits = ImmutableMultimap.builder(); for (Split sourceSplit : splits) { @@ -254,8 +252,7 @@ public TaskInfo getTaskInfo() outputBuffer.getInfo(), ImmutableSet.of(), taskContext.getTaskStats(), - true, - false); + true); } @Override @@ -375,6 +372,19 @@ public void addStateChangeListener(StateChangeListener stateChangeLi taskStateMachine.addStateChangeListener(newValue -> stateChangeListener.stateChanged(getTaskStatus())); } + @Override + public void addFinalTaskInfoListener(StateChangeListener stateChangeListener) + { + AtomicBoolean done = new AtomicBoolean(); + StateChangeListener fireOnceStateChangeListener = state -> { + if (state.isDone() && done.compareAndSet(false, true)) { + stateChangeListener.stateChanged(getTaskInfo()); + } + }; + taskStateMachine.addStateChangeListener(fireOnceStateChangeListener); + fireOnceStateChangeListener.stateChanged(taskStateMachine.getState()); + } + @Override public synchronized ListenableFuture whenSplitQueueHasSpace(int threshold) { diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TaskTestUtils.java b/presto-main/src/test/java/com/facebook/presto/execution/TaskTestUtils.java index fd3cbbbf2737d..72d2bf79c1131 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TaskTestUtils.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TaskTestUtils.java @@ -17,10 +17,9 @@ import com.facebook.presto.ScheduledSplit; import com.facebook.presto.TaskSource; import com.facebook.presto.block.BlockEncodingManager; -import com.facebook.presto.client.NodeVersion; import com.facebook.presto.connector.ConnectorId; -import com.facebook.presto.event.query.QueryMonitor; -import com.facebook.presto.event.query.QueryMonitorConfig; +import com.facebook.presto.cost.StatsAndCosts; +import com.facebook.presto.event.SplitMonitor; import com.facebook.presto.eventlistener.EventListenerManager; import com.facebook.presto.execution.TestSqlTaskManager.MockExchangeClientSupplier; import com.facebook.presto.execution.scheduler.LegacyNetworkTopology; @@ -29,7 +28,6 @@ import com.facebook.presto.index.IndexManager; import com.facebook.presto.metadata.InMemoryNodeManager; import com.facebook.presto.metadata.MetadataManager; -import com.facebook.presto.metadata.SessionPropertyManager; import com.facebook.presto.metadata.Split; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.operator.LookupJoinOperators; @@ -37,7 +35,6 @@ import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.operator.index.IndexJoinLookupStats; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.TestingTypeManager; import com.facebook.presto.spiller.GenericSpillerFactory; import com.facebook.presto.split.PageSinkManager; @@ -66,7 +63,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.json.ObjectMapperProvider; -import io.airlift.node.NodeInfo; import java.util.List; import java.util.Optional; @@ -77,7 +73,6 @@ import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; -import static io.airlift.json.JsonCodec.jsonCodec; public final class TaskTestUtils { @@ -103,16 +98,14 @@ private TaskTestUtils() TABLE_SCAN_NODE_ID, new TableHandle(CONNECTOR_ID, new TestingTableHandle()), ImmutableList.of(SYMBOL), - ImmutableMap.of(SYMBOL, new TestingColumnHandle("column", 0, BIGINT)), - Optional.empty(), - TupleDomain.all(), - null), + ImmutableMap.of(SYMBOL, new TestingColumnHandle("column", 0, BIGINT))), ImmutableMap.of(SYMBOL, VARCHAR), SOURCE_DISTRIBUTION, ImmutableList.of(TABLE_SCAN_NODE_ID), new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), ImmutableList.of(SYMBOL)) .withBucketToPartition(Optional.of(new int[1])), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); public static LocalExecutionPlanner createTestingPlanner() { @@ -167,17 +160,10 @@ public static TaskInfo updateTask(SqlTask sqlTask, List taskSources, return sqlTask.updateTask(TEST_SESSION, Optional.of(PLAN_FRAGMENT), taskSources, outputBuffers, OptionalInt.empty()); } - public static QueryMonitor createTestQueryMonitor() + public static SplitMonitor createTestSplitMonitor() { - MetadataManager metadata = MetadataManager.createTestMetadataManager(); - return new QueryMonitor( - new ObjectMapperProvider().get(), - jsonCodec(StageInfo.class), + return new SplitMonitor( new EventListenerManager(), - new NodeInfo("test"), - new NodeVersion("testVersion"), - new SessionPropertyManager(), - metadata, - new QueryMonitorConfig()); + new ObjectMapperProvider().get()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestCommitTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestCommitTask.java index 1b0cc4206e1ef..65e796623655e 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestCommitTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestCommitTask.java @@ -16,12 +16,12 @@ import com.facebook.presto.Session; import com.facebook.presto.Session.SessionBuilder; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.MetadataManager; -import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.tree.Commit; import com.facebook.presto.transaction.TransactionId; import com.facebook.presto.transaction.TransactionManager; @@ -29,6 +29,7 @@ import org.testng.annotations.Test; import java.net.URI; +import java.util.Optional; import java.util.concurrent.ExecutorService; import static com.facebook.presto.spi.StandardErrorCode.NOT_IN_TRANSACTION; @@ -60,18 +61,17 @@ public void tearDown() public void testCommit() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .setTransactionId(transactionManager.beginTransaction(false)) .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "COMMIT", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("COMMIT", session, transactionManager); assertTrue(stateMachine.getSession().getTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); getFutureValue(new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); - assertTrue(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } @@ -80,11 +80,10 @@ public void testCommit() public void testNoTransactionCommit() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "COMMIT", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("COMMIT", session, transactionManager); try { getFutureValue(new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); @@ -93,8 +92,8 @@ public void testNoTransactionCommit() catch (PrestoException e) { assertEquals(e.getErrorCode(), NOT_IN_TRANSACTION.toErrorCode()); } - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } @@ -103,12 +102,11 @@ public void testNoTransactionCommit() public void testUnknownTransactionCommit() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .setTransactionId(TransactionId.create()) // Use a random transaction ID that is unknown to the system .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "COMMIT", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("COMMIT", session, transactionManager); try { getFutureValue(new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); @@ -117,12 +115,27 @@ public void testUnknownTransactionCommit() catch (PrestoException e) { assertEquals(e.getErrorCode(), UNKNOWN_TRANSACTION.toErrorCode()); } - assertTrue(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); // Still issue clear signal - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); // Still issue clear signal + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } + private QueryStateMachine createQueryStateMachine(String query, Session session, TransactionManager transactionManager) + { + return QueryStateMachine.begin( + query, + session, + URI.create("fake://uri"), + new ResourceGroupId("test"), + true, + transactionManager, + new AccessControlManager(transactionManager), + executor, + metadata, + WarningCollector.NOOP); + } + private static SessionBuilder sessionBuilder() { return testSessionBuilder() diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestDeallocateTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestDeallocateTask.java index 9a48ddf2e241a..b40b8647293e6 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestDeallocateTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestDeallocateTask.java @@ -14,12 +14,13 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.tree.Deallocate; import com.facebook.presto.sql.tree.Identifier; import com.facebook.presto.transaction.TransactionManager; @@ -80,7 +81,17 @@ private Set executeDeallocate(String statementName, String sqlString, Se { TransactionManager transactionManager = createTestTransactionManager(); AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), sqlString, session, URI.create("fake://uri"), false, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = QueryStateMachine.begin( + sqlString, + session, + URI.create("fake://uri"), + new ResourceGroupId("test"), + false, + transactionManager, + accessControl, + executor, + metadata, + WarningCollector.NOOP); Deallocate deallocate = new Deallocate(new Identifier(statementName)); new DeallocateTask().execute(deallocate, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()); return stateMachine.getDeallocatedPreparedStatements(); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestMemoryRevokingScheduler.java b/presto-main/src/test/java/com/facebook/presto/execution/TestMemoryRevokingScheduler.java index 839b4321f9442..ee17a112d0855 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestMemoryRevokingScheduler.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestMemoryRevokingScheduler.java @@ -50,7 +50,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicInteger; -import static com.facebook.presto.execution.TaskTestUtils.createTestQueryMonitor; +import static com.facebook.presto.execution.SqlTask.createSqlTask; +import static com.facebook.presto.execution.TaskTestUtils.createTestSplitMonitor; import static com.facebook.presto.execution.TaskTestUtils.createTestingPlanner; import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; import static io.airlift.concurrent.Threads.threadsNamed; @@ -96,7 +97,7 @@ public void setUp() executor, taskExecutor, planner, - createTestQueryMonitor(), + createTestSplitMonitor(), new TaskManagerConfig()); allOperatorContexts = null; @@ -118,7 +119,7 @@ public void testScheduleMemoryRevoking() SqlTask sqlTask2 = newSqlTask(); TaskContext taskContext1 = sqlTask1.getQueryContext().addTaskContext(new TaskStateMachine(new TaskId("q1", 1, 1), executor), session, false, false, OptionalInt.empty()); - PipelineContext pipelineContext11 = taskContext1.addPipelineContext(0, false, false); + PipelineContext pipelineContext11 = taskContext1.addPipelineContext(0, false, false, false); DriverContext driverContext111 = pipelineContext11.addDriverContext(); OperatorContext operatorContext1 = driverContext111.addOperatorContext(1, new PlanNodeId("na"), "na"); OperatorContext operatorContext2 = driverContext111.addOperatorContext(2, new PlanNodeId("na"), "na"); @@ -126,7 +127,7 @@ public void testScheduleMemoryRevoking() OperatorContext operatorContext3 = driverContext112.addOperatorContext(3, new PlanNodeId("na"), "na"); TaskContext taskContext2 = sqlTask2.getQueryContext().addTaskContext(new TaskStateMachine(new TaskId("q2", 1, 1), executor), session, false, false, OptionalInt.empty()); - PipelineContext pipelineContext21 = taskContext2.addPipelineContext(1, false, false); + PipelineContext pipelineContext21 = taskContext2.addPipelineContext(1, false, false, false); DriverContext driverContext211 = pipelineContext21.addDriverContext(); OperatorContext operatorContext4 = driverContext211.addOperatorContext(4, new PlanNodeId("na"), "na"); OperatorContext operatorContext5 = driverContext211.addOperatorContext(5, new PlanNodeId("na"), "na"); @@ -248,7 +249,7 @@ public void testImmediateMemoryRevoking() private OperatorContext createContexts(SqlTask sqlTask) { TaskContext taskContext = sqlTask.getQueryContext().addTaskContext(new TaskStateMachine(new TaskId("q", 1, 1), executor), session, false, false, OptionalInt.empty()); - PipelineContext pipelineContext = taskContext.addPipelineContext(0, false, false); + PipelineContext pipelineContext = taskContext.addPipelineContext(0, false, false, false); DriverContext driverContext = pipelineContext.addDriverContext(); OperatorContext operatorContext = driverContext.addOperatorContext(1, new PlanNodeId("na"), "na"); @@ -288,7 +289,7 @@ private SqlTask newSqlTask() TaskId taskId = new TaskId("query", 0, idGeneator.incrementAndGet()); URI location = URI.create("fake://task/" + taskId); - return new SqlTask( + return createSqlTask( taskId, location, "fake", diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestPlannerWarnings.java b/presto-main/src/test/java/com/facebook/presto/execution/TestPlannerWarnings.java new file mode 100644 index 0000000000000..987492663f26b --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestPlannerWarnings.java @@ -0,0 +1,167 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.DefaultWarningCollector; +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.matching.Captures; +import com.facebook.presto.matching.Pattern; +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import com.facebook.presto.sql.analyzer.SemanticException; +import com.facebook.presto.sql.planner.LogicalPlanner; +import com.facebook.presto.sql.planner.Plan; +import com.facebook.presto.sql.planner.RuleStatsRecorder; +import com.facebook.presto.sql.planner.iterative.IterativeOptimizer; +import com.facebook.presto.sql.planner.iterative.Rule; +import com.facebook.presto.sql.planner.optimizations.PlanOptimizer; +import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.testing.LocalQueryRunner; +import com.facebook.presto.tpch.TpchConnectorFactory; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static com.facebook.presto.sql.planner.plan.Patterns.project; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static java.util.Objects.requireNonNull; +import static java.util.stream.IntStream.range; +import static org.testng.Assert.fail; + +public class TestPlannerWarnings +{ + private LocalQueryRunner queryRunner; + + @BeforeClass + public void setUp() + { + queryRunner = new LocalQueryRunner(testSessionBuilder() + .setCatalog("local") + .setSchema("tiny") + .build()); + + queryRunner.createCatalog( + queryRunner.getDefaultSession().getCatalog().get(), + new TpchConnectorFactory(1), + ImmutableMap.of()); + } + + @AfterClass(alwaysRun = true) + public void tearDown() + { + queryRunner.close(); + } + + @Test + public void testWarning() + { + List warnings = createTestWarnings(3); + List warningCodes = warnings.stream() + .map(PrestoWarning::getWarningCode) + .collect(toImmutableList()); + assertPlannerWarnings(queryRunner, "SELECT * FROM NATION", ImmutableMap.of(), warningCodes, Optional.of(ImmutableList.of(new TestWarningsRule(warnings)))); + } + + public static void assertPlannerWarnings(LocalQueryRunner queryRunner, @Language("SQL") String sql, Map sessionProperties, List expectedWarnings, Optional>> rules) + { + Session.SessionBuilder sessionBuilder = testSessionBuilder() + .setCatalog(queryRunner.getDefaultSession().getCatalog().get()) + .setSchema(queryRunner.getDefaultSession().getSchema().get()); + sessionProperties.forEach(sessionBuilder::setSystemProperty); + WarningCollector warningCollector = new DefaultWarningCollector(new WarningCollectorConfig()); + try { + queryRunner.inTransaction(sessionBuilder.build(), transactionSession -> { + if (rules.isPresent()) { + createPlan(queryRunner, transactionSession, sql, warningCollector, rules.get()); + } + else { + queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.CREATED, false, warningCollector); + } + return null; + }); + } + catch (SemanticException e) { + // ignore + } + Set warnings = warningCollector.getWarnings().stream() + .map(PrestoWarning::getWarningCode) + .collect(toImmutableSet()); + for (WarningCode expectedWarning : expectedWarnings) { + if (!warnings.contains(expectedWarning)) { + fail("Expected warning: " + expectedWarning); + } + } + } + + private static Plan createPlan(LocalQueryRunner queryRunner, Session session, String sql, WarningCollector warningCollector, List> rules) + { + // Warnings from testing rules will be added + PlanOptimizer optimizer = new IterativeOptimizer( + new RuleStatsRecorder(), + queryRunner.getStatsCalculator(), + queryRunner.getCostCalculator(), + ImmutableSet.copyOf(rules)); + + return queryRunner.createPlan(session, sql, ImmutableList.of(optimizer), LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, warningCollector); + } + + public static List createTestWarnings(int numberOfWarnings) + { + checkArgument(numberOfWarnings > 0, "numberOfWarnings must be > 0"); + ImmutableList.Builder builder = ImmutableList.builder(); + range(1, numberOfWarnings) + .mapToObj(code -> new PrestoWarning(new WarningCode(code, "testWarning"), "Test warning " + code)) + .forEach(builder::add); + return builder.build(); + } + + public static class TestWarningsRule + implements Rule + { + private final List warnings; + + public TestWarningsRule(List warnings) + { + this.warnings = ImmutableList.copyOf(requireNonNull(warnings, "warnings is null")); + } + + @Override + public Pattern getPattern() + { + return project(); + } + + @Override + public Result apply(ProjectNode node, Captures captures, Context context) + { + warnings.stream() + .forEach(context.getWarningCollector()::add); + return Result.empty(); + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestPrepareTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestPrepareTask.java index 67c19bd220fe9..25ad9bb2adf75 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestPrepareTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestPrepareTask.java @@ -14,12 +14,12 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.MetadataManager; -import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.tree.AllColumns; import com.facebook.presto.sql.tree.Execute; @@ -102,8 +102,17 @@ public void testPrepareInvalidStatement() private Map executePrepare(String statementName, Statement statement, String sqlString, Session session) { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), sqlString, session, URI.create("fake://uri"), false, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = QueryStateMachine.begin( + sqlString, + session, + URI.create("fake://uri"), + new ResourceGroupId("test"), + false, + transactionManager, + new AccessControlManager(transactionManager), + executor, + metadata, + WarningCollector.NOOP); Prepare prepare = new Prepare(identifier(statementName), statement); new PrepareTask(new SqlParser()).execute(prepare, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()); return stateMachine.getAddedPreparedStatements(); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java index 952419dffefe8..8f2c0020ef831 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java @@ -46,7 +46,9 @@ public void testDefaults() .setQueryMaxExecutionTime(new Duration(100, TimeUnit.DAYS)) .setQueryMaxCpuTime(new Duration(1_000_000_000, TimeUnit.DAYS)) .setInitializationRequiredWorkers(1) - .setInitializationTimeout(new Duration(5, TimeUnit.MINUTES))); + .setInitializationTimeout(new Duration(5, TimeUnit.MINUTES)) + .setRequiredWorkers(1) + .setRequiredWorkersMaxWait(new Duration(5, TimeUnit.MINUTES))); } @Test @@ -73,6 +75,8 @@ public void testExplicitPropertyMappings() .put("query.max-cpu-time", "2d") .put("query-manager.initialization-required-workers", "200") .put("query-manager.initialization-timeout", "1m") + .put("query-manager.required-workers", "333") + .put("query-manager.required-workers-max-wait", "33m") .build(); QueryManagerConfig expected = new QueryManagerConfig() @@ -95,7 +99,9 @@ public void testExplicitPropertyMappings() .setQueryMaxExecutionTime(new Duration(3, TimeUnit.HOURS)) .setQueryMaxCpuTime(new Duration(2, TimeUnit.DAYS)) .setInitializationRequiredWorkers(200) - .setInitializationTimeout(new Duration(1, TimeUnit.MINUTES)); + .setInitializationTimeout(new Duration(1, TimeUnit.MINUTES)) + .setRequiredWorkers(333) + .setRequiredWorkersMaxWait(new Duration(33, TimeUnit.MINUTES)); ConfigAssertions.assertFullMapping(properties, expected); } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestUnwrapExecute.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryPreparer.java similarity index 72% rename from presto-main/src/test/java/com/facebook/presto/execution/TestUnwrapExecute.java rename to presto-main/src/test/java/com/facebook/presto/execution/TestQueryPreparer.java index 7983a42301424..0fca0c516ba3b 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestUnwrapExecute.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryPreparer.java @@ -14,18 +14,15 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.tree.AllColumns; -import com.facebook.presto.sql.tree.Execute; import com.facebook.presto.sql.tree.QualifiedName; -import com.facebook.presto.sql.tree.Statement; import org.testng.annotations.Test; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; -import static com.facebook.presto.execution.SqlQueryManager.unwrapExecuteStatement; -import static com.facebook.presto.execution.SqlQueryManager.validateParameters; import static com.facebook.presto.spi.StandardErrorCode.NOT_FOUND; import static com.facebook.presto.sql.QueryUtil.selectList; import static com.facebook.presto.sql.QueryUtil.simpleQuery; @@ -35,15 +32,16 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; -public class TestUnwrapExecute +public class TestQueryPreparer { private static final SqlParser SQL_PARSER = new SqlParser(); + private static final QueryPreparer QUERY_PREPARER = new QueryPreparer(SQL_PARSER); @Test public void testSelectStatement() { - Statement statement = SQL_PARSER.createStatement("SELECT * FROM foo"); - assertEquals(unwrapExecuteStatement(statement, SQL_PARSER, TEST_SESSION), + PreparedQuery preparedQuery = QUERY_PREPARER.prepareQuery(TEST_SESSION, "SELECT * FROM foo"); + assertEquals(preparedQuery.getStatement(), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("foo")))); } @@ -53,8 +51,8 @@ public void testExecuteStatement() Session session = testSessionBuilder() .addPreparedStatement("my_query", "SELECT * FROM foo") .build(); - Statement statement = SQL_PARSER.createStatement("EXECUTE my_query"); - assertEquals(unwrapExecuteStatement(statement, SQL_PARSER, session), + PreparedQuery preparedQuery = QUERY_PREPARER.prepareQuery(session, "EXECUTE my_query"); + assertEquals(preparedQuery.getStatement(), simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("foo")))); } @@ -62,8 +60,7 @@ public void testExecuteStatement() public void testExecuteStatementDoesNotExist() { try { - Statement statement = SQL_PARSER.createStatement("execute my_query"); - unwrapExecuteStatement(statement, SQL_PARSER, TEST_SESSION); + QUERY_PREPARER.prepareQuery(TEST_SESSION, "execute my_query"); fail("expected exception"); } catch (PrestoException e) { @@ -78,8 +75,7 @@ public void testTooManyParameters() Session session = testSessionBuilder() .addPreparedStatement("my_query", "SELECT * FROM foo where col1 = ?") .build(); - Statement statement = SQL_PARSER.createStatement("EXECUTE my_query USING 1,2"); - validateParameters(unwrapExecuteStatement(statement, SQL_PARSER, session), ((Execute) statement).getParameters()); + QUERY_PREPARER.prepareQuery(session, "EXECUTE my_query USING 1,2"); fail("expected exception"); } catch (SemanticException e) { @@ -94,8 +90,7 @@ public void testTooFewParameters() Session session = testSessionBuilder() .addPreparedStatement("my_query", "SELECT ? FROM foo where col1 = ?") .build(); - Statement statement = SQL_PARSER.createStatement("EXECUTE my_query USING 1"); - validateParameters(unwrapExecuteStatement(statement, SQL_PARSER, session), ((Execute) statement).getParameters()); + QUERY_PREPARER.prepareQuery(session, "EXECUTE my_query USING 1"); fail("expected exception"); } catch (SemanticException e) { diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java index dfbeb7b7983f4..c19dda8a379cb 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java @@ -16,20 +16,22 @@ import com.facebook.presto.Session; import com.facebook.presto.client.FailureInfo; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.VersionedMemoryPoolId; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.memory.MemoryPoolId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.type.Type; import com.facebook.presto.transaction.TransactionManager; import com.google.common.base.Ticker; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.testing.TestingTicker; +import io.airlift.units.Duration; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -41,7 +43,7 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; import static com.facebook.presto.execution.QueryState.FAILED; @@ -58,6 +60,8 @@ import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; import static io.airlift.concurrent.MoreFutures.tryGetFutureValue; import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; @@ -67,7 +71,6 @@ public class TestQueryStateMachine { - private static final QueryId QUERY_ID = new QueryId("query_id"); private static final String QUERY = "sql"; private static final URI LOCATION = URI.create("fake://fake-query"); private static final SQLException FAILED_CAUSE = new SQLException("FAILED"); @@ -107,7 +110,7 @@ public void testBasicStateChanges() assertState(stateMachine, RUNNING); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); } @@ -130,35 +133,43 @@ public void testStateChangesWithResourceWaiting() assertState(stateMachine, RUNNING); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); } @Test public void testQueued() { - QueryStateMachine stateMachine = createQueryStateMachine(); - assertState(stateMachine, QUEUED); - - assertTrue(stateMachine.transitionToPlanning()); - assertState(stateMachine, PLANNING); - - stateMachine = createQueryStateMachine(); - assertTrue(stateMachine.transitionToStarting()); - assertState(stateMachine, STARTING); + // all time before the first state transition is accounted to queueing + assertAllTimeSpentInQueueing(QUEUED, queryStateMachine -> {}); + assertAllTimeSpentInQueueing(WAITING_FOR_RESOURCES, QueryStateMachine::transitionToWaitingForResources); + assertAllTimeSpentInQueueing(PLANNING, QueryStateMachine::transitionToPlanning); + assertAllTimeSpentInQueueing(STARTING, QueryStateMachine::transitionToStarting); + assertAllTimeSpentInQueueing(RUNNING, QueryStateMachine::transitionToRunning); + + assertAllTimeSpentInQueueing(FINISHED, stateMachine -> { + stateMachine.transitionToFinishing(); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); + }); + + assertAllTimeSpentInQueueing(FAILED, stateMachine -> stateMachine.transitionToFailed(FAILED_CAUSE)); + } - stateMachine = createQueryStateMachine(); - assertTrue(stateMachine.transitionToRunning()); - assertState(stateMachine, RUNNING); + private void assertAllTimeSpentInQueueing(QueryState expectedState, Consumer stateTransition) + { + TestingTicker ticker = new TestingTicker(); + QueryStateMachine stateMachine = createQueryStateMachineWithTicker(ticker); + ticker.increment(7, MILLISECONDS); - stateMachine = createQueryStateMachine(); - assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); - assertState(stateMachine, FINISHED); + stateTransition.accept(stateMachine); + assertEquals(stateMachine.getQueryState(), expectedState); - stateMachine = createQueryStateMachine(); - assertTrue(stateMachine.transitionToFailed(FAILED_CAUSE)); - assertState(stateMachine, FAILED, FAILED_CAUSE); + QueryStats queryStats = stateMachine.getQueryInfo(Optional.empty()).getQueryStats(); + assertEquals(queryStats.getQueuedTime(), new Duration(7, MILLISECONDS)); + assertEquals(queryStats.getResourceWaitingTime(), new Duration(0, MILLISECONDS)); + assertEquals(queryStats.getTotalPlanningTime(), new Duration(0, MILLISECONDS)); + assertEquals(queryStats.getExecutionTime(), new Duration(0, MILLISECONDS)); + assertEquals(queryStats.getFinishingTime(), new Duration(0, MILLISECONDS)); } @Test @@ -182,7 +193,7 @@ public void testPlanning() stateMachine = createQueryStateMachine(); stateMachine.transitionToPlanning(); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); stateMachine = createQueryStateMachine(); @@ -210,7 +221,7 @@ public void testStarting() stateMachine = createQueryStateMachine(); stateMachine.transitionToStarting(); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); stateMachine = createQueryStateMachine(); @@ -236,7 +247,7 @@ public void testRunning() assertState(stateMachine, RUNNING); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); stateMachine = createQueryStateMachine(); @@ -250,7 +261,7 @@ public void testFinished() { QueryStateMachine stateMachine = createQueryStateMachine(); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertFinalState(stateMachine, FINISHED); } @@ -277,26 +288,36 @@ public void testPlanningTimeDuration() QueryStateMachine stateMachine = createQueryStateMachineWithTicker(mockTicker); assertState(stateMachine, QUEUED); - mockTicker.increment(100, TimeUnit.MILLISECONDS); + mockTicker.increment(50, MILLISECONDS); + assertTrue(stateMachine.transitionToWaitingForResources()); + assertState(stateMachine, WAITING_FOR_RESOURCES); + + mockTicker.increment(100, MILLISECONDS); assertTrue(stateMachine.transitionToPlanning()); assertState(stateMachine, PLANNING); - mockTicker.increment(500, TimeUnit.MILLISECONDS); + mockTicker.increment(200, MILLISECONDS); assertTrue(stateMachine.transitionToStarting()); assertState(stateMachine, STARTING); - mockTicker.increment(300, TimeUnit.MILLISECONDS); + mockTicker.increment(300, MILLISECONDS); assertTrue(stateMachine.transitionToRunning()); assertState(stateMachine, RUNNING); - mockTicker.increment(200, TimeUnit.MILLISECONDS); + mockTicker.increment(400, MILLISECONDS); assertTrue(stateMachine.transitionToFinishing()); - tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, TimeUnit.SECONDS); + tryGetFutureValue(stateMachine.getStateChange(FINISHING), 2, SECONDS); assertState(stateMachine, FINISHED); QueryStats queryStats = stateMachine.getQueryInfo(Optional.empty()).getQueryStats(); - assertTrue(queryStats.getQueuedTime().toMillis() == 100); - assertTrue(queryStats.getTotalPlanningTime().toMillis() == 500); + assertEquals(queryStats.getElapsedTime().toMillis(), 1050); + assertEquals(queryStats.getQueuedTime().toMillis(), 50); + assertEquals(queryStats.getResourceWaitingTime().toMillis(), 100); + assertEquals(queryStats.getTotalPlanningTime().toMillis(), 200); + // there is no way to induce finishing time without a transaction and connector + assertEquals(queryStats.getFinishingTime().toMillis(), 0); + // query execution time is starts when query transitions to planning + assertEquals(queryStats.getExecutionTime().toMillis(), 900); } @Test @@ -362,14 +383,14 @@ private static void assertState(QueryStateMachine stateMachine, QueryState expec private static void assertState(QueryStateMachine stateMachine, QueryState expectedState, Exception expectedException) { - assertEquals(stateMachine.getQueryId(), QUERY_ID); + assertEquals(stateMachine.getQueryId(), TEST_SESSION.getQueryId()); assertEqualSessionsWithoutTransactionId(stateMachine.getSession(), TEST_SESSION); assertSame(stateMachine.getMemoryPool(), MEMORY_POOL); assertEquals(stateMachine.getSetSessionProperties(), SET_SESSION_PROPERTIES); assertEquals(stateMachine.getResetSessionProperties(), RESET_SESSION_PROPERTIES); QueryInfo queryInfo = stateMachine.getQueryInfo(Optional.empty()); - assertEquals(queryInfo.getQueryId(), QUERY_ID); + assertEquals(queryInfo.getQueryId(), TEST_SESSION.getQueryId()); assertEquals(queryInfo.getSelf(), LOCATION); assertFalse(queryInfo.getOutputStage().isPresent()); assertEquals(queryInfo.getQuery(), QUERY); @@ -380,59 +401,25 @@ private static void assertState(QueryStateMachine stateMachine, QueryState expec assertEquals(queryInfo.getMemoryPool(), MEMORY_POOL.getId()); QueryStats queryStats = queryInfo.getQueryStats(); - if (queryInfo.getState() == QUEUED) { - assertNull(queryStats.getTotalPlanningTime()); + assertNotNull(queryStats.getElapsedTime()); + assertNotNull(queryStats.getQueuedTime()); + assertNotNull(queryStats.getResourceWaitingTime()); + assertNotNull(queryStats.getExecutionTime()); + assertNotNull(queryStats.getTotalPlanningTime()); + assertNotNull(queryStats.getFinishingTime()); + + assertNotNull(queryStats.getCreateTime()); + if (queryInfo.getState() == QUEUED || queryInfo.getState() == WAITING_FOR_RESOURCES) { assertNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); } - else if (queryInfo.getState() == WAITING_FOR_RESOURCES) { - assertNotNull(queryStats.getQueuedTime()); - assertNull(queryStats.getResourceWaitingTime()); - assertNull(queryStats.getTotalPlanningTime()); - assertNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); - } - else if (queryInfo.getState() == PLANNING) { - assertNotNull(queryStats.getQueuedTime()); - assertNotNull(queryStats.getResourceWaitingTime()); - assertNull(queryStats.getTotalPlanningTime()); - assertNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); - } - else if (queryInfo.getState() == STARTING) { - assertNotNull(queryStats.getQueuedTime()); - assertNotNull(queryStats.getResourceWaitingTime()); - assertNotNull(queryStats.getTotalPlanningTime()); - assertNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); - } - else if (queryInfo.getState() == RUNNING) { - assertNotNull(queryStats.getQueuedTime()); - assertNotNull(queryStats.getResourceWaitingTime()); - assertNotNull(queryStats.getTotalPlanningTime()); + else { assertNotNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); } - else if (queryInfo.getState() == FINISHING) { - assertNotNull(queryStats.getQueuedTime()); - assertNotNull(queryStats.getResourceWaitingTime()); - assertNotNull(queryStats.getTotalPlanningTime()); - assertNotNull(queryStats.getExecutionStartTime()); - assertNull(queryStats.getFinishingTime()); - assertNull(queryStats.getEndTime()); + if (queryInfo.getState().isDone()) { + assertNotNull(queryStats.getEndTime()); } else { - assertNotNull(queryStats.getQueuedTime()); - assertNotNull(queryStats.getResourceWaitingTime()); - assertNotNull(queryStats.getTotalPlanningTime()); - assertNotNull(queryStats.getExecutionStartTime()); - assertNotNull(queryStats.getFinishingTime()); - assertNotNull(queryStats.getEndTime()); + assertNull(queryStats.getEndTime()); } assertEquals(stateMachine.getQueryState(), expectedState); @@ -440,7 +427,8 @@ else if (queryInfo.getState() == FINISHING) { assertEquals(stateMachine.isDone(), expectedState.isDone()); if (expectedState == FAILED) { - FailureInfo failure = queryInfo.getFailureInfo(); + assertNotNull(queryInfo.getFailureInfo()); + FailureInfo failure = queryInfo.getFailureInfo().toFailureInfo(); assertNotNull(failure); assertEquals(failure.getType(), expectedException.getClass().getName()); if (expectedException instanceof PrestoException) { @@ -465,7 +453,18 @@ private QueryStateMachine createQueryStateMachineWithTicker(Ticker ticker) Metadata metadata = MetadataManager.createTestMetadataManager(); TransactionManager transactionManager = createTestTransactionManager(); AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.beginWithTicker(QUERY_ID, QUERY, TEST_SESSION, LOCATION, false, transactionManager, accessControl, executor, ticker, metadata); + QueryStateMachine stateMachine = QueryStateMachine.beginWithTicker( + QUERY, + TEST_SESSION, + LOCATION, + new ResourceGroupId("test"), + false, + transactionManager, + accessControl, + executor, + ticker, + metadata, + WarningCollector.NOOP); stateMachine.setInputs(INPUTS); stateMachine.setOutput(OUTPUT); stateMachine.setColumns(OUTPUT_FIELD_NAMES, OUTPUT_FIELD_TYPES); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStats.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStats.java index d77e8906062f5..d8b8f041f9a3a 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStats.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStats.java @@ -39,102 +39,99 @@ public class TestQueryStats { public static final List operatorSummaries = ImmutableList.of( new OperatorStats( - 1, - 1, - new PlanNodeId("1"), + 10, + 11, + 12, + new PlanNodeId("13"), TableWriterOperator.class.getSimpleName(), - 0L, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(500L), - 100L, - 1.0, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - 1L, - succinctBytes(1L), - new Duration(1, NANOSECONDS), - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), + 14L, + 15L, + new Duration(16, NANOSECONDS), + new Duration(17, NANOSECONDS), + succinctBytes(18L), + succinctBytes(19L), + 110L, + 111.0, + 112L, + new Duration(113, NANOSECONDS), + new Duration(114, NANOSECONDS), + succinctBytes(116L), + 117L, + succinctBytes(118L), + new Duration(119, NANOSECONDS), + 120L, + new Duration(121, NANOSECONDS), + new Duration(122, NANOSECONDS), + succinctBytes(124L), + succinctBytes(125L), + succinctBytes(126L), + succinctBytes(127L), + succinctBytes(128L), + succinctBytes(129L), Optional.empty(), null), new OperatorStats( - 1, - 1, - new PlanNodeId("2"), + 20, + 21, + 22, + new PlanNodeId("23"), FilterAndProjectOperator.class.getSimpleName(), - 0L, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - 1L, - 1.0, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(500L), - 100L, - succinctBytes(1L), - new Duration(1, NANOSECONDS), - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), + 24L, + 25L, + new Duration(26, NANOSECONDS), + new Duration(27, NANOSECONDS), + succinctBytes(28L), + succinctBytes(29L), + 210L, + 211.0, + 212L, + new Duration(213, NANOSECONDS), + new Duration(214, NANOSECONDS), + succinctBytes(216L), + 217L, + succinctBytes(218L), + new Duration(219, NANOSECONDS), + 220L, + new Duration(221, NANOSECONDS), + new Duration(222, NANOSECONDS), + succinctBytes(224L), + succinctBytes(225L), + succinctBytes(226L), + succinctBytes(227L), + succinctBytes(228L), + succinctBytes(229L), Optional.empty(), null), new OperatorStats( - 1, - 1, - new PlanNodeId("3"), + 30, + 31, + 32, + new PlanNodeId("33"), TableWriterOperator.class.getSimpleName(), - 0L, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1000L), - 300L, - 1.0, - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - 1L, - succinctBytes(1L), - new Duration(1, NANOSECONDS), - 0L, - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - new Duration(1, NANOSECONDS), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), - succinctBytes(1L), + 34L, + 35L, + new Duration(36, NANOSECONDS), + new Duration(37, NANOSECONDS), + succinctBytes(38L), + succinctBytes(39L), + 310L, + 311.0, + 312L, + new Duration(313, NANOSECONDS), + new Duration(314, NANOSECONDS), + succinctBytes(316L), + 317L, + succinctBytes(318L), + new Duration(319, NANOSECONDS), + 320L, + new Duration(321, NANOSECONDS), + new Duration(322, NANOSECONDS), + succinctBytes(324L), + succinctBytes(325L), + succinctBytes(326L), + succinctBytes(327L), + succinctBytes(328L), + succinctBytes(329L), Optional.empty(), null)); @@ -146,6 +143,7 @@ public class TestQueryStats new Duration(6, NANOSECONDS), new Duration(5, NANOSECONDS), new Duration(31, NANOSECONDS), + new Duration(41, NANOSECONDS), new Duration(7, NANOSECONDS), new Duration(8, NANOSECONDS), @@ -172,7 +170,6 @@ public class TestQueryStats true, new Duration(20, NANOSECONDS), new Duration(21, NANOSECONDS), - new Duration(22, NANOSECONDS), new Duration(23, NANOSECONDS), false, ImmutableSet.of(), @@ -219,7 +216,7 @@ public static void assertExpectedQueryStats(QueryStats actual) assertEquals(actual.getElapsedTime(), new Duration(6, NANOSECONDS)); assertEquals(actual.getQueuedTime(), new Duration(5, NANOSECONDS)); - assertEquals(actual.getExecutionTime(), new Duration(1, NANOSECONDS)); + assertEquals(actual.getExecutionTime(), new Duration(41, NANOSECONDS)); assertEquals(actual.getAnalysisTime(), new Duration(7, NANOSECONDS)); assertEquals(actual.getDistributedPlanningTime(), new Duration(8, NANOSECONDS)); @@ -244,7 +241,6 @@ public static void assertExpectedQueryStats(QueryStats actual) assertEquals(actual.getTotalScheduledTime(), new Duration(20, NANOSECONDS)); assertEquals(actual.getTotalCpuTime(), new Duration(21, NANOSECONDS)); - assertEquals(actual.getTotalUserTime(), new Duration(22, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(23, NANOSECONDS)); assertEquals(actual.getRawInputDataSize(), new DataSize(24, BYTE)); @@ -268,7 +264,7 @@ public static void assertExpectedQueryStats(QueryStats actual) assertEquals(gcStatistics.getTotalFullGcSec(), 106); assertEquals(gcStatistics.getAverageFullGcSec(), 107); - assertEquals(400L, actual.getWrittenPositions()); - assertEquals(1500L, actual.getLogicalWrittenDataSize().toBytes()); + assertEquals(420, actual.getWrittenPositions()); + assertEquals(58, actual.getLogicalWrittenDataSize().toBytes()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestResetSessionTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestResetSessionTask.java index 4d2e6f147d2d6..1e6930e9c79c6 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestResetSessionTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestResetSessionTask.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.block.BlockEncodingManager; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.ColumnPropertyManager; @@ -24,7 +25,7 @@ import com.facebook.presto.metadata.TablePropertyManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AllowAllAccessControl; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.ResetSession; @@ -103,15 +104,16 @@ public void test() .build(); QueryStateMachine stateMachine = QueryStateMachine.begin( - new QueryId("query"), "reset foo", session, URI.create("fake://uri"), + new ResourceGroupId("test"), false, transactionManager, accessControl, executor, - metadata); + metadata, + WarningCollector.NOOP); getFutureValue(new ResetSessionTask().execute( new ResetSession(QualifiedName.of(CATALOG_NAME, "baz")), diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestRollbackTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestRollbackTask.java index 167d3208a2b4e..1a34dcab9d295 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestRollbackTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestRollbackTask.java @@ -16,12 +16,11 @@ import com.facebook.presto.Session; import com.facebook.presto.Session.SessionBuilder; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.MetadataManager; -import com.facebook.presto.security.AccessControl; -import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.tree.Rollback; import com.facebook.presto.transaction.TransactionId; import com.facebook.presto.transaction.TransactionManager; @@ -29,6 +28,7 @@ import org.testng.annotations.Test; import java.net.URI; +import java.util.Optional; import java.util.concurrent.ExecutorService; import static com.facebook.presto.spi.StandardErrorCode.NOT_IN_TRANSACTION; @@ -59,18 +59,17 @@ public void tearDown() public void testRollback() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .setTransactionId(transactionManager.beginTransaction(false)) .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "ROLLBACK", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("ROLLBACK", session, transactionManager); assertTrue(stateMachine.getSession().getTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); - assertTrue(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } @@ -79,11 +78,10 @@ public void testRollback() public void testNoTransactionRollback() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "ROLLBACK", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("ROLLBACK", session, transactionManager); try { getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); @@ -92,8 +90,8 @@ public void testNoTransactionRollback() catch (PrestoException e) { assertEquals(e.getErrorCode(), NOT_IN_TRANSACTION.toErrorCode()); } - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } @@ -102,20 +100,34 @@ public void testNoTransactionRollback() public void testUnknownTransactionRollback() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); Session session = sessionBuilder() .setTransactionId(TransactionId.create()) // Use a random transaction ID that is unknown to the system .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "ROLLBACK", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("ROLLBACK", session, transactionManager); getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); - assertTrue(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); // Still issue clear signal - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); // Still issue clear signal + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); } + private QueryStateMachine createQueryStateMachine(String query, Session session, TransactionManager transactionManager) + { + return QueryStateMachine.begin( + query, + session, + URI.create("fake://uri"), + new ResourceGroupId("test"), + true, + transactionManager, + new AllowAllAccessControl(), + executor, + metadata, + WarningCollector.NOOP); + } + private static SessionBuilder sessionBuilder() { return testSessionBuilder() diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSetPathTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSetPathTask.java index f1fd6549b666a..ccd17875fe41f 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestSetPathTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSetPathTask.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.block.BlockEncodingManager; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.ColumnPropertyManager; import com.facebook.presto.metadata.MetadataManager; @@ -23,7 +24,7 @@ import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.tree.Identifier; import com.facebook.presto.sql.tree.PathElement; @@ -81,9 +82,8 @@ public void tearDown() @Test public void testSetPath() { - PathSpecification pathSpecification = new PathSpecification( - Optional.empty(), ImmutableList.of( - new PathElement(Optional.empty(), new Identifier("foo")))); + PathSpecification pathSpecification = new PathSpecification(Optional.empty(), ImmutableList.of( + new PathElement(Optional.empty(), new Identifier("foo")))); QueryStateMachine stateMachine = createQueryStateMachine("SET PATH foo"); executeSetPathTask(pathSpecification, stateMachine); @@ -94,9 +94,8 @@ public void testSetPath() @Test(expectedExceptions = PrestoException.class, expectedExceptionsMessageRegExp = "Catalog does not exist: .*") public void testSetPathInvalidCatalog() { - PathSpecification invalidPathSpecification = new PathSpecification( - Optional.empty(), ImmutableList.of( - new PathElement(Optional.of(new Identifier("invalidCatalog")), new Identifier("thisDoesNotMatter")))); + PathSpecification invalidPathSpecification = new PathSpecification(Optional.empty(), ImmutableList.of( + new PathElement(Optional.of(new Identifier("invalidCatalog")), new Identifier("thisDoesNotMatter")))); QueryStateMachine stateMachine = createQueryStateMachine("SET PATH invalidCatalog.thisDoesNotMatter"); executeSetPathTask(invalidPathSpecification, stateMachine); @@ -105,15 +104,16 @@ public void testSetPathInvalidCatalog() private QueryStateMachine createQueryStateMachine(String query) { return QueryStateMachine.begin( - new QueryId("query"), query, TEST_SESSION, URI.create("fake://uri"), + new ResourceGroupId("test"), false, transactionManager, accessControl, executor, - metadata); + metadata, + WarningCollector.NOOP); } private void executeSetPathTask(PathSpecification pathSpecification, QueryStateMachine stateMachine) diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSetSessionTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSetSessionTask.java index 335d283e26845..93c347d5e5878 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestSetSessionTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSetSessionTask.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.block.BlockEncodingManager; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.ColumnPropertyManager; @@ -23,10 +24,13 @@ import com.facebook.presto.metadata.TablePropertyManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AllowAllAccessControl; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.spi.session.PropertyMetadata; import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; +import com.facebook.presto.sql.tree.LongLiteral; import com.facebook.presto.sql.tree.Parameter; import com.facebook.presto.sql.tree.QualifiedName; import com.facebook.presto.sql.tree.SetSession; @@ -45,18 +49,23 @@ import java.util.concurrent.ExecutorService; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_SESSION_PROPERTY; import static com.facebook.presto.spi.session.PropertyMetadata.stringProperty; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.testing.TestingSession.createBogusTestingCatalog; import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; import static io.airlift.concurrent.MoreFutures.getFutureValue; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.concurrent.Executors.newCachedThreadPool; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; public class TestSetSessionTask { private static final String CATALOG_NAME = "foo"; + private static final String MUST_BE_POSITIVE = "property must be positive"; private final TransactionManager transactionManager; private final AccessControl accessControl; private final MetadataManager metadata; @@ -84,14 +93,37 @@ public TestSetSessionTask() false)); Catalog bogusTestingCatalog = createBogusTestingCatalog(CATALOG_NAME); - metadata.getSessionPropertyManager().addConnectorSessionProperties(bogusTestingCatalog.getConnectorId(), ImmutableList.of(stringProperty( - "bar", - "test property", - null, - false))); + + List> sessionProperties = ImmutableList.of( + stringProperty( + "bar", + "test property", + null, + false), + new PropertyMetadata<>( + "positive_property", + "property that should be positive", + INTEGER, + Integer.class, + null, + false, + value -> validatePositive(value), + value -> value)); + + metadata.getSessionPropertyManager().addConnectorSessionProperties(bogusTestingCatalog.getConnectorId(), sessionProperties); + catalogManager.registerCatalog(bogusTestingCatalog); } + private static int validatePositive(Object value) + { + int intValue = ((Number) value).intValue(); + if (intValue < 0) { + throw new PrestoException(INVALID_SESSION_PROPERTY, MUST_BE_POSITIVE); + } + return intValue; + } + private final ExecutorService executor = newCachedThreadPool(daemonThreadsNamed("stage-executor-%s")); @AfterClass(alwaysRun = true) @@ -109,26 +141,57 @@ public void testSetSession() new StringLiteral("ana"))), "banana"); } + @Test + public void testSetSessionWithValidation() + { + testSetSessionWithValidation(new LongLiteral("0"), "0"); + testSetSessionWithValidation(new LongLiteral("2"), "2"); + + try { + testSetSessionWithValidation(new LongLiteral("-1"), "-1"); + fail(); + } + catch (PrestoException e) { + assertEquals(e.getMessage(), MUST_BE_POSITIVE); + } + } + @Test public void testSetSessionWithParameters() { List expressionList = new ArrayList<>(); expressionList.add(new StringLiteral("ban")); expressionList.add(new Parameter(0)); - testSetSessionWithParameters(new FunctionCall(QualifiedName.of("concat"), expressionList), "banana", ImmutableList.of(new StringLiteral("ana"))); + testSetSessionWithParameters("bar", new FunctionCall(QualifiedName.of("concat"), expressionList), "banana", ImmutableList.of(new StringLiteral("ana"))); } private void testSetSession(Expression expression, String expectedValue) { - testSetSessionWithParameters(expression, expectedValue, emptyList()); + testSetSessionWithParameters("bar", expression, expectedValue, emptyList()); + } + + private void testSetSessionWithValidation(Expression expression, String expectedValue) + { + testSetSessionWithParameters("positive_property", expression, expectedValue, emptyList()); } - private void testSetSessionWithParameters(Expression expression, String expectedValue, List parameters) + private void testSetSessionWithParameters(String property, Expression expression, String expectedValue, List parameters) { - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "set foo.bar = 'baz'", TEST_SESSION, URI.create("fake://uri"), false, transactionManager, accessControl, executor, metadata); - getFutureValue(new SetSessionTask().execute(new SetSession(QualifiedName.of(CATALOG_NAME, "bar"), expression), transactionManager, metadata, accessControl, stateMachine, parameters)); + QualifiedName qualifiedPropName = QualifiedName.of(CATALOG_NAME, property); + QueryStateMachine stateMachine = QueryStateMachine.begin( + format("set %s = 'old_value'", qualifiedPropName), + TEST_SESSION, + URI.create("fake://uri"), + new ResourceGroupId("test"), + false, + transactionManager, + accessControl, + executor, + metadata, + WarningCollector.NOOP); + getFutureValue(new SetSessionTask().execute(new SetSession(qualifiedPropName, expression), transactionManager, metadata, accessControl, stateMachine, parameters)); Map sessionProperties = stateMachine.getSetSessionProperties(); - assertEquals(sessionProperties, ImmutableMap.of("foo.bar", expectedValue)); + assertEquals(sessionProperties, ImmutableMap.of(qualifiedPropName.toString(), expectedValue)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlStageExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlStageExecution.java new file mode 100644 index 0000000000000..b8a0b324b0ccd --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlStageExecution.java @@ -0,0 +1,183 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.client.NodeVersion; +import com.facebook.presto.cost.StatsAndCosts; +import com.facebook.presto.execution.TestSqlTaskManager.MockLocationFactory; +import com.facebook.presto.execution.scheduler.SplitSchedulerStats; +import com.facebook.presto.failureDetector.NoOpFailureDetector; +import com.facebook.presto.metadata.PrestoNode; +import com.facebook.presto.operator.StageExecutionStrategy; +import com.facebook.presto.spi.Node; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.planner.Partitioning; +import com.facebook.presto.sql.planner.PartitioningScheme; +import com.facebook.presto.sql.planner.PlanFragment; +import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.plan.PlanFragmentId; +import com.facebook.presto.sql.planner.plan.PlanNode; +import com.facebook.presto.sql.planner.plan.PlanNodeId; +import com.facebook.presto.sql.planner.plan.RemoteSourceNode; +import com.facebook.presto.util.FinalizerService; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.SettableFuture; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.net.URI; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; + +import static com.facebook.presto.OutputBuffers.BufferType.ARBITRARY; +import static com.facebook.presto.OutputBuffers.createInitialEmptyOutputBuffers; +import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.execution.SqlStageExecution.createSqlStageExecution; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; +import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.REPARTITION; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.Executors.newScheduledThreadPool; +import static java.util.concurrent.TimeUnit.MINUTES; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +public class TestSqlStageExecution +{ + private ExecutorService executor; + private ScheduledExecutorService scheduledExecutor; + + @BeforeClass + public void setUp() + { + executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); + scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); + } + + @AfterClass + public void tearDown() + { + executor.shutdownNow(); + executor = null; + scheduledExecutor.shutdownNow(); + scheduledExecutor = null; + } + + @Test(timeOut = 2 * 60 * 1000) + public void testFinalStageInfo() + throws Exception + { + // run test a few times to catch any race conditions + // this is not done with TestNG invocation count so there can be a global time limit on the test + for (int iteration = 0; iteration < 10; iteration++) { + testFinalStageInfoInternal(); + } + } + + private void testFinalStageInfoInternal() + throws Exception + { + NodeTaskMap nodeTaskMap = new NodeTaskMap(new FinalizerService()); + + StageId stageId = new StageId(new QueryId("query"), 0); + SqlStageExecution stage = createSqlStageExecution( + stageId, + new MockLocationFactory().createStageLocation(stageId), + createExchangePlanFragment(), + new MockRemoteTaskFactory(executor, scheduledExecutor), + TEST_SESSION, + true, + nodeTaskMap, + executor, + new NoOpFailureDetector(), + new SplitSchedulerStats()); + stage.setOutputBuffers(createInitialEmptyOutputBuffers(ARBITRARY)); + + // add listener that fetches stage info when the final status is available + SettableFuture finalStageInfo = SettableFuture.create(); + stage.addFinalStageInfoListener(finalStageInfo::set); + + // in a background thread add a ton of tasks + CountDownLatch latch = new CountDownLatch(1000); + Future addTasksTask = executor.submit(() -> { + try { + for (int i = 0; i < 1_000_000; i++) { + if (Thread.interrupted()) { + return; + } + Node node = new PrestoNode( + "source" + i, + URI.create("http://10.0.0." + (i / 10_000) + ":" + (i % 10_000)), + NodeVersion.UNKNOWN, + false); + stage.scheduleTask(node, i, OptionalInt.empty()); + latch.countDown(); + } + } + finally { + while (latch.getCount() > 0) { + latch.countDown(); + } + } + }); + + // wait for some tasks to be created, and then abort the query + latch.await(1, MINUTES); + assertFalse(stage.getStageInfo().getTasks().isEmpty()); + stage.abort(); + + // once the final stage info is available, verify that it is complete + StageInfo stageInfo = finalStageInfo.get(1, MINUTES); + assertFalse(stageInfo.getTasks().isEmpty()); + assertTrue(stageInfo.isCompleteInfo()); + assertSame(stage.getStageInfo(), stageInfo); + + // cancel the background thread adding tasks + addTasksTask.cancel(true); + } + + private static PlanFragment createExchangePlanFragment() + { + PlanNode planNode = new RemoteSourceNode( + new PlanNodeId("exchange"), + ImmutableList.of(new PlanFragmentId("source")), + ImmutableList.of(new Symbol("column")), + Optional.empty(), + REPARTITION); + + ImmutableMap.Builder types = ImmutableMap.builder(); + for (Symbol symbol : planNode.getOutputSymbols()) { + types.put(symbol, VARCHAR); + } + return new PlanFragment( + new PlanFragmentId("exchange_fragment_id"), + planNode, + types.build(), + SOURCE_DISTRIBUTION, + ImmutableList.of(planNode.getId()), + new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), planNode.getOutputSymbols()), + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTask.java index 96e6958ea7825..d7b7caf8f9e0b 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTask.java @@ -46,11 +46,12 @@ import static com.facebook.presto.OutputBuffers.BufferType.PARTITIONED; import static com.facebook.presto.OutputBuffers.createInitialEmptyOutputBuffers; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.execution.SqlTask.createSqlTask; import static com.facebook.presto.execution.TaskTestUtils.EMPTY_SOURCES; import static com.facebook.presto.execution.TaskTestUtils.PLAN_FRAGMENT; import static com.facebook.presto.execution.TaskTestUtils.SPLIT; import static com.facebook.presto.execution.TaskTestUtils.TABLE_SCAN_NODE_ID; -import static com.facebook.presto.execution.TaskTestUtils.createTestQueryMonitor; +import static com.facebook.presto.execution.TaskTestUtils.createTestSplitMonitor; import static com.facebook.presto.execution.TaskTestUtils.createTestingPlanner; import static com.facebook.presto.execution.TaskTestUtils.updateTask; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; @@ -91,7 +92,7 @@ public TestSqlTask() taskNotificationExecutor, taskExecutor, planner, - createTestQueryMonitor(), + createTestSplitMonitor(), new TaskManagerConfig()); } @@ -313,7 +314,7 @@ public SqlTask createInitialTask() queryContext.addTaskContext(new TaskStateMachine(taskId, taskNotificationExecutor), testSessionBuilder().build(), false, false, OptionalInt.empty()); - return new SqlTask( + return createSqlTask( taskId, location, "fake", diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskExecution.java index 825187c67969e..322522753e77e 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskExecution.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskExecution.java @@ -90,7 +90,7 @@ import static com.facebook.presto.block.BlockAssertions.createStringSequenceBlock; import static com.facebook.presto.block.BlockAssertions.createStringsBlock; import static com.facebook.presto.execution.TaskTestUtils.TABLE_SCAN_NODE_ID; -import static com.facebook.presto.execution.TaskTestUtils.createTestQueryMonitor; +import static com.facebook.presto.execution.TaskTestUtils.createTestSplitMonitor; import static com.facebook.presto.execution.buffer.BufferState.OPEN; import static com.facebook.presto.execution.buffer.BufferState.TERMINAL_BUFFER_STATES; import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; @@ -122,7 +122,7 @@ public class TestSqlTaskExecution @DataProvider public static Object[][] executionStrategies() { - return new Object[][]{{UNGROUPED_EXECUTION}, {GROUPED_EXECUTION}}; + return new Object[][] {{UNGROUPED_EXECUTION}, {GROUPED_EXECUTION}}; } @Test(dataProvider = "executionStrategies", timeOut = 20_000) @@ -178,7 +178,7 @@ public void testSimple(PipelineExecutionStrategy executionStrategy) localExecutionPlan, taskExecutor, taskNotificationExecutor, - createTestQueryMonitor()); + createTestSplitMonitor()); // // test body @@ -429,7 +429,7 @@ public void testComplex(PipelineExecutionStrategy executionStrategy) localExecutionPlan, taskExecutor, taskNotificationExecutor, - createTestQueryMonitor()); + createTestSplitMonitor()); // // test body @@ -700,6 +700,8 @@ private ScheduledSplit newScheduledSplit(int sequenceId, PlanNodeId planNodeId, public static class Pauser { private volatile SettableFuture future = SettableFuture.create(); + + public Pauser() { future.set(null); } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskManager.java b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskManager.java index 173db3f33f5b0..e1fb019a7429a 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskManager.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestSqlTaskManager.java @@ -52,7 +52,7 @@ import static com.facebook.presto.execution.TaskTestUtils.PLAN_FRAGMENT; import static com.facebook.presto.execution.TaskTestUtils.SPLIT; import static com.facebook.presto.execution.TaskTestUtils.TABLE_SCAN_NODE_ID; -import static com.facebook.presto.execution.TaskTestUtils.createTestQueryMonitor; +import static com.facebook.presto.execution.TaskTestUtils.createTestSplitMonitor; import static com.facebook.presto.execution.TaskTestUtils.createTestingPlanner; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; @@ -240,7 +240,7 @@ public SqlTaskManager createSqlTaskManager(TaskManagerConfig config) createTestingPlanner(), new MockLocationFactory(), taskExecutor, - createTestQueryMonitor(), + createTestSplitMonitor(), new NodeInfo("test"), localMemoryManager, taskManagementExecutor, diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestStageStateMachine.java b/presto-main/src/test/java/com/facebook/presto/execution/TestStageStateMachine.java index 13b6906c7cf9f..101f9f3297b70 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestStageStateMachine.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestStageStateMachine.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.execution; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.execution.scheduler.SplitSchedulerStats; import com.facebook.presto.operator.StageExecutionStrategy; import com.facebook.presto.sql.planner.Partitioning; @@ -291,7 +292,7 @@ private static void assertState(StageStateMachine stateMachine, StageState expec assertEquals(stateMachine.getLocation(), LOCATION); assertSame(stateMachine.getSession(), TEST_SESSION); - StageInfo stageInfo = stateMachine.getStageInfo(ImmutableList::of, ImmutableList::of); + StageInfo stageInfo = stateMachine.getStageInfo(ImmutableList::of); assertEquals(stageInfo.getStageId(), STAGE_ID); assertEquals(stageInfo.getSelf(), LOCATION); assertEquals(stageInfo.getSubStages(), ImmutableList.of()); @@ -330,7 +331,8 @@ private static PlanFragment createValuesPlan() SOURCE_DISTRIBUTION, ImmutableList.of(valuesNodeId), new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), ImmutableList.of(symbol)), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); return planFragment; } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestStageStats.java b/presto-main/src/test/java/com/facebook/presto/execution/TestStageStats.java index cc7815156f06d..76b4e5080edd8 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestStageStats.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestStageStats.java @@ -30,12 +30,10 @@ public class TestStageStats { - public static final StageStats EXPECTED = new StageStats( + private static final StageStats EXPECTED = new StageStats( new DateTime(0), getTestDistribution(1), - getTestDistribution(2), - getTestDistribution(3), 4, 5, @@ -54,7 +52,6 @@ public class TestStageStats new Duration(15, NANOSECONDS), new Duration(16, NANOSECONDS), - new Duration(17, NANOSECONDS), new Duration(18, NANOSECONDS), false, ImmutableSet.of(), @@ -93,13 +90,11 @@ public void testJson() assertExpectedStageStats(actual); } - public static void assertExpectedStageStats(StageStats actual) + private static void assertExpectedStageStats(StageStats actual) { assertEquals(actual.getSchedulingComplete().getMillis(), 0); assertEquals(actual.getGetSplitDistribution().getCount(), 1.0); - assertEquals(actual.getScheduleTaskDistribution().getCount(), 2.0); - assertEquals(actual.getAddSplitDistribution().getCount(), 3.0); assertEquals(actual.getTotalTasks(), 4); assertEquals(actual.getRunningTasks(), 5); @@ -118,7 +113,6 @@ public static void assertExpectedStageStats(StageStats actual) assertEquals(actual.getTotalScheduledTime(), new Duration(15, NANOSECONDS)); assertEquals(actual.getTotalCpuTime(), new Duration(16, NANOSECONDS)); - assertEquals(actual.getTotalUserTime(), new Duration(17, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(18, NANOSECONDS)); assertEquals(actual.getRawInputDataSize(), new DataSize(19, BYTE)); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestStartTransactionTask.java b/presto-main/src/test/java/com/facebook/presto/execution/TestStartTransactionTask.java index 76d7f6246f691..bd25fca59c5c6 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestStartTransactionTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestStartTransactionTask.java @@ -15,13 +15,13 @@ import com.facebook.presto.Session; import com.facebook.presto.Session.SessionBuilder; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.metadata.MetadataManager; -import com.facebook.presto.security.AccessControl; import com.facebook.presto.security.AccessControlManager; import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; -import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.transaction.IsolationLevel; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.sql.tree.Isolation; @@ -38,6 +38,7 @@ import org.testng.annotations.Test; import java.net.URI; +import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -76,8 +77,7 @@ public void testNonTransactionalClient() { Session session = sessionBuilder().build(); TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); try { @@ -89,21 +89,19 @@ public void testNonTransactionalClient() } assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); } @Test public void testNestedTransaction() { TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - Session session = sessionBuilder() .setTransactionId(TransactionId.create()) .setClientTransactionSupport() .build(); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); try { getFutureValue(new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); @@ -114,8 +112,8 @@ public void testNestedTransaction() } assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); } @Test @@ -125,16 +123,15 @@ public void testStartTransaction() .setClientTransactionSupport() .build(); TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); getFutureValue(new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertTrue(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); - TransactionInfo transactionInfo = transactionManager.getTransactionInfo(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().get()); + TransactionInfo transactionInfo = transactionManager.getTransactionInfo(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().get()); assertFalse(transactionInfo.isAutoCommitContext()); } @@ -145,8 +142,7 @@ public void testStartTransactionExplicitModes() .setClientTransactionSupport() .build(); TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); getFutureValue(new StartTransactionTask().execute( @@ -156,11 +152,11 @@ public void testStartTransactionExplicitModes() new AllowAllAccessControl(), stateMachine, emptyList())); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertTrue(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); - TransactionInfo transactionInfo = transactionManager.getTransactionInfo(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().get()); + TransactionInfo transactionInfo = transactionManager.getTransactionInfo(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().get()); assertEquals(transactionInfo.getIsolationLevel(), IsolationLevel.SERIALIZABLE); assertTrue(transactionInfo.isReadOnly()); assertFalse(transactionInfo.isAutoCommitContext()); @@ -173,8 +169,7 @@ public void testStartTransactionTooManyIsolationLevels() .setClientTransactionSupport() .build(); TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); try { @@ -192,8 +187,8 @@ public void testStartTransactionTooManyIsolationLevels() } assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); } @Test @@ -203,8 +198,7 @@ public void testStartTransactionTooManyAccessModes() .setClientTransactionSupport() .build(); TransactionManager transactionManager = createTestTransactionManager(); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); try { @@ -222,8 +216,8 @@ public void testStartTransactionTooManyAccessModes() } assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertFalse(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); } @Test @@ -240,8 +234,7 @@ public void testStartTransactionIdleExpiration() scheduledExecutor, new CatalogManager(), executor); - AccessControl accessControl = new AccessControlManager(transactionManager); - QueryStateMachine stateMachine = QueryStateMachine.begin(new QueryId("query"), "START TRANSACTION", session, URI.create("fake://uri"), true, transactionManager, accessControl, executor, metadata); + QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); getFutureValue(new StartTransactionTask().execute( @@ -251,8 +244,8 @@ public void testStartTransactionIdleExpiration() new AllowAllAccessControl(), stateMachine, emptyList())); - assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId()); - assertTrue(stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent()); + assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); + assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); long start = System.nanoTime(); while (!transactionManager.getAllTransactionInfos().isEmpty()) { @@ -263,6 +256,21 @@ public void testStartTransactionIdleExpiration() } } + private QueryStateMachine createQueryStateMachine(String query, Session session, TransactionManager transactionManager) + { + return QueryStateMachine.begin( + query, + session, + URI.create("fake://uri"), + new ResourceGroupId("test"), + true, + transactionManager, + new AccessControlManager(transactionManager), + executor, + metadata, + WarningCollector.NOOP); + } + private static SessionBuilder sessionBuilder() { return testSessionBuilder() diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestStateMachine.java b/presto-main/src/test/java/com/facebook/presto/execution/TestStateMachine.java index ac4609f210281..eb753a9a51f90 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestStateMachine.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestStateMachine.java @@ -21,6 +21,7 @@ import java.util.concurrent.ExecutorService; +import static io.airlift.concurrent.MoreFutures.tryGetFutureValue; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.TimeUnit.SECONDS; @@ -46,12 +47,13 @@ public void tearDown() @Test public void testNullState() + throws Exception { try { new StateMachine<>("test", executor, null); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException ignored) { } StateMachine stateMachine = new StateMachine<>("test", executor, State.BREAKFAST); @@ -61,7 +63,7 @@ public void testNullState() stateMachine.set(null); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException expected) { } }); @@ -70,7 +72,7 @@ public void testNullState() stateMachine.compareAndSet(State.BREAKFAST, null); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException expected) { } }); @@ -79,7 +81,7 @@ public void testNullState() stateMachine.compareAndSet(State.LUNCH, null); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException expected) { } }); @@ -88,7 +90,7 @@ public void testNullState() stateMachine.setIf(null, currentState -> true); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException expected) { } }); @@ -97,7 +99,7 @@ public void testNullState() stateMachine.setIf(null, currentState -> false); fail("expected a NullPointerException"); } - catch (NullPointerException exception) { + catch (NullPointerException expected) { } }); } @@ -218,21 +220,20 @@ public void testSetIf() assertNoStateChange(stateMachine, () -> stateMachine.setIf(State.DINNER, currentState -> true)); } - private void assertStateChange(StateMachine stateMachine, StateChanger stateChange, State expectedState) + private static void assertStateChange(StateMachine stateMachine, StateChanger stateChange, State expectedState) throws Exception { State initialState = stateMachine.get(); ListenableFuture futureChange = stateMachine.getStateChange(initialState); - SettableFuture listenerChange = SettableFuture.create(); - stateMachine.addStateChangeListener(listenerChange::set); + SettableFuture listenerChange = addTestListener(stateMachine); stateChange.run(); assertEquals(stateMachine.get(), expectedState); - assertEquals(futureChange.get(1, SECONDS), expectedState); - assertEquals(listenerChange.get(1, SECONDS), expectedState); + assertEquals(futureChange.get(10, SECONDS), expectedState); + assertEquals(listenerChange.get(10, SECONDS), expectedState); // listeners should not be retained if we are in a terminal state boolean isTerminalState = stateMachine.isTerminalState(expectedState); @@ -241,15 +242,14 @@ private void assertStateChange(StateMachine stateMachine, StateChanger st } } - private void assertNoStateChange(StateMachine stateMachine, StateChanger stateChange) + private static void assertNoStateChange(StateMachine stateMachine, StateChanger stateChange) { State initialState = stateMachine.get(); ListenableFuture futureChange = stateMachine.getStateChange(initialState); - SettableFuture listenerChange = SettableFuture.create(); - stateMachine.addStateChangeListener(listenerChange::set); + SettableFuture listenerChange = addTestListener(stateMachine); - // listeners should not be added if we are in a terminal state + // listeners should not be added if we are in a terminal state, but listener should fire boolean isTerminalState = stateMachine.isTerminalState(initialState); if (isTerminalState) { assertEquals(stateMachine.getStateChangeListeners(), ImmutableSet.of()); @@ -259,14 +259,42 @@ private void assertNoStateChange(StateMachine stateMachine, StateChanger assertEquals(stateMachine.get(), initialState); - // the state change listeners will trigger if the state machine is in a terminal state + // the future change will trigger if the state machine is in a terminal state // this is to prevent waiting for state changes that will never occur assertEquals(futureChange.isDone(), isTerminalState); futureChange.cancel(true); - assertEquals(listenerChange.isDone(), isTerminalState); + + // test listener future only completes if the state actually changed + assertFalse(listenerChange.isDone()); listenerChange.cancel(true); } + private static SettableFuture addTestListener(StateMachine stateMachine) + { + State initialState = stateMachine.get(); + SettableFuture initialStateNotified = SettableFuture.create(); + SettableFuture stateChanged = SettableFuture.create(); + Thread addingThread = Thread.currentThread(); + stateMachine.addStateChangeListener(newState -> { + Thread callbackThread = Thread.currentThread(); + if (callbackThread == addingThread) { + stateChanged.setException(new AssertionError("Listener was not called back on a different thread")); + return; + } + + if (newState == initialState) { + initialStateNotified.set(true); + } + else { + stateChanged.set(newState); + } + }); + + assertTrue(tryGetFutureValue(initialStateNotified, 10, SECONDS).isPresent(), "Initial state notification not fired"); + + return stateChanged; + } + private interface StateChanger { void run(); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestTaskManagerConfig.java b/presto-main/src/test/java/com/facebook/presto/execution/TestTaskManagerConfig.java index 6f7838c93632c..3820c8717ada3 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestTaskManagerConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestTaskManagerConfig.java @@ -37,7 +37,7 @@ public void testDefaults() .setSplitConcurrencyAdjustmentInterval(new Duration(100, TimeUnit.MILLISECONDS)) .setStatusRefreshMaxWait(new Duration(1, TimeUnit.SECONDS)) .setInfoUpdateInterval(new Duration(3, TimeUnit.SECONDS)) - .setVerboseStats(false) + .setPerOperatorCpuTimerEnabled(true) .setTaskCpuTimerEnabled(true) .setMaxWorkerThreads(Runtime.getRuntime().availableProcessors() * 2) .setMinDrivers(Runtime.getRuntime().availableProcessors() * 2 * 2) @@ -57,7 +57,8 @@ public void testDefaults() .setHttpTimeoutThreads(3) .setTaskNotificationThreads(5) .setTaskYieldThreads(3) - .setLevelTimeMultiplier(new BigDecimal("2"))); + .setLevelTimeMultiplier(new BigDecimal("2")) + .setStatisticsCpuTimerEnabled(true)); } @Test @@ -68,7 +69,7 @@ public void testExplicitPropertyMappings() .put("task.split-concurrency-adjustment-interval", "1s") .put("task.status-refresh-max-wait", "2s") .put("task.info-update-interval", "2s") - .put("task.verbose-stats", "true") + .put("task.per-operator-cpu-timer-enabled", "false") .put("task.cpu-timer-enabled", "false") .put("task.max-index-memory", "512MB") .put("task.share-index-loading", "true") @@ -89,6 +90,7 @@ public void testExplicitPropertyMappings() .put("task.task-notification-threads", "13") .put("task.task-yield-threads", "8") .put("task.level-time-multiplier", "2.1") + .put("task.statistics-cpu-timer-enabled", "false") .build(); TaskManagerConfig expected = new TaskManagerConfig() @@ -96,7 +98,7 @@ public void testExplicitPropertyMappings() .setSplitConcurrencyAdjustmentInterval(new Duration(1, TimeUnit.SECONDS)) .setStatusRefreshMaxWait(new Duration(2, TimeUnit.SECONDS)) .setInfoUpdateInterval(new Duration(2, TimeUnit.SECONDS)) - .setVerboseStats(true) + .setPerOperatorCpuTimerEnabled(false) .setTaskCpuTimerEnabled(false) .setMaxIndexMemoryUsage(new DataSize(512, Unit.MEGABYTE)) .setShareIndexLoading(true) @@ -116,7 +118,8 @@ public void testExplicitPropertyMappings() .setHttpTimeoutThreads(10) .setTaskNotificationThreads(13) .setTaskYieldThreads(8) - .setLevelTimeMultiplier(new BigDecimal("2.1")); + .setLevelTimeMultiplier(new BigDecimal("2.1")) + .setStatisticsCpuTimerEnabled(false); assertFullMapping(properties, expected); } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/executor/Histogram.java b/presto-main/src/test/java/com/facebook/presto/execution/executor/Histogram.java index 7f6ffbe5948c9..82b32796ad932 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/executor/Histogram.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/executor/Histogram.java @@ -22,6 +22,7 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.function.Function; +import java.util.function.ToLongFunction; import static com.google.common.base.Preconditions.checkArgument; @@ -47,7 +48,7 @@ public static > Histogram fromContinuous(Collection(buckets, false); } - public static Histogram fromContinuous(Collection initialData, Function keyFunction) + public static Histogram fromContinuous(Collection initialData, ToLongFunction keyFunction) { if (initialData.isEmpty()) { return new Histogram<>(ImmutableList.of(), false); @@ -55,11 +56,11 @@ public static Histogram fromContinuous(Collection initialData, Func int numBuckets = Math.min(10, (int) Math.sqrt(initialData.size())); long min = initialData.stream() - .mapToLong(keyFunction::apply) + .mapToLong(keyFunction::applyAsLong) .min() .getAsLong(); long max = initialData.stream() - .mapToLong(keyFunction::apply) + .mapToLong(keyFunction::applyAsLong) .max() .getAsLong(); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/executor/SimulationTask.java b/presto-main/src/test/java/com/facebook/presto/execution/executor/SimulationTask.java index b4e292869fbc9..6898ef9b71e9d 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/executor/SimulationTask.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/executor/SimulationTask.java @@ -19,6 +19,7 @@ import com.google.common.collect.Sets; import io.airlift.units.Duration; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -39,7 +40,7 @@ public SimulationTask(TaskExecutor taskExecutor, TaskSpecification specification { this.specification = specification; this.taskId = taskId; - taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, SECONDS)); + taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, SECONDS), OptionalInt.empty()); } public void setKilled() diff --git a/presto-main/src/test/java/com/facebook/presto/execution/executor/TestTaskExecutor.java b/presto-main/src/test/java/com/facebook/presto/execution/executor/TestTaskExecutor.java index cdc94347f5029..3b964657cc411 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/executor/TestTaskExecutor.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/executor/TestTaskExecutor.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.List; +import java.util.OptionalInt; import java.util.concurrent.Future; import java.util.concurrent.Phaser; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,7 +56,7 @@ public void testTasksComplete() try { TaskId taskId = new TaskId("test", 0, 0); - TaskHandle taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, MILLISECONDS)); + TaskHandle taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); Phaser beginPhase = new Phaser(); beginPhase.register(); @@ -74,8 +75,10 @@ public void testTasksComplete() beginPhase.arriveAndAwaitAdvance(); assertEquals(driver1.getCompletedPhases(), 0); assertEquals(driver2.getCompletedPhases(), 0); - ticker.increment(10, MILLISECONDS); - assertEquals(taskExecutor.getMaxActiveSplitTime(), 10); + ticker.increment(60, SECONDS); + assertEquals(taskExecutor.getRunAwaySplitCount(), 0); + ticker.increment(600, SECONDS); + assertEquals(taskExecutor.getRunAwaySplitCount(), 2); verificationComplete.arriveAndAwaitAdvance(); // advance one phase and verify @@ -129,8 +132,8 @@ public void testTasksComplete() assertEquals(driver3.getLastPhase(), 12); // no splits remaining - ticker.increment(30, MILLISECONDS); - assertEquals(taskExecutor.getMaxActiveSplitTime(), 0); + ticker.increment(610, SECONDS); + assertEquals(taskExecutor.getRunAwaySplitCount(), 0); } finally { taskExecutor.stop(); @@ -146,8 +149,8 @@ public void testQuantaFairness() ticker.increment(20, MILLISECONDS); try { - TaskHandle shortQuantaTaskHandle = taskExecutor.addTask(new TaskId("shortQuanta", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)); - TaskHandle longQuantaTaskHandle = taskExecutor.addTask(new TaskId("longQuanta", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)); + TaskHandle shortQuantaTaskHandle = taskExecutor.addTask(new TaskId("shortQuanta", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); + TaskHandle longQuantaTaskHandle = taskExecutor.addTask(new TaskId("longQuanta", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); Phaser globalPhaser = new Phaser(); @@ -180,7 +183,7 @@ public void testLevelMovement() ticker.increment(20, MILLISECONDS); try { - TaskHandle testTaskHandle = taskExecutor.addTask(new TaskId("test", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)); + TaskHandle testTaskHandle = taskExecutor.addTask(new TaskId("test", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); Phaser globalPhaser = new Phaser(); globalPhaser.bulkRegister(3); @@ -221,9 +224,9 @@ public void testLevelMultipliers() try { for (int i = 0; i < (LEVEL_THRESHOLD_SECONDS.length - 1); i++) { TaskHandle[] taskHandles = { - taskExecutor.addTask(new TaskId("test1", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)), - taskExecutor.addTask(new TaskId("test2", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)), - taskExecutor.addTask(new TaskId("test3", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)) + taskExecutor.addTask(new TaskId("test1", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()), + taskExecutor.addTask(new TaskId("test2", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()), + taskExecutor.addTask(new TaskId("test3", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()) }; // move task 0 to next level @@ -303,7 +306,7 @@ public void testTaskHandle() try { TaskId taskId = new TaskId("test", 0, 0); - TaskHandle taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, MILLISECONDS)); + TaskHandle taskHandle = taskExecutor.addTask(taskId, () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); Phaser beginPhase = new Phaser(); beginPhase.register(); @@ -334,8 +337,8 @@ public void testTaskHandle() public void testLevelContributionCap() { MultilevelSplitQueue splitQueue = new MultilevelSplitQueue(2); - TaskHandle handle0 = new TaskHandle(new TaskId("test0", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS)); - TaskHandle handle1 = new TaskHandle(new TaskId("test1", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS)); + TaskHandle handle0 = new TaskHandle(new TaskId("test0", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS), OptionalInt.empty()); + TaskHandle handle1 = new TaskHandle(new TaskId("test1", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS), OptionalInt.empty()); for (int i = 0; i < (LEVEL_THRESHOLD_SECONDS.length - 1); i++) { long levelAdvanceTime = SECONDS.toNanos(LEVEL_THRESHOLD_SECONDS[i + 1] - LEVEL_THRESHOLD_SECONDS[i]); @@ -354,7 +357,7 @@ public void testLevelContributionCap() public void testUpdateLevelWithCap() { MultilevelSplitQueue splitQueue = new MultilevelSplitQueue(2); - TaskHandle handle0 = new TaskHandle(new TaskId("test0", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS)); + TaskHandle handle0 = new TaskHandle(new TaskId("test0", 0, 0), splitQueue, () -> 1, 1, new Duration(1, SECONDS), OptionalInt.empty()); long quantaNanos = MINUTES.toNanos(10); handle0.addScheduledNanos(quantaNanos); @@ -376,7 +379,7 @@ public void testMinMaxDriversPerTask() TaskExecutor taskExecutor = new TaskExecutor(4, 16, 1, maxDriversPerTask, splitQueue, ticker); taskExecutor.start(); try { - TaskHandle testTaskHandle = taskExecutor.addTask(new TaskId("test", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS)); + TaskHandle testTaskHandle = taskExecutor.addTask(new TaskId("test", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.empty()); // enqueue all batches of splits int batchCount = 4; @@ -385,8 +388,8 @@ public void testMinMaxDriversPerTask() for (int batch = 0; batch < batchCount; batch++) { phasers[batch] = new Phaser(); phasers[batch].register(); - TestingJob split1 = new TestingJob(ticker, new Phaser(), new Phaser(), phasers[batch], maxDriversPerTask, 0); - TestingJob split2 = new TestingJob(ticker, new Phaser(), new Phaser(), phasers[batch], maxDriversPerTask, 0); + TestingJob split1 = new TestingJob(ticker, new Phaser(), new Phaser(), phasers[batch], 1, 0); + TestingJob split2 = new TestingJob(ticker, new Phaser(), new Phaser(), phasers[batch], 1, 0); splits[2 * batch] = split1; splits[2 * batch + 1] = split2; taskExecutor.enqueueSplits(testTaskHandle, false, ImmutableList.of(split1, split2)); @@ -407,6 +410,45 @@ public void testMinMaxDriversPerTask() } } + @Test(timeOut = 30_000) + public void testUserSpecifiedMaxDriversPerTask() + { + MultilevelSplitQueue splitQueue = new MultilevelSplitQueue(2); + TestingTicker ticker = new TestingTicker(); + // create a task executor with min/max drivers per task to be 2 and 4 + TaskExecutor taskExecutor = new TaskExecutor(4, 16, 2, 4, splitQueue, ticker); + taskExecutor.start(); + try { + // overwrite the max drivers per task to be 1 + TaskHandle testTaskHandle = taskExecutor.addTask(new TaskId("test", 0, 0), () -> 0, 10, new Duration(1, MILLISECONDS), OptionalInt.of(1)); + + // enqueue all batches of splits + int batchCount = 4; + TestingJob[] splits = new TestingJob[4]; + Phaser[] phasers = new Phaser[batchCount]; + for (int batch = 0; batch < batchCount; batch++) { + phasers[batch] = new Phaser(); + phasers[batch].register(); + TestingJob split = new TestingJob(ticker, new Phaser(), new Phaser(), phasers[batch], 1, 0); + splits[batch] = split; + taskExecutor.enqueueSplits(testTaskHandle, false, ImmutableList.of(split)); + } + + // assert that the splits are processed in batches as expected + for (int batch = 0; batch < batchCount; batch++) { + // wait until the current batch starts + waitUntilSplitsStart(ImmutableList.of(splits[batch])); + // assert that only the splits including and up to the current batch are running and the rest haven't started yet + assertSplitStates(batch, splits); + // complete the current batch + phasers[batch].arriveAndDeregister(); + } + } + finally { + taskExecutor.stop(); + } + } + private void assertSplitStates(int endIndex, TestingJob[] splits) { // assert that splits up to and including endIndex are all started diff --git a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java index 7db5346a50318..2a5c5466a5a34 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java @@ -39,6 +39,7 @@ import static com.facebook.presto.execution.QueryState.RUNNING; import static com.facebook.presto.spi.resourceGroups.ResourceGroupState.CAN_QUEUE; import static com.facebook.presto.spi.resourceGroups.ResourceGroupState.CAN_RUN; +import static com.facebook.presto.spi.resourceGroups.SchedulingPolicy.FAIR; import static com.facebook.presto.spi.resourceGroups.SchedulingPolicy.QUERY_PRIORITY; import static com.facebook.presto.spi.resourceGroups.SchedulingPolicy.WEIGHTED; import static com.facebook.presto.spi.resourceGroups.SchedulingPolicy.WEIGHTED_FAIR; @@ -50,7 +51,6 @@ import static io.airlift.units.DataSize.Unit.GIGABYTE; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.util.Collections.reverse; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -73,7 +73,7 @@ public void testQueueFull() MockQueryExecution query3 = new MockQueryExecution(0); root.run(query3); assertEquals(query3.getState(), FAILED); - assertEquals(query3.getFailureCause().getMessage(), "Too many queued queries for \"root\""); + assertEquals(query3.getThrowable().getMessage(), "Too many queued queries for \"root\""); } @Test(timeOut = 10_000) @@ -131,6 +131,48 @@ public void testFairEligibility() assertEquals(query2b.getState(), QUEUED); } + @Test + public void testSetSchedulingPolicy() + { + RootInternalResourceGroup root = new RootInternalResourceGroup("root", (group, export) -> {}, directExecutor()); + root.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); + root.setMaxQueuedQueries(4); + root.setHardConcurrencyLimit(1); + InternalResourceGroup group1 = root.getOrCreateSubGroup("1"); + group1.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); + group1.setMaxQueuedQueries(4); + group1.setHardConcurrencyLimit(2); + InternalResourceGroup group2 = root.getOrCreateSubGroup("2"); + group2.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); + group2.setMaxQueuedQueries(4); + group2.setHardConcurrencyLimit(2); + MockQueryExecution query1a = new MockQueryExecution(0); + group1.run(query1a); + assertEquals(query1a.getState(), RUNNING); + MockQueryExecution query1b = new MockQueryExecution(0); + group1.run(query1b); + assertEquals(query1b.getState(), QUEUED); + MockQueryExecution query1c = new MockQueryExecution(0); + group1.run(query1c); + assertEquals(query1c.getState(), QUEUED); + MockQueryExecution query2a = new MockQueryExecution(0); + group2.run(query2a); + assertEquals(query2a.getState(), QUEUED); + + assertEquals(root.getInfo().getNumEligibleSubGroups(), 2); + assertEquals(root.getOrCreateSubGroup("1").getQueuedQueries(), 2); + assertEquals(root.getOrCreateSubGroup("2").getQueuedQueries(), 1); + assertEquals(root.getSchedulingPolicy(), FAIR); + root.setSchedulingPolicy(QUERY_PRIORITY); + assertEquals(root.getInfo().getNumEligibleSubGroups(), 2); + assertEquals(root.getOrCreateSubGroup("1").getQueuedQueries(), 2); + assertEquals(root.getOrCreateSubGroup("2").getQueuedQueries(), 1); + + assertEquals(root.getSchedulingPolicy(), QUERY_PRIORITY); + assertEquals(root.getOrCreateSubGroup("1").getSchedulingPolicy(), QUERY_PRIORITY); + assertEquals(root.getOrCreateSubGroup("2").getSchedulingPolicy(), QUERY_PRIORITY); + } + @Test(timeOut = 10_000) public void testFairQueuing() { @@ -664,8 +706,6 @@ public void testGetResourceGroupStateInfo() assertEquals(rootInfo.getSubGroups().get(0).getId(), rootA.getId()); assertEquals(rootInfo.getSubGroups().get(0).getState(), CAN_QUEUE); assertEquals(rootInfo.getSubGroups().get(0).getSoftMemoryLimit(), rootA.getSoftMemoryLimit()); - assertEquals(rootInfo.getSubGroups().get(0).getRunningTimeLimit(), rootA.getRunningTimeLimit()); - assertEquals(rootInfo.getSubGroups().get(0).getQueuedTimeLimit(), rootA.getQueuedTimeLimit()); assertEquals(rootInfo.getSubGroups().get(0).getHardConcurrencyLimit(), rootA.getHardConcurrencyLimit()); assertEquals(rootInfo.getSubGroups().get(0).getMaxQueuedQueries(), rootA.getMaxQueuedQueries()); assertEquals(rootInfo.getSubGroups().get(0).getNumEligibleSubGroups(), 2); @@ -675,8 +715,6 @@ public void testGetResourceGroupStateInfo() assertEquals(rootInfo.getSubGroups().get(1).getId(), rootB.getId()); assertEquals(rootInfo.getSubGroups().get(1).getState(), CAN_QUEUE); assertEquals(rootInfo.getSubGroups().get(1).getSoftMemoryLimit(), rootB.getSoftMemoryLimit()); - assertEquals(rootInfo.getSubGroups().get(1).getRunningTimeLimit(), rootA.getRunningTimeLimit()); - assertEquals(rootInfo.getSubGroups().get(1).getQueuedTimeLimit(), rootA.getQueuedTimeLimit()); assertEquals(rootInfo.getSubGroups().get(1).getHardConcurrencyLimit(), rootB.getHardConcurrencyLimit()); assertEquals(rootInfo.getSubGroups().get(1).getMaxQueuedQueries(), rootB.getMaxQueuedQueries()); assertEquals(rootInfo.getSubGroups().get(1).getNumEligibleSubGroups(), 0); @@ -684,9 +722,7 @@ public void testGetResourceGroupStateInfo() assertEquals(rootInfo.getSubGroups().get(1).getNumQueuedQueries(), 9); assertEquals(rootInfo.getSoftConcurrencyLimit(), root.getSoftConcurrencyLimit()); assertEquals(rootInfo.getHardConcurrencyLimit(), root.getHardConcurrencyLimit()); - assertEquals(rootInfo.getRunningTimeLimit(), new Duration(Long.MAX_VALUE, MILLISECONDS)); assertEquals(rootInfo.getMaxQueuedQueries(), root.getMaxQueuedQueries()); - assertEquals(rootInfo.getQueuedTimeLimit(), new Duration(Long.MAX_VALUE, MILLISECONDS)); assertEquals(rootInfo.getNumQueuedQueries(), 19); assertEquals(rootInfo.getRunningQueries().size(), 1); QueryStateInfo queryInfo = rootInfo.getRunningQueries().get(0); @@ -801,8 +837,6 @@ private static void assertGroupInfoEquals(ResourceGroupInfo actual, ResourceGrou actual.getState() == expected.getState() && actual.getSchedulingPolicy() == expected.getSchedulingPolicy() && Objects.equals(actual.getSoftMemoryLimit(), expected.getSoftMemoryLimit()) && - Objects.equals(actual.getRunningTimeLimit(), expected.getRunningTimeLimit()) && - Objects.equals(actual.getQueuedTimeLimit(), expected.getQueuedTimeLimit()) && Objects.equals(actual.getMemoryUsage(), expected.getMemoryUsage())); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestFixedCountScheduler.java b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestFixedCountScheduler.java index 009c8dbf8d4d1..03a38e08067e5 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestFixedCountScheduler.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestFixedCountScheduler.java @@ -21,15 +21,17 @@ import com.facebook.presto.metadata.PrestoNode; import com.facebook.presto.spi.Node; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; import java.net.URI; -import java.util.Map; +import java.util.List; +import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.IntStream; +import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.util.concurrent.Executors.newCachedThreadPool; @@ -59,10 +61,10 @@ public void destroyExecutor() public void testSingleNode() { FixedCountScheduler nodeScheduler = new FixedCountScheduler( - (node, partition, totalPartitions) -> taskFactory.createTableScanTask( + (node, partition, totalPartitions) -> Optional.of(taskFactory.createTableScanTask( new TaskId("test", 1, 1), node, ImmutableList.of(), - new PartitionedSplitCountTracker(delta -> {})), + new PartitionedSplitCountTracker(delta -> {}))), generateRandomNodes(1)); ScheduleResult result = nodeScheduler.schedule(); @@ -76,10 +78,10 @@ public void testSingleNode() public void testMultipleNodes() { FixedCountScheduler nodeScheduler = new FixedCountScheduler( - (node, partition, totalPartitions) -> taskFactory.createTableScanTask( + (node, partition, totalPartitions) -> Optional.of(taskFactory.createTableScanTask( new TaskId("test", 1, 1), node, ImmutableList.of(), - new PartitionedSplitCountTracker(delta -> {})), + new PartitionedSplitCountTracker(delta -> {}))), generateRandomNodes(5)); ScheduleResult result = nodeScheduler.schedule(); @@ -89,12 +91,10 @@ public void testMultipleNodes() assertEquals(result.getNewTasks().stream().map(RemoteTask::getNodeId).collect(toImmutableSet()).size(), 5); } - private static Map generateRandomNodes(int count) + private static List generateRandomNodes(int count) { - ImmutableMap.Builder nodes = ImmutableMap.builder(); - for (int i = 0; i < count; i++) { - nodes.put(i, new PrestoNode("other " + i, URI.create("http://127.0.0.1:11"), NodeVersion.UNKNOWN, false)); - } - return nodes.build(); + return IntStream.range(0, count) + .mapToObj(i -> new PrestoNode("other " + i, URI.create("http://127.0.0.1:11"), NodeVersion.UNKNOWN, false)) + .collect(toImmutableList()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestPhasedExecutionSchedule.java b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestPhasedExecutionSchedule.java index 915dd9daeb025..d796a5ae1c6bf 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestPhasedExecutionSchedule.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestPhasedExecutionSchedule.java @@ -14,9 +14,9 @@ package com.facebook.presto.execution.scheduler; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.operator.StageExecutionStrategy; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.Partitioning; import com.facebook.presto.sql.planner.PartitioningScheme; @@ -45,7 +45,8 @@ import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; -import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.PARTITIONED; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.REPARTITION; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.REPLICATE; import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.RIGHT; @@ -156,7 +157,8 @@ private static PlanFragment createExchangePlanFragment(String name, PlanFragment .map(PlanFragment::getId) .collect(toImmutableList()), fragments[0].getPartitioningScheme().getOutputLayout(), - Optional.empty()); + Optional.empty(), + REPARTITION); return createFragment(planNode); } @@ -166,7 +168,7 @@ private static PlanFragment createUnionPlanFragment(String name, PlanFragment... PlanNode planNode = new UnionNode( new PlanNodeId(name + "_id"), Stream.of(fragments) - .map(fragment -> new RemoteSourceNode(new PlanNodeId(fragment.getId().toString()), fragment.getId(), fragment.getPartitioningScheme().getOutputLayout(), Optional.empty())) + .map(fragment -> new RemoteSourceNode(new PlanNodeId(fragment.getId().toString()), fragment.getId(), fragment.getPartitioningScheme().getOutputLayout(), Optional.empty(), REPARTITION)) .collect(toImmutableList()), ImmutableListMultimap.of(), ImmutableList.of()); @@ -181,12 +183,9 @@ private static PlanFragment createBroadcastJoinPlanFragment(String name, PlanFra new PlanNodeId(name), new TableHandle(new ConnectorId("test"), new TestingTableHandle()), ImmutableList.of(symbol), - ImmutableMap.of(symbol, new TestingColumnHandle("column")), - Optional.empty(), - TupleDomain.all(), - null); + ImmutableMap.of(symbol, new TestingColumnHandle("column"))); - RemoteSourceNode remote = new RemoteSourceNode(new PlanNodeId("build_id"), buildFragment.getId(), ImmutableList.of(), Optional.empty()); + RemoteSourceNode remote = new RemoteSourceNode(new PlanNodeId("build_id"), buildFragment.getId(), ImmutableList.of(), Optional.empty(), REPLICATE); PlanNode join = new JoinNode( new PlanNodeId(name + "_id"), INNER, @@ -207,8 +206,8 @@ private static PlanFragment createBroadcastJoinPlanFragment(String name, PlanFra private static PlanFragment createJoinPlanFragment(JoinNode.Type joinType, String name, PlanFragment buildFragment, PlanFragment probeFragment) { - RemoteSourceNode probe = new RemoteSourceNode(new PlanNodeId("probe_id"), probeFragment.getId(), ImmutableList.of(), Optional.empty()); - RemoteSourceNode build = new RemoteSourceNode(new PlanNodeId("build_id"), buildFragment.getId(), ImmutableList.of(), Optional.empty()); + RemoteSourceNode probe = new RemoteSourceNode(new PlanNodeId("probe_id"), probeFragment.getId(), ImmutableList.of(), Optional.empty(), REPARTITION); + RemoteSourceNode build = new RemoteSourceNode(new PlanNodeId("build_id"), buildFragment.getId(), ImmutableList.of(), Optional.empty(), REPARTITION); PlanNode planNode = new JoinNode( new PlanNodeId(name + "_id"), joinType, @@ -222,7 +221,7 @@ private static PlanFragment createJoinPlanFragment(JoinNode.Type joinType, Strin Optional.empty(), Optional.empty(), Optional.empty(), - Optional.of(PARTITIONED)); + Optional.empty()); return createFragment(planNode); } @@ -234,10 +233,7 @@ private static PlanFragment createTableScanPlanFragment(String name) new PlanNodeId(name), new TableHandle(new ConnectorId("test"), new TestingTableHandle()), ImmutableList.of(symbol), - ImmutableMap.of(symbol, new TestingColumnHandle("column")), - Optional.empty(), - TupleDomain.all(), - null); + ImmutableMap.of(symbol, new TestingColumnHandle("column"))); return createFragment(planNode); } @@ -255,6 +251,7 @@ private static PlanFragment createFragment(PlanNode planNode) SOURCE_DISTRIBUTION, ImmutableList.of(planNode.getId()), new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), planNode.getOutputSymbols()), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestSourcePartitionedScheduler.java b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestSourcePartitionedScheduler.java index e712ef7604575..3dcd70e9d4b58 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestSourcePartitionedScheduler.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/scheduler/TestSourcePartitionedScheduler.java @@ -16,6 +16,7 @@ import com.facebook.presto.OutputBuffers.OutputBufferId; import com.facebook.presto.client.NodeVersion; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.execution.MockRemoteTaskFactory; import com.facebook.presto.execution.MockRemoteTaskFactory.MockRemoteTask; @@ -37,7 +38,6 @@ import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; -import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.split.ConnectorAwareSplitSource; import com.facebook.presto.split.SplitSource; import com.facebook.presto.sql.planner.Partitioning; @@ -81,6 +81,7 @@ import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SOURCE_DISTRIBUTION; +import static com.facebook.presto.sql.planner.plan.ExchangeNode.Type.GATHER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.google.common.base.Preconditions.checkArgument; import static io.airlift.concurrent.Threads.daemonThreadsNamed; @@ -137,9 +138,8 @@ public void testScheduleNoSplits() ScheduleResult scheduleResult = scheduler.schedule(); - assertTrue(scheduleResult.isFinished()); - assertTrue(scheduleResult.getBlocked().isDone()); assertEquals(scheduleResult.getNewTasks().size(), 1); + assertEffectivelyFinished(scheduleResult, scheduler); stage.abort(); } @@ -157,7 +157,12 @@ public void testScheduleSplitsOneAtATime() ScheduleResult scheduleResult = scheduler.schedule(); // only finishes when last split is fetched - assertEquals(scheduleResult.isFinished(), i == 59); + if (i == 59) { + assertEffectivelyFinished(scheduleResult, scheduler); + } + else { + assertFalse(scheduleResult.isFinished()); + } // never blocks assertTrue(scheduleResult.getBlocked().isDone()); @@ -189,7 +194,12 @@ public void testScheduleSplitsBatched() ScheduleResult scheduleResult = scheduler.schedule(); // finishes when last split is fetched - assertEquals(scheduleResult.isFinished(), i == (60 / 7)); + if (i == (60 / 7)) { + assertEffectivelyFinished(scheduleResult, scheduler); + } + else { + assertFalse(scheduleResult.isFinished()); + } // never blocks assertTrue(scheduleResult.getBlocked().isDone()); @@ -247,7 +257,12 @@ public void testScheduleSplitsBlock() ScheduleResult scheduleResult = scheduler.schedule(); // finishes when last split is fetched - assertEquals(scheduleResult.isFinished(), i == 19); + if (i == 19) { + assertEffectivelyFinished(scheduleResult, scheduler); + } + else { + assertFalse(scheduleResult.isFinished()); + } // does not block again assertTrue(scheduleResult.getBlocked().isDone()); @@ -281,8 +296,8 @@ public void testScheduleSlowSplitSource() ScheduleResult scheduleResult = scheduler.schedule(); assertFalse(scheduleResult.isFinished()); assertFalse(scheduleResult.getBlocked().isDone()); - assertEquals(scheduleResult.getNewTasks().size(), 3); - assertEquals(stage.getAllTasks().size(), 3); + assertEquals(scheduleResult.getNewTasks().size(), 0); + assertEquals(stage.getAllTasks().size(), 0); queuedSplitSource.addSplits(1); assertTrue(scheduleResult.getBlocked().isDone()); @@ -331,7 +346,7 @@ public void testBalancedSplitAssignment() StageScheduler firstScheduler = getSourcePartitionedScheduler(firstPlan, firstStage, nodeManager, nodeTaskMap, 200); ScheduleResult scheduleResult = firstScheduler.schedule(); - assertTrue(scheduleResult.isFinished()); + assertEffectivelyFinished(scheduleResult, firstScheduler); assertTrue(scheduleResult.getBlocked().isDone()); assertEquals(scheduleResult.getNewTasks().size(), 3); assertEquals(firstStage.getAllTasks().size(), 3); @@ -349,7 +364,7 @@ public void testBalancedSplitAssignment() StageScheduler secondScheduler = getSourcePartitionedScheduler(secondPlan, secondStage, nodeManager, nodeTaskMap, 200); scheduleResult = secondScheduler.schedule(); - assertTrue(scheduleResult.isFinished()); + assertEffectivelyFinished(scheduleResult, secondScheduler); assertTrue(scheduleResult.getBlocked().isDone()); assertEquals(scheduleResult.getNewTasks().size(), 1); assertEquals(secondStage.getAllTasks().size(), 1); @@ -371,7 +386,7 @@ public void testBlockCausesFullSchedule() StageScheduler firstScheduler = getSourcePartitionedScheduler(firstPlan, firstStage, nodeManager, nodeTaskMap, 200); ScheduleResult scheduleResult = firstScheduler.schedule(); - assertTrue(scheduleResult.isFinished()); + assertEffectivelyFinished(scheduleResult, firstScheduler); assertTrue(scheduleResult.getBlocked().isDone()); assertEquals(scheduleResult.getNewTasks().size(), 3); assertEquals(firstStage.getAllTasks().size(), 3); @@ -402,6 +417,21 @@ private static void assertPartitionedSplitCount(SqlStageExecution stage, int exp assertEquals(stage.getAllTasks().stream().mapToInt(RemoteTask::getPartitionedSplitCount).sum(), expectedPartitionedSplitCount); } + private static void assertEffectivelyFinished(ScheduleResult scheduleResult, StageScheduler scheduler) + { + if (scheduleResult.isFinished()) { + assertTrue(scheduleResult.getBlocked().isDone()); + return; + } + + assertTrue(scheduleResult.getBlocked().isDone()); + ScheduleResult nextScheduleResult = scheduler.schedule(); + assertTrue(nextScheduleResult.isFinished()); + assertTrue(nextScheduleResult.getBlocked().isDone()); + assertEquals(nextScheduleResult.getNewTasks().size(), 0); + assertEquals(nextScheduleResult.getSplitsScheduled(), 0); + } + private static StageScheduler getSourcePartitionedScheduler( StageExecutionPlan plan, SqlStageExecution stage, @@ -431,12 +461,9 @@ private static StageExecutionPlan createPlan(ConnectorSplitSource splitSource) tableScanNodeId, new TableHandle(CONNECTOR_ID, new TestingTableHandle()), ImmutableList.of(symbol), - ImmutableMap.of(symbol, new TestingColumnHandle("column")), - Optional.empty(), - TupleDomain.all(), - null); + ImmutableMap.of(symbol, new TestingColumnHandle("column"))); - RemoteSourceNode remote = new RemoteSourceNode(new PlanNodeId("remote_id"), new PlanFragmentId("plan_fragment_id"), ImmutableList.of(), Optional.empty()); + RemoteSourceNode remote = new RemoteSourceNode(new PlanNodeId("remote_id"), new PlanFragmentId("plan_fragment_id"), ImmutableList.of(), Optional.empty(), GATHER); PlanFragment testFragment = new PlanFragment( new PlanFragmentId("plan_id"), new JoinNode(new PlanNodeId("join_id"), @@ -451,12 +478,13 @@ private static StageExecutionPlan createPlan(ConnectorSplitSource splitSource) Optional.empty(), Optional.empty(), Optional.empty(), - Optional.of(JoinNode.DistributionType.PARTITIONED)), + Optional.empty()), ImmutableMap.of(symbol, VARCHAR), SOURCE_DISTRIBUTION, ImmutableList.of(tableScanNodeId), new PartitioningScheme(Partitioning.create(SINGLE_DISTRIBUTION, ImmutableList.of()), ImmutableList.of(symbol)), - StageExecutionStrategy.ungroupedExecution()); + StageExecutionStrategy.ungroupedExecution(), + StatsAndCosts.empty()); return new StageExecutionPlan( testFragment, @@ -477,7 +505,7 @@ private static ConnectorSplitSource createFixedSplitSource(int splitCount, Suppl private SqlStageExecution createSqlStageExecution(StageExecutionPlan tableScanPlan, NodeTaskMap nodeTaskMap) { StageId stageId = new StageId(new QueryId("query"), 0); - SqlStageExecution stage = new SqlStageExecution(stageId, + SqlStageExecution stage = SqlStageExecution.createSqlStageExecution(stageId, locationFactory.createStageLocation(stageId), tableScanPlan.getFragment(), new MockRemoteTaskFactory(queryExecutor, scheduledExecutor), diff --git a/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestDefaultWarningCollector.java b/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestDefaultWarningCollector.java new file mode 100644 index 0000000000000..3dd217d9aa7cc --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestDefaultWarningCollector.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class TestDefaultWarningCollector +{ + @Test + public void testNoWarnings() + { + WarningCollector warningCollector = new DefaultWarningCollector(new WarningCollectorConfig().setMaxWarnings(0)); + warningCollector.add(new PrestoWarning(new WarningCode(1, "1"), "warning 1")); + assertEquals(warningCollector.getWarnings().size(), 0); + } + + @Test + public void testMaxWarnings() + { + WarningCollector warningCollector = new DefaultWarningCollector(new WarningCollectorConfig().setMaxWarnings(2)); + warningCollector.add(new PrestoWarning(new WarningCode(1, "1"), "warning 1")); + warningCollector.add(new PrestoWarning(new WarningCode(2, "2"), "warning 2")); + warningCollector.add(new PrestoWarning(new WarningCode(3, "3"), "warning 3")); + assertEquals(warningCollector.getWarnings().size(), 2); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestTestingWarningCollector.java b/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestTestingWarningCollector.java new file mode 100644 index 0000000000000..1b8aa452d6e5d --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/execution/warnings/TestTestingWarningCollector.java @@ -0,0 +1,35 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution.warnings; + +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.testing.TestingWarningCollector; +import com.facebook.presto.testing.TestingWarningCollectorConfig; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.Test; + +import static com.facebook.presto.testing.TestingWarningCollector.createTestWarning; +import static org.testng.Assert.assertEquals; + +public class TestTestingWarningCollector +{ + @Test + public void testAddWarnings() + { + TestingWarningCollector collector = new TestingWarningCollector(new WarningCollectorConfig(), new TestingWarningCollectorConfig().setAddWarnings(true)); + ImmutableList.Builder expectedWarningsBuilder = ImmutableList.builder(); + expectedWarningsBuilder.add(createTestWarning(1)); + assertEquals(collector.getWarnings(), expectedWarningsBuilder.build()); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryPools.java b/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryPools.java index a0ea9d562f1e1..bb2b18ef93828 100644 --- a/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryPools.java +++ b/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryPools.java @@ -118,7 +118,7 @@ private RevocableMemoryOperator setupConsumeRevocableMemory(DataSize reservedPer { AtomicReference createOperator = new AtomicReference<>(); setUp(() -> { - DriverContext driverContext = taskContext.addPipelineContext(0, false, false).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, false, false, false).addDriverContext(); OperatorContext revokableOperatorContext = driverContext.addOperatorContext( Integer.MAX_VALUE, new PlanNodeId("revokable_operator"), diff --git a/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryTracking.java b/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryTracking.java index 975dc24dc6b63..d73ac4bb3f101 100644 --- a/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryTracking.java +++ b/presto-main/src/test/java/com/facebook/presto/memory/TestMemoryTracking.java @@ -113,7 +113,7 @@ public void setUpTest() true, true, OptionalInt.empty()); - pipelineContext = taskContext.addPipelineContext(0, true, true); + pipelineContext = taskContext.addPipelineContext(0, true, true, false); driverContext = pipelineContext.addDriverContext(); operatorContext = driverContext.addOperatorContext(1, new PlanNodeId("a"), "test-operator"); } @@ -152,10 +152,10 @@ public void testLocalTotalMemoryLimitExceeded() assertOperatorMemoryAllocations(operatorContext.getOperatorMemoryContext(), 0, queryMaxTotalMemory.toBytes(), 0); try { systemMemoryContext.setBytes(queryMaxTotalMemory.toBytes() + 1); - fail("allocation should hit the local total memory limit"); + fail("allocation should hit the per-node total memory limit"); } catch (ExceededMemoryLimitException e) { - assertEquals(e.getMessage(), format("Query exceeded local total memory limit of %s", queryMaxTotalMemory)); + assertEquals(e.getMessage(), format("Query exceeded per-node total memory limit of %1$s when increasing allocation of %1$s by 1B", queryMaxTotalMemory)); } } @@ -328,6 +328,16 @@ public void testTrySetBytes() 0); } + @Test + public void testTrySetZeroBytesFullPool() + { + LocalMemoryContext localMemoryContext = operatorContext.localUserMemoryContext(); + // fill up the pool + memoryPool.reserve(new QueryId("test_query"), "test", memoryPool.getFreeBytes()); + // try to reserve 0 bytes in the full pool + assertTrue(localMemoryContext.trySetBytes(localMemoryContext.getBytes())); + } + @Test public void testDestroy() { diff --git a/presto-main/src/test/java/com/facebook/presto/memory/TestNodeMemoryConfig.java b/presto-main/src/test/java/com/facebook/presto/memory/TestNodeMemoryConfig.java index dec3029f3117f..0828ba46aad5d 100644 --- a/presto-main/src/test/java/com/facebook/presto/memory/TestNodeMemoryConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/memory/TestNodeMemoryConfig.java @@ -35,7 +35,8 @@ public void testDefaults() .setLegacySystemPoolEnabled(false) .setMaxQueryMemoryPerNode(new DataSize(AVAILABLE_HEAP_MEMORY * 0.1, BYTE)) .setMaxQueryTotalMemoryPerNode(new DataSize(AVAILABLE_HEAP_MEMORY * 0.3, BYTE)) - .setHeapHeadroom(new DataSize(AVAILABLE_HEAP_MEMORY * 0.3, BYTE))); + .setHeapHeadroom(new DataSize(AVAILABLE_HEAP_MEMORY * 0.3, BYTE)) + .setReservedPoolEnabled(true)); } @Test @@ -46,13 +47,15 @@ public void testExplicitPropertyMappings() .put("query.max-total-memory-per-node", "3GB") .put("memory.heap-headroom-per-node", "1GB") .put("deprecated.legacy-system-pool-enabled", "true") + .put("experimental.reserved-pool-enabled", "false") .build(); NodeMemoryConfig expected = new NodeMemoryConfig() .setLegacySystemPoolEnabled(true) .setMaxQueryMemoryPerNode(new DataSize(1, GIGABYTE)) .setMaxQueryTotalMemoryPerNode(new DataSize(3, GIGABYTE)) - .setHeapHeadroom(new DataSize(1, GIGABYTE)); + .setHeapHeadroom(new DataSize(1, GIGABYTE)) + .setReservedPoolEnabled(false); assertFullMapping(properties, expected); } diff --git a/presto-main/src/test/java/com/facebook/presto/memory/TestQueryContext.java b/presto-main/src/test/java/com/facebook/presto/memory/TestQueryContext.java index b3aca7db9bc5b..5f38f0003f0e1 100644 --- a/presto-main/src/test/java/com/facebook/presto/memory/TestQueryContext.java +++ b/presto-main/src/test/java/com/facebook/presto/memory/TestQueryContext.java @@ -18,7 +18,6 @@ import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.operator.DriverContext; import com.facebook.presto.operator.OperatorContext; -import com.facebook.presto.operator.PipelineContext; import com.facebook.presto.operator.TaskContext; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spiller.SpillSpaceTracker; @@ -28,7 +27,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.stats.TestingGcMonitor; import io.airlift.units.DataSize; -import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -51,7 +50,7 @@ public class TestQueryContext { private static final ScheduledExecutorService TEST_EXECUTOR = newScheduledThreadPool(1, threadsNamed("test-executor-%s")); - @AfterMethod(alwaysRun = true) + @AfterClass(alwaysRun = true) public void tearDown() { TEST_EXECUTOR.shutdownNow(); @@ -159,8 +158,7 @@ public void testMoveTaggedAllocations(boolean useLegacyQueryContext) QueryContext queryContext = createQueryContext(useLegacyQueryContext, queryId, generalPool, systemPool); TaskStateMachine taskStateMachine = new TaskStateMachine(TaskId.valueOf("task-id"), TEST_EXECUTOR); TaskContext taskContext = queryContext.addTaskContext(taskStateMachine, TEST_SESSION, false, false, OptionalInt.empty()); - PipelineContext pipelineContext = taskContext.addPipelineContext(0, false, false); - DriverContext driverContext = pipelineContext.addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, false, false, false).addDriverContext(); OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), "test"); // allocate some memory in the general pool diff --git a/presto-main/src/test/java/com/facebook/presto/memory/TestSystemMemoryBlocking.java b/presto-main/src/test/java/com/facebook/presto/memory/TestSystemMemoryBlocking.java index 7c32426da422d..0b078e508e01d 100644 --- a/presto-main/src/test/java/com/facebook/presto/memory/TestSystemMemoryBlocking.java +++ b/presto-main/src/test/java/com/facebook/presto/memory/TestSystemMemoryBlocking.java @@ -78,7 +78,7 @@ public void setUp() .build(); memoryPool = taskContext.getQueryContext().getMemoryPool(); driverContext = taskContext - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java b/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java index 38b0b2b86093d..fcdbabd6b04d4 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/AbstractMockMetadata.java @@ -32,6 +32,7 @@ import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; import com.facebook.presto.spi.type.TypeSignature; +import com.facebook.presto.sql.planner.PartitioningHandle; import com.facebook.presto.sql.tree.QualifiedName; import io.airlift.slice.Slice; @@ -116,6 +117,18 @@ public TableLayout getLayout(Session session, TableLayoutHandle handle) throw new UnsupportedOperationException(); } + @Override + public TableLayoutHandle getAlternativeLayoutHandle(Session session, TableLayoutHandle tableLayoutHandle, PartitioningHandle partitioningHandle) + { + throw new UnsupportedOperationException(); + } + + @Override + public Optional getCommonPartitioning(Session session, PartitioningHandle left, PartitioningHandle right) + { + throw new UnsupportedOperationException(); + } + @Override public Optional getInfo(Session session, TableLayoutHandle handle) { diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionInvokerProvider.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionInvokerProvider.java index 5835ac31a6199..0b517e6f76719 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionInvokerProvider.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestFunctionInvokerProvider.java @@ -15,8 +15,8 @@ import com.facebook.presto.operator.scalar.AbstractTestFunctions; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty; -import com.facebook.presto.spi.InvocationConvention; -import com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention; +import com.facebook.presto.spi.function.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention; import com.google.common.collect.ImmutableList; import org.testng.annotations.Test; @@ -28,10 +28,10 @@ import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.RETURN_NULL_ON_NULL; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_BOXED_TYPE; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_NULL_FLAG; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.NULL_FLAG; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.NULL_FLAG; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -42,39 +42,51 @@ public class TestFunctionInvokerProvider public void testFunctionInvokerProvider() { assertTrue(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), true, false, Optional.of(new InvocationConvention(ImmutableList.of(BOXED_NULLABLE, BOXED_NULLABLE), InvocationReturnConvention.NULLABLE_RETURN, false)))); assertTrue(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(RETURN_NULL_ON_NULL), Optional.empty()), - new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(RETURN_NULL_ON_NULL), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), false, false, Optional.of(new InvocationConvention(ImmutableList.of(NEVER_NULL, BLOCK_POSITION, BLOCK_POSITION), InvocationReturnConvention.FAIL_ON_NULL, false)))); assertTrue(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), - new ArgumentProperty(VALUE_TYPE, Optional.of(USE_NULL_FLAG), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_NULL_FLAG), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), false, false, Optional.of(new InvocationConvention(ImmutableList.of(BLOCK_POSITION, NULL_FLAG, BLOCK_POSITION), InvocationReturnConvention.FAIL_ON_NULL, false)))); assertFalse(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), false, false, Optional.of(new InvocationConvention(ImmutableList.of(BLOCK_POSITION, BOXED_NULLABLE), InvocationReturnConvention.NULLABLE_RETURN, false)))); assertFalse(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(BLOCK_AND_POSITION), Optional.empty())), false, false, Optional.of(new InvocationConvention(ImmutableList.of(BLOCK_POSITION, NULL_FLAG), InvocationReturnConvention.NULLABLE_RETURN, false)))); assertFalse(checkChoice( - ImmutableList.of(new ArgumentProperty(VALUE_TYPE, Optional.of(USE_NULL_FLAG), Optional.empty()), new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), + ImmutableList.of( + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_NULL_FLAG), Optional.empty()), + new ArgumentProperty(VALUE_TYPE, Optional.of(USE_BOXED_TYPE), Optional.empty())), true, false, Optional.of(new InvocationConvention(ImmutableList.of(BLOCK_POSITION, BOXED_NULLABLE), InvocationReturnConvention.FAIL_ON_NULL, false)))); diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestInformationSchemaMetadata.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestInformationSchemaMetadata.java index 3912252a66136..8cf42157b975a 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestInformationSchemaMetadata.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestInformationSchemaMetadata.java @@ -170,13 +170,13 @@ public void testInformationSchemaPredicatePushdownWithConstraintPredicate() private ConnectorSession createNewSession(TransactionId transactionId) { return testSessionBuilder() - .setCatalog("test_catalog") - .setSchema("information_schema") - .setClientCapabilities(stream(ClientCapabilities.values()) - .map(ClientCapabilities::toString) - .collect(toImmutableSet())) - .setTransactionId(transactionId) - .build() + .setCatalog("test_catalog") + .setSchema("information_schema") + .setClientCapabilities(stream(ClientCapabilities.values()) + .map(ClientCapabilities::toString) + .collect(toImmutableSet())) + .setTransactionId(transactionId) + .build() .toConnectorSession(); } } diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestPolymorphicScalarFunction.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestPolymorphicScalarFunction.java index 15647350c6a3d..60c25194bf4d9 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestPolymorphicScalarFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestPolymorphicScalarFunction.java @@ -15,6 +15,8 @@ import com.facebook.presto.block.BlockEncodingManager; import com.facebook.presto.operator.scalar.ScalarFunctionImplementation; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.LongArrayBlock; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.TypeSignature; import com.facebook.presto.sql.analyzer.FeaturesConfig; @@ -26,15 +28,27 @@ import io.airlift.slice.Slices; import org.testng.annotations.Test; +import java.util.Collections; +import java.util.Optional; + import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.Signature.comparableWithVariadicBound; import static com.facebook.presto.metadata.TestPolymorphicScalarFunction.TestMethods.VARCHAR_TO_BIGINT_RETURN_VALUE; import static com.facebook.presto.metadata.TestPolymorphicScalarFunction.TestMethods.VARCHAR_TO_VARCHAR_RETURN_VALUE; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.BLOCK_AND_POSITION; +import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.NullConvention.USE_NULL_FLAG; import static com.facebook.presto.spi.function.OperatorType.ADD; +import static com.facebook.presto.spi.function.OperatorType.IS_DISTINCT_FROM; +import static com.facebook.presto.spi.type.Decimals.MAX_SHORT_PRECISION; +import static com.facebook.presto.spi.type.StandardTypes.BOOLEAN; import static com.facebook.presto.spi.type.StandardTypes.VARCHAR; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static java.lang.Math.toIntExact; +import static java.util.Arrays.asList; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; public class TestPolymorphicScalarFunction { @@ -54,6 +68,57 @@ public class TestPolymorphicScalarFunction ImmutableMap.of("V", TYPE_REGISTRY.getType(INPUT_VARCHAR_TYPE)), ImmutableMap.of("x", INPUT_VARCHAR_LENGTH)); + private static final TypeSignature DECIMAL_SIGNATURE = parseTypeSignature("decimal(a_precision, a_scale)", ImmutableSet.of("a_precision", "a_scale")); + private static final BoundVariables LONG_DECIMAL_BOUND_VARIABLES = new BoundVariables( + ImmutableMap.of(), + ImmutableMap.of("a_precision", MAX_SHORT_PRECISION + 1L, "a_scale", 2L)); + private static final BoundVariables SHORT_DECIMAL_BOUND_VARIABLES = new BoundVariables( + ImmutableMap.of(), + ImmutableMap.of("a_precision", (long) MAX_SHORT_PRECISION, "a_scale", 2L)); + + @Test + public void testSelectsMultipleChoiceWithBlockPosition() + throws Throwable + { + Signature signature = Signature.builder() + .kind(SCALAR) + .operatorType(IS_DISTINCT_FROM) + .argumentTypes(DECIMAL_SIGNATURE, DECIMAL_SIGNATURE) + .returnType(parseTypeSignature(BOOLEAN)) + .build(); + + SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) + .signature(signature) + .deterministic(true) + .choice(choice -> choice + .argumentProperties( + valueTypeArgumentProperty(USE_NULL_FLAG), + valueTypeArgumentProperty(USE_NULL_FLAG)) + .implementation(methodsGroup -> methodsGroup + .methods("shortShort", "longLong"))) + .choice(choice -> choice + .argumentProperties( + valueTypeArgumentProperty(BLOCK_AND_POSITION), + valueTypeArgumentProperty(BLOCK_AND_POSITION)) + .implementation(methodsGroup -> methodsGroup + .methodWithExplicitJavaTypes("blockPositionLongLong", + asList(Optional.of(Slice.class), Optional.of(Slice.class))) + .methodWithExplicitJavaTypes("blockPositionShortShort", + asList(Optional.of(long.class), Optional.of(long.class))))) + .build(); + + ScalarFunctionImplementation functionImplementation = function.specialize(SHORT_DECIMAL_BOUND_VARIABLES, 2, TYPE_REGISTRY, REGISTRY); + + assertEquals(functionImplementation.getAllChoices().size(), 2); + assertEquals(functionImplementation.getAllChoices().get(0).getArgumentProperties(), Collections.nCopies(2, valueTypeArgumentProperty(USE_NULL_FLAG))); + assertEquals(functionImplementation.getAllChoices().get(1).getArgumentProperties(), Collections.nCopies(2, valueTypeArgumentProperty(BLOCK_AND_POSITION))); + Block block1 = new LongArrayBlock(0, Optional.empty(), new long[0]); + Block block2 = new LongArrayBlock(0, Optional.empty(), new long[0]); + assertFalse((boolean) functionImplementation.getAllChoices().get(1).getMethodHandle().invoke(block1, 0, block2, 0)); + functionImplementation = function.specialize(LONG_DECIMAL_BOUND_VARIABLES, 2, TYPE_REGISTRY, REGISTRY); + assertTrue((boolean) functionImplementation.getAllChoices().get(1).getMethodHandle().invoke(block1, 0, block2, 0)); + } + @Test public void testSelectsMethodBasedOnArgumentTypes() throws Throwable @@ -61,10 +126,11 @@ public void testSelectsMethodBasedOnArgumentTypes() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b.methods("bigintToBigintReturnExtraParameter")) - .implementation(b -> b - .methods("varcharToBigintReturnExtraParameter") - .withExtraParameters(context -> ImmutableList.of(context.getLiteral("x")))) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("bigintToBigintReturnExtraParameter")) + .implementation(methodsGroup -> methodsGroup + .methods("varcharToBigintReturnExtraParameter") + .withExtraParameters(context -> ImmutableList.of(context.getLiteral("x"))))) .build(); ScalarFunctionImplementation functionImplementation = function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -78,10 +144,11 @@ public void testSelectsMethodBasedOnReturnType() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b.methods("varcharToVarcharCreateSliceWithExtraParameterLength")) - .implementation(b -> b - .methods("varcharToBigintReturnExtraParameter") - .withExtraParameters(context -> ImmutableList.of(42))) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("varcharToVarcharCreateSliceWithExtraParameterLength")) + .implementation(methodsGroup -> methodsGroup + .methods("varcharToBigintReturnExtraParameter") + .withExtraParameters(context -> ImmutableList.of(42)))) .build(); ScalarFunctionImplementation functionImplementation = function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -103,7 +170,8 @@ public void testSameLiteralInArgumentsAndReturnValue() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(signature) .deterministic(true) - .implementation(b -> b.methods("varcharToVarchar")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("varcharToVarchar"))) .build(); ScalarFunctionImplementation functionImplementation = function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -126,7 +194,8 @@ public void testTypeParameters() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(signature) .deterministic(true) - .implementation(b -> b.methods("varcharToVarchar")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("varcharToVarchar"))) .build(); ScalarFunctionImplementation functionImplementation = function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -147,7 +216,8 @@ public void testSetsHiddenToTrueForOperators() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(signature) .deterministic(true) - .implementation(b -> b.methods("varcharToVarchar")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("varcharToVarchar"))) .build(); ScalarFunctionImplementation functionImplementation = function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -160,8 +230,9 @@ public void testFailIfNotAllMethodsPresent() SqlScalarFunction.builder(TestMethods.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b.methods("bigintToBigintReturnExtraParameter")) - .implementation(b -> b.methods("foo")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("bigintToBigintReturnExtraParameter")) + .implementation(methodsGroup -> methodsGroup.methods("foo"))) .build(); } @@ -172,8 +243,9 @@ public void testFailNoMethodsAreSelectedWhenExtraParametersFunctionIsSet() SqlScalarFunction.builder(TestMethods.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b - .withExtraParameters(context -> ImmutableList.of(42))) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup + .withExtraParameters(context -> ImmutableList.of(42)))) .build(); } @@ -184,8 +256,9 @@ public void testFailIfTwoMethodsWithSameArguments() SqlScalarFunction function = SqlScalarFunction.builder(TestMethods.class) .signature(SIGNATURE) .deterministic(true) - .implementation(b -> b.methods("varcharToBigintReturnFirstExtraParameter")) - .implementation(b -> b.methods("varcharToBigintReturnExtraParameter")) + .choice(choice -> choice + .implementation(methodsGroup -> methodsGroup.methods("varcharToBigintReturnFirstExtraParameter")) + .implementation(methodsGroup -> methodsGroup.methods("varcharToBigintReturnExtraParameter"))) .build(); function.specialize(BOUND_VARIABLES, 1, TYPE_REGISTRY, REGISTRY); @@ -225,5 +298,25 @@ public static Slice varcharToVarcharCreateSliceWithExtraParameterLength(Slice st { return Slices.allocate(extraParameter); } + + public static boolean blockPositionLongLong(Block left, int leftPosition, Block right, int rightPosition) + { + return true; + } + + public static boolean blockPositionShortShort(Block left, int leftPosition, Block right, int rightPosition) + { + return false; + } + + public static boolean shortShort(long left, boolean leftNull, long right, boolean rightNull) + { + return false; + } + + public static boolean longLong(Slice left, boolean leftNull, Slice right, boolean rightNull) + { + return false; + } } } diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestQualifiedTablePrefix.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestQualifiedTablePrefix.java index 36d2741fdbe07..f198815adaf2f 100644 --- a/presto-main/src/test/java/com/facebook/presto/metadata/TestQualifiedTablePrefix.java +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestQualifiedTablePrefix.java @@ -13,8 +13,10 @@ */ package com.facebook.presto.metadata; +import io.airlift.json.JsonCodec; import org.testng.annotations.Test; +import static io.airlift.json.JsonCodec.jsonCodec; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -22,6 +24,8 @@ public class TestQualifiedTablePrefix { + private static final JsonCodec CODEC = jsonCodec(QualifiedTablePrefix.class); + @Test public void testCatalog() { @@ -68,4 +72,11 @@ public void testBadTable() // ok } } + + @Test + public void testRoundTrip() + { + QualifiedTablePrefix table = new QualifiedTablePrefix("abc", "xyz", "fgh"); + assertEquals(CODEC.fromJson(CODEC.toJson(table)), table); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/metadata/TestTableLayoutResult.java b/presto-main/src/test/java/com/facebook/presto/metadata/TestTableLayoutResult.java new file mode 100644 index 0000000000000..1e96aab1681a2 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/metadata/TestTableLayoutResult.java @@ -0,0 +1,118 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.metadata; + +import com.facebook.presto.spi.ColumnHandle; +import com.facebook.presto.spi.TestingColumnHandle; +import com.facebook.presto.spi.predicate.Domain; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import static com.facebook.presto.metadata.TableLayoutResult.computeEnforced; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.testing.TestingConnectorSession.SESSION; +import static org.testng.Assert.fail; + +public class TestTableLayoutResult +{ + @Test + public void testComputeEnforced() + { + assertComputeEnforced(TupleDomain.all(), TupleDomain.all(), TupleDomain.all()); + assertComputeEnforcedFails(TupleDomain.all(), TupleDomain.none()); + assertComputeEnforced(TupleDomain.none(), TupleDomain.all(), TupleDomain.none()); + assertComputeEnforced(TupleDomain.none(), TupleDomain.none(), TupleDomain.all()); + + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.all(), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L)))); + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.all()); + assertComputeEnforcedFails( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.none()); + assertComputeEnforcedFails( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 9999L)))); + assertComputeEnforcedFails( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c9999"), Domain.singleValue(BIGINT, 1L)))); + + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.all(), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L)))); + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L)))); + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L)))); + assertComputeEnforced( + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.withColumnDomains(ImmutableMap.of( + new TestingColumnHandle("c1"), Domain.singleValue(BIGINT, 1L), + new TestingColumnHandle("c2"), Domain.singleValue(BIGINT, 2L))), + TupleDomain.all()); + } + + private void assertComputeEnforcedFails(TupleDomain predicate, TupleDomain unenforced) + { + try { + TupleDomain enforced = computeEnforced(predicate, unenforced); + fail(String.format("expected IllegalArgumentException but found [%s]", enforced.toString(SESSION))); + } + catch (IllegalArgumentException e) { + // do nothing + } + } + + private void assertComputeEnforced(TupleDomain predicate, TupleDomain unenforced, TupleDomain expectedEnforced) + { + TupleDomain enforced = computeEnforced(predicate, unenforced); + if (!enforced.equals(expectedEnforced)) { + fail(String.format("expected [%s] but found [%s]", expectedEnforced.toString(SESSION), enforced.toString(SESSION))); + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashAndStreamingAggregationOperators.java b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashAndStreamingAggregationOperators.java index 8228819b55431..5d7a1fd726151 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashAndStreamingAggregationOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashAndStreamingAggregationOperators.java @@ -165,12 +165,13 @@ private OperatorFactory createHashAggregationOperatorFactory(Optional h hashChannel, Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), false, succinctBytes(8), succinctBytes(Integer.MAX_VALUE), spillerFactory, - joinCompiler); + joinCompiler, + false); } private static void repeatToStringBlock(String value, int count, BlockBuilder blockBuilder) @@ -199,7 +200,7 @@ public List getPages() @Benchmark public List benchmark(Context context) { - DriverContext driverContext = context.createTaskContext().addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = context.createTaskContext().addPipelineContext(0, true, true, false).addDriverContext(); Operator operator = context.getOperatorFactory().createOperator(driverContext); Iterator input = context.getPages().iterator(); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashBuildAndJoinOperators.java b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashBuildAndJoinOperators.java index 3964a90ecce25..dced7ad112005 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashBuildAndJoinOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkHashBuildAndJoinOperators.java @@ -185,7 +185,7 @@ public static class JoinContext protected List probePages; protected List outputChannels; - protected JoinBridgeDataManager lookupSourceFactory; + protected JoinBridgeManager lookupSourceFactory; @Override @Setup @@ -211,7 +211,7 @@ public void setup() initializeProbePages(); } - public JoinBridgeDataManager getLookupSourceFactory() + public JoinBridgeManager getLookupSourceFactory() { return lookupSourceFactory; } @@ -275,16 +275,16 @@ else if (matchRate > 1) { } @Benchmark - public JoinBridgeDataManager benchmarkBuildHash(BuildContext buildContext) + public JoinBridgeManager benchmarkBuildHash(BuildContext buildContext) { return benchmarkBuildHash(buildContext, ImmutableList.of(0, 1, 2)); } - private JoinBridgeDataManager benchmarkBuildHash(BuildContext buildContext, List outputChannels) + private JoinBridgeManager benchmarkBuildHash(BuildContext buildContext, List outputChannels) { - DriverContext driverContext = buildContext.createTaskContext().addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = buildContext.createTaskContext().addPipelineContext(0, true, true, false).addDriverContext(); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( + JoinBridgeManager lookupSourceFactoryManager = JoinBridgeManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( buildContext.getTypes(), outputChannels.stream() .map(buildContext.getTypes()::get) @@ -316,7 +316,7 @@ private JoinBridgeDataManager benchmarkBuildHash(BuildConte } operator.finish(); - LookupSourceFactory lookupSourceFactory = lookupSourceFactoryManager.forLifespan(Lifespan.taskWide()); + LookupSourceFactory lookupSourceFactory = lookupSourceFactoryManager.getJoinBridge(Lifespan.taskWide()); ListenableFuture lookupSourceProvider = lookupSourceFactory.createLookupSourceProvider(); if (!lookupSourceProvider.isDone()) { throw new AssertionError("Expected lookup source provider to be ready"); @@ -340,7 +340,7 @@ public List benchmarkJoinHash(JoinContext joinContext) OptionalInt.empty(), unsupportedPartitioningSpillerFactory()); - DriverContext driverContext = joinContext.createTaskContext().addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = joinContext.createTaskContext().addPipelineContext(0, true, true, false).addDriverContext(); Operator joinOperator = joinOperatorFactory.createOperator(driverContext); Iterator input = joinContext.getProbePages().iterator(); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkPartitionedOutputOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkPartitionedOutputOperator.java index 21e04aed737f3..f64edfa4c8e51 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkPartitionedOutputOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkPartitionedOutputOperator.java @@ -195,7 +195,7 @@ private DriverContext createDriverContext() return TestingTaskContext.builder(EXECUTOR, SCHEDULER, TEST_SESSION) .setMemoryPoolSize(MAX_MEMORY) .build() - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkWindowOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkWindowOperator.java new file mode 100644 index 0000000000000..353b92ffa542f --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/BenchmarkWindowOperator.java @@ -0,0 +1,330 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.facebook.presto.RowPagesBuilder; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.block.SortOrder; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.testing.TestingTaskContext; +import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; +import io.airlift.units.DataSize; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; + +import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.block.BlockAssertions.createLongSequenceBlock; +import static com.facebook.presto.operator.BenchmarkWindowOperator.Context.ROWS_PER_PAGE; +import static com.facebook.presto.operator.BenchmarkWindowOperator.Context.TOTAL_PAGES; +import static com.facebook.presto.operator.TestWindowOperator.ROW_NUMBER; +import static com.facebook.presto.operator.TestWindowOperator.createFactoryUnbounded; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static io.airlift.units.DataSize.Unit.GIGABYTE; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.Executors.newScheduledThreadPool; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; +import static org.openjdk.jmh.annotations.Scope.Thread; +import static org.testng.Assert.assertEquals; + +@State(Thread) +@OutputTimeUnit(MILLISECONDS) +@BenchmarkMode(AverageTime) +@Fork(3) +@Warmup(iterations = 5) +@Measurement(iterations = 10, time = 2, timeUnit = SECONDS) +public class BenchmarkWindowOperator +{ + @State(Thread) + public static class Context + { + public static final int NUMBER_OF_GROUP_COLUMNS = 2; + public static final int TOTAL_PAGES = 140; + public static final int ROWS_PER_PAGE = 10000; + private static final List PARTITION_CHANNELS = Ints.asList(0, 1); + + @Param({"10", "20", "100"}) + public int rowsPerPartition; + + @Param({"0", "1", "2", "3"}) + public int numberOfPregroupedColumns; + + @Param({"10", "50", "100"}) + public int partitionsPerGroup; + + private ExecutorService executor; + private ScheduledExecutorService scheduledExecutor; + private OperatorFactory operatorFactory; + + private List pages; + + @Setup + public void setup() + { + executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); + scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); + + createOperatorFactoryAndGenerateTestData(numberOfPregroupedColumns); + } + + private void createOperatorFactoryAndGenerateTestData(int numberOfPreGroupedColumns) + { + pages = generateTestData(); + + if (numberOfPreGroupedColumns == 0) { + // Ungrouped + operatorFactory = createFactoryUnbounded( + ImmutableList.of(BIGINT, BIGINT, BIGINT, BIGINT), + Ints.asList(0, 1, 2, 3), + ROW_NUMBER, + PARTITION_CHANNELS, + Ints.asList(), + Ints.asList(3), + ImmutableList.of(SortOrder.ASC_NULLS_LAST), + 0); + } + else if (numberOfPreGroupedColumns < NUMBER_OF_GROUP_COLUMNS) { + // Partially grouped + operatorFactory = createFactoryUnbounded( + ImmutableList.of(BIGINT, BIGINT, BIGINT, BIGINT), + Ints.asList(0, 1, 2, 3), + ROW_NUMBER, + PARTITION_CHANNELS, + Ints.asList(1), + Ints.asList(3), + ImmutableList.of(SortOrder.ASC_NULLS_LAST), + 0); + } + else { + // Fully grouped and (potentially) sorted + operatorFactory = createFactoryUnbounded( + ImmutableList.of(BIGINT, BIGINT, BIGINT, BIGINT), + Ints.asList(0, 1, 2, 3), + ROW_NUMBER, + PARTITION_CHANNELS, + Ints.asList(0, 1), + Ints.asList(3), + ImmutableList.of(SortOrder.ASC_NULLS_LAST), + (numberOfPreGroupedColumns - NUMBER_OF_GROUP_COLUMNS)); + } + } + + private List generateTestData() + { + List typesArray = new ArrayList<>(); + int currentPartitionIdentifier = 1; + + typesArray.add(BIGINT); + typesArray.add(BIGINT); + typesArray.add(BIGINT); + typesArray.add(BIGINT); + + RowPagesBuilder pagesBuilder = buildPages(currentPartitionIdentifier, typesArray); + + return pagesBuilder.build(); + } + + private RowPagesBuilder buildPages(int currentPartitionIdentifier, List typesArray) + { + int groupIdentifier = 100; + RowPagesBuilder rowPagesBuilder = RowPagesBuilder.rowPagesBuilder(false, ImmutableList.of(0), typesArray); + + for (int i = 0; i < TOTAL_PAGES; i++) { + BlockBuilder firstColumnBlockBuilder = BIGINT.createBlockBuilder(null, ROWS_PER_PAGE); + BlockBuilder secondColumnBlockBuilder = BIGINT.createBlockBuilder(null, ROWS_PER_PAGE); + int currentNumberOfRowsInPartition = 0; + int numberOfPartitionsInCurrentGroup = 0; + int currentGroupIdentifier = groupIdentifier++; + + for (int j = 0; j < ROWS_PER_PAGE; j++) { + if (currentNumberOfRowsInPartition == rowsPerPartition) { + ++currentPartitionIdentifier; + ++numberOfPartitionsInCurrentGroup; + currentNumberOfRowsInPartition = 0; + } + + if (numberOfPartitionsInCurrentGroup == partitionsPerGroup) { + numberOfPartitionsInCurrentGroup = 0; + currentGroupIdentifier = groupIdentifier++; + } + + firstColumnBlockBuilder.writeLong(currentGroupIdentifier); + secondColumnBlockBuilder.writeLong(currentPartitionIdentifier); + ++currentNumberOfRowsInPartition; + } + + rowPagesBuilder.addBlocksPage( + firstColumnBlockBuilder.build(), + secondColumnBlockBuilder.build(), + createLongSequenceBlock(0, ROWS_PER_PAGE), + createLongSequenceBlock(0, ROWS_PER_PAGE)); + } + + return rowPagesBuilder; + } + + public TaskContext createTaskContext() + { + return TestingTaskContext.createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(2, GIGABYTE)); + } + + public OperatorFactory getOperatorFactory() + { + return operatorFactory; + } + + public List getPages() + { + return pages; + } + } + + @Benchmark + public List benchmark(BenchmarkWindowOperator.Context context) + { + DriverContext driverContext = context.createTaskContext().addPipelineContext(0, true, true, false).addDriverContext(); + Operator operator = context.getOperatorFactory().createOperator(driverContext); + + Iterator input = context.getPages().iterator(); + ImmutableList.Builder outputPages = ImmutableList.builder(); + + boolean finishing = false; + for (int loops = 0; !operator.isFinished() && loops < 1_000_000; loops++) { + if (operator.needsInput()) { + if (input.hasNext()) { + Page inputPage = input.next(); + operator.addInput(inputPage); + } + else if (!finishing) { + operator.finish(); + finishing = true; + } + } + + Page outputPage = operator.getOutput(); + if (outputPage != null) { + outputPages.add(outputPage); + } + } + + return outputPages.build(); + } + + @Test + public void verifyUnGroupedWithMultiplePartitions() + { + verify(10, 0, false); + } + + @Test + public void verifyUnGroupedWithSinglePartition() + { + verify(10, 0, true); + } + + @Test + public void verifyPartiallyGroupedWithMultiplePartitions() + { + verify(10, 1, false); + } + + @Test + public void verifyPartiallyGroupedWithSinglePartition() + { + verify(10, 1, true); + } + + @Test + public void verifyFullyGroupedWithMultiplePartitions() + { + verify(10, 2, false); + } + + @Test + public void verifyFullyGroupedWithSinglePartition() + { + verify(10, 2, true); + } + + @Test + public void verifyFullyGroupedAndFullySortedWithMultiplePartitions() + { + verify(10, 3, false); + } + + @Test + public void verifyFullyGroupedAndFullySortedWithSinglePartition() + { + verify(10, 3, true); + } + + private void verify( + int numberOfRowsPerPartition, + int numberOfPreGroupedColumns, + boolean useSinglePartition) + { + Context context = new Context(); + + context.rowsPerPartition = numberOfRowsPerPartition; + context.numberOfPregroupedColumns = numberOfPreGroupedColumns; + + if (useSinglePartition) { + context.partitionsPerGroup = 1; + context.rowsPerPartition = ROWS_PER_PAGE; + } + + context.setup(); + + assertEquals(TOTAL_PAGES, context.getPages().size()); + for (int i = 0; i < TOTAL_PAGES; i++) { + assertEquals(ROWS_PER_PAGE, context.getPages().get(i).getPositionCount()); + } + + benchmark(context); + } + + public static void main(String[] args) + throws RunnerException + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkWindowOperator.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/GroupByHashYieldAssertion.java b/presto-main/src/test/java/com/facebook/presto/operator/GroupByHashYieldAssertion.java index ddc1f180997af..7174ad5ee3ec5 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/GroupByHashYieldAssertion.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/GroupByHashYieldAssertion.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; -import java.util.function.Function; +import java.util.function.ToIntFunction; import static com.facebook.presto.RowPagesBuilder.rowPagesBuilder; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; @@ -71,7 +71,7 @@ public static List createPagesWithDistinctHashKeys(Type type, int pageCoun * @param getHashCapacity returns the hash table capacity for the input operator * @param additionalMemoryInBytes the memory used in addition to the GroupByHash in the operator (e.g., aggregator) */ - public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List input, Type hashKeyType, OperatorFactory operatorFactory, Function getHashCapacity, long additionalMemoryInBytes) + public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List input, Type hashKeyType, OperatorFactory operatorFactory, ToIntFunction getHashCapacity, long additionalMemoryInBytes) { assertLessThan(additionalMemoryInBytes, 1L << 21, "additionalMemoryInBytes should be a relatively small number"); List result = new LinkedList<>(); @@ -91,7 +91,7 @@ public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List< new SpillSpaceTracker(new DataSize(512, MEGABYTE))); DriverContext driverContext = createTaskContext(queryContext, EXECUTOR, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); Operator operator = operatorFactory.createOperator(driverContext); @@ -107,7 +107,7 @@ public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List< memoryPool.reserve(queryId, "test", reservedMemoryInBytes); long oldMemoryUsage = operator.getOperatorContext().getDriverContext().getMemoryUsage(); - int oldCapacity = getHashCapacity.apply(operator); + int oldCapacity = getHashCapacity.applyAsInt(operator); // add a page and verify different behaviors operator.addInput(page); @@ -139,7 +139,7 @@ public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List< assertTrue(operator.getOperatorContext().isWaitingForMemory().isDone()); // assert the hash capacity is not changed; otherwise, we should have yielded - assertTrue(oldCapacity == getHashCapacity.apply(operator)); + assertTrue(oldCapacity == getHashCapacity.applyAsInt(operator)); // We are not going to rehash; therefore, assert the memory increase only comes from the aggregator assertLessThan(actualIncreasedMemory, additionalMemoryInBytes); @@ -155,7 +155,7 @@ public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List< assertFalse(operator.getOperatorContext().isWaitingForMemory().isDone()); // Hash table capacity should not change - assertEquals(oldCapacity, (long) getHashCapacity.apply(operator)); + assertEquals(oldCapacity, (long) getHashCapacity.applyAsInt(operator)); // Increased memory is no smaller than the hash table size and no greater than the hash table size + the memory used by aggregator if (hashKeyType == BIGINT) { @@ -182,7 +182,7 @@ public static GroupByHashYieldResult finishOperatorWithYieldingGroupByHash(List< assertTrue(operator.needsInput()); // Hash table capacity has increased - assertGreaterThan(getHashCapacity.apply(operator), oldCapacity); + assertGreaterThan(getHashCapacity.applyAsInt(operator), oldCapacity); // Assert the estimated reserved memory before rehash is very close to the one after rehash long rehashedMemoryUsage = operator.getOperatorContext().getDriverContext().getMemoryUsage(); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestAggregationOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestAggregationOperator.java index 60ec41cff1a84..9727fca3fcdbe 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestAggregationOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestAggregationOperator.java @@ -36,6 +36,7 @@ import static com.facebook.presto.SessionTestUtils.TEST_SESSION; import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; import static com.facebook.presto.operator.OperatorAssertion.assertOperatorEquals; +import static com.facebook.presto.operator.OperatorAssertion.toPages; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.RealType.REAL; @@ -43,9 +44,14 @@ import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.testing.MaterializedResult.resultBuilder; import static com.facebook.presto.testing.TestingTaskContext.createTaskContext; +import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.util.Collections.emptyIterator; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newScheduledThreadPool; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; @Test(singleThreaded = true) public class TestAggregationOperator @@ -65,17 +71,12 @@ public class TestAggregationOperator private ExecutorService executor; private ScheduledExecutorService scheduledExecutor; - private DriverContext driverContext; @BeforeMethod public void setUp() { executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); - - driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) - .addDriverContext(); } @AfterMethod @@ -109,12 +110,63 @@ public void testAggregation() LONG_SUM.bind(ImmutableList.of(3), Optional.empty()), REAL_SUM.bind(ImmutableList.of(4), Optional.empty()), DOUBLE_SUM.bind(ImmutableList.of(5), Optional.empty()), - maxVarcharColumn.bind(ImmutableList.of(6), Optional.empty()))); + maxVarcharColumn.bind(ImmutableList.of(6), Optional.empty())), + false); + + DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) + .addPipelineContext(0, true, true, false) + .addDriverContext(); MaterializedResult expected = resultBuilder(driverContext.getSession(), BIGINT, BIGINT, DOUBLE, VARCHAR, BIGINT, BIGINT, REAL, DOUBLE, VARCHAR) .row(100L, 4950L, 49.5, "399", 100L, 54950L, 44950.0f, 54950.0, "599") .build(); assertOperatorEquals(operatorFactory, driverContext, input, expected); + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertEquals(driverContext.getMemoryUsage(), 0); + } + + @Test + public void testMemoryTracking() + throws Exception + { + testMemoryTracking(false); + testMemoryTracking(true); + } + + private void testMemoryTracking(boolean useSystemMemory) + throws Exception + { + Page input = getOnlyElement(rowPagesBuilder(BIGINT).addSequencePage(100, 0).build()); + + OperatorFactory operatorFactory = new AggregationOperatorFactory( + 0, + new PlanNodeId("test"), + Step.SINGLE, + ImmutableList.of(LONG_SUM.bind(ImmutableList.of(0), Optional.empty())), + useSystemMemory); + + DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) + .addPipelineContext(0, true, true, false) + .addDriverContext(); + + try (Operator operator = operatorFactory.createOperator(driverContext)) { + assertTrue(operator.needsInput()); + operator.addInput(input); + + if (useSystemMemory) { + assertThat(driverContext.getSystemMemoryUsage()).isGreaterThan(0); + assertEquals(driverContext.getMemoryUsage(), 0); + } + else { + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertThat(driverContext.getMemoryUsage()).isGreaterThan(0); + } + + toPages(operator, emptyIterator()); + } + + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertEquals(driverContext.getMemoryUsage(), 0); } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestAnnotationEngineForAggregates.java b/presto-main/src/test/java/com/facebook/presto/operator/TestAnnotationEngineForAggregates.java index 8964d7d84303c..c56a3e340ee2e 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestAnnotationEngineForAggregates.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestAnnotationEngineForAggregates.java @@ -260,8 +260,8 @@ public void testCustomStateSerializerAggregationParse() assertTrue(implementation.getStateSerializerFactory().isPresent()); InternalAggregationFunction specialized = aggregation.specialize(BoundVariables.builder().build(), 1, new TypeRegistry(), null); - Object createdSerializer = ((LazyAccumulatorFactoryBinder) specialized.getAccumulatorFactoryBinder()) - .getGenericAccumulatorFactoryBinder().getStateSerializer(); + AccumulatorStateSerializer createdSerializer = getOnlyElement(((LazyAccumulatorFactoryBinder) specialized.getAccumulatorFactoryBinder()) + .getGenericAccumulatorFactoryBinder().getStateDescriptors()).getSerializer(); Class serializerFactory = implementation.getStateSerializerFactory().get().type().returnType(); assertTrue(serializerFactory.isInstance(createdSerializer)); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestColumnarPageProcessor.java b/presto-main/src/test/java/com/facebook/presto/operator/TestColumnarPageProcessor.java index df6cebb93f688..be88b3bf64252 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestColumnarPageProcessor.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestColumnarPageProcessor.java @@ -27,8 +27,10 @@ import static com.facebook.presto.SequencePageBuilder.createSequencePage; import static com.facebook.presto.SequencePageBuilder.createSequencePageWithDictionaryBlocks; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.operator.PageAssertions.assertPageEquals; +import static com.facebook.presto.operator.project.PageProcessor.MAX_BATCH_SIZE; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.relational.Expressions.field; @@ -40,22 +42,34 @@ public class TestColumnarPageProcessor private static final int POSITIONS = 100; private final List types = ImmutableList.of(BIGINT, VARCHAR); private final MetadataManager metadata = createTestMetadataManager(); - private final PageProcessor processor = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)) - .compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, types.get(0)), field(1, types.get(1)))).get(); @Test public void testProcess() { + PageProcessor processor = newPageProcessor(); Page page = createPage(types, false); - Page outputPage = getOnlyElement(processor.process(SESSION, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertPageEquals(types, outputPage, page); } @Test public void testProcessWithDictionary() { + PageProcessor processor = newPageProcessor(); Page page = createPage(types, true); - Page outputPage = getOnlyElement(processor.process(SESSION, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertPageEquals(types, outputPage, page); } @@ -63,4 +77,10 @@ private static Page createPage(List types, boolean dictionary) { return dictionary ? createSequencePageWithDictionaryBlocks(types, POSITIONS) : createSequencePage(types, POSITIONS); } + + private PageProcessor newPageProcessor() + { + return new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)) + .compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, types.get(0)), field(1, types.get(1))), MAX_BATCH_SIZE).get(); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestDistinctLimitOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestDistinctLimitOperator.java index 7e4fc369b7315..86ca2b0989661 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestDistinctLimitOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestDistinctLimitOperator.java @@ -62,7 +62,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); joinCompiler = new JoinCompiler(MetadataManager.createTestMetadataManager(), new FeaturesConfig()); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestDriver.java b/presto-main/src/test/java/com/facebook/presto/operator/TestDriver.java index 232b57512b99d..7f16379acdbb8 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestDriver.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestDriver.java @@ -84,7 +84,7 @@ public void setUp() scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } @@ -118,7 +118,7 @@ public void testNormalFinish() } // The race can be reproduced somewhat reliably when the invocationCount is 10K, but we use 1K iterations to cap the test runtime. - @Test(invocationCount = 1_000, timeOut = 1_000) + @Test(invocationCount = 1_000, timeOut = 10_000) public void testConcurrentClose() { List types = ImmutableList.of(VARCHAR, BIGINT, BIGINT); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestDriverStats.java b/presto-main/src/test/java/com/facebook/presto/operator/TestDriverStats.java index 910279c72607a..1098d5114b4e9 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestDriverStats.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestDriverStats.java @@ -46,7 +46,6 @@ public class TestDriverStats new Duration(9, NANOSECONDS), new Duration(10, NANOSECONDS), - new Duration(11, NANOSECONDS), new Duration(12, NANOSECONDS), false, ImmutableSet.of(), @@ -92,7 +91,6 @@ public static void assertExpectedDriverStats(DriverStats actual) assertEquals(actual.getTotalScheduledTime(), new Duration(9, NANOSECONDS)); assertEquals(actual.getTotalCpuTime(), new Duration(10, NANOSECONDS)); - assertEquals(actual.getTotalUserTime(), new Duration(11, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(12, NANOSECONDS)); assertEquals(actual.getRawInputDataSize(), new DataSize(13, BYTE)); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestExchangeOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestExchangeOperator.java index a4262c4b96477..60e515c7db911 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestExchangeOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestExchangeOperator.java @@ -250,7 +250,7 @@ private SourceOperator createExchangeOperator() ExchangeOperatorFactory operatorFactory = new ExchangeOperatorFactory(0, new PlanNodeId("test"), exchangeClientSupplier, SERDE_FACTORY); DriverContext driverContext = createTaskContext(scheduler, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); SourceOperator operator = operatorFactory.createOperator(driverContext); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestFilterAndProjectOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestFilterAndProjectOperator.java index e95be33459457..49b8107f437eb 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestFilterAndProjectOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestFilterAndProjectOperator.java @@ -68,7 +68,7 @@ public void setUp() scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestGroupIdOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestGroupIdOperator.java index 7fd3afabcaafd..34d2695f71f2e 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestGroupIdOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestGroupIdOperator.java @@ -54,7 +54,7 @@ public void setUp() scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestHashAggregationOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestHashAggregationOperator.java index 82248b5cc3933..bfc8f9de4a80e 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestHashAggregationOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestHashAggregationOperator.java @@ -73,6 +73,7 @@ import static com.facebook.presto.testing.TestingTaskContext.createTaskContext; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static com.google.common.util.concurrent.Futures.immediateFuture; import static io.airlift.concurrent.Threads.daemonThreadsNamed; @@ -84,8 +85,10 @@ import static io.airlift.units.DataSize.Unit.MEGABYTE; import static io.airlift.units.DataSize.succinctBytes; import static java.lang.String.format; +import static java.util.Collections.emptyIterator; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newScheduledThreadPool; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -183,12 +186,13 @@ public void testHashAggregation(boolean hashEnabled, boolean spillEnabled, long rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), spillEnabled, succinctBytes(memoryLimitForMerge), succinctBytes(memoryLimitForMergeWithMemory), spillerFactory, - joinCompiler); + joinCompiler, + false); DriverContext driverContext = createDriverContext(memoryLimitForMerge); @@ -243,12 +247,13 @@ public void testHashAggregationWithGlobals(boolean hashEnabled, boolean spillEna rowPagesBuilder.getHashChannel(), groupIdChannel, 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), spillEnabled, succinctBytes(memoryLimitForMerge), succinctBytes(memoryLimitForMergeWithMemory), spillerFactory, - joinCompiler); + joinCompiler, + false); DriverContext driverContext = createDriverContext(memoryLimitForMerge); MaterializedResult expected = resultBuilder(driverContext.getSession(), VARCHAR, BIGINT, BIGINT, BIGINT, DOUBLE, VARCHAR, BIGINT, BIGINT) @@ -275,7 +280,7 @@ public void testHashAggregationMemoryReservation(boolean hashEnabled, boolean sp .build(); DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(10, Unit.MEGABYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); HashAggregationOperatorFactory operatorFactory = new HashAggregationOperatorFactory( @@ -290,19 +295,20 @@ public void testHashAggregationMemoryReservation(boolean hashEnabled, boolean sp rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), spillEnabled, succinctBytes(memoryLimitForMerge), succinctBytes(memoryLimitForMergeWithMemory), spillerFactory, - joinCompiler); + joinCompiler, + false); Operator operator = operatorFactory.createOperator(driverContext); toPages(operator, input.iterator()); assertEquals(operator.getOperatorContext().getOperatorStats().getUserMemoryReservation().toBytes(), 0); } - @Test(dataProvider = "hashEnabled", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of 10B") + @Test(dataProvider = "hashEnabled", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of 10B when increasing allocation of 0B by .*") public void testMemoryLimit(boolean hashEnabled) { MetadataManager metadata = MetadataManager.createTestMetadataManager(); @@ -318,7 +324,7 @@ public void testMemoryLimit(boolean hashEnabled) .build(); DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(10, Unit.BYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); HashAggregationOperatorFactory operatorFactory = new HashAggregationOperatorFactory( @@ -335,8 +341,9 @@ public void testMemoryLimit(boolean hashEnabled) rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), - joinCompiler); + Optional.of(new DataSize(16, MEGABYTE)), + joinCompiler, + false); toPages(operatorFactory, driverContext, input); } @@ -370,12 +377,13 @@ public void testHashBuilderResize(boolean hashEnabled, boolean spillEnabled, lon rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), spillEnabled, succinctBytes(memoryLimitForMerge), succinctBytes(memoryLimitForMergeWithMemory), spillerFactory, - joinCompiler); + joinCompiler, + false); toPages(operatorFactory, driverContext, input); } @@ -385,18 +393,19 @@ public void testMemoryReservationYield(Type type) { List input = createPagesWithDistinctHashKeys(type, 6_000, 600); OperatorFactory operatorFactory = new HashAggregationOperatorFactory( - 0, - new PlanNodeId("test"), - ImmutableList.of(type), - ImmutableList.of(0), - ImmutableList.of(), - Step.SINGLE, - ImmutableList.of(COUNT.bind(ImmutableList.of(0), Optional.empty())), - Optional.of(1), - Optional.empty(), - 1, - new DataSize(16, MEGABYTE), - joinCompiler); + 0, + new PlanNodeId("test"), + ImmutableList.of(type), + ImmutableList.of(0), + ImmutableList.of(), + Step.SINGLE, + ImmutableList.of(COUNT.bind(ImmutableList.of(0), Optional.empty())), + Optional.of(1), + Optional.empty(), + 1, + Optional.of(new DataSize(16, MEGABYTE)), + joinCompiler, + false); // get result with yield; pick a relatively small buffer for aggregator's memory usage GroupByHashYieldResult result; @@ -416,7 +425,7 @@ public void testMemoryReservationYield(Type type) assertEquals(count, 6_000 * 600); } - @Test(dataProvider = "hashEnabled", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of 3MB") + @Test(dataProvider = "hashEnabled", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of 3MB when increasing allocation of 0B by .*") public void testHashBuilderResizeLimit(boolean hashEnabled) { BlockBuilder builder = VARCHAR.createBlockBuilder(null, 1, MAX_BLOCK_SIZE_IN_BYTES); @@ -432,7 +441,7 @@ public void testHashBuilderResizeLimit(boolean hashEnabled) .build(); DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(3, MEGABYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); HashAggregationOperatorFactory operatorFactory = new HashAggregationOperatorFactory( @@ -446,8 +455,9 @@ public void testHashBuilderResizeLimit(boolean hashEnabled) rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), - joinCompiler); + Optional.of(new DataSize(16, MEGABYTE)), + joinCompiler, + false); toPages(operatorFactory, driverContext, input); } @@ -479,8 +489,9 @@ public void testMultiSliceAggregationOutput(boolean hashEnabled) rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), - joinCompiler); + Optional.of(new DataSize(16, MEGABYTE)), + joinCompiler, + false); assertEquals(toPages(operatorFactory, createDriverContext(), input).size(), 2); } @@ -509,8 +520,9 @@ public void testMultiplePartialFlushes(boolean hashEnabled) rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(1, KILOBYTE), - joinCompiler); + Optional.of(new DataSize(1, KILOBYTE)), + joinCompiler, + true); DriverContext driverContext = createDriverContext(1024); @@ -529,6 +541,9 @@ public void testMultiplePartialFlushes(boolean hashEnabled) operator.addInput(inputIterator.next()); } + assertThat(driverContext.getSystemMemoryUsage()).isGreaterThan(0); + assertEquals(driverContext.getMemoryUsage(), 0); + // Drain the output (partial flush) List outputPages = new ArrayList<>(); while (true) { @@ -558,6 +573,9 @@ public void testMultiplePartialFlushes(boolean hashEnabled) assertEquals(actual.getTypes(), expected.getTypes()); assertEqualsIgnoreOrder(actual.getMaterializedRows(), expected.getMaterializedRows()); } + + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertEquals(driverContext.getMemoryUsage(), 0); } @Test @@ -584,12 +602,13 @@ public void testMergeWithMemorySpill() rowPagesBuilder.getHashChannel(), Optional.empty(), 1, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), true, new DataSize(smallPagesSpillThresholdSize, Unit.BYTE), succinctBytes(Integer.MAX_VALUE), spillerFactory, - joinCompiler); + joinCompiler, + false); DriverContext driverContext = createDriverContext(smallPagesSpillThresholdSize); @@ -621,7 +640,7 @@ public void testSpillerFailure() .setQueryMaxMemory(DataSize.valueOf("7MB")) .setMemoryPoolSize(DataSize.valueOf("1GB")) .build() - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); HashAggregationOperatorFactory operatorFactory = new HashAggregationOperatorFactory( @@ -639,12 +658,13 @@ public void testSpillerFailure() rowPagesBuilder.getHashChannel(), Optional.empty(), 100_000, - new DataSize(16, MEGABYTE), + Optional.of(new DataSize(16, MEGABYTE)), true, succinctBytes(8), succinctBytes(Integer.MAX_VALUE), new FailingSpillerFactory(), - joinCompiler); + joinCompiler, + false); try { toPages(operatorFactory, driverContext, input); @@ -657,6 +677,58 @@ public void testSpillerFailure() } } + @Test + public void testMemoryTracking() + throws Exception + { + testMemoryTracking(false); + testMemoryTracking(true); + } + + private void testMemoryTracking(boolean useSystemMemory) + throws Exception + { + List hashChannels = Ints.asList(0); + RowPagesBuilder rowPagesBuilder = rowPagesBuilder(false, hashChannels, BIGINT); + Page input = getOnlyElement(rowPagesBuilder.addSequencePage(500, 0).build()); + + HashAggregationOperatorFactory operatorFactory = new HashAggregationOperatorFactory( + 0, + new PlanNodeId("test"), + ImmutableList.of(BIGINT), + hashChannels, + ImmutableList.of(), + Step.SINGLE, + ImmutableList.of(LONG_SUM.bind(ImmutableList.of(0), Optional.empty())), + rowPagesBuilder.getHashChannel(), + Optional.empty(), + 100_000, + Optional.of(new DataSize(16, MEGABYTE)), + joinCompiler, + useSystemMemory); + + DriverContext driverContext = createDriverContext(1024); + + try (Operator operator = operatorFactory.createOperator(driverContext)) { + assertTrue(operator.needsInput()); + operator.addInput(input); + + if (useSystemMemory) { + assertThat(driverContext.getSystemMemoryUsage()).isGreaterThan(0); + assertEquals(driverContext.getMemoryUsage(), 0); + } + else { + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertThat(driverContext.getMemoryUsage()).isGreaterThan(0); + } + + toPages(operator, emptyIterator()); + } + + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertEquals(driverContext.getMemoryUsage(), 0); + } + private DriverContext createDriverContext() { return createDriverContext(Integer.MAX_VALUE); @@ -667,7 +739,7 @@ private DriverContext createDriverContext(long memoryLimit) return TestingTaskContext.builder(executor, scheduledExecutor, TEST_SESSION) .setMemoryPoolSize(succinctBytes(memoryLimit)) .build() - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestHashJoinOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestHashJoinOperator.java index d542d9f8d1a0b..f259b4e485926 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestHashJoinOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestHashJoinOperator.java @@ -159,18 +159,23 @@ public void testInnerJoin(boolean parallelBuild, boolean probeHashEnabled, boole { TaskContext taskContext = createTaskContext(); - // build + // build factory RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(10, 20, 30, 40); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT, BIGINT)); List probeInput = probePages .addSequencePage(1000, 0, 1000, 2000) .build(); OperatorFactory joinOperatorFactory = innerJoinOperatorFactory(lookupSourceFactory, probePages, PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probePages.getTypesWithoutHash(), buildPages.getTypesWithoutHash())) .row("20", 1020L, 2020L, "20", 30L, 40L) @@ -185,7 +190,7 @@ public void testInnerJoin(boolean parallelBuild, boolean probeHashEnabled, boole .row("29", 1029L, 2029L, "29", 39L, 49L) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test @@ -195,7 +200,7 @@ public void testYield() // verify we will yield #match times totally TaskContext taskContext = createTaskContext(); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // force a yield for every match AtomicInteger filterFunctionCalls = new AtomicInteger(); @@ -210,7 +215,8 @@ public void testYield() int entries = 40; RowPagesBuilder buildPages = rowPagesBuilder(true, Ints.asList(0), ImmutableList.of(BIGINT)) .addSequencePage(entries, 42); - JoinBridgeDataManager lookupSourceFactory = buildHash(true, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction)); + BuildSideSetup buildSideSetup = setupBuildSide(true, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); // probe matching the above 40 entries RowPagesBuilder probePages = rowPagesBuilder(false, Ints.asList(0), ImmutableList.of(BIGINT)); @@ -226,6 +232,8 @@ public void testYield() OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); Operator operator = joinOperatorFactory.createOperator(driverContext); assertTrue(operator.needsInput()); operator.addInput(probeInput.get(0)); @@ -357,7 +365,7 @@ private void innerJoinWithSpill(boolean probeHashEnabled, List whenSp TaskStateMachine taskStateMachine = new TaskStateMachine(new TaskId("query", 0, 0), executor); TaskContext taskContext = TestingTaskContext.createTaskContext(executor, scheduledExecutor, TEST_SESSION, taskStateMachine); - DriverContext joinDriverContext = taskContext.addPipelineContext(2, true, true).addDriverContext(); + DriverContext joinDriverContext = taskContext.addPipelineContext(2, true, true, false).addDriverContext(); // force a yield for every match in LookupJoinOperator, set called to true after first AtomicBoolean called = new AtomicBoolean(false); @@ -368,7 +376,7 @@ private void innerJoinWithSpill(boolean probeHashEnabled, List whenSp return true; }); - // build side + // build factory RowPagesBuilder buildPages = rowPagesBuilder(ImmutableList.of(VARCHAR, BIGINT)) .addSequencePage(4, 20, 200) .addSequencePage(4, 20, 200) @@ -376,21 +384,24 @@ private void innerJoinWithSpill(boolean probeHashEnabled, List whenSp .addSequencePage(4, 40, 400); BuildSideSetup buildSideSetup = setupBuildSide(true, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), true, buildSpillerFactory); - List buildDrivers = buildSideSetup.getBuildDrivers(); - int buildOperatorCount = buildDrivers.size(); - checkState(buildOperatorCount == whenSpill.size()); - JoinBridgeDataManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - LookupSourceFactory lookupSourceFactory = lookupSourceFactoryManager.forLifespan(Lifespan.taskWide()); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe side + // probe factory RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT)) .row("20", 123_000L) .row("20", 123_000L) .pageBreak() .addSequencePage(20, 0, 123_000) .addSequencePage(10, 30, 123_000); - OperatorFactory joinOperatorFactory = innerJoinOperatorFactory(lookupSourceFactoryManager, probePages, joinSpillerFactory); + + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + List buildDrivers = buildSideSetup.getBuildDrivers(); + int buildOperatorCount = buildDrivers.size(); + checkState(buildOperatorCount == whenSpill.size()); + LookupSourceFactory lookupSourceFactory = lookupSourceFactoryManager.getJoinBridge(Lifespan.taskWide()); + try (Operator joinOperator = joinOperatorFactory.createOperator(joinDriverContext)) { // build lookup source ListenableFuture lookupSourceProvider = lookupSourceFactory.createLookupSourceProvider(); @@ -529,15 +540,16 @@ public void testInnerJoinWithNullProbe(boolean parallelBuild, boolean probeHashE { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") .row("b") .row("c"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -549,6 +561,10 @@ public void testInnerJoinWithNullProbe(boolean parallelBuild, boolean probeHashE .build(); OperatorFactory joinOperatorFactory = innerJoinOperatorFactory(lookupSourceFactory, probePages, PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypesWithoutHash())) .row("a", "a") @@ -556,7 +572,7 @@ public void testInnerJoinWithNullProbe(boolean parallelBuild, boolean probeHashE .row("b", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -564,7 +580,7 @@ public void testInnerJoinWithNullBuild(boolean parallelBuild, boolean probeHashE { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") @@ -572,9 +588,10 @@ public void testInnerJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -584,6 +601,10 @@ public void testInnerJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .build(); OperatorFactory joinOperatorFactory = innerJoinOperatorFactory(lookupSourceFactory, probePages, PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -591,7 +612,7 @@ public void testInnerJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .row("b", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -599,7 +620,7 @@ public void testInnerJoinWithNullOnBothSides(boolean parallelBuild, boolean prob { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") @@ -607,9 +628,10 @@ public void testInnerJoinWithNullOnBothSides(boolean parallelBuild, boolean prob .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -620,6 +642,10 @@ public void testInnerJoinWithNullOnBothSides(boolean parallelBuild, boolean prob .build(); OperatorFactory joinOperatorFactory = innerJoinOperatorFactory(lookupSourceFactory, probePages, PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -627,7 +653,7 @@ public void testInnerJoinWithNullOnBothSides(boolean parallelBuild, boolean prob .row("b", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -635,13 +661,14 @@ public void testProbeOuterJoin(boolean parallelBuild, boolean probeHashEnabled, { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR, BIGINT, BIGINT); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(10, 20, 30, 40); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR, BIGINT, BIGINT); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -649,7 +676,10 @@ public void testProbeOuterJoin(boolean parallelBuild, boolean probeHashEnabled, .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); - // expected + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("20", 1020L, 2020L, "20", 30L, 40L) @@ -669,7 +699,7 @@ public void testProbeOuterJoin(boolean parallelBuild, boolean probeHashEnabled, .row("34", 1034L, 2034L, null, null, null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -680,13 +710,14 @@ public void testProbeOuterJoinWithFilterFunction(boolean parallelBuild, boolean InternalJoinFilterFunction filterFunction = new TestInternalJoinFilterFunction(( (leftPosition, leftPage, rightPosition, rightPage) -> BIGINT.getLong(rightPage.getBlock(1), rightPosition) >= 1025)); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR, BIGINT, BIGINT); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(10, 20, 30, 40); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction)); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR, BIGINT, BIGINT); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -694,6 +725,10 @@ public void testProbeOuterJoinWithFilterFunction(boolean parallelBuild, boolean .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("20", 1020L, 2020L, null, null, null) @@ -713,7 +748,7 @@ public void testProbeOuterJoinWithFilterFunction(boolean parallelBuild, boolean .row("34", 1034L, 2034L, null, null, null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -721,15 +756,16 @@ public void testOuterJoinWithNullProbe(boolean parallelBuild, boolean probeHashE { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") .row("b") .row("c"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -741,6 +777,10 @@ public void testOuterJoinWithNullProbe(boolean parallelBuild, boolean probeHashE .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -750,7 +790,7 @@ public void testOuterJoinWithNullProbe(boolean parallelBuild, boolean probeHashE .row("b", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -761,15 +801,16 @@ public void testOuterJoinWithNullProbeAndFilterFunction(boolean parallelBuild, b InternalJoinFilterFunction filterFunction = new TestInternalJoinFilterFunction(( (leftPosition, leftPage, rightPosition, rightPage) -> VARCHAR.getSlice(rightPage.getBlock(0), rightPosition).toStringAscii().equals("a"))); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") .row("b") .row("c"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction)); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -781,6 +822,10 @@ public void testOuterJoinWithNullProbeAndFilterFunction(boolean parallelBuild, b .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -790,7 +835,7 @@ public void testOuterJoinWithNullProbeAndFilterFunction(boolean parallelBuild, b .row("b", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -798,7 +843,7 @@ public void testOuterJoinWithNullBuild(boolean parallelBuild, boolean probeHashE { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR)) .row("a") @@ -806,9 +851,10 @@ public void testOuterJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -818,6 +864,10 @@ public void testOuterJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -826,7 +876,7 @@ public void testOuterJoinWithNullBuild(boolean parallelBuild, boolean probeHashE .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -838,7 +888,7 @@ public void testOuterJoinWithNullBuildAndFilterFunction(boolean parallelBuild, b (leftPosition, leftPage, rightPosition, rightPage) -> ImmutableSet.of("a", "c").contains(VARCHAR.getSlice(rightPage.getBlock(0), rightPosition).toStringAscii()))); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR)) .row("a") @@ -846,9 +896,10 @@ public void testOuterJoinWithNullBuildAndFilterFunction(boolean parallelBuild, b .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction)); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -858,6 +909,10 @@ public void testOuterJoinWithNullBuildAndFilterFunction(boolean parallelBuild, b .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", "a") @@ -866,7 +921,7 @@ public void testOuterJoinWithNullBuildAndFilterFunction(boolean parallelBuild, b .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -874,16 +929,17 @@ public void testOuterJoinWithNullOnBothSides(boolean parallelBuild, boolean prob { TaskContext taskContext = createTaskContext(); - // build + // build factory RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR)) .row("a") .row((String) null) .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -894,6 +950,10 @@ public void testOuterJoinWithNullOnBothSides(boolean parallelBuild, boolean prob .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypesWithoutHash())) .row("a", "a") @@ -903,7 +963,7 @@ public void testOuterJoinWithNullOnBothSides(boolean parallelBuild, boolean prob .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -915,16 +975,17 @@ public void testOuterJoinWithNullOnBothSidesAndFilterFunction(boolean parallelBu (leftPosition, leftPage, rightPosition, rightPage) -> ImmutableSet.of("a", "c").contains(VARCHAR.getSlice(rightPage.getBlock(0), rightPosition).toStringAscii()))); - // build + // build factory RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR)) .row("a") .row((String) null) .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager lookupSourceFactory = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction)); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.of(filterFunction), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -935,6 +996,10 @@ public void testOuterJoinWithNullOnBothSidesAndFilterFunction(boolean parallelBu .build(); OperatorFactory joinOperatorFactory = probeOuterJoinOperatorFactory(lookupSourceFactory, probePages); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypesWithoutHash())) .row("a", "a") @@ -944,17 +1009,19 @@ public void testOuterJoinWithNullOnBothSidesAndFilterFunction(boolean parallelBu .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } - @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of.*", dataProvider = "testMemoryLimitProvider") + @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of.*", dataProvider = "testMemoryLimitProvider") public void testMemoryLimit(boolean parallelBuild, boolean buildHashEnabled) { TaskContext taskContext = TestingTaskContext.createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(100, BYTE)); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(10, 20, 30, 40); - buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); } @Test(dataProvider = "hashJoinTestValues") @@ -962,12 +1029,13 @@ public void testInnerJoinWithEmptyLookupSource(boolean parallelBuild, boolean pr { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes); - JoinBridgeDataManager lookupSourceFactoryManager = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); OperatorFactory joinOperatorFactory = new LookupJoinOperators().innerJoin( @@ -980,7 +1048,11 @@ public void testInnerJoinWithEmptyLookupSource(boolean parallelBuild, boolean pr Optional.empty(), OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); - Operator operator = joinOperatorFactory.createOperator(taskContext.addPipelineContext(0, true, true).addDriverContext()); + + // drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + Operator operator = joinOperatorFactory.createOperator(taskContext.addPipelineContext(0, true, true, false).addDriverContext()); List pages = probePages.row("test").build(); operator.addInput(pages.get(0)); @@ -993,12 +1065,13 @@ public void testLookupOuterJoinWithEmptyLookupSource(boolean parallelBuild, bool { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes); - JoinBridgeDataManager lookupSourceFactoryManager = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); OperatorFactory joinOperatorFactory = new LookupJoinOperators().lookupOuterJoin( @@ -1011,7 +1084,11 @@ public void testLookupOuterJoinWithEmptyLookupSource(boolean parallelBuild, bool Optional.empty(), OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); - Operator operator = joinOperatorFactory.createOperator(taskContext.addPipelineContext(0, true, true).addDriverContext()); + + // drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + Operator operator = joinOperatorFactory.createOperator(taskContext.addPipelineContext(0, true, true, false).addDriverContext()); List pages = probePages.row("test").build(); operator.addInput(pages.get(0)); @@ -1024,12 +1101,13 @@ public void testProbeOuterJoinWithEmptyLookupSource(boolean parallelBuild, boole { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes); - JoinBridgeDataManager lookupSourceFactoryManager = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -1049,6 +1127,10 @@ public void testProbeOuterJoinWithEmptyLookupSource(boolean parallelBuild, boole OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", null) @@ -1056,7 +1138,7 @@ public void testProbeOuterJoinWithEmptyLookupSource(boolean parallelBuild, boole .row(null, null) .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -1064,12 +1146,13 @@ public void testFullOuterJoinWithEmptyLookupSource(boolean parallelBuild, boolea { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes); - JoinBridgeDataManager lookupSourceFactoryManager = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages @@ -1089,6 +1172,10 @@ public void testFullOuterJoinWithEmptyLookupSource(boolean parallelBuild, boolea OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)) .row("a", null) @@ -1096,7 +1183,7 @@ public void testFullOuterJoinWithEmptyLookupSource(boolean parallelBuild, boolea .row(null, null) .row("c", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @Test(dataProvider = "hashJoinTestValues") @@ -1104,16 +1191,17 @@ public void testInnerJoinWithNonEmptyLookupSourceAndEmptyProbe(boolean parallelB { TaskContext taskContext = createTaskContext(); - // build + // build factory List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildHashEnabled, Ints.asList(0), buildTypes) .row("a") .row("b") .row((String) null) .row("c"); - JoinBridgeDataManager lookupSourceFactoryManager = buildHash(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty()); + BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, Ints.asList(0), buildPages, Optional.empty(), false, SINGLE_STREAM_SPILLER_FACTORY); + JoinBridgeManager lookupSourceFactoryManager = buildSideSetup.getLookupSourceFactoryManager(); - // probe + // probe factory List probeTypes = ImmutableList.of(VARCHAR); RowPagesBuilder probePages = rowPagesBuilder(probeHashEnabled, Ints.asList(0), probeTypes); List probeInput = probePages.build(); @@ -1128,9 +1216,13 @@ public void testInnerJoinWithNonEmptyLookupSourceAndEmptyProbe(boolean parallelB OptionalInt.of(1), PARTITIONING_SPILLER_FACTORY); + // build drivers and operators + instantiateBuildDrivers(buildSideSetup, taskContext); + buildLookupSource(buildSideSetup); + // expected MaterializedResult expected = MaterializedResult.resultBuilder(taskContext.getSession(), concat(probeTypes, buildTypes)).build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected, true, getHashChannels(probePages, buildPages)); } @DataProvider @@ -1160,7 +1252,7 @@ private static List getHashChannels(RowPagesBuilder probe, RowPagesBuil return hashChannels.build(); } - private OperatorFactory probeOuterJoinOperatorFactory(JoinBridgeDataManager lookupSourceFactoryManager, RowPagesBuilder probePages) + private OperatorFactory probeOuterJoinOperatorFactory(JoinBridgeManager lookupSourceFactoryManager, RowPagesBuilder probePages) { return LOOKUP_JOIN_OPERATORS.probeOuterJoin( 0, @@ -1174,7 +1266,7 @@ private OperatorFactory probeOuterJoinOperatorFactory(JoinBridgeDataManager lookupSourceFactoryManager, RowPagesBuilder probePages, PartitioningSpillerFactory partitioningSpillerFactory) + private OperatorFactory innerJoinOperatorFactory(JoinBridgeManager lookupSourceFactoryManager, RowPagesBuilder probePages, PartitioningSpillerFactory partitioningSpillerFactory) { return LOOKUP_JOIN_OPERATORS.innerJoin( 0, @@ -1188,18 +1280,6 @@ private OperatorFactory innerJoinOperatorFactory(JoinBridgeDataManager buildHash( - boolean parallelBuild, - TaskContext taskContext, - List hashChannels, - RowPagesBuilder buildPages, - Optional filterFunction) - { - BuildSideSetup buildSideSetup = setupBuildSide(parallelBuild, taskContext, hashChannels, buildPages, filterFunction, false, SINGLE_STREAM_SPILLER_FACTORY); - buildLookupSource(buildSideSetup); - return buildSideSetup.getLookupSourceFactoryManager(); - } - private BuildSideSetup setupBuildSide( boolean parallelBuild, TaskContext taskContext, @@ -1210,7 +1290,7 @@ private BuildSideSetup setupBuildSide( SingleStreamSpillerFactory singleStreamSpillerFactory) { Optional filterFunctionFactory = filterFunction - .map(function -> (session, addresses, channels) -> new StandardJoinFilterFunction(function, addresses, channels)); + .map(function -> (session, addresses, pages) -> new StandardJoinFilterFunction(function, addresses, pages)); int partitionCount = parallelBuild ? PARTITION_COUNT : 1; LocalExchangeFactory localExchangeFactory = new LocalExchangeFactory( @@ -1225,7 +1305,7 @@ private BuildSideSetup setupBuildSide( localExchangeFactory.noMoreSinkFactories(); // collect input data into the partitioned exchange - DriverContext collectDriverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext collectDriverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); ValuesOperatorFactory valuesOperatorFactory = new ValuesOperatorFactory(0, new PlanNodeId("values"), buildPages.build()); LocalExchangeSinkOperatorFactory sinkOperatorFactory = new LocalExchangeSinkOperatorFactory(localExchangeFactory, 1, new PlanNodeId("sink"), localExchangeSinkFactoryId, Function.identity()); Driver sourceDriver = Driver.createDriver(collectDriverContext, @@ -1238,9 +1318,9 @@ private BuildSideSetup setupBuildSide( sourceDriver.process(); } - // build hash tables + // build side operator factories LocalExchangeSourceOperatorFactory sourceOperatorFactory = new LocalExchangeSourceOperatorFactory(0, new PlanNodeId("source"), localExchangeFactory); - JoinBridgeDataManager lookupSourceFactoryManager = JoinBridgeDataManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( + JoinBridgeManager lookupSourceFactoryManager = JoinBridgeManager.lookupAllAtOnce(new PartitionedLookupSourceFactory( buildPages.getTypes(), rangeList(buildPages.getTypes().size()).stream() .map(buildPages.getTypes()::get) @@ -1251,6 +1331,7 @@ private BuildSideSetup setupBuildSide( partitionCount, requireNonNull(ImmutableMap.of(), "layout is null"), false)); + HashBuilderOperatorFactory buildOperatorFactory = new HashBuilderOperatorFactory( 1, new PlanNodeId("build"), @@ -1266,29 +1347,33 @@ private BuildSideSetup setupBuildSide( new PagesIndex.TestingFactory(false), spillEnabled, singleStreamSpillerFactory); - PipelineContext buildPipeline = taskContext.addPipelineContext(1, true, true); + return new BuildSideSetup(lookupSourceFactoryManager, buildOperatorFactory, sourceOperatorFactory, partitionCount); + } + private void instantiateBuildDrivers(BuildSideSetup buildSideSetup, TaskContext taskContext) + { + PipelineContext buildPipeline = taskContext.addPipelineContext(1, true, true, false); List buildDrivers = new ArrayList<>(); List buildOperators = new ArrayList<>(); - for (int i = 0; i < partitionCount; i++) { + for (int i = 0; i < buildSideSetup.getPartitionCount(); i++) { DriverContext buildDriverContext = buildPipeline.addDriverContext(); - HashBuilderOperator buildOperator = buildOperatorFactory.createOperator(buildDriverContext); + HashBuilderOperator buildOperator = buildSideSetup.getBuildOperatorFactory().createOperator(buildDriverContext); Driver driver = Driver.createDriver( buildDriverContext, - sourceOperatorFactory.createOperator(buildDriverContext), + buildSideSetup.getBuildSideSourceOperatorFactory().createOperator(buildDriverContext), buildOperator); buildDrivers.add(driver); buildOperators.add(buildOperator); } - return new BuildSideSetup(lookupSourceFactoryManager, buildDrivers, buildOperators); + buildSideSetup.setDriversAndOperators(buildDrivers, buildOperators); } private void buildLookupSource(BuildSideSetup buildSideSetup) { requireNonNull(buildSideSetup, "buildSideSetup is null"); - LookupSourceFactory lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager().forLifespan(Lifespan.taskWide()); + LookupSourceFactory lookupSourceFactory = buildSideSetup.getLookupSourceFactoryManager().getJoinBridge(Lifespan.taskWide()); Future lookupSourceProvider = lookupSourceFactory.createLookupSourceProvider(); List buildDrivers = buildSideSetup.getBuildDrivers(); @@ -1354,30 +1439,57 @@ private static List concat(List initialElements, List moreElements) private static class BuildSideSetup { - private final JoinBridgeDataManager lookupSourceFactoryManager; - private final List buildDrivers; - private final List buildOperators; + private final JoinBridgeManager lookupSourceFactoryManager; + private final HashBuilderOperatorFactory buildOperatorFactory; + private final LocalExchangeSourceOperatorFactory buildSideSourceOperatorFactory; + private final int partitionCount; + private List buildDrivers; + private List buildOperators; + + BuildSideSetup(JoinBridgeManager lookupSourceFactoryManager, HashBuilderOperatorFactory buildOperatorFactory, LocalExchangeSourceOperatorFactory buildSideSourceOperatorFactory, int partitionCount) + { + this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactoryManager, "lookupSourceFactoryManager is null"); + this.buildOperatorFactory = requireNonNull(buildOperatorFactory, "buildOperatorFactory is null"); + this.buildSideSourceOperatorFactory = buildSideSourceOperatorFactory; + this.partitionCount = partitionCount; + } - BuildSideSetup(JoinBridgeDataManager lookupSourceFactoryManager, List buildDrivers, List buildOperators) + void setDriversAndOperators(List buildDrivers, List buildOperators) { checkArgument(buildDrivers.size() == buildOperators.size()); - this.lookupSourceFactoryManager = requireNonNull(lookupSourceFactoryManager, "lookupSourceFactoryManager is null"); this.buildDrivers = ImmutableList.copyOf(buildDrivers); this.buildOperators = ImmutableList.copyOf(buildOperators); } - JoinBridgeDataManager getLookupSourceFactoryManager() + JoinBridgeManager getLookupSourceFactoryManager() { return lookupSourceFactoryManager; } + HashBuilderOperatorFactory getBuildOperatorFactory() + { + return buildOperatorFactory; + } + + public LocalExchangeSourceOperatorFactory getBuildSideSourceOperatorFactory() + { + return buildSideSourceOperatorFactory; + } + + public int getPartitionCount() + { + return partitionCount; + } + List getBuildDrivers() { + checkState(buildDrivers != null, "buildDrivers is not initialized yet"); return buildDrivers; } List getBuildOperators() { + checkState(buildOperators != null, "buildDrivers is not initialized yet"); return buildOperators; } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestHashSemiJoinOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestHashSemiJoinOperator.java index 5f4360abff4cc..c1c0faeec38e6 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestHashSemiJoinOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestHashSemiJoinOperator.java @@ -92,7 +92,7 @@ public Object[][] dataType() @Test(dataProvider = "hashEnabledValues") public void testSemiJoin(boolean hashEnabled) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // build OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), ValuesOperator.class.getSimpleName()); @@ -183,7 +183,7 @@ public void testSemiJoinMemoryReservationYield(Type type) @Test(dataProvider = "hashEnabledValues") public void testBuildSideNulls(boolean hashEnabled) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // build OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), ValuesOperator.class.getSimpleName()); @@ -239,7 +239,7 @@ public void testBuildSideNulls(boolean hashEnabled) @Test(dataProvider = "hashEnabledValues") public void testProbeSideNulls(boolean hashEnabled) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // build OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), ValuesOperator.class.getSimpleName()); @@ -295,7 +295,7 @@ public void testProbeSideNulls(boolean hashEnabled) @Test(dataProvider = "hashEnabledValues") public void testProbeAndBuildNulls(boolean hashEnabled) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); // build OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), ValuesOperator.class.getSimpleName()); @@ -349,11 +349,11 @@ public void testProbeAndBuildNulls(boolean hashEnabled) OperatorAssertion.assertOperatorEquals(joinOperatorFactory, driverContext, probeInput, expected, hashEnabled, ImmutableList.of(probeTypes.size())); } - @Test(dataProvider = "hashEnabledValues", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of.*") + @Test(dataProvider = "hashEnabledValues", expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of.*") public void testMemoryLimit(boolean hashEnabled) { DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(100, BYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); OperatorContext operatorContext = driverContext.addOperatorContext(0, new PlanNodeId("test"), ValuesOperator.class.getSimpleName()); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestLimitOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestLimitOperator.java index 3c321739972ab..5e2a11a610a67 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestLimitOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestLimitOperator.java @@ -49,7 +49,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestLookupJoinPageBuilder.java b/presto-main/src/test/java/com/facebook/presto/operator/TestLookupJoinPageBuilder.java index 30444a54e0d31..572c20fabf6c0 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestLookupJoinPageBuilder.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestLookupJoinPageBuilder.java @@ -44,7 +44,7 @@ public void testPageBuilder() Block block = blockBuilder.build(); Page page = new Page(block, block); - JoinProbeFactory joinProbeFactory = new JoinProbeFactory(new int[]{0, 1}, ImmutableList.of(0, 1), OptionalInt.empty()); + JoinProbeFactory joinProbeFactory = new JoinProbeFactory(new int[] {0, 1}, ImmutableList.of(0, 1), OptionalInt.empty()); JoinProbe probe = joinProbeFactory.createJoinProbe(page); LookupSource lookupSource = new TestLookupSource(ImmutableList.of(BIGINT, BIGINT), page); LookupJoinPageBuilder lookupJoinPageBuilder = new LookupJoinPageBuilder(ImmutableList.of(BIGINT, BIGINT)); @@ -92,7 +92,7 @@ public void testDifferentPositions() } Block block = blockBuilder.build(); Page page = new Page(block); - JoinProbeFactory joinProbeFactory = new JoinProbeFactory(new int[]{0}, ImmutableList.of(0), OptionalInt.empty()); + JoinProbeFactory joinProbeFactory = new JoinProbeFactory(new int[] {0}, ImmutableList.of(0), OptionalInt.empty()); LookupSource lookupSource = new TestLookupSource(ImmutableList.of(BIGINT), page); LookupJoinPageBuilder lookupJoinPageBuilder = new LookupJoinPageBuilder(ImmutableList.of(BIGINT)); @@ -164,7 +164,7 @@ public void testCrossJoinWithEmptyBuild() // nothing on the build side so we don't append anything LookupSource lookupSource = new TestLookupSource(ImmutableList.of(), page); - JoinProbe probe = (new JoinProbeFactory(new int[]{0}, ImmutableList.of(0), OptionalInt.empty())).createJoinProbe(page); + JoinProbe probe = (new JoinProbeFactory(new int[] {0}, ImmutableList.of(0), OptionalInt.empty())).createJoinProbe(page); LookupJoinPageBuilder lookupJoinPageBuilder = new LookupJoinPageBuilder(ImmutableList.of(BIGINT)); // append the same row many times should also flush in the end diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestMarkDistinctOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestMarkDistinctOperator.java index d8626a638856f..52daf1227f4a7 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestMarkDistinctOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestMarkDistinctOperator.java @@ -63,7 +63,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestMergeOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestMergeOperator.java index ef9f940e7c82f..e0dc76fa87579 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestMergeOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestMergeOperator.java @@ -343,7 +343,7 @@ private MergeOperator createMergeOperator(List sourceTypes, List sortChannels, sortOrder); DriverContext driverContext = createTaskContext(executor, executor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); return (MergeOperator) factory.createOperator(driverContext); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopBuildOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopBuildOperator.java index 63c2cc81e44f8..f86dbb73d66cb 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopBuildOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopBuildOperator.java @@ -64,17 +64,18 @@ public void testNestedLoopBuild() { TaskContext taskContext = createTaskContext(); List buildTypes = ImmutableList.of(BIGINT); - JoinBridgeDataManager nestedLoopJoinPagesSupplierManager = JoinBridgeDataManager.nestedLoop( + JoinBridgeManager nestedLoopJoinBridgeManager = new JoinBridgeManager<>( + false, PipelineExecutionStrategy.UNGROUPED_EXECUTION, PipelineExecutionStrategy.UNGROUPED_EXECUTION, lifespan -> new NestedLoopJoinPagesSupplier(), buildTypes); - NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesSupplierManager); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinBridgeManager); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); NestedLoopBuildOperator nestedLoopBuildOperator = (NestedLoopBuildOperator) nestedLoopBuildOperatorFactory.createOperator(driverContext); - NestedLoopJoinPagesBridge nestedLoopJoinPagesBridge = nestedLoopJoinPagesSupplierManager.forLifespan(Lifespan.taskWide()); + NestedLoopJoinBridge nestedLoopJoinBridge = nestedLoopJoinBridgeManager.getJoinBridge(Lifespan.taskWide()); - assertFalse(nestedLoopJoinPagesBridge.getPagesFuture().isDone()); + assertFalse(nestedLoopJoinBridge.getPagesFuture().isDone()); // build pages Page buildPage1 = new Page(3, createLongSequenceBlock(11, 14)); @@ -86,8 +87,8 @@ public void testNestedLoopBuild() nestedLoopBuildOperator.addInput(buildPage2); nestedLoopBuildOperator.finish(); - assertTrue(nestedLoopJoinPagesBridge.getPagesFuture().isDone()); - List buildPages = nestedLoopJoinPagesBridge.getPagesFuture().get().getPages(); + assertTrue(nestedLoopJoinBridge.getPagesFuture().isDone()); + List buildPages = nestedLoopJoinBridge.getPagesFuture().get().getPages(); assertEquals(buildPages.get(0), buildPage1); assertEquals(buildPages.get(1), buildPage2); @@ -100,17 +101,18 @@ public void testNestedLoopBuildNoBlock() { TaskContext taskContext = createTaskContext(); List buildTypes = ImmutableList.of(); - JoinBridgeDataManager nestedLoopJoinPagesSupplierManager = JoinBridgeDataManager.nestedLoop( + JoinBridgeManager nestedLoopJoinBridgeManager = new JoinBridgeManager<>( + false, PipelineExecutionStrategy.UNGROUPED_EXECUTION, PipelineExecutionStrategy.UNGROUPED_EXECUTION, lifespan -> new NestedLoopJoinPagesSupplier(), buildTypes); - NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesSupplierManager); - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinBridgeManager); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); NestedLoopBuildOperator nestedLoopBuildOperator = (NestedLoopBuildOperator) nestedLoopBuildOperatorFactory.createOperator(driverContext); - NestedLoopJoinPagesBridge nestedLoopJoinPagesBridge = nestedLoopJoinPagesSupplierManager.forLifespan(Lifespan.taskWide()); + NestedLoopJoinBridge nestedLoopJoinBridge = nestedLoopJoinBridgeManager.getJoinBridge(Lifespan.taskWide()); - assertFalse(nestedLoopJoinPagesBridge.getPagesFuture().isDone()); + assertFalse(nestedLoopJoinBridge.getPagesFuture().isDone()); // build pages Page buildPage1 = new Page(3); @@ -122,8 +124,8 @@ public void testNestedLoopBuildNoBlock() nestedLoopBuildOperator.addInput(buildPage2); nestedLoopBuildOperator.finish(); - assertTrue(nestedLoopJoinPagesBridge.getPagesFuture().isDone()); - List buildPages = nestedLoopJoinPagesBridge.getPagesFuture().get().getPages(); + assertTrue(nestedLoopJoinBridge.getPagesFuture().isDone()); + List buildPages = nestedLoopJoinBridge.getPagesFuture().get().getPages(); assertEquals(buildPages.get(0), buildPage1); assertEquals(buildPages.get(1), buildPage2); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopJoinOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopJoinOperator.java index d70170d552a66..556079a0a3197 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopJoinOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestNestedLoopJoinOperator.java @@ -72,14 +72,13 @@ public void testNestedLoopJoin() // build RowPagesBuilder buildPages = rowPagesBuilder(ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(3, 20, 30, 40); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe RowPagesBuilder probePages = rowPagesBuilder(ImmutableList.of(VARCHAR, BIGINT, BIGINT)); List probeInput = probePages .addSequencePage(2, 0, 1000, 2000) .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probePages.getTypes(), buildPages.getTypes())) @@ -91,13 +90,12 @@ public void testNestedLoopJoin() .row("1", 1001L, 2001L, "22", 32L, 42L) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); // Test probe pages has more rows buildPages = rowPagesBuilder(ImmutableList.of(VARCHAR, BIGINT, BIGINT)) .addSequencePage(2, 20, 30, 40); - nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); - joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // probe probePages = rowPagesBuilder(ImmutableList.of(VARCHAR, BIGINT, BIGINT)); @@ -115,7 +113,7 @@ public void testNestedLoopJoin() .row("2", 1002L, 2002L, "21", 31L, 41L) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -128,7 +126,6 @@ public void testCrossJoinWithNullProbe() RowPagesBuilder buildPages = rowPagesBuilder(buildTypes) .row("a") .row("b"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -141,7 +138,7 @@ public void testCrossJoinWithNullProbe() .row("B") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -157,7 +154,7 @@ public void testCrossJoinWithNullProbe() .row("B", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -173,7 +170,6 @@ public void testCrossJoinWithNullBuild() .row((String) null) .row("a") .row("b"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -182,7 +178,7 @@ public void testCrossJoinWithNullBuild() .row("A") .row("B") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -198,7 +194,7 @@ public void testCrossJoinWithNullBuild() .row("B", "b") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -214,7 +210,6 @@ public void testCrossJoinWithNullOnBothSides() .row("b") .row("c") .row((String) null); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -225,7 +220,7 @@ public void testCrossJoinWithNullOnBothSides() .row((String) null) .row("C") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -251,7 +246,7 @@ public void testCrossJoinWithNullOnBothSides() .row("C", null) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -269,7 +264,6 @@ public void testBuildMultiplePages() .row("c") .pageBreak() .row("d"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -278,7 +272,7 @@ public void testBuildMultiplePages() .row("A") .row("B") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -294,7 +288,7 @@ public void testBuildMultiplePages() .row("B", "d") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -307,7 +301,6 @@ public void testProbeMultiplePages() RowPagesBuilder buildPages = rowPagesBuilder(buildTypes) .row("A") .row("B"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -321,7 +314,7 @@ public void testProbeMultiplePages() .pageBreak() .row("d") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -337,7 +330,7 @@ public void testProbeMultiplePages() .row("d", "B") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -352,7 +345,6 @@ public void testProbeAndBuildMultiplePages() .row("B") .pageBreak() .row("C"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -366,7 +358,7 @@ public void testProbeAndBuildMultiplePages() .pageBreak() .row("d") .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) @@ -387,7 +379,7 @@ public void testProbeAndBuildMultiplePages() .row("d", "C") .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -402,7 +394,6 @@ public void testEmptyProbePage() .row("B") .pageBreak() .row("C"); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -410,13 +401,13 @@ public void testEmptyProbePage() List probeInput = probePages .pageBreak() .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -428,7 +419,6 @@ public void testEmptyBuildPage() List buildTypes = ImmutableList.of(VARCHAR); RowPagesBuilder buildPages = rowPagesBuilder(buildTypes) .pageBreak(); - JoinBridgeDataManager nestedLoopJoinPagesBridgeManager = buildPageSource(taskContext, buildPages); // probe List probeTypes = ImmutableList.of(VARCHAR); @@ -438,13 +428,13 @@ public void testEmptyBuildPage() .row("B") .pageBreak() .build(); - NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinPagesBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = newJoinOperatorFactoryWithCompletedBuild(taskContext, buildPages); // expected MaterializedResult expected = resultBuilder(taskContext.getSession(), concat(probeTypes, buildPages.getTypes())) .build(); - assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true).addDriverContext(), probeInput, expected); + assertOperatorEquals(joinOperatorFactory, taskContext.addPipelineContext(0, true, true, false).addDriverContext(), probeInput, expected); } @Test @@ -479,18 +469,20 @@ private TaskContext createTaskContext() return TestingTaskContext.createTaskContext(executor, scheduledExecutor, TEST_SESSION); } - private static JoinBridgeDataManager buildPageSource(TaskContext taskContext, RowPagesBuilder buildPages) + private static NestedLoopJoinOperatorFactory newJoinOperatorFactoryWithCompletedBuild(TaskContext taskContext, RowPagesBuilder buildPages) { - DriverContext driverContext = taskContext.addPipelineContext(0, true, true).addDriverContext(); + DriverContext driverContext = taskContext.addPipelineContext(0, true, true, false).addDriverContext(); ValuesOperatorFactory valuesOperatorFactory = new ValuesOperatorFactory(0, new PlanNodeId("test"), buildPages.build()); - JoinBridgeDataManager nestedLoopJoinPagesSupplierManager = JoinBridgeDataManager.nestedLoop( + JoinBridgeManager nestedLoopJoinBridgeManager = new JoinBridgeManager<>( + false, PipelineExecutionStrategy.UNGROUPED_EXECUTION, PipelineExecutionStrategy.UNGROUPED_EXECUTION, lifespan -> new NestedLoopJoinPagesSupplier(), buildPages.getTypes()); - NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(1, new PlanNodeId("test"), nestedLoopJoinPagesSupplierManager); + NestedLoopBuildOperatorFactory nestedLoopBuildOperatorFactory = new NestedLoopBuildOperatorFactory(1, new PlanNodeId("test"), nestedLoopJoinBridgeManager); + NestedLoopJoinOperatorFactory joinOperatorFactory = new NestedLoopJoinOperatorFactory(3, new PlanNodeId("test"), nestedLoopJoinBridgeManager); Operator valuesOperator = valuesOperatorFactory.createOperator(driverContext); Operator nestedLoopBuildOperator = nestedLoopBuildOperatorFactory.createOperator(driverContext); @@ -504,6 +496,7 @@ private static JoinBridgeDataManager buildPageSource( while (nestedLoopBuildOperator.isBlocked().isDone()) { driver.process(); } - return nestedLoopJoinPagesSupplierManager; + + return joinOperatorFactory; } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestOperationTimer.java b/presto-main/src/test/java/com/facebook/presto/operator/TestOperationTimer.java new file mode 100644 index 0000000000000..d92839f0785f8 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestOperationTimer.java @@ -0,0 +1,151 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.facebook.presto.operator.OperationTimer.OperationTiming; +import io.airlift.slice.XxHash64; +import org.testng.annotations.Test; + +import java.util.Random; +import java.util.function.Consumer; + +import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; +import static io.airlift.slice.Slices.wrappedBuffer; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.testng.Assert.assertEquals; + +public class TestOperationTimer +{ + public static volatile long blackHole; + + @Test + public void testOverallTiming() + { + testOverallTiming(false); + testOverallTiming(true); + } + + private void testOverallTiming(boolean trackCpuTime) + { + InternalTiming timing = new InternalTiming(trackCpuTime); + for (int i = 1; i <= 5; i++) { + OperationTimer timer = new OperationTimer(trackCpuTime, false); + doSomething(); + timing.record(timer::end); + } + } + + @Test + public void testOperationTiming() + { + testOperationTiming(false); + testOperationTiming(true); + } + + private void testOperationTiming(boolean trackCpuTime) + { + InternalTiming overallTiming = new InternalTiming(true); + + InternalTiming operationTiming1 = new InternalTiming(trackCpuTime); + InternalTiming operationTiming2 = new InternalTiming(trackCpuTime); + InternalTiming operationTiming3 = new InternalTiming(trackCpuTime); + + OperationTimer timer = new OperationTimer(true, trackCpuTime); + + doSomething(); + operationTiming1.record(timer::recordOperationComplete); + doSomething(); + operationTiming1.record(timer::recordOperationComplete); + doSomething(); + operationTiming2.record(timer::recordOperationComplete); + doSomething(); + operationTiming1.record(timer::recordOperationComplete); + doSomething(); + operationTiming2.record(timer::recordOperationComplete); + doSomething(); + operationTiming3.record(timer::recordOperationComplete); + + overallTiming.record(timer::end); + + assertThat(operationTiming1.getTiming().getWallNanos() + operationTiming2.getTiming().getWallNanos() + operationTiming3.getTiming().getWallNanos()) + .isLessThanOrEqualTo(overallTiming.getTiming().getWallNanos()); + assertThat(operationTiming1.getTiming().getCpuNanos() + operationTiming2.getTiming().getCpuNanos() + operationTiming3.getTiming().getCpuNanos()) + .isLessThanOrEqualTo(overallTiming.getTiming().getCpuNanos()); + } + + @Test + public void testOperationAfterEndAreNotAllowed() + { + OperationTiming timing = new OperationTiming(); + OperationTimer timer = new OperationTimer(true, false); + timer.end(timing); + assertThatThrownBy(() -> timer.end(timing)).isInstanceOf(IllegalStateException.class); + assertThatThrownBy(() -> timer.recordOperationComplete(timing)).isInstanceOf(IllegalStateException.class); + } + + @Test + public void testInvalidConstructorArguments() + { + assertThatThrownBy(() -> new OperationTimer(false, true)).isInstanceOf(IllegalArgumentException.class); + } + + private static void doSomething() + { + byte[] data = new byte[10_000]; + new Random(blackHole).nextBytes(data); + blackHole = XxHash64.hash(wrappedBuffer(data)); + sleepUninterruptibly(50, MILLISECONDS); + } + + private static class InternalTiming + { + private final boolean trackCpuTime; + + private final OperationTiming timing = new OperationTiming(); + + private long calls; + private long previousWallNanos; + private long previousCpuNanos; + + private InternalTiming(boolean trackCpuTime) + { + this.trackCpuTime = trackCpuTime; + } + + public OperationTiming getTiming() + { + return timing; + } + + public void record(Consumer timer) + { + previousWallNanos = timing.getWallNanos(); + previousCpuNanos = timing.getCpuNanos(); + assertEquals(timing.getCalls(), calls); + timer.accept(timing); + calls++; + assertEquals(timing.getCalls(), calls); + assertThat(timing.getWallNanos()).isGreaterThan(previousWallNanos); + if (trackCpuTime) { + assertThat(timing.getCpuNanos()).isGreaterThan(previousCpuNanos); + assertThat(timing.getWallNanos()).isGreaterThan(timing.getCpuNanos()); + } + else { + assertEquals(timing.getCpuNanos(), 0); + } + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestOperatorStats.java b/presto-main/src/test/java/com/facebook/presto/operator/TestOperatorStats.java index d160dde06a628..2357ef7aa7c4a 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestOperatorStats.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestOperatorStats.java @@ -33,6 +33,7 @@ public class TestOperatorStats private static final PartitionedOutputInfo MERGEABLE_INFO = new PartitionedOutputInfo(1, 2, 1024); public static final OperatorStats EXPECTED = new OperatorStats( + 0, 1, 41, new PlanNodeId("test"), @@ -43,7 +44,7 @@ public class TestOperatorStats 2, new Duration(3, NANOSECONDS), new Duration(4, NANOSECONDS), - new Duration(5, NANOSECONDS), + new DataSize(5, BYTE), new DataSize(6, BYTE), 7, 8d, @@ -51,29 +52,28 @@ public class TestOperatorStats 9, new Duration(10, NANOSECONDS), new Duration(11, NANOSECONDS), - new Duration(12, NANOSECONDS), - new DataSize(13, BYTE), - 14, + new DataSize(12, BYTE), + 13, - new DataSize(23, BYTE), + new DataSize(14, BYTE), new Duration(15, NANOSECONDS), 16, new Duration(17, NANOSECONDS), new Duration(18, NANOSECONDS), - new Duration(19, NANOSECONDS), + new DataSize(19, BYTE), new DataSize(20, BYTE), new DataSize(21, BYTE), new DataSize(22, BYTE), new DataSize(23, BYTE), new DataSize(24, BYTE), - new DataSize(25, BYTE), Optional.empty(), NON_MERGEABLE_INFO); public static final OperatorStats MERGEABLE = new OperatorStats( + 0, 1, 41, new PlanNodeId("test"), @@ -84,7 +84,7 @@ public class TestOperatorStats 2, new Duration(3, NANOSECONDS), new Duration(4, NANOSECONDS), - new Duration(5, NANOSECONDS), + new DataSize(5, BYTE), new DataSize(6, BYTE), 7, 8d, @@ -92,25 +92,23 @@ public class TestOperatorStats 9, new Duration(10, NANOSECONDS), new Duration(11, NANOSECONDS), - new Duration(12, NANOSECONDS), - new DataSize(13, BYTE), - 14, + new DataSize(12, BYTE), + 13, - new DataSize(23, BYTE), + new DataSize(14, BYTE), new Duration(15, NANOSECONDS), 16, new Duration(17, NANOSECONDS), new Duration(18, NANOSECONDS), - new Duration(19, NANOSECONDS), + new DataSize(19, BYTE), new DataSize(20, BYTE), new DataSize(21, BYTE), new DataSize(22, BYTE), new DataSize(23, BYTE), new DataSize(24, BYTE), - new DataSize(25, BYTE), Optional.empty(), MERGEABLE_INFO); @@ -127,6 +125,7 @@ public void testJson() public static void assertExpectedOperatorStats(OperatorStats actual) { + assertEquals(actual.getStageId(), 0); assertEquals(actual.getOperatorId(), 41); assertEquals(actual.getOperatorType(), "test"); @@ -134,7 +133,7 @@ public static void assertExpectedOperatorStats(OperatorStats actual) assertEquals(actual.getAddInputCalls(), 2); assertEquals(actual.getAddInputWall(), new Duration(3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(4, NANOSECONDS)); - assertEquals(actual.getAddInputUser(), new Duration(5, NANOSECONDS)); + assertEquals(actual.getRawInputDataSize(), new DataSize(5, BYTE)); assertEquals(actual.getInputDataSize(), new DataSize(6, BYTE)); assertEquals(actual.getInputPositions(), 7); assertEquals(actual.getSumSquaredInputPositions(), 8.0); @@ -142,25 +141,23 @@ public static void assertExpectedOperatorStats(OperatorStats actual) assertEquals(actual.getGetOutputCalls(), 9); assertEquals(actual.getGetOutputWall(), new Duration(10, NANOSECONDS)); assertEquals(actual.getGetOutputCpu(), new Duration(11, NANOSECONDS)); - assertEquals(actual.getGetOutputUser(), new Duration(12, NANOSECONDS)); - assertEquals(actual.getOutputDataSize(), new DataSize(13, BYTE)); - assertEquals(actual.getOutputPositions(), 14); + assertEquals(actual.getOutputDataSize(), new DataSize(12, BYTE)); + assertEquals(actual.getOutputPositions(), 13); - assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(23, BYTE)); + assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(14, BYTE)); assertEquals(actual.getBlockedWall(), new Duration(15, NANOSECONDS)); assertEquals(actual.getFinishCalls(), 16); assertEquals(actual.getFinishWall(), new Duration(17, NANOSECONDS)); assertEquals(actual.getFinishCpu(), new Duration(18, NANOSECONDS)); - assertEquals(actual.getFinishUser(), new Duration(19, NANOSECONDS)); - - assertEquals(actual.getUserMemoryReservation(), new DataSize(20, BYTE)); - assertEquals(actual.getRevocableMemoryReservation(), new DataSize(21, BYTE)); - assertEquals(actual.getSystemMemoryReservation(), new DataSize(22, BYTE)); - assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(23, BYTE)); - assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(24, BYTE)); - assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(25, BYTE)); + + assertEquals(actual.getUserMemoryReservation(), new DataSize(19, BYTE)); + assertEquals(actual.getRevocableMemoryReservation(), new DataSize(20, BYTE)); + assertEquals(actual.getSystemMemoryReservation(), new DataSize(21, BYTE)); + assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(22, BYTE)); + assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(23, BYTE)); + assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(24, BYTE)); assertEquals(actual.getInfo().getClass(), SplitOperatorInfo.class); assertEquals(((SplitOperatorInfo) actual.getInfo()).getSplitInfo(), NON_MERGEABLE_INFO.getSplitInfo()); } @@ -170,6 +167,7 @@ public void testAdd() { OperatorStats actual = EXPECTED.add(EXPECTED, EXPECTED); + assertEquals(actual.getStageId(), 0); assertEquals(actual.getOperatorId(), 41); assertEquals(actual.getOperatorType(), "test"); @@ -177,7 +175,7 @@ public void testAdd() assertEquals(actual.getAddInputCalls(), 3 * 2); assertEquals(actual.getAddInputWall(), new Duration(3 * 3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(3 * 4, NANOSECONDS)); - assertEquals(actual.getAddInputUser(), new Duration(3 * 5, NANOSECONDS)); + assertEquals(actual.getRawInputDataSize(), new DataSize(3 * 5, BYTE)); assertEquals(actual.getInputDataSize(), new DataSize(3 * 6, BYTE)); assertEquals(actual.getInputPositions(), 3 * 7); assertEquals(actual.getSumSquaredInputPositions(), 3 * 8.0); @@ -185,24 +183,22 @@ public void testAdd() assertEquals(actual.getGetOutputCalls(), 3 * 9); assertEquals(actual.getGetOutputWall(), new Duration(3 * 10, NANOSECONDS)); assertEquals(actual.getGetOutputCpu(), new Duration(3 * 11, NANOSECONDS)); - assertEquals(actual.getGetOutputUser(), new Duration(3 * 12, NANOSECONDS)); - assertEquals(actual.getOutputDataSize(), new DataSize(3 * 13, BYTE)); - assertEquals(actual.getOutputPositions(), 3 * 14); + assertEquals(actual.getOutputDataSize(), new DataSize(3 * 12, BYTE)); + assertEquals(actual.getOutputPositions(), 3 * 13); - assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(3 * 23, BYTE)); + assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(3 * 14, BYTE)); assertEquals(actual.getBlockedWall(), new Duration(3 * 15, NANOSECONDS)); assertEquals(actual.getFinishCalls(), 3 * 16); assertEquals(actual.getFinishWall(), new Duration(3 * 17, NANOSECONDS)); assertEquals(actual.getFinishCpu(), new Duration(3 * 18, NANOSECONDS)); - assertEquals(actual.getFinishUser(), new Duration(3 * 19, NANOSECONDS)); - assertEquals(actual.getUserMemoryReservation(), new DataSize(3 * 20, BYTE)); - assertEquals(actual.getRevocableMemoryReservation(), new DataSize(3 * 21, BYTE)); - assertEquals(actual.getSystemMemoryReservation(), new DataSize(3 * 22, BYTE)); - assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(23, BYTE)); - assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(24, BYTE)); - assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(25, BYTE)); + assertEquals(actual.getUserMemoryReservation(), new DataSize(3 * 19, BYTE)); + assertEquals(actual.getRevocableMemoryReservation(), new DataSize(3 * 20, BYTE)); + assertEquals(actual.getSystemMemoryReservation(), new DataSize(3 * 21, BYTE)); + assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(22, BYTE)); + assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(23, BYTE)); + assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(24, BYTE)); assertNull(actual.getInfo()); } @@ -211,6 +207,7 @@ public void testAddMergeable() { OperatorStats actual = MERGEABLE.add(MERGEABLE, MERGEABLE); + assertEquals(actual.getStageId(), 0); assertEquals(actual.getOperatorId(), 41); assertEquals(actual.getOperatorType(), "test"); @@ -218,7 +215,7 @@ public void testAddMergeable() assertEquals(actual.getAddInputCalls(), 3 * 2); assertEquals(actual.getAddInputWall(), new Duration(3 * 3, NANOSECONDS)); assertEquals(actual.getAddInputCpu(), new Duration(3 * 4, NANOSECONDS)); - assertEquals(actual.getAddInputUser(), new Duration(3 * 5, NANOSECONDS)); + assertEquals(actual.getRawInputDataSize(), new DataSize(3 * 5, BYTE)); assertEquals(actual.getInputDataSize(), new DataSize(3 * 6, BYTE)); assertEquals(actual.getInputPositions(), 3 * 7); assertEquals(actual.getSumSquaredInputPositions(), 3 * 8.0); @@ -226,24 +223,22 @@ public void testAddMergeable() assertEquals(actual.getGetOutputCalls(), 3 * 9); assertEquals(actual.getGetOutputWall(), new Duration(3 * 10, NANOSECONDS)); assertEquals(actual.getGetOutputCpu(), new Duration(3 * 11, NANOSECONDS)); - assertEquals(actual.getGetOutputUser(), new Duration(3 * 12, NANOSECONDS)); - assertEquals(actual.getOutputDataSize(), new DataSize(3 * 13, BYTE)); - assertEquals(actual.getOutputPositions(), 3 * 14); + assertEquals(actual.getOutputDataSize(), new DataSize(3 * 12, BYTE)); + assertEquals(actual.getOutputPositions(), 3 * 13); - assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(3 * 23, BYTE)); + assertEquals(actual.getPhysicalWrittenDataSize(), new DataSize(3 * 14, BYTE)); assertEquals(actual.getBlockedWall(), new Duration(3 * 15, NANOSECONDS)); assertEquals(actual.getFinishCalls(), 3 * 16); assertEquals(actual.getFinishWall(), new Duration(3 * 17, NANOSECONDS)); assertEquals(actual.getFinishCpu(), new Duration(3 * 18, NANOSECONDS)); - assertEquals(actual.getFinishUser(), new Duration(3 * 19, NANOSECONDS)); - assertEquals(actual.getUserMemoryReservation(), new DataSize(3 * 20, BYTE)); - assertEquals(actual.getRevocableMemoryReservation(), new DataSize(3 * 21, BYTE)); - assertEquals(actual.getSystemMemoryReservation(), new DataSize(3 * 22, BYTE)); - assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(23, BYTE)); - assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(24, BYTE)); - assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(25, BYTE)); + assertEquals(actual.getUserMemoryReservation(), new DataSize(3 * 19, BYTE)); + assertEquals(actual.getRevocableMemoryReservation(), new DataSize(3 * 20, BYTE)); + assertEquals(actual.getSystemMemoryReservation(), new DataSize(3 * 21, BYTE)); + assertEquals(actual.getPeakUserMemoryReservation(), new DataSize(22, BYTE)); + assertEquals(actual.getPeakSystemMemoryReservation(), new DataSize(23, BYTE)); + assertEquals(actual.getPeakTotalMemoryReservation(), new DataSize(24, BYTE)); assertEquals(actual.getInfo().getClass(), PartitionedOutputInfo.class); assertEquals(((PartitionedOutputInfo) actual.getInfo()).getPagesAdded(), 3 * MERGEABLE_INFO.getPagesAdded()); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestOrderByOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestOrderByOperator.java index bb0297353c87b..77789c3c1e6bc 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestOrderByOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestOrderByOperator.java @@ -57,7 +57,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } @@ -161,7 +161,7 @@ public void testReverseOrder() assertOperatorEquals(operatorFactory, driverContext, input, expected); } - @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of 10B") + @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of 10B when increasing allocation of 0B by .*") public void testMemoryLimit() { List input = rowPagesBuilder(BIGINT, DOUBLE) @@ -173,7 +173,7 @@ public void testMemoryLimit() .build(); DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(10, Unit.BYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); OrderByOperatorFactory operatorFactory = new OrderByOperatorFactory( diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestPipelineStats.java b/presto-main/src/test/java/com/facebook/presto/operator/TestPipelineStats.java index 3ff39b1a535d0..b7467a1743457 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestPipelineStats.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestPipelineStats.java @@ -59,7 +59,6 @@ public class TestPipelineStats new Duration(10, NANOSECONDS), new Duration(11, NANOSECONDS), - new Duration(12, NANOSECONDS), new Duration(13, NANOSECONDS), false, ImmutableSet.of(), @@ -114,7 +113,6 @@ public static void assertExpectedPipelineStats(PipelineStats actual) assertEquals(actual.getTotalScheduledTime(), new Duration(10, NANOSECONDS)); assertEquals(actual.getTotalCpuTime(), new Duration(11, NANOSECONDS)); - assertEquals(actual.getTotalUserTime(), new Duration(12, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(13, NANOSECONDS)); assertEquals(actual.getRawInputDataSize(), new DataSize(14, BYTE)); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestRowNumberOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestRowNumberOperator.java index 38f75cde7f4ae..c9452acc7205a 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestRowNumberOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestRowNumberOperator.java @@ -93,7 +93,7 @@ public static Object[][] hashEnabledValuesProvider() private DriverContext getDriverContext() { return createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java index 810ee380c65e4..8104407021cae 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestScanFilterAndProjectOperator.java @@ -57,6 +57,7 @@ import static com.facebook.presto.metadata.Signature.internalScalarFunction; import static com.facebook.presto.operator.OperatorAssertion.toMaterializedResult; import static com.facebook.presto.operator.PageAssertions.assertPageEquals; +import static com.facebook.presto.operator.project.PageProcessor.MAX_BATCH_SIZE; import static com.facebook.presto.spi.function.OperatorType.EQUAL; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -267,7 +268,7 @@ public void testPageYield() projections.add(call(internalScalarFunction("generic_long_page_col" + i, BIGINT.getTypeSignature(), ImmutableList.of(BIGINT.getTypeSignature())), BIGINT, field(0, BIGINT))); } Supplier cursorProcessor = expressionCompiler.compileCursorProcessor(Optional.empty(), projections.build(), "key"); - Supplier pageProcessor = expressionCompiler.compilePageProcessor(Optional.empty(), projections.build()); + Supplier pageProcessor = expressionCompiler.compilePageProcessor(Optional.empty(), projections.build(), MAX_BATCH_SIZE); ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory factory = new ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory( 0, @@ -285,21 +286,26 @@ public void testPageYield() operator.addSplit(new Split(new ConnectorId("test"), TestingTransactionHandle.create(), TestingSplit.createLocalSplit())); operator.noMoreSplits(); - // yield for every cell: 20 X 1000 times - // currently we enforce a yield check for every position; free feel to adjust the number if the behavior changes - for (int i = 0; i < totalRows * totalColumns; i++) { + // In the below loop we yield for every cell: 20 X 1000 times + // Currently we don't check for the yield signal in the generated projection loop, we only check for the yield signal + // in the PageProcessor.PositionsPageProcessorIterator::computeNext() method. Therefore, after 20 calls we will have + // exactly 20 blocks (one for each column) and the PageProcessor will be able to create a Page out of it. + for (int i = 1; i <= totalRows * totalColumns; i++) { driverContext.getYieldSignal().setWithDelay(SECONDS.toNanos(1000), driverContext.getYieldExecutor()); - assertNull(operator.getOutput()); + Page page = operator.getOutput(); + if (i == totalColumns) { + assertNotNull(page); + assertEquals(page.getPositionCount(), totalRows); + assertEquals(page.getChannelCount(), totalColumns); + for (int j = 0; j < totalColumns; j++) { + assertEquals(toValues(BIGINT, page.getBlock(j)), toValues(BIGINT, input.getBlock(0))); + } + } + else { + assertNull(page); + } driverContext.getYieldSignal().reset(); } - - // the last call will return the whole page - Page output = operator.getOutput(); - assertEquals(output.getPositionCount(), totalRows); - assertEquals(output.getChannelCount(), totalColumns); - for (int i = 0; i < totalColumns; i++) { - assertEquals(toValues(BIGINT, output.getBlock(i)), toValues(BIGINT, input.getBlock(0))); - } } @Test @@ -384,7 +390,7 @@ private static List toPages(Operator operator) private DriverContext newDriverContext() { return createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestStreamingAggregationOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestStreamingAggregationOperator.java index a881e9e4ffcbb..6b688234ff5a1 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestStreamingAggregationOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestStreamingAggregationOperator.java @@ -69,7 +69,7 @@ public void setUp() scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); operatorFactory = new StreamingAggregationOperatorFactory( diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestTableFinishOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestTableFinishOperator.java new file mode 100644 index 0000000000000..b9cda84928013 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestTableFinishOperator.java @@ -0,0 +1,186 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator; + +import com.facebook.presto.Session; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.TableFinishOperator.TableFinishOperatorFactory; +import com.facebook.presto.operator.TableFinishOperator.TableFinisher; +import com.facebook.presto.operator.aggregation.InternalAggregationFunction; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.LongArrayBlockBuilder; +import com.facebook.presto.spi.connector.ConnectorOutputMetadata; +import com.facebook.presto.spi.statistics.ColumnStatisticMetadata; +import com.facebook.presto.spi.statistics.ComputedStatistics; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.planner.plan.AggregationNode; +import com.facebook.presto.sql.planner.plan.PlanNodeId; +import com.facebook.presto.sql.planner.plan.StatisticAggregationsDescriptor; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.airlift.slice.Slice; +import io.airlift.slice.Slices; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; + +import static com.facebook.presto.RowPagesBuilder.rowPagesBuilder; +import static com.facebook.presto.block.BlockAssertions.assertBlockEquals; +import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; +import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; +import static com.facebook.presto.operator.PageAssertions.assertPageEquals; +import static com.facebook.presto.spi.statistics.ColumnStatisticType.MAX_VALUE; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.facebook.presto.testing.TestingTaskContext.createTaskContext; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; +import static io.airlift.concurrent.Threads.daemonThreadsNamed; +import static java.util.concurrent.Executors.newScheduledThreadPool; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +public class TestTableFinishOperator +{ + private static final InternalAggregationFunction LONG_MAX = createTestMetadataManager().getFunctionRegistry().getAggregateFunctionImplementation( + new Signature("max", AGGREGATE, BIGINT.getTypeSignature(), BIGINT.getTypeSignature())); + + private ScheduledExecutorService scheduledExecutor; + + @BeforeClass + public void setUp() + { + scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); + } + + @AfterClass(alwaysRun = true) + public void tearDown() + { + scheduledExecutor.shutdownNow(); + scheduledExecutor = null; + } + + @Test + public void testStatisticsAggregation() + throws Exception + { + TestTableFinisher tableFinisher = new TestTableFinisher(); + ColumnStatisticMetadata statisticMetadata = new ColumnStatisticMetadata("column", MAX_VALUE); + StatisticAggregationsDescriptor descriptor = new StatisticAggregationsDescriptor<>( + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableMap.of(statisticMetadata, 0)); + Session session = testSessionBuilder() + .setSystemProperty("statistics_cpu_timer_enabled", "true") + .build(); + TableFinishOperatorFactory operatorFactory = new TableFinishOperatorFactory( + 0, + new PlanNodeId("node"), + tableFinisher, + new AggregationOperator.AggregationOperatorFactory( + 1, + new PlanNodeId("test"), + AggregationNode.Step.SINGLE, + ImmutableList.of(LONG_MAX.bind(ImmutableList.of(2), Optional.empty())), + true), + descriptor, + session); + DriverContext driverContext = createTaskContext(scheduledExecutor, scheduledExecutor, session) + .addPipelineContext(0, true, true, false) + .addDriverContext(); + TableFinishOperator operator = (TableFinishOperator) operatorFactory.createOperator(driverContext); + + List inputTypes = ImmutableList.of(BIGINT, VARBINARY, BIGINT); + + operator.addInput(rowPagesBuilder(inputTypes).row(4, null, null).build().get(0)); + operator.addInput(rowPagesBuilder(inputTypes).row(5, null, null).build().get(0)); + operator.addInput(rowPagesBuilder(inputTypes).row(null, new byte[] {1}, null).build().get(0)); + operator.addInput(rowPagesBuilder(inputTypes).row(null, new byte[] {2}, null).build().get(0)); + operator.addInput(rowPagesBuilder(inputTypes).row(null, null, 6).build().get(0)); + operator.addInput(rowPagesBuilder(inputTypes).row(null, null, 7).build().get(0)); + + assertThat(driverContext.getSystemMemoryUsage()).isGreaterThan(0); + assertEquals(driverContext.getMemoryUsage(), 0); + + assertTrue(operator.isBlocked().isDone()); + assertTrue(operator.needsInput()); + + operator.finish(); + assertFalse(operator.isFinished()); + + assertNull(operator.getOutput()); + List outputTypes = ImmutableList.of(BIGINT); + assertPageEquals(outputTypes, operator.getOutput(), rowPagesBuilder(outputTypes).row(9).build().get(0)); + + assertTrue(operator.isBlocked().isDone()); + assertFalse(operator.needsInput()); + assertTrue(operator.isFinished()); + + operator.close(); + + assertEquals(tableFinisher.getFragments(), ImmutableList.of(Slices.wrappedBuffer(new byte[] {1}), Slices.wrappedBuffer(new byte[] {2}))); + assertEquals(tableFinisher.getComputedStatistics().size(), 1); + assertEquals(getOnlyElement(tableFinisher.getComputedStatistics()).getColumnStatistics().size(), 1); + Block expectedStatisticsBlock = new LongArrayBlockBuilder(null, 1) + .writeLong(7) + .closeEntry() + .build(); + assertBlockEquals(BIGINT, getOnlyElement(tableFinisher.getComputedStatistics()).getColumnStatistics().get(statisticMetadata), expectedStatisticsBlock); + + TableFinishInfo tableFinishInfo = operator.getInfo(); + assertThat(tableFinishInfo.getStatisticsWallTime().getValue(NANOSECONDS)).isGreaterThan(0); + assertThat(tableFinishInfo.getStatisticsCpuTime().getValue(NANOSECONDS)).isGreaterThan(0); + + assertEquals(driverContext.getSystemMemoryUsage(), 0); + assertEquals(driverContext.getMemoryUsage(), 0); + } + + private static class TestTableFinisher + implements TableFinisher + { + private boolean finished; + private Collection fragments; + private Collection computedStatistics; + + @Override + public Optional finishTable(Collection fragments, Collection computedStatistics) + { + checkState(!finished, "already finished"); + finished = true; + this.fragments = fragments; + this.computedStatistics = computedStatistics; + return Optional.empty(); + } + + public Collection getFragments() + { + return fragments; + } + + public Collection getComputedStatistics() + { + return computedStatistics; + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestTableWriterOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestTableWriterOperator.java index 801b3be9bb378..94f2dd2b0cbeb 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestTableWriterOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestTableWriterOperator.java @@ -14,6 +14,7 @@ package com.facebook.presto.operator; import com.facebook.presto.RowPagesBuilder; +import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; import com.facebook.presto.memory.context.MemoryTrackingContext; import com.facebook.presto.metadata.OutputTableHandle; @@ -57,11 +58,14 @@ import static com.facebook.presto.operator.PageAssertions.assertPageEquals; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.testing.TestingTaskContext.createTaskContext; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.concurrent.Executors.newCachedThreadPool; import static java.util.concurrent.Executors.newScheduledThreadPool; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNull; @@ -159,7 +163,7 @@ public void addInputFailsOnBlockedOperator() public void testTableWriterInfo() { PageSinkManager pageSinkManager = new PageSinkManager(); - pageSinkManager.addConnectorPageSinkProvider(CONNECTOR_ID, new ConstantPageSinkProvider(new MemoryAccountingTestPageSink())); + pageSinkManager.addConnectorPageSinkProvider(CONNECTOR_ID, new ConstantPageSinkProvider(new TableWriteInfoTestPageSink())); TableWriterOperator tableWriterOperator = (TableWriterOperator) createTableWriterOperator( pageSinkManager, new DevNullOperatorFactory(1, new PlanNodeId("test")), @@ -172,12 +176,15 @@ public void testTableWriterInfo() List pages = rowPagesBuilder.build(); long peakMemoryUsage = 0; + long validationCpuNanos = 0; for (int i = 0; i < pages.size(); i++) { Page page = pages.get(i); peakMemoryUsage += page.getRetainedSizeInBytes(); + validationCpuNanos += page.getPositionCount(); tableWriterOperator.addInput(page); TableWriterInfo info = tableWriterOperator.getInfo(); assertEquals(info.getPageSinkPeakMemoryUsage(), peakMemoryUsage); + assertEquals((long) (info.getValidationCpuTime().getValue(NANOSECONDS)), validationCpuNanos); } } @@ -186,16 +193,25 @@ public void testStatisticsAggregation() throws Exception { PageSinkManager pageSinkManager = new PageSinkManager(); - pageSinkManager.addConnectorPageSinkProvider(CONNECTOR_ID, new ConstantPageSinkProvider(new MemoryAccountingTestPageSink())); + pageSinkManager.addConnectorPageSinkProvider(CONNECTOR_ID, new ConstantPageSinkProvider(new TableWriteInfoTestPageSink())); ImmutableList outputTypes = ImmutableList.of(BIGINT, VARBINARY, BIGINT); + Session session = testSessionBuilder() + .setSystemProperty("statistics_cpu_timer_enabled", "true") + .build(); + DriverContext driverContext = createTaskContext(executor, scheduledExecutor, session) + .addPipelineContext(0, true, true, false) + .addDriverContext(); TableWriterOperator operator = (TableWriterOperator) createTableWriterOperator( pageSinkManager, new AggregationOperatorFactory( 1, new PlanNodeId("test"), AggregationNode.Step.SINGLE, - ImmutableList.of(LONG_MAX.bind(ImmutableList.of(0), Optional.empty()))), - outputTypes); + ImmutableList.of(LONG_MAX.bind(ImmutableList.of(0), Optional.empty())), + true), + outputTypes, + session, + driverContext); operator.addInput(rowPagesBuilder(BIGINT).row(42).build().get(0)); operator.addInput(rowPagesBuilder(BIGINT).row(43).build().get(0)); @@ -203,6 +219,9 @@ public void testStatisticsAggregation() assertTrue(operator.isBlocked().isDone()); assertTrue(operator.needsInput()); + assertThat(driverContext.getSystemMemoryUsage()).isGreaterThan(0); + assertEquals(driverContext.getMemoryUsage(), 0); + operator.finish(); assertFalse(operator.isFinished()); @@ -220,6 +239,10 @@ public void testStatisticsAggregation() operator.close(); assertMemoryIsReleased(operator); + + TableWriterInfo info = operator.getInfo(); + assertThat(info.getStatisticsWallTime().getValue(NANOSECONDS)).isGreaterThan(0); + assertThat(info.getStatisticsCpuTime().getValue(NANOSECONDS)).isGreaterThan(0); } private void assertMemoryIsReleased(TableWriterOperator tableWriterOperator) @@ -248,6 +271,24 @@ private Operator createTableWriterOperator(BlockingPageSink blockingPageSink) } private Operator createTableWriterOperator(PageSinkManager pageSinkManager, OperatorFactory statisticsAggregation, List outputTypes) + { + return createTableWriterOperator(pageSinkManager, statisticsAggregation, outputTypes, TEST_SESSION); + } + + private Operator createTableWriterOperator(PageSinkManager pageSinkManager, OperatorFactory statisticsAggregation, List outputTypes, Session session) + { + DriverContext driverContext = createTaskContext(executor, scheduledExecutor, session) + .addPipelineContext(0, true, true, false) + .addDriverContext(); + return createTableWriterOperator(pageSinkManager, statisticsAggregation, outputTypes, session, driverContext); + } + + private Operator createTableWriterOperator( + PageSinkManager pageSinkManager, + OperatorFactory statisticsAggregation, + List outputTypes, + Session session, + DriverContext driverContext) { TableWriterOperatorFactory factory = new TableWriterOperatorFactory( 0, @@ -259,13 +300,10 @@ private Operator createTableWriterOperator(PageSinkManager pageSinkManager, Oper new ConnectorOutputTableHandle() {}), new SchemaTableName("testSchema", "testTable")), ImmutableList.of(0), - TEST_SESSION, + session, statisticsAggregation, outputTypes); - - return factory.createOperator(createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) - .addDriverContext()); + return factory.createOperator(driverContext); } private static class ConstantPageSinkProvider @@ -323,7 +361,7 @@ void complete() } } - private static class MemoryAccountingTestPageSink + private static class TableWriteInfoTestPageSink implements ConnectorPageSink { private final List pages = new ArrayList<>(); @@ -351,6 +389,16 @@ public long getSystemMemoryUsage() return memoryUsage; } + @Override + public long getValidationCpuNanos() + { + long validationCpuNanos = 0; + for (Page page : pages) { + validationCpuNanos += page.getPositionCount(); + } + return validationCpuNanos; + } + @Override public void abort() {} } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestTaskStats.java b/presto-main/src/test/java/com/facebook/presto/operator/TestTaskStats.java index 6e0d513705900..37b5a10392949 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestTaskStats.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestTaskStats.java @@ -52,7 +52,6 @@ public class TestTaskStats new DataSize(14, BYTE), new Duration(15, NANOSECONDS), new Duration(16, NANOSECONDS), - new Duration(17, NANOSECONDS), new Duration(18, NANOSECONDS), false, ImmutableSet.of(), @@ -109,7 +108,6 @@ public static void assertExpectedTaskStats(TaskStats actual) assertEquals(actual.getTotalScheduledTime(), new Duration(15, NANOSECONDS)); assertEquals(actual.getTotalCpuTime(), new Duration(16, NANOSECONDS)); - assertEquals(actual.getTotalUserTime(), new Duration(17, NANOSECONDS)); assertEquals(actual.getTotalBlockedTime(), new Duration(18, NANOSECONDS)); assertEquals(actual.getRawInputDataSize(), new DataSize(19, BYTE)); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestTopNOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestTopNOperator.java index 96401ccdfac12..84d74679a8696 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestTopNOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestTopNOperator.java @@ -58,7 +58,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } @@ -195,7 +195,7 @@ public void testExceedMemoryLimit() .build(); DriverContext smallDiverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(1, BYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); TopNOperatorFactory operatorFactory = new TopNOperatorFactory( diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestTopNRowNumberOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestTopNRowNumberOperator.java index 2e414c86e563d..598aa6857ee87 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestTopNRowNumberOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestTopNRowNumberOperator.java @@ -64,7 +64,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); joinCompiler = new JoinCompiler(MetadataManager.createTestMetadataManager(), new FeaturesConfig()); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestUnnestOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestUnnestOperator.java index 970d4dde23c27..450684f97f57d 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestUnnestOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestUnnestOperator.java @@ -63,7 +63,7 @@ public void setUp() scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestWindowOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/TestWindowOperator.java index 6e185c495f8e2..e6152ae50c11c 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestWindowOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestWindowOperator.java @@ -66,7 +66,7 @@ public class TestWindowOperator { private static final FrameInfo UNBOUNDED_FRAME = new FrameInfo(RANGE, UNBOUNDED_PRECEDING, Optional.empty(), UNBOUNDED_FOLLOWING, Optional.empty()); - private static final List ROW_NUMBER = ImmutableList.of( + public static final List ROW_NUMBER = ImmutableList.of( window(new ReflectionWindowFunctionSupplier<>("row_number", BIGINT, ImmutableList.of(), RowNumberFunction.class), BIGINT, UNBOUNDED_FRAME)); private static final List FIRST_VALUE = ImmutableList.of( @@ -94,7 +94,7 @@ public void setUp() executor = newCachedThreadPool(daemonThreadsNamed("test-executor-%s")); scheduledExecutor = newScheduledThreadPool(2, daemonThreadsNamed("test-scheduledExecutor-%s")); driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } @@ -204,7 +204,7 @@ public void testRowNumberArbitrary() assertOperatorEquals(operatorFactory, driverContext, input, expected); } - @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded local user memory limit of 10B") + @Test(expectedExceptions = ExceededMemoryLimitException.class, expectedExceptionsMessageRegExp = "Query exceeded per-node user memory limit of 10B when increasing allocation of 0B by .*") public void testMemoryLimit() { List input = rowPagesBuilder(BIGINT, DOUBLE) @@ -216,7 +216,7 @@ public void testMemoryLimit() .build(); DriverContext driverContext = createTaskContext(executor, scheduledExecutor, TEST_SESSION, new DataSize(10, Unit.BYTE)) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); WindowOperatorFactory operatorFactory = createFactoryUnbounded( @@ -601,7 +601,6 @@ public void testFindEndPosition() assertFindEndPosition("0001", 3); assertFindEndPosition("0000000001", 9); - assertFindEndPosition("00100", 2); assertFindEndPosition("000111", 3); assertFindEndPosition("0001111", 3); assertFindEndPosition("0000111", 4); @@ -633,7 +632,7 @@ private static WindowOperatorFactory createFactoryUnbounded( 0); } - private static WindowOperatorFactory createFactoryUnbounded( + public static WindowOperatorFactory createFactoryUnbounded( List sourceTypes, List outputChannels, List functions, diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestWorkProcessor.java b/presto-main/src/test/java/com/facebook/presto/operator/TestWorkProcessor.java index edeae3f6d5993..919651c9b234b 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestWorkProcessor.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestWorkProcessor.java @@ -13,7 +13,8 @@ */ package com.facebook.presto.operator; -import com.facebook.presto.operator.WorkProcessor.ProcessorState; +import com.facebook.presto.operator.WorkProcessor.ProcessState; +import com.facebook.presto.operator.WorkProcessor.TransformationState; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.SettableFuture; import org.testng.annotations.Test; @@ -22,6 +23,7 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; import static com.facebook.presto.operator.WorkProcessorAssertion.assertBlocks; import static com.facebook.presto.operator.WorkProcessorAssertion.assertFinishes; @@ -39,9 +41,9 @@ public class TestWorkProcessor public void testIterator() { WorkProcessor processor = processorFrom(ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.ofResult(2), - ProcessorState.finished())); + ProcessState.ofResult(1), + ProcessState.ofResult(2), + ProcessState.finished())); Iterator iterator = processor.iterator(); assertTrue(iterator.hasNext()); @@ -55,7 +57,7 @@ public void testIterator() public void testIteratorFailsWhenWorkProcessorHasYielded() { // iterator should fail if underlying work has yielded - WorkProcessor processor = processorFrom(ImmutableList.of(ProcessorState.yield())); + WorkProcessor processor = processorFrom(ImmutableList.of(ProcessState.yield())); Iterator iterator = processor.iterator(); iterator.hasNext(); } @@ -64,7 +66,7 @@ public void testIteratorFailsWhenWorkProcessorHasYielded() public void testIteratorFailsWhenWorkProcessorIsBlocked() { // iterator should fail if underlying work is blocked - WorkProcessor processor = processorFrom(ImmutableList.of(ProcessorState.blocked(SettableFuture.create()))); + WorkProcessor processor = processorFrom(ImmutableList.of(ProcessState.blocked(SettableFuture.create()))); Iterator iterator = processor.iterator(); iterator.hasNext(); } @@ -72,19 +74,19 @@ public void testIteratorFailsWhenWorkProcessorIsBlocked() @Test(timeOut = 5000) public void testMergeSorted() { - List> firstStream = ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.ofResult(3), - ProcessorState.yield(), - ProcessorState.ofResult(5), - ProcessorState.finished()); + List> firstStream = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.ofResult(3), + ProcessState.yield(), + ProcessState.ofResult(5), + ProcessState.finished()); SettableFuture secondFuture = SettableFuture.create(); - List> secondStream = ImmutableList.of( - ProcessorState.ofResult(2), - ProcessorState.ofResult(4), - ProcessorState.blocked(secondFuture), - ProcessorState.finished()); + List> secondStream = ImmutableList.of( + ProcessState.ofResult(2), + ProcessState.ofResult(4), + ProcessState.blocked(secondFuture), + ProcessState.finished()); WorkProcessor mergedStream = WorkProcessorUtils.mergeSorted( ImmutableList.of(processorFrom(firstStream), processorFrom(secondStream)), @@ -122,15 +124,15 @@ public void testMergeSorted() public void testMergeSortedEmptyStreams() { SettableFuture firstFuture = SettableFuture.create(); - List> firstStream = ImmutableList.of( - ProcessorState.blocked(firstFuture), - ProcessorState.yield(), - ProcessorState.finished()); + List> firstStream = ImmutableList.of( + ProcessState.blocked(firstFuture), + ProcessState.yield(), + ProcessState.finished()); SettableFuture secondFuture = SettableFuture.create(); - List> secondStream = ImmutableList.of( - ProcessorState.blocked(secondFuture), - ProcessorState.finished()); + List> secondStream = ImmutableList.of( + ProcessState.blocked(secondFuture), + ProcessState.finished()); WorkProcessor mergedStream = WorkProcessorUtils.mergeSorted( ImmutableList.of(processorFrom(firstStream), processorFrom(secondStream)), @@ -161,11 +163,11 @@ public void testMergeSortedEmptyStreams() @Test(timeOut = 5000) public void testMergeSortedEmptyStreamsWithFinishedOnly() { - List> firstStream = ImmutableList.of( - ProcessorState.finished()); + List> firstStream = ImmutableList.of( + ProcessState.finished()); - List> secondStream = ImmutableList.of( - ProcessorState.finished()); + List> secondStream = ImmutableList.of( + ProcessState.finished()); WorkProcessor mergedStream = WorkProcessorUtils.mergeSorted( ImmutableList.of(processorFrom(firstStream), processorFrom(secondStream)), @@ -178,13 +180,53 @@ public void testMergeSortedEmptyStreamsWithFinishedOnly() assertFinishes(mergedStream); } + @Test(timeOut = 5000) + public void testYield() + { + SettableFuture future = SettableFuture.create(); + + List> baseScenario = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.ofResult(2), + ProcessState.blocked(future), + ProcessState.ofResult(3), + ProcessState.ofResult(4), + ProcessState.finished()); + + AtomicBoolean yieldSignal = new AtomicBoolean(); + WorkProcessor processor = processorFrom(baseScenario) + .yielding(yieldSignal::get); + + // no yield, process normally + assertResult(processor, 1); + + yieldSignal.set(true); + assertYields(processor); + + // processor should progress since it yielded last time + assertResult(processor, 2); + + // base scenario future blocks + assertBlocks(processor); + assertUnblocks(processor, future); + + // yield signal is still set + assertYields(processor); + + // continue to process normally + yieldSignal.set(false); + assertResult(processor, 3); + assertResult(processor, 4); + assertFinishes(processor); + } + @Test(timeOut = 5000) public void testFlatMap() { - List> baseScenario = ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.ofResult(2), - ProcessorState.finished()); + List> baseScenario = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.ofResult(2), + ProcessState.finished()); WorkProcessor processor = processorFrom(baseScenario) .flatMap(element -> WorkProcessor.fromIterable(ImmutableList.of((Double) 2. * element, (Double) 3. * element))); @@ -199,10 +241,10 @@ public void testFlatMap() @Test(timeOut = 5000) public void testMap() { - List> baseScenario = ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.ofResult(2), - ProcessorState.finished()); + List> baseScenario = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.ofResult(2), + ProcessState.finished()); WorkProcessor processor = processorFrom(baseScenario) .map(element -> 2. * element); @@ -216,42 +258,42 @@ public void testMap() public void testFlatTransform() { SettableFuture baseFuture = SettableFuture.create(); - List> baseScenario = ImmutableList.of( - ProcessorState.ofResult(1.0), - ProcessorState.blocked(baseFuture), - ProcessorState.ofResult(2.0), - ProcessorState.yield(), - ProcessorState.ofResult(3.0), - ProcessorState.ofResult(4.0), - ProcessorState.finished()); + List> baseScenario = ImmutableList.of( + ProcessState.ofResult(1.0), + ProcessState.blocked(baseFuture), + ProcessState.ofResult(2.0), + ProcessState.yield(), + ProcessState.ofResult(3.0), + ProcessState.ofResult(4.0), + ProcessState.finished()); SettableFuture mappedFuture1 = SettableFuture.create(); - List> mappedScenario1 = ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.yield(), - ProcessorState.blocked(mappedFuture1), - ProcessorState.ofResult(2), - ProcessorState.finished()); + List> mappedScenario1 = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.yield(), + ProcessState.blocked(mappedFuture1), + ProcessState.ofResult(2), + ProcessState.finished()); - List> mappedScenario2 = ImmutableList.of(ProcessorState.finished()); + List> mappedScenario2 = ImmutableList.of(ProcessState.finished()); SettableFuture mappedFuture3 = SettableFuture.create(); - List> mappedScenario3 = ImmutableList.of( - ProcessorState.blocked(mappedFuture3), - ProcessorState.finished()); + List> mappedScenario3 = ImmutableList.of( + ProcessState.blocked(mappedFuture3), + ProcessState.finished()); - List> mappedScenario4 = ImmutableList.of( - ProcessorState.ofResult(3), - ProcessorState.finished()); + List> mappedScenario4 = ImmutableList.of( + ProcessState.ofResult(3), + ProcessState.finished()); SettableFuture transformationFuture = SettableFuture.create(); List>> transformationScenario = ImmutableList.of( - Transform.of(Optional.of(1.0), ProcessorState.ofResult(processorFrom(mappedScenario1), false)), - Transform.of(Optional.of(1.0), ProcessorState.ofResult(processorFrom(mappedScenario2), false)), - Transform.of(Optional.of(1.0), ProcessorState.ofResult(processorFrom(mappedScenario3))), - Transform.of(Optional.of(2.0), ProcessorState.blocked(transformationFuture)), - Transform.of(Optional.of(2.0), ProcessorState.ofResult(processorFrom(mappedScenario4))), - Transform.of(Optional.of(3.0), ProcessorState.finished())); + Transform.of(Optional.of(1.0), TransformationState.ofResult(processorFrom(mappedScenario1), false)), + Transform.of(Optional.of(1.0), TransformationState.ofResult(processorFrom(mappedScenario2), false)), + Transform.of(Optional.of(1.0), TransformationState.ofResult(processorFrom(mappedScenario3))), + Transform.of(Optional.of(2.0), TransformationState.blocked(transformationFuture)), + Transform.of(Optional.of(2.0), TransformationState.ofResult(processorFrom(mappedScenario4))), + Transform.of(Optional.of(3.0), TransformationState.finished())); WorkProcessor processor = processorFrom(baseScenario) .flatTransform(transformationFrom(transformationScenario)); @@ -303,24 +345,24 @@ public void testFlatTransform() public void testTransform() { SettableFuture baseFuture = SettableFuture.create(); - List> baseScenario = ImmutableList.of( - ProcessorState.ofResult(1), - ProcessorState.yield(), - ProcessorState.blocked(baseFuture), - ProcessorState.ofResult(2), - ProcessorState.ofResult(3), - ProcessorState.finished()); + List> baseScenario = ImmutableList.of( + ProcessState.ofResult(1), + ProcessState.yield(), + ProcessState.blocked(baseFuture), + ProcessState.ofResult(2), + ProcessState.ofResult(3), + ProcessState.finished()); SettableFuture transformationFuture = SettableFuture.create(); List> transformationScenario = ImmutableList.of( - Transform.of(Optional.of(1), ProcessorState.needsMoreData()), - Transform.of(Optional.of(2), ProcessorState.ofResult("foo")), - Transform.of(Optional.of(3), ProcessorState.blocked(transformationFuture)), - Transform.of(Optional.of(3), ProcessorState.yield()), - Transform.of(Optional.of(3), ProcessorState.ofResult("bar", false)), - Transform.of(Optional.of(3), ProcessorState.ofResult("zoo", true)), - Transform.of(Optional.empty(), ProcessorState.ofResult("car", false)), - Transform.of(Optional.empty(), ProcessorState.finished())); + Transform.of(Optional.of(1), TransformationState.needsMoreData()), + Transform.of(Optional.of(2), TransformationState.ofResult("foo")), + Transform.of(Optional.of(3), TransformationState.blocked(transformationFuture)), + Transform.of(Optional.of(3), TransformationState.yield()), + Transform.of(Optional.of(3), TransformationState.ofResult("bar", false)), + Transform.of(Optional.of(3), TransformationState.ofResult("zoo", true)), + Transform.of(Optional.empty(), TransformationState.ofResult("car", false)), + Transform.of(Optional.empty(), TransformationState.finished())); WorkProcessor processor = processorFrom(baseScenario) .transform(transformationFrom(transformationScenario)); @@ -367,13 +409,13 @@ public void testTransform() public void testCreateFrom() { SettableFuture future = SettableFuture.create(); - List> scenario = ImmutableList.of( - ProcessorState.yield(), - ProcessorState.ofResult(1), - ProcessorState.blocked(future), - ProcessorState.yield(), - ProcessorState.ofResult(2), - ProcessorState.finished()); + List> scenario = ImmutableList.of( + ProcessState.yield(), + ProcessState.ofResult(1), + ProcessState.blocked(future), + ProcessState.yield(), + ProcessState.ofResult(2), + ProcessState.finished()); WorkProcessor processor = processorFrom(scenario); // before @@ -398,14 +440,14 @@ private static WorkProcessor.Transformation transformationFrom(List }; } - private static WorkProcessor processorFrom(List> states) + private static WorkProcessor processorFrom(List> states) { return WorkProcessorUtils.create(processFrom(states)); } - private static WorkProcessor.Process processFrom(List> states) + private static WorkProcessor.Process processFrom(List> states) { - Iterator> iterator = states.iterator(); + Iterator> iterator = states.iterator(); return () -> { assertTrue(iterator.hasNext()); return iterator.next(); @@ -415,20 +457,20 @@ private static WorkProcessor.Process processFrom(List> private static class Transform { final Optional from; - final ProcessorState to; + final TransformationState to; - Transform(Optional from, ProcessorState to) + Transform(Optional from, TransformationState to) { this.from = requireNonNull(from); this.to = requireNonNull(to); } - static Transform of(Optional from, ProcessorState to) + static Transform of(Optional from, TransformationState to) { return new Transform<>(from, to); } - ProcessorState transform(Optional from) + TransformationState transform(Optional from) { assertEquals(from, this.from); return to; diff --git a/presto-main/src/test/java/com/facebook/presto/operator/TestingOperatorContext.java b/presto-main/src/test/java/com/facebook/presto/operator/TestingOperatorContext.java index f8c2d6d39f246..f337a33ca9ec7 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/TestingOperatorContext.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/TestingOperatorContext.java @@ -45,6 +45,7 @@ public static OperatorContext create(ScheduledExecutorService scheduledExecutor) scheduledExecutor, pipelineMemoryContext, false, + false, false); DriverContext driverContext = new DriverContext( @@ -52,7 +53,6 @@ public static OperatorContext create(ScheduledExecutorService scheduledExecutor) executor, scheduledExecutor, pipelineMemoryContext, - false, Lifespan.taskWide()); OperatorContext operatorContext = driverContext.addOperatorContext( diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java index 84957ce17e6e9..6c8fd6e12f7a9 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AbstractTestAggregationFunction.java @@ -16,6 +16,7 @@ import com.facebook.presto.block.BlockEncodingManager; import com.facebook.presto.metadata.FunctionRegistry; import com.facebook.presto.metadata.Signature; +import com.facebook.presto.spi.Plugin; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.block.RunLengthEncodedBlock; @@ -32,6 +33,7 @@ import java.util.List; +import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; import static com.facebook.presto.sql.analyzer.TypeSignatureProvider.fromTypeSignatures; @@ -56,6 +58,18 @@ public final void destroyTestAggregationFunction() public abstract Block[] getSequenceBlocks(int start, int length); + protected void registerFunctions(Plugin plugin) + { + functionRegistry.addFunctions(extractFunctions(plugin.getFunctions())); + } + + protected void registerTypes(Plugin plugin) + { + for (Type type : plugin.getTypes()) { + typeRegistry.addType(type); + } + } + protected final InternalAggregationFunction getFunction() { List parameterTypes = fromTypeSignatures(Lists.transform(getFunctionParameterTypes(), TypeSignature::parseTypeSignature)); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AggregationTestUtils.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AggregationTestUtils.java index 241b6bd7abb22..9ed8023ccc09c 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AggregationTestUtils.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/AggregationTestUtils.java @@ -121,7 +121,7 @@ private static void assertAggregationInternal(InternalAggregationFunction functi } } - private static void assertFunctionEquals(BiFunction isEqual, String testDescription, Object expectedValue, Object actualValue) + private static void assertFunctionEquals(BiFunction isEqual, String testDescription, Object actualValue, Object expectedValue) { if (!isEqual.apply(actualValue, expectedValue)) { StringBuilder sb = new StringBuilder(); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/BenchmarkGroupedTypedHistogram.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/BenchmarkGroupedTypedHistogram.java index 428ad578f88e6..f4aac9375db11 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/BenchmarkGroupedTypedHistogram.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/BenchmarkGroupedTypedHistogram.java @@ -80,7 +80,7 @@ public static class Data private float mainFillRatio; @Param("0.5f") // found slight benefit over 0.75, the canonical starting point private float valueStoreFillRatio; -// these must be manually set in each class now; the mechanism to change and test was removed; the enum was kept in case we want to revisit. Retesting showed linear was superior + // these must be manually set in each class now; the mechanism to change and test was removed; the enum was kept in case we want to revisit. Retesting showed linear was superior // // @Param({"LINEAR", "SUM_OF_COUNT", "SUM_OF_SQUARE"}) // @Param({"LINEAR"}) // found to be best, by about 10-15% // private ProbeType mainProbeTyepe; @@ -192,8 +192,6 @@ public static void main(String[] args) public enum ProbeType { - LINEAR, - SUM_OF_COUNT, - SUM_OF_SQUARE,; + LINEAR, SUM_OF_COUNT, SUM_OF_SQUARE } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMergeQuantileDigestFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMergeQuantileDigestFunction.java new file mode 100644 index 0000000000000..d3cb074d2608c --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMergeQuantileDigestFunction.java @@ -0,0 +1,117 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.DoubleType; +import com.facebook.presto.spi.type.SqlVarbinary; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeParameter; +import com.google.common.collect.ImmutableList; +import io.airlift.stats.QuantileDigest; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.function.BiFunction; + +import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; +import static com.facebook.presto.spi.type.QuantileDigestParametricType.QDIGEST; +import static io.airlift.slice.Slices.wrappedBuffer; +import static java.util.Objects.requireNonNull; + +public class TestMergeQuantileDigestFunction + extends AbstractTestAggregationFunction +{ + public static final BiFunction QDIGEST_EQUALITY = (actualBinary, expectedBinary) -> { + if (actualBinary == null && expectedBinary == null) { + return true; + } + requireNonNull(actualBinary, "actual value was null"); + requireNonNull(expectedBinary, "expected value was null"); + + QuantileDigest actual = new QuantileDigest(wrappedBuffer(((SqlVarbinary) actualBinary).getBytes())); + QuantileDigest expected = new QuantileDigest(wrappedBuffer(((SqlVarbinary) expectedBinary).getBytes())); + return actual.getCount() == expected.getCount() && + actual.getMin() == expected.getMin() && + actual.getMax() == expected.getMax() && + actual.getAlpha() == expected.getAlpha() && + actual.getMaxError() == expected.getMaxError(); + }; + + @Override + public Block[] getSequenceBlocks(int start, int length) + { + Type type = QDIGEST.createType(typeRegistry, ImmutableList.of(TypeParameter.of(DoubleType.DOUBLE))); + BlockBuilder blockBuilder = type.createBlockBuilder(null, length); + for (int i = start; i < start + length; i++) { + QuantileDigest qdigest = new QuantileDigest(0.0); + qdigest.add(i); + type.writeSlice(blockBuilder, qdigest.serialize()); + } + return new Block[] {blockBuilder.build()}; + } + + @Override + protected String getFunctionName() + { + return "merge"; + } + + @Override + protected List getFunctionParameterTypes() + { + return ImmutableList.of("qdigest(double)"); + } + + @Override + public Object getExpectedValue(int start, int length) + { + if (length == 0) { + return null; + } + + QuantileDigest qdigest = new QuantileDigest(0.00); + for (int i = start; i < start + length; i++) { + qdigest.add(i); + } + return new SqlVarbinary(qdigest.serialize().getBytes()); + } + + // The following tests are overridden because by default simple equality checks are done, which often won't work with + // qdigests due to the way they are serialized. I am instead overridding these methods and using the QDIGEST_EQUALITY + // function to perform equality checks. + @Test + @Override + public void testMultiplePositions() + { + assertAggregation(getFunction(), + QDIGEST_EQUALITY, + "test multiple positions", + new Page(getSequenceBlocks(0, 5)), + getExpectedValue(0, 5)); + } + + @Test + @Override + public void testMixedNullAndNonNullPositions() + { + assertAggregation(getFunction(), + QDIGEST_EQUALITY, + "test mixed null and nonnull position", + new Page(createAlternatingNullsBlock(getFunction().getParameterTypes(), getSequenceBlocks(0, 10))), + getExpectedValueIncludingNulls(0, 10, 20)); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMultimapAggAggregation.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMultimapAggAggregation.java index 07e0558ec2820..56a5772393f9f 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMultimapAggAggregation.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestMultimapAggAggregation.java @@ -16,28 +16,40 @@ import com.facebook.presto.RowPageBuilder; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.aggregation.groupByAggregations.AggregationTestInput; +import com.facebook.presto.operator.aggregation.groupByAggregations.AggregationTestInputBuilder; +import com.facebook.presto.operator.aggregation.groupByAggregations.AggregationTestOutput; +import com.facebook.presto.operator.aggregation.groupByAggregations.GroupByAggregationTestUtils; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.RowType; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableMultimap; +import com.google.common.primitives.Ints; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Random; import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; -import static com.facebook.presto.operator.aggregation.MultimapAggregationFunction.NAME; +import static com.facebook.presto.operator.aggregation.multimapagg.MultimapAggregationFunction.NAME; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.util.StructuralTestUtil.mapType; import static com.google.common.base.Preconditions.checkState; +import static org.testng.Assert.assertTrue; public class TestMultimapAggAggregation { @@ -107,14 +119,77 @@ public void testDoubleRowMap() testMultimapAgg(DOUBLE, ImmutableList.of(1.0, 2.0, 3.0), innerRowType, ImmutableList.of(ImmutableList.of(1L, 1.0), ImmutableList.of(2L, 2.0), ImmutableList.of(3L, 3.0))); } + @Test + public void testMultiplePages() + { + InternalAggregationFunction aggFunction = getInternalAggregationFunction(BIGINT, BIGINT); + GroupedAccumulator groupedAccumulator = getGroupedAccumulator(aggFunction); + + testMultimapAggWithGroupBy(aggFunction, groupedAccumulator, 0, BIGINT, ImmutableList.of(1L, 1L), BIGINT, ImmutableList.of(2L, 3L)); + } + + @Test + public void testMultiplePagesAndGroups() + { + InternalAggregationFunction aggFunction = getInternalAggregationFunction(BIGINT, BIGINT); + GroupedAccumulator groupedAccumulator = getGroupedAccumulator(aggFunction); + + testMultimapAggWithGroupBy(aggFunction, groupedAccumulator, 0, BIGINT, ImmutableList.of(1L, 1L), BIGINT, ImmutableList.of(2L, 3L)); + testMultimapAggWithGroupBy(aggFunction, groupedAccumulator, 300, BIGINT, ImmutableList.of(7L, 7L), BIGINT, ImmutableList.of(8L, 9L)); + } + + @Test + public void testManyValues() + { + InternalAggregationFunction aggFunction = getInternalAggregationFunction(BIGINT, BIGINT); + GroupedAccumulator groupedAccumulator = getGroupedAccumulator(aggFunction); + + int numGroups = 30000; + int numKeys = 10; + int numValueArraySize = 2; + Random random = new Random(); + + for (int group = 0; group < numGroups; group++) { + ImmutableList.Builder keyBuilder = ImmutableList.builder(); + ImmutableList.Builder valueBuilder = ImmutableList.builder(); + for (int i = 0; i < numKeys; i++) { + long key = random.nextLong(); + for (int j = 0; j < numValueArraySize; j++) { + long value = random.nextLong(); + keyBuilder.add(key); + valueBuilder.add(value); + } + } + testMultimapAggWithGroupBy(aggFunction, groupedAccumulator, group, BIGINT, keyBuilder.build(), BIGINT, valueBuilder.build()); + } + } + + @Test + public void testEmptyStateOutputIsNull() + { + InternalAggregationFunction aggregationFunction = getInternalAggregationFunction(BIGINT, BIGINT); + GroupedAccumulator groupedAccumulator = aggregationFunction.bind(Ints.asList(), Optional.empty()).createGroupedAccumulator(); + BlockBuilder blockBuilder = groupedAccumulator.getFinalType().createBlockBuilder(null, 1); + groupedAccumulator.evaluateFinal(0, blockBuilder); + assertTrue(blockBuilder.isNull(0)); + } + private static void testMultimapAgg(Type keyType, List expectedKeys, Type valueType, List expectedValues) { checkState(expectedKeys.size() == expectedValues.size(), "expectedKeys and expectedValues should have equal size"); + InternalAggregationFunction aggFunc = getInternalAggregationFunction(keyType, valueType); + testMultimapAgg(aggFunc, keyType, expectedKeys, valueType, expectedValues); + } + private static InternalAggregationFunction getInternalAggregationFunction(Type keyType, Type valueType) + { MapType mapType = mapType(keyType, new ArrayType(valueType)); Signature signature = new Signature(NAME, AGGREGATE, mapType.getTypeSignature(), keyType.getTypeSignature(), valueType.getTypeSignature()); - InternalAggregationFunction aggFunc = metadata.getFunctionRegistry().getAggregateFunctionImplementation(signature); + return metadata.getFunctionRegistry().getAggregateFunctionImplementation(signature); + } + private static void testMultimapAgg(InternalAggregationFunction aggFunc, Type keyType, List expectedKeys, Type valueType, List expectedValues) + { Map> map = new HashMap<>(); for (int i = 0; i < expectedKeys.size(); i++) { if (!map.containsKey(expectedKeys.get(i))) { @@ -130,4 +205,34 @@ private static void testMultimapAgg(Type keyType, List expectedKeys, T assertAggregation(aggFunc, map.isEmpty() ? null : map, builder.build()); } + + private static void testMultimapAggWithGroupBy( + InternalAggregationFunction aggregationFunction, + GroupedAccumulator groupedAccumulator, + int groupId, + Type keyType, + List expectedKeys, + Type valueType, + List expectedValues) + { + RowPageBuilder pageBuilder = RowPageBuilder.rowPageBuilder(keyType, valueType); + ImmutableMultimap.Builder outputBuilder = ImmutableMultimap.builder(); + for (int i = 0; i < expectedValues.size(); i++) { + pageBuilder.row(expectedKeys.get(i), expectedValues.get(i)); + outputBuilder.put(expectedKeys.get(i), expectedValues.get(i)); + } + Page page = pageBuilder.build(); + + AggregationTestInput input = new AggregationTestInputBuilder( + new Block[] {page.getBlock(0), page.getBlock(1)}, + aggregationFunction).build(); + + AggregationTestOutput testOutput = new AggregationTestOutput(outputBuilder.build().asMap()); + input.runPagesOnAccumulatorWithAssertion(groupId, groupedAccumulator, testOutput); + } + + private GroupedAccumulator getGroupedAccumulator(InternalAggregationFunction aggFunction) + { + return aggFunction.bind(Ints.asList(GroupByAggregationTestUtils.createArgs(aggFunction)), Optional.empty()).createGroupedAccumulator(); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestQuantileDigestAggregationFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestQuantileDigestAggregationFunction.java new file mode 100644 index 0000000000000..29c71f04e1a40 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestQuantileDigestAggregationFunction.java @@ -0,0 +1,390 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.aggregation; + +import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.scalar.AbstractTestFunctions; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.SqlVarbinary; +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.TypeSignature; +import com.google.common.base.Joiner; +import com.google.common.primitives.Floats; +import io.airlift.stats.QuantileDigest; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +import static com.facebook.presto.block.BlockAssertions.createBlockOfReals; +import static com.facebook.presto.block.BlockAssertions.createDoubleSequenceBlock; +import static com.facebook.presto.block.BlockAssertions.createDoublesBlock; +import static com.facebook.presto.block.BlockAssertions.createLongSequenceBlock; +import static com.facebook.presto.block.BlockAssertions.createLongsBlock; +import static com.facebook.presto.block.BlockAssertions.createRLEBlock; +import static com.facebook.presto.block.BlockAssertions.createSequenceBlockOfReal; +import static com.facebook.presto.metadata.FunctionKind.AGGREGATE; +import static com.facebook.presto.operator.aggregation.AggregationTestUtils.assertAggregation; +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.doubleToSortableLong; +import static com.facebook.presto.operator.aggregation.FloatingPointBitsConverterUtil.floatToSortableInt; +import static com.facebook.presto.operator.aggregation.TestMergeQuantileDigestFunction.QDIGEST_EQUALITY; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.lang.Double.NaN; +import static java.lang.Integer.max; +import static java.lang.Integer.min; +import static java.lang.String.format; + +public class TestQuantileDigestAggregationFunction + extends AbstractTestFunctions +{ + private static final Joiner ARRAY_JOINER = Joiner.on(","); + private static final MetadataManager METADATA = MetadataManager.createTestMetadataManager(); + + @Test + public void testDoublesWithWeights() + { + testAggregationDouble( + createDoublesBlock(1.0, null, 2.0, null, 3.0, null, 4.0, null, 5.0, null), + createRLEBlock(1, 10), + 0.01, 1.0, 2.0, 3.0, 4.0, 5.0); + testAggregationDouble( + createDoublesBlock(null, null, null, null, null), + createRLEBlock(1, 5), + NaN); + testAggregationDouble( + createDoublesBlock(-1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -10.0), + createRLEBlock(1, 10), + 0.01, -1.0, -2.0, -3.0, -4.0, -5.0, -6.0, -7.0, -8.0, -9.0, -10.0); + testAggregationDouble( + createDoublesBlock(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0), + createRLEBlock(1, 10), + 0.01, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0); + testAggregationDouble( + createDoublesBlock(), + createRLEBlock(1, 0), + NaN); + testAggregationDouble( + createDoublesBlock(1.0), + createRLEBlock(1, 1), + 0.01, 1.0); + testAggregationDouble( + createDoubleSequenceBlock(-1000, 1000), + createRLEBlock(1, 2000), + 0.01, + LongStream.range(-1000, 1000).asDoubleStream().toArray()); + } + + @Test + public void testRealsWithWeights() + { + testAggregationReal( + createBlockOfReals(1.0F, null, 2.0F, null, 3.0F, null, 4.0F, null, 5.0F, null), + createRLEBlock(1, 10), + 0.01, 1.0F, 2.0F, 3.0F, 4.0F, 5.0F); + testAggregationReal( + createBlockOfReals(null, null, null, null, null), + createRLEBlock(1, 5), + NaN); + testAggregationReal( + createBlockOfReals(-1.0F, -2.0F, -3.0F, -4.0F, -5.0F, -6.0F, -7.0F, -8.0F, -9.0F, -10.0F), + createRLEBlock(1, 10), + 0.01, -1.0F, -2.0F, -3.0F, -4.0F, -5.0F, -6.0F, -7.0F, -8.0F, -9.0F, -10.0F); + testAggregationReal( + createBlockOfReals(1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F, 7.0F, 8.0F, 9.0F, 10.0F), + createRLEBlock(1, 10), + 0.01, 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F, 7.0F, 8.0F, 9.0F, 10.0F); + testAggregationReal( + createBlockOfReals(), + createRLEBlock(1, 0), + NaN); + testAggregationReal( + createBlockOfReals(1.0F), + createRLEBlock(1, 1), + 0.01, 1.0F); + testAggregationReal( + createSequenceBlockOfReal(-1000, 1000), + createRLEBlock(1, 2000), + 0.01, + Floats.toArray(LongStream.range(-1000, 1000).mapToObj(Float::new).collect(toImmutableList()))); + } + + @Test + public void testBigintsWithWeight() + { + testAggregationBigint( + createLongsBlock(1L, null, 2L, null, 3L, null, 4L, null, 5L, null), + createRLEBlock(1, 10), + 0.01, 1, 2, 3, 4, 5); + testAggregationBigint( + createLongsBlock(null, null, null, null, null), + createRLEBlock(1, 5), + NaN); + testAggregationBigint( + createLongsBlock(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10), + createRLEBlock(1, 10), + 0.01, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10); + testAggregationBigint( + createLongsBlock(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + createRLEBlock(1, 10), + 0.01, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + testAggregationBigint( + createLongsBlock(new int[] {}), + createRLEBlock(1, 0), + NaN); + testAggregationBigint( + createLongsBlock(1), + createRLEBlock(1, 1), + 0.01, 1); + testAggregationBigint( + createLongSequenceBlock(-1000, 1000), + createRLEBlock(1, 2000), + 0.01, + LongStream.range(-1000, 1000).toArray()); + } + + private InternalAggregationFunction getAggregationFunction(String... type) + { + TypeSignature[] typeSignatures = Arrays.stream(type).map(TypeSignature::parseTypeSignature).toArray(TypeSignature[]::new); + return METADATA.getFunctionRegistry().getAggregateFunctionImplementation( + new Signature("qdigest_agg", + AGGREGATE, + parseTypeSignature(format("qdigest(%s)", type[0])), + typeSignatures)); + } + + private void testAggregationBigint(Block inputBlock, Block weightsBlock, double maxError, long... inputs) + { + // Test without weights and accuracy + testAggregationBigints( + getAggregationFunction(StandardTypes.BIGINT), + new Page(inputBlock), + maxError, + inputs); + + // Test with weights and without accuracy + testAggregationBigints( + getAggregationFunction(StandardTypes.BIGINT, StandardTypes.BIGINT), + new Page(inputBlock, weightsBlock), + maxError, + inputs); + // Test with weights and accuracy + testAggregationBigints( + getAggregationFunction(StandardTypes.BIGINT, StandardTypes.BIGINT, StandardTypes.DOUBLE), + new Page(inputBlock, weightsBlock, createRLEBlock(maxError, inputBlock.getPositionCount())), + maxError, + inputs); + } + + private void testAggregationReal(Block longsBlock, Block weightsBlock, double maxError, float... inputs) + { + // Test without weights and accuracy + testAggregationReal( + getAggregationFunction(StandardTypes.REAL), + new Page(longsBlock), + maxError, + inputs); + // Test with weights and without accuracy + testAggregationReal( + getAggregationFunction(StandardTypes.REAL, StandardTypes.BIGINT), + new Page(longsBlock, weightsBlock), + maxError, + inputs); + // Test with weights and accuracy + testAggregationReal( + getAggregationFunction(StandardTypes.REAL, StandardTypes.BIGINT, StandardTypes.DOUBLE), + new Page(longsBlock, weightsBlock, createRLEBlock(maxError, longsBlock.getPositionCount())), + maxError, + inputs); + } + + private void testAggregationDouble(Block longsBlock, Block weightsBlock, double maxError, double... inputs) + { + // Test without weights and accuracy + testAggregationDoubles( + getAggregationFunction(StandardTypes.DOUBLE), + new Page(longsBlock), + maxError, + inputs); + // Test with weights and without accuracy + testAggregationDoubles( + getAggregationFunction(StandardTypes.DOUBLE, StandardTypes.BIGINT), + new Page(longsBlock, weightsBlock), + maxError, + inputs); + // Test with weights and accuracy + testAggregationDoubles( + getAggregationFunction(StandardTypes.DOUBLE, StandardTypes.BIGINT, StandardTypes.DOUBLE), + new Page(longsBlock, weightsBlock, createRLEBlock(maxError, longsBlock.getPositionCount())), + maxError, + inputs); + } + + private void testAggregationBigints(InternalAggregationFunction function, Page page, double maxError, long... inputs) + { + // aggregate level + assertAggregation(function, + QDIGEST_EQUALITY, + "test multiple positions", + page, + getExpectedValueLongs(maxError, inputs)); + + // test scalars + List rows = Arrays.stream(inputs).sorted().boxed().collect(Collectors.toList()); + + SqlVarbinary returned = (SqlVarbinary) AggregationTestUtils.aggregation(function, page); + assertPercentileWithinError(StandardTypes.BIGINT, returned, maxError, rows, 0.1, 0.5, 0.9, 0.99); + } + + private void testAggregationDoubles(InternalAggregationFunction function, Page page, double maxError, double... inputs) + { + assertAggregation(function, + QDIGEST_EQUALITY, + "test multiple positions", + page, + getExpectedValueDoubles(maxError, inputs)); + + // test scalars + List rows = Arrays.stream(inputs).sorted().boxed().collect(Collectors.toList()); + + SqlVarbinary returned = (SqlVarbinary) AggregationTestUtils.aggregation(function, page); + assertPercentileWithinError(StandardTypes.DOUBLE, returned, maxError, rows, 0.1, 0.5, 0.9, 0.99); + } + + private void testAggregationReal(InternalAggregationFunction function, Page page, double maxError, float... inputs) + { + assertAggregation(function, + QDIGEST_EQUALITY, + "test multiple positions", + page, + getExpectedValuesFloats(maxError, inputs)); + + // test scalars + List rows = Floats.asList(inputs).stream().sorted().map(Float::doubleValue).collect(Collectors.toList()); + + SqlVarbinary returned = (SqlVarbinary) AggregationTestUtils.aggregation(function, page); + assertPercentileWithinError(StandardTypes.REAL, returned, maxError, rows, 0.1, 0.5, 0.9, 0.99); + } + + private Object getExpectedValueLongs(double maxError, long... values) + { + if (values.length == 0) { + return null; + } + QuantileDigest qdigest = new QuantileDigest(maxError); + Arrays.stream(values).forEach(qdigest::add); + return new SqlVarbinary(qdigest.serialize().getBytes()); + } + + private Object getExpectedValueDoubles(double maxError, double... values) + { + if (values.length == 0) { + return null; + } + QuantileDigest qdigest = new QuantileDigest(maxError); + Arrays.stream(values).forEach(value -> qdigest.add(doubleToSortableLong(value))); + return new SqlVarbinary(qdigest.serialize().getBytes()); + } + + private Object getExpectedValuesFloats(double maxError, float... values) + { + if (values.length == 0) { + return null; + } + QuantileDigest qdigest = new QuantileDigest(maxError); + Floats.asList(values).forEach(value -> qdigest.add(floatToSortableInt(value))); + return new SqlVarbinary(qdigest.serialize().getBytes()); + } + + private void assertPercentileWithinError(String type, SqlVarbinary binary, double error, List rows, double... percentiles) + { + if (rows.isEmpty()) { + // Nothing to assert except that the qdigest is empty + return; + } + + // Test each quantile individually (value_at_quantile) + for (double percentile : percentiles) { + assertPercentileWithinError(type, binary, error, rows, percentile); + } + + // Test all the quantiles (values_at_quantiles) + assertPercentilesWithinError(type, binary, error, rows, percentiles); + } + + private void assertPercentileWithinError(String type, SqlVarbinary binary, double error, List rows, double percentile) + { + Number lowerBound = getLowerBound(error, rows, percentile); + Number upperBound = getUpperBound(error, rows, percentile); + + // Check that the chosen quantile is within the upper and lower bound of the error + functionAssertions.assertFunction( + format("value_at_quantile(CAST(X'%s' AS qdigest(%s)), %s) >= %s", binary.toString().replaceAll("\\s+", " "), type, percentile, lowerBound), + BOOLEAN, + true); + functionAssertions.assertFunction( + format("value_at_quantile(CAST(X'%s' AS qdigest(%s)), %s) <= %s", binary.toString().replaceAll("\\s+", " "), type, percentile, upperBound), + BOOLEAN, + true); + } + + private void assertPercentilesWithinError(String type, SqlVarbinary binary, double error, List rows, double[] percentiles) + { + List boxedPercentiles = Arrays.stream(percentiles).sorted().boxed().collect(toImmutableList()); + List lowerBounds = boxedPercentiles.stream().map(percentile -> getLowerBound(error, rows, percentile)).collect(toImmutableList()); + List upperBounds = boxedPercentiles.stream().map(percentile -> getUpperBound(error, rows, percentile)).collect(toImmutableList()); + + // Ensure that the lower bound of each item in the distribution is not greater than the chosen quantiles + functionAssertions.assertFunction( + format( + "zip_with(values_at_quantiles(CAST(X'%s' AS qdigest(%s)), ARRAY[%s]), ARRAY[%s], (value, lowerbound) -> value >= lowerbound)", + binary.toString().replaceAll("\\s+", " "), + type, + ARRAY_JOINER.join(boxedPercentiles), + ARRAY_JOINER.join(lowerBounds)), + METADATA.getType(parseTypeSignature("array(boolean)")), + Collections.nCopies(percentiles.length, true)); + + // Ensure that the upper bound of each item in the distribution is not less than the chosen quantiles + functionAssertions.assertFunction( + format( + "zip_with(values_at_quantiles(CAST(X'%s' AS qdigest(%s)), ARRAY[%s]), ARRAY[%s], (value, upperbound) -> value <= upperbound)", + binary.toString().replaceAll("\\s+", " "), + type, + ARRAY_JOINER.join(boxedPercentiles), + ARRAY_JOINER.join(upperBounds)), + METADATA.getType(parseTypeSignature("array(boolean)")), + Collections.nCopies(percentiles.length, true)); + } + + private Number getLowerBound(double error, List rows, double percentile) + { + int medianIndex = (int) (rows.size() * percentile); + int marginOfError = (int) (rows.size() * error / 2); + return rows.get(max(medianIndex - marginOfError, 0)); + } + + private Number getUpperBound(double error, List rows, double percentile) + { + int medianIndex = (int) (rows.size() * percentile); + int marginOfError = (int) (rows.size() * error / 2); + return rows.get(min(medianIndex + marginOfError, rows.size() - 1)); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestStateCompiler.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestStateCompiler.java index a50c000bf566a..8632fd84c11da 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestStateCompiler.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestStateCompiler.java @@ -290,7 +290,7 @@ private static long getReferenceCountMapOverhead(TestComplexState state) return overhead; } - @Test + @Test(invocationCount = 100, successPercentage = 90) public void testComplexStateEstimatedSize() { Map fieldMap = ImmutableMap.of("Block", new ArrayType(BIGINT), "AnotherBlock", mapType(BIGINT, VARCHAR)); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestTypedSet.java b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestTypedSet.java index 7f8f8738bb164..92ac036d96ea4 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestTypedSet.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/aggregation/TestTypedSet.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.operator.aggregation; +import com.facebook.presto.spi.PageBuilder; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; @@ -33,6 +34,7 @@ import static java.util.Collections.nCopies; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; public class TestTypedSet @@ -67,40 +69,134 @@ public void testConstructor() public void testGetElementPosition() { int elementCount = 100; - TypedSet typedSet = new TypedSet(BIGINT, elementCount, FUNCTION_NAME); + // Set initialTypedSetEntryCount to a small number to trigger rehash() + int initialTypedSetEntryCount = 10; + TypedSet typedSet = new TypedSet(BIGINT, initialTypedSetEntryCount, FUNCTION_NAME); BlockBuilder blockBuilder = BIGINT.createFixedSizeBlockBuilder(elementCount); for (int i = 0; i < elementCount; i++) { BIGINT.writeLong(blockBuilder, i); typedSet.add(blockBuilder, i); } + + assertEquals(typedSet.size(), elementCount); + for (int j = 0; j < blockBuilder.getPositionCount(); j++) { assertEquals(typedSet.positionOf(blockBuilder, j), j); } } @Test - public void testGetElementPositionRandom() + public void testGetElementPositionWithNull() { - BlockBuilder keys = VARCHAR.createBlockBuilder(null, 5); - VARCHAR.writeSlice(keys, utf8Slice("hello")); - VARCHAR.writeSlice(keys, utf8Slice("bye")); - VARCHAR.writeSlice(keys, utf8Slice("abc")); + int elementCount = 100; + // Set initialTypedSetEntryCount to a small number to trigger rehash() + int initialTypedSetEntryCount = 10; + TypedSet typedSet = new TypedSet(BIGINT, initialTypedSetEntryCount, FUNCTION_NAME); + BlockBuilder blockBuilder = BIGINT.createFixedSizeBlockBuilder(elementCount); + for (int i = 0; i < elementCount; i++) { + if (i % 10 == 0) { + blockBuilder.appendNull(); + } + else { + BIGINT.writeLong(blockBuilder, i); + } + typedSet.add(blockBuilder, i); + } - TypedSet set = new TypedSet(VARCHAR, keys.getPositionCount(), FUNCTION_NAME); - for (int i = 0; i < keys.getPositionCount(); i++) { - set.add(keys, i); + // The internal elementBlock and hashtable of the typedSet should contain + // all distinct non-null elements plus one null + assertEquals(typedSet.size(), elementCount - elementCount / 10 + 1); + + int nullCount = 0; + for (int j = 0; j < blockBuilder.getPositionCount(); j++) { + // The null is only added to typedSet once, so the internal elementBlock subscript is shifted by nullCountMinusOne + if (!blockBuilder.isNull(j)) { + assertEquals(typedSet.positionOf(blockBuilder, j), j - nullCount + 1); + } + else { + // The first null added to typedSet is at position 0 + assertEquals(typedSet.positionOf(blockBuilder, j), 0); + nullCount++; + } } + } - BlockBuilder values = VARCHAR.createBlockBuilder(null, 5); - VARCHAR.writeSlice(values, utf8Slice("bye")); - VARCHAR.writeSlice(values, utf8Slice("abc")); - VARCHAR.writeSlice(values, utf8Slice("hello")); - VARCHAR.writeSlice(values, utf8Slice("bad")); + @Test + public void testGetElementPositionWithProvidedEmptyBlockBuilder() + { + int elementCount = 100; + // Set initialTypedSetEntryCount to a small number to trigger rehash() + int initialTypedSetEntryCount = 10; - assertEquals(set.positionOf(values, 2), 0); - assertEquals(set.positionOf(values, 1), 2); - assertEquals(set.positionOf(values, 0), 1); - assertFalse(set.contains(values, 3)); + BlockBuilder emptyBlockBuilder = BIGINT.createFixedSizeBlockBuilder(elementCount); + TypedSet typedSet = new TypedSet(BIGINT, emptyBlockBuilder, initialTypedSetEntryCount, FUNCTION_NAME); + BlockBuilder externalBlockBuilder = BIGINT.createFixedSizeBlockBuilder(elementCount); + for (int i = 0; i < elementCount; i++) { + if (i % 10 == 0) { + externalBlockBuilder.appendNull(); + } + else { + BIGINT.writeLong(externalBlockBuilder, i); + } + typedSet.add(externalBlockBuilder, i); + } + + assertEquals(typedSet.size(), emptyBlockBuilder.getPositionCount()); + assertEquals(typedSet.size(), elementCount - elementCount / 10 + 1); + + for (int j = 0; j < typedSet.size(); j++) { + assertEquals(typedSet.positionOf(emptyBlockBuilder, j), j); + } + } + + @Test + public void testGetElementPositionWithProvidedNonEmptyBlockBuilder() + { + int elementCount = 100; + // Set initialTypedSetEntryCount to a small number to trigger rehash() + int initialTypedSetEntryCount = 10; + + PageBuilder pageBuilder = new PageBuilder(ImmutableList.of(BIGINT)); + BlockBuilder firstBlockBuilder = pageBuilder.getBlockBuilder(0); + + for (int i = 0; i < elementCount; i++) { + BIGINT.writeLong(firstBlockBuilder, i); + } + pageBuilder.declarePositions(elementCount); + + // The secondBlockBuilder should already have elementCount rows. + BlockBuilder secondBlockBuilder = pageBuilder.getBlockBuilder(0); + + TypedSet typedSet = new TypedSet(BIGINT, secondBlockBuilder, initialTypedSetEntryCount, FUNCTION_NAME); + BlockBuilder externalBlockBuilder = BIGINT.createFixedSizeBlockBuilder(elementCount); + for (int i = 0; i < elementCount; i++) { + if (i % 10 == 0) { + externalBlockBuilder.appendNull(); + } + else { + BIGINT.writeLong(externalBlockBuilder, i); + } + typedSet.add(externalBlockBuilder, i); + } + + assertEquals(typedSet.size(), secondBlockBuilder.getPositionCount() - elementCount); + assertEquals(typedSet.size(), elementCount - elementCount / 10 + 1); + + for (int i = 0; i < typedSet.size(); i++) { + int expectedPositionInSecondBlockBuilder = i + elementCount; + assertEquals(typedSet.positionOf(secondBlockBuilder, expectedPositionInSecondBlockBuilder), expectedPositionInSecondBlockBuilder); + } + } + + @Test + public void testGetElementPositionRandom() + { + TypedSet set = new TypedSet(VARCHAR, 1, FUNCTION_NAME); + testGetElementPositionRandomFor(set); + + BlockBuilder emptyBlockBuilder = VARCHAR.createBlockBuilder(null, 3); + TypedSet setWithPassedInBuilder = new TypedSet(VARCHAR, emptyBlockBuilder, 1, FUNCTION_NAME); + testGetElementPositionRandomFor(setWithPassedInBuilder); } @Test @@ -145,9 +241,46 @@ public void testMemoryExceeded() } } + private void testGetElementPositionRandomFor(TypedSet set) + { + BlockBuilder keys = VARCHAR.createBlockBuilder(null, 5); + VARCHAR.writeSlice(keys, utf8Slice("hello")); + VARCHAR.writeSlice(keys, utf8Slice("bye")); + VARCHAR.writeSlice(keys, utf8Slice("abc")); + + for (int i = 0; i < keys.getPositionCount(); i++) { + set.add(keys, i); + } + + BlockBuilder values = VARCHAR.createBlockBuilder(null, 5); + VARCHAR.writeSlice(values, utf8Slice("bye")); + VARCHAR.writeSlice(values, utf8Slice("abc")); + VARCHAR.writeSlice(values, utf8Slice("hello")); + VARCHAR.writeSlice(values, utf8Slice("bad")); + values.appendNull(); + + assertEquals(set.positionOf(values, 4), -1); + assertEquals(set.positionOf(values, 2), 0); + assertEquals(set.positionOf(values, 1), 2); + assertEquals(set.positionOf(values, 0), 1); + assertFalse(set.contains(values, 3)); + + set.add(values, 4); + assertTrue(set.contains(values, 4)); + } + private static void testBigint(Block longBlock, int expectedSetSize) { TypedSet typedSet = new TypedSet(BIGINT, expectedSetSize, FUNCTION_NAME); + testBigintFor(typedSet, longBlock); + + BlockBuilder emptyBlockBuilder = BIGINT.createBlockBuilder(null, expectedSetSize); + TypedSet typedSetWithPassedInBuilder = new TypedSet(BIGINT, emptyBlockBuilder, expectedSetSize, FUNCTION_NAME); + testBigintFor(typedSetWithPassedInBuilder, longBlock); + } + + private static void testBigintFor(TypedSet typedSet, Block longBlock) + { Set set = new HashSet<>(); for (int blockPosition = 0; blockPosition < longBlock.getPositionCount(); blockPosition++) { long number = BIGINT.getLong(longBlock, blockPosition); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/index/TestTupleFilterProcessor.java b/presto-main/src/test/java/com/facebook/presto/operator/index/TestTupleFilterProcessor.java index acbfe4ada8751..36294f3f811ec 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/index/TestTupleFilterProcessor.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/index/TestTupleFilterProcessor.java @@ -24,10 +24,13 @@ import org.testng.annotations.Test; import java.util.List; +import java.util.OptionalInt; import static com.facebook.presto.RowPagesBuilder.rowPagesBuilder; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.operator.PageAssertions.assertPageEquals; +import static com.facebook.presto.operator.project.PageProcessor.MAX_BATCH_SIZE; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; @@ -60,8 +63,14 @@ public void testFilter() new int[] {1, 0, 3}, outputTypes, new PageFunctionCompiler(createTestMetadataManager(), 0)); - PageProcessor tupleFilterProcessor = filterFactory.createPageProcessor(tuplePage).get(); - Page actualPage = getOnlyElement(tupleFilterProcessor.process(SESSION, new DriverYieldSignal(), inputPage)).orElseThrow(() -> new AssertionError("page is not present")); + PageProcessor tupleFilterProcessor = filterFactory.createPageProcessor(tuplePage, OptionalInt.of(MAX_BATCH_SIZE)).get(); + Page actualPage = getOnlyElement( + tupleFilterProcessor.process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + inputPage)) + .orElseThrow(() -> new AssertionError("page is not present")); Page expectedPage = Iterables.getOnlyElement(rowPagesBuilder(outputTypes) .row("a", 1L, true, 0.1, 0.0) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/project/BenchmarkDictionaryBlockGetSizeInBytes.java b/presto-main/src/test/java/com/facebook/presto/operator/project/BenchmarkDictionaryBlockGetSizeInBytes.java new file mode 100644 index 0000000000000..eb2ff49808443 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/project/BenchmarkDictionaryBlockGetSizeInBytes.java @@ -0,0 +1,163 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.project; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.DictionaryBlock; +import com.facebook.presto.spi.function.OperatorType; +import com.facebook.presto.spi.type.MapType; +import com.facebook.presto.spi.type.Type; +import com.google.common.collect.ImmutableList; +import io.airlift.slice.Slice; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.lang.invoke.MethodHandle; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; +import static com.facebook.presto.spi.block.MethodHandleUtil.compose; +import static com.facebook.presto.spi.block.MethodHandleUtil.nativeValueGetter; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.facebook.presto.testing.TestingEnvironment.TYPE_MANAGER; +import static io.airlift.slice.Slices.utf8Slice; +import static java.util.concurrent.TimeUnit.MICROSECONDS; + +@SuppressWarnings("MethodMayBeStatic") +@State(Scope.Thread) +@OutputTimeUnit(MICROSECONDS) +@Fork(5) +@Warmup(iterations = 10) +@Measurement(iterations = 10) +@BenchmarkMode(Mode.AverageTime) +public class BenchmarkDictionaryBlockGetSizeInBytes +{ + @Benchmark + public long getSizeInBytes(BenchmarkData data) + { + return data.getDictionaryBlock().getSizeInBytes(); + } + + @State(Scope.Thread) + public static class BenchmarkData + { + private static final int POSITIONS = 100_000; + @Param({"100", "1000", "10000", "100000"}) + private String selectedPositions = "100"; + + private DictionaryBlock dictionaryBlock; + + @Setup(Level.Invocation) + public void setup() + { + dictionaryBlock = new DictionaryBlock(createMapBlock(POSITIONS), generateIds(Integer.parseInt(selectedPositions), POSITIONS)); + } + + private static Block createMapBlock(int positionCount) + { + Type keyType = VARCHAR; + Type valueType = VARCHAR; + MethodHandle keyNativeEquals = TYPE_MANAGER.resolveOperator(OperatorType.EQUAL, ImmutableList.of(keyType, keyType)); + MethodHandle keyBlockNativeEquals = compose(keyNativeEquals, nativeValueGetter(keyType)); + MethodHandle keyBlockEquals = compose(keyNativeEquals, nativeValueGetter(keyType), nativeValueGetter(keyType)); + MethodHandle keyNativeHashCode = TYPE_MANAGER.resolveOperator(OperatorType.HASH_CODE, ImmutableList.of(keyType)); + MethodHandle keyBlockHashCode = compose(keyNativeHashCode, nativeValueGetter(keyType)); + MapType mapType = new MapType( + keyType, + valueType, + keyBlockNativeEquals, + keyBlockEquals, + keyNativeHashCode, + keyBlockHashCode); + Block keyBlock = createDictionaryBlock(generateList("key", positionCount)); + Block valueBlock = createDictionaryBlock(generateList("value", positionCount)); + int[] offsets = new int[positionCount + 1]; + int mapSize = keyBlock.getPositionCount() / positionCount; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = mapSize * i; + } + return mapType.createBlockFromKeyValue(Optional.empty(), offsets, keyBlock, valueBlock); + } + + private static Block createDictionaryBlock(List values) + { + Block dictionary = createSliceArrayBlock(values); + int[] ids = new int[values.size()]; + for (int i = 0; i < ids.length; i++) { + ids[i] = i; + } + return new DictionaryBlock(dictionary, ids); + } + + private static Block createSliceArrayBlock(List values) + { + // last position is reserved for null + Slice[] sliceArray = new Slice[values.size() + 1]; + for (int i = 0; i < values.size(); i++) { + sliceArray[i] = utf8Slice(values.get(i)); + } + return createSlicesBlock(sliceArray); + } + + private static List generateList(String prefix, int count) + { + ImmutableList.Builder list = ImmutableList.builder(); + for (int i = 0; i < count; i++) { + list.add(prefix + Integer.toString(i)); + } + return list.build(); + } + + private static int[] generateIds(int count, int range) + { + return new Random().ints(count, 0, range).toArray(); + } + + public DictionaryBlock getDictionaryBlock() + { + return dictionaryBlock; + } + } + + public static void main(String[] args) + throws Throwable + { + // assure the benchmarks are valid before running + BenchmarkData data = new BenchmarkData(); + data.setup(); + new BenchmarkDictionaryBlockGetSizeInBytes().getSizeInBytes(data); + + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkDictionaryBlockGetSizeInBytes.class.getSimpleName() + ".*") + .build(); + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/project/TestMergingPageOutput.java b/presto-main/src/test/java/com/facebook/presto/operator/project/TestMergingPageOutput.java index 69fb3e2e12d4d..966add21d0e90 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/project/TestMergingPageOutput.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/project/TestMergingPageOutput.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import org.testng.annotations.Test; +import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -48,7 +49,7 @@ public void testMinPageSizeThreshold() assertTrue(output.needsInput()); assertNull(output.getOutput()); - output.addInput(createPageProcessorOutput(page)); + output.addInput(createPagesIterator(page)); assertFalse(output.needsInput()); assertSame(output.getOutput(), page); } @@ -63,7 +64,7 @@ public void testMinRowCountThreshold() assertTrue(output.needsInput()); assertNull(output.getOutput()); - output.addInput(createPageProcessorOutput(page)); + output.addInput(createPagesIterator(page)); assertFalse(output.needsInput()); assertSame(output.getOutput(), page); } @@ -80,12 +81,12 @@ public void testBufferSmallPages() assertTrue(output.needsInput()); assertNull(output.getOutput()); - output.addInput(createPageProcessorOutput(splits.get(0))); + output.addInput(createPagesIterator(splits.get(0))); assertFalse(output.needsInput()); assertNull(output.getOutput()); assertTrue(output.needsInput()); - output.addInput(createPageProcessorOutput(splits.get(1))); + output.addInput(createPagesIterator(splits.get(1))); assertFalse(output.needsInput()); assertNull(output.getOutput()); @@ -106,12 +107,12 @@ public void testFlushOnBigPage() assertTrue(output.needsInput()); assertNull(output.getOutput()); - output.addInput(createPageProcessorOutput(smallPage)); + output.addInput(createPagesIterator(smallPage)); assertFalse(output.needsInput()); assertNull(output.getOutput()); assertTrue(output.needsInput()); - output.addInput(createPageProcessorOutput(bigPage)); + output.addInput(createPagesIterator(bigPage)); assertFalse(output.needsInput()); assertPageEquals(TYPES, output.getOutput(), smallPage); assertFalse(output.needsInput()); @@ -131,28 +132,27 @@ public void testFlushOnFullPage() assertTrue(output.needsInput()); assertNull(output.getOutput()); - output.addInput(createPageProcessorOutput(splits.get(0))); + output.addInput(createPagesIterator(splits.get(0))); assertFalse(output.needsInput()); assertNull(output.getOutput()); assertTrue(output.needsInput()); - output.addInput(createPageProcessorOutput(splits.get(1))); + output.addInput(createPagesIterator(splits.get(1))); assertFalse(output.needsInput()); assertPageEquals(types, output.getOutput(), page); - output.addInput(createPageProcessorOutput(splits.get(0), splits.get(1))); + output.addInput(createPagesIterator(splits.get(0), splits.get(1))); assertFalse(output.needsInput()); assertPageEquals(types, output.getOutput(), page); } - private static PageProcessorOutput createPageProcessorOutput(Page... pages) + private static Iterator> createPagesIterator(Page... pages) { - return createPageProcessorOutput(ImmutableList.copyOf(pages)); + return createPagesIterator(ImmutableList.copyOf(pages)); } - private static PageProcessorOutput createPageProcessorOutput(List pages) + private static Iterator> createPagesIterator(List pages) { - long retainedSizeInBytes = pages.stream().mapToLong(Page::getRetainedSizeInBytes).sum(); - return new PageProcessorOutput(() -> retainedSizeInBytes, transform(pages.iterator(), Optional::of)); + return transform(pages.iterator(), Optional::of); } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/project/TestPageProcessor.java b/presto-main/src/test/java/com/facebook/presto/operator/project/TestPageProcessor.java index 4dec1281cb8c4..de0395e44f2c4 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/project/TestPageProcessor.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/project/TestPageProcessor.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.operator.project; +import com.facebook.presto.memory.context.AggregatedMemoryContext; +import com.facebook.presto.memory.context.LocalMemoryContext; import com.facebook.presto.operator.CompletedWork; import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.Work; @@ -22,39 +24,57 @@ import com.facebook.presto.spi.block.LazyBlock; import com.facebook.presto.spi.block.VariableWidthBlock; import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.gen.ExpressionProfiler; +import com.facebook.presto.sql.gen.PageFunctionCompiler; +import com.facebook.presto.sql.relational.CallExpression; import com.google.common.collect.ImmutableList; import io.airlift.slice.Slice; import io.airlift.slice.Slices; +import io.airlift.testing.TestingTicker; +import io.airlift.units.Duration; import org.openjdk.jol.info.ClassLayout; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.OptionalInt; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; import static com.facebook.presto.block.BlockAssertions.createLongSequenceBlock; import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; import static com.facebook.presto.block.BlockAssertions.createStringsBlock; +import static com.facebook.presto.execution.executor.PrioritizedSplitRunner.SPLIT_RUN_QUANTA; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; +import static com.facebook.presto.metadata.Signature.internalOperator; import static com.facebook.presto.operator.PageAssertions.assertPageEquals; import static com.facebook.presto.operator.project.PageProcessor.MAX_BATCH_SIZE; import static com.facebook.presto.operator.project.PageProcessor.MAX_PAGE_SIZE_IN_BYTES; import static com.facebook.presto.operator.project.PageProcessor.MIN_PAGE_SIZE_IN_BYTES; import static com.facebook.presto.operator.project.SelectedPositions.positionsRange; +import static com.facebook.presto.spi.function.OperatorType.ADD; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.facebook.presto.sql.relational.Expressions.call; +import static com.facebook.presto.sql.relational.Expressions.constant; +import static com.facebook.presto.sql.relational.Expressions.field; import static com.facebook.presto.testing.TestingConnectorSession.SESSION; import static io.airlift.concurrent.Threads.daemonThreadsNamed; import static java.lang.String.join; import static java.util.Collections.nCopies; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import static sun.misc.Unsafe.ARRAY_OBJECT_INDEX_SCALE; public class TestPageProcessor { @@ -63,12 +83,11 @@ public class TestPageProcessor @Test public void testProjectNoColumns() { - PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of()); + PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(), OptionalInt.of(MAX_BATCH_SIZE)); Page inputPage = new Page(createLongSequenceBlock(0, 100)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 1); @@ -84,8 +103,9 @@ public void testFilterNoColumns() Page inputPage = new Page(createLongSequenceBlock(0, 100)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + LocalMemoryContext memoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()); + Iterator> output = pageProcessor.process(SESSION, new DriverYieldSignal(), memoryContext, inputPage); + assertEquals(memoryContext.getBytes(), 0); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 1); @@ -97,12 +117,14 @@ public void testFilterNoColumns() @Test public void testPartialFilter() { - PageProcessor pageProcessor = new PageProcessor(Optional.of(new TestingPageFilter(positionsRange(25, 50))), ImmutableList.of(new InputPageProjection(0, BIGINT))); + PageProcessor pageProcessor = new PageProcessor( + Optional.of(new TestingPageFilter(positionsRange(25, 50))), + ImmutableList.of(new InputPageProjection(0, BIGINT)), + OptionalInt.of(MAX_BATCH_SIZE)); Page inputPage = new Page(createLongSequenceBlock(0, 100)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 1); @@ -112,12 +134,11 @@ public void testPartialFilter() @Test public void testSelectAllFilter() { - PageProcessor pageProcessor = new PageProcessor(Optional.of(new SelectAllFilter()), ImmutableList.of(new InputPageProjection(0, BIGINT))); + PageProcessor pageProcessor = new PageProcessor(Optional.of(new SelectAllFilter()), ImmutableList.of(new InputPageProjection(0, BIGINT)), OptionalInt.of(MAX_BATCH_SIZE)); Page inputPage = new Page(createLongSequenceBlock(0, 100)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 1); @@ -131,8 +152,9 @@ public void testSelectNoneFilter() Page inputPage = new Page(createLongSequenceBlock(0, 100)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), 0); + LocalMemoryContext memoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()); + Iterator> output = pageProcessor.process(SESSION, new DriverYieldSignal(), memoryContext, inputPage); + assertEquals(memoryContext.getBytes(), 0); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 0); @@ -145,8 +167,9 @@ public void testProjectEmptyPage() Page inputPage = new Page(createLongSequenceBlock(0, 0)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), 0); + LocalMemoryContext memoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()); + Iterator> output = pageProcessor.process(SESSION, new DriverYieldSignal(), memoryContext, inputPage); + assertEquals(memoryContext.getBytes(), 0); // output should be one page containing no columns (only a count) List> outputPages = ImmutableList.copyOf(output); @@ -163,9 +186,9 @@ public void testSelectNoneFilterLazyLoad() throw new AssertionError("Lazy block should not be loaded"); })); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), 0); - + LocalMemoryContext memoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()); + Iterator> output = pageProcessor.process(SESSION, new DriverYieldSignal(), memoryContext, inputPage); + assertEquals(memoryContext.getBytes(), 0); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 0); } @@ -173,15 +196,15 @@ public void testSelectNoneFilterLazyLoad() @Test public void testProjectLazyLoad() { - PageProcessor pageProcessor = new PageProcessor(Optional.of(new SelectAllFilter()), ImmutableList.of(new LazyPagePageProjection())); + PageProcessor pageProcessor = new PageProcessor(Optional.of(new SelectAllFilter()), ImmutableList.of(new LazyPagePageProjection()), OptionalInt.of(MAX_BATCH_SIZE)); // if channel 1 is loaded, test will fail Page inputPage = new Page(createLongSequenceBlock(0, 100), new LazyBlock(100, lazyBlock -> { throw new AssertionError("Lazy block should not be loaded"); })); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), new Page(createLongSequenceBlock(0, 100)).getRetainedSizeInBytes() + ARRAY_OBJECT_INDEX_SCALE); + LocalMemoryContext memoryContext = newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()); + Iterator> output = pageProcessor.process(SESSION, new DriverYieldSignal(), memoryContext, inputPage); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 1); @@ -191,12 +214,11 @@ public void testProjectLazyLoad() @Test public void testBatchedOutput() { - PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(new InputPageProjection(0, BIGINT))); + PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(new InputPageProjection(0, BIGINT)), OptionalInt.of(MAX_BATCH_SIZE)); Page inputPage = new Page(createLongSequenceBlock(0, (int) (MAX_BATCH_SIZE * 2.5))); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); List> outputPages = ImmutableList.copyOf(output); assertEquals(outputPages.size(), 3); @@ -211,15 +233,14 @@ public void testBatchedOutput() @Test public void testAdaptiveBatchSize() { - PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(new InputPageProjection(0, VARCHAR))); + PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(new InputPageProjection(0, VARCHAR)), OptionalInt.of(MAX_BATCH_SIZE)); // process large page which will reduce batch size Slice[] slices = new Slice[(int) (MAX_BATCH_SIZE * 2.5)]; Arrays.fill(slices, Slices.allocate(1024)); Page inputPage = new Page(createSlicesBlock(slices)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, new DriverYieldSignal(), inputPage); List> outputPages = ImmutableList.copyOf(output); int batchSize = MAX_BATCH_SIZE; @@ -235,8 +256,7 @@ public void testAdaptiveBatchSize() Arrays.fill(slices, Slices.allocate(128)); inputPage = new Page(createSlicesBlock(slices)); - output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + output = processAndAssertRetainedPageSize(pageProcessor, new DriverYieldSignal(), inputPage); outputPages = ImmutableList.copyOf(output); int offset = 0; @@ -255,15 +275,14 @@ public void testOptimisticProcessing() { InvocationCountPageProjection firstProjection = new InvocationCountPageProjection(new InputPageProjection(0, VARCHAR)); InvocationCountPageProjection secondProjection = new InvocationCountPageProjection(new InputPageProjection(0, VARCHAR)); - PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(firstProjection, secondProjection)); + PageProcessor pageProcessor = new PageProcessor(Optional.empty(), ImmutableList.of(firstProjection, secondProjection), OptionalInt.of(MAX_BATCH_SIZE)); // process large page which will reduce batch size Slice[] slices = new Slice[(int) (MAX_BATCH_SIZE * 2.5)]; Arrays.fill(slices, Slices.allocate(1024)); Page inputPage = new Page(createSlicesBlock(slices)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); - assertEquals(output.getRetainedSizeInBytes(), inputPage.getRetainedSizeInBytes()); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); // batch size will be reduced before the first page is produced until the first block is within the page size bounds int batchSize = MAX_BATCH_SIZE; @@ -295,14 +314,19 @@ public void testOptimisticProcessing() @Test public void testRetainedSize() { - PageProcessor pageProcessor = new PageProcessor(Optional.of(new SelectAllFilter()), ImmutableList.of(new InputPageProjection(0, VARCHAR), new InputPageProjection(1, VARCHAR))); + PageProcessor pageProcessor = new PageProcessor( + Optional.of(new SelectAllFilter()), + ImmutableList.of(new InputPageProjection(0, VARCHAR), new InputPageProjection(1, VARCHAR)), + OptionalInt.of(MAX_BATCH_SIZE)); // create 2 columns X 800 rows of strings with each string's size = 10KB // this can force previouslyComputedResults to be saved given the page is 16MB in size String value = join("", nCopies(10_000, "a")); List values = nCopies(800, value); Page inputPage = new Page(createStringsBlock(values), createStringsBlock(values)); - PageProcessorOutput output = pageProcessor.process(SESSION, new DriverYieldSignal(), inputPage); + + AggregatedMemoryContext memoryContext = newSimpleAggregatedMemoryContext(); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, new DriverYieldSignal(), memoryContext, inputPage); // force a compute // one block of previouslyComputedResults will be saved given the first column is with 8MB @@ -310,7 +334,7 @@ public void testRetainedSize() // verify we do not count block sizes twice // comparing with the input page, the output page also contains an extra instance size for previouslyComputedResults - assertEquals(output.getRetainedSizeInBytes() - ClassLayout.parseClass(VariableWidthBlock.class).instanceSize(), inputPage.getRetainedSizeInBytes()); + assertEquals(memoryContext.getBytes() - ClassLayout.parseClass(VariableWidthBlock.class).instanceSize(), inputPage.getRetainedSizeInBytes()); } @Test @@ -323,13 +347,14 @@ public void testYieldProjection() DriverYieldSignal yieldSignal = new DriverYieldSignal(); PageProcessor pageProcessor = new PageProcessor( Optional.empty(), - Collections.nCopies(columns, new YieldPageProjection(new InputPageProjection(0, VARCHAR)))); + Collections.nCopies(columns, new YieldPageProjection(new InputPageProjection(0, VARCHAR))), + OptionalInt.of(MAX_BATCH_SIZE)); Slice[] slices = new Slice[rows]; Arrays.fill(slices, Slices.allocate(rows)); Page inputPage = new Page(createSlicesBlock(slices)); - PageProcessorOutput output = pageProcessor.process(SESSION, yieldSignal, inputPage); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, yieldSignal, inputPage); // Test yield signal works for page processor. // The purpose of this test is NOT to test the yield signal in page projection; we have other tests to cover that. @@ -355,6 +380,128 @@ public void testYieldProjection() assertFalse(output.hasNext()); } + @Test + public void testExpressionProfiler() + { + CallExpression add10Expression = call( + internalOperator(ADD, BIGINT.getTypeSignature(), ImmutableList.of(BIGINT.getTypeSignature(), BIGINT.getTypeSignature())), + BIGINT, + field(0, BIGINT), + constant(10L, BIGINT)); + + TestingTicker testingTicker = new TestingTicker(); + PageFunctionCompiler functionCompiler = new PageFunctionCompiler(createTestMetadataManager(), 0); + Supplier projectionSupplier = functionCompiler.compileProjection(add10Expression, Optional.empty()); + PageProjection projection = projectionSupplier.get(); + Page page = new Page(createLongSequenceBlock(1, 11)); + ExpressionProfiler profiler = new ExpressionProfiler(testingTicker, SPLIT_RUN_QUANTA); + for (int i = 0; i < 100; i++) { + profiler.start(); + Work work = projection.project(SESSION, new DriverYieldSignal(), page, SelectedPositions.positionsRange(0, page.getPositionCount())); + if (i < 10) { + // increment the ticker with a large value to mark the expression as expensive + testingTicker.increment(10, SECONDS); + profiler.stop(page.getPositionCount()); + assertTrue(profiler.isExpressionExpensive()); + } + else { + testingTicker.increment(0, NANOSECONDS); + profiler.stop(page.getPositionCount()); + assertFalse(profiler.isExpressionExpensive()); + } + work.process(); + } + } + + @Test + public void testIncreasingBatchSize() + { + int rows = 1024; + + // We deliberately do not set the ticker, so that the expression is always cheap and the batch size gets doubled until other limits are hit + TestingTicker testingTicker = new TestingTicker(); + ExpressionProfiler profiler = new ExpressionProfiler(testingTicker, SPLIT_RUN_QUANTA); + PageProcessor pageProcessor = new PageProcessor( + Optional.empty(), + ImmutableList.of(new InputPageProjection(0, BIGINT)), + OptionalInt.of(1), + profiler); + + Slice[] slices = new Slice[rows]; + Arrays.fill(slices, Slices.allocate(rows)); + Page inputPage = new Page(createSlicesBlock(slices)); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); + + long previousPositionCount = 1; + long totalPositionCount = 0; + while (totalPositionCount < rows) { + Optional page = output.next(); + assertTrue(page.isPresent()); + long positionCount = page.get().getPositionCount(); + totalPositionCount += positionCount; + // skip the first read && skip the last read, which can be a partial page + if (positionCount > 1 && totalPositionCount != rows) { + assertEquals(positionCount, previousPositionCount * 2); + } + previousPositionCount = positionCount; + } + } + + @Test + public void testDecreasingBatchSize() + { + int rows = 1024; + + // We set the expensive expression threshold to 0, so the expression is always considered expensive and the batch size gets halved until it becomes 1 + TestingTicker testingTicker = new TestingTicker(); + ExpressionProfiler profiler = new ExpressionProfiler(testingTicker, new Duration(0, MILLISECONDS)); + PageProcessor pageProcessor = new PageProcessor( + Optional.empty(), + ImmutableList.of(new InputPageProjection(0, BIGINT)), + OptionalInt.of(512), + profiler); + + Slice[] slices = new Slice[rows]; + Arrays.fill(slices, Slices.allocate(rows)); + Page inputPage = new Page(createSlicesBlock(slices)); + Iterator> output = processAndAssertRetainedPageSize(pageProcessor, inputPage); + + long previousPositionCount = 1; + long totalPositionCount = 0; + while (totalPositionCount < rows) { + Optional page = output.next(); + assertTrue(page.isPresent()); + long positionCount = page.get().getPositionCount(); + totalPositionCount += positionCount; + // the batch size doesn't get smaller than 1 + if (positionCount > 1 && previousPositionCount != 1) { + assertEquals(positionCount, previousPositionCount / 2); + } + previousPositionCount = positionCount; + } + } + + private Iterator> processAndAssertRetainedPageSize(PageProcessor pageProcessor, Page inputPage) + { + return processAndAssertRetainedPageSize(pageProcessor, new DriverYieldSignal(), inputPage); + } + + private Iterator> processAndAssertRetainedPageSize(PageProcessor pageProcessor, DriverYieldSignal yieldSignal, Page inputPage) + { + return processAndAssertRetainedPageSize(pageProcessor, yieldSignal, newSimpleAggregatedMemoryContext(), inputPage); + } + + private Iterator> processAndAssertRetainedPageSize(PageProcessor pageProcessor, DriverYieldSignal yieldSignal, AggregatedMemoryContext memoryContext, Page inputPage) + { + Iterator> output = pageProcessor.process( + SESSION, + yieldSignal, + memoryContext.newLocalMemoryContext(PageProcessor.class.getSimpleName()), + inputPage); + assertEquals(memoryContext.getBytes(), 0); + return output; + } + private static class InvocationCountPageProjection implements PageProjection { diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java index 7be783faa99d3..ec2e489c8e948 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/AbstractTestFunctions.java @@ -19,6 +19,7 @@ import com.facebook.presto.metadata.SqlFunction; import com.facebook.presto.metadata.SqlScalarFunction; import com.facebook.presto.spi.ErrorCodeSupplier; +import com.facebook.presto.spi.Plugin; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.StandardErrorCode; import com.facebook.presto.spi.function.OperatorType; @@ -39,6 +40,7 @@ import java.util.Map; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; import static com.facebook.presto.metadata.FunctionRegistry.mangleOperatorName; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; @@ -206,6 +208,18 @@ protected void registerParametricScalar(Class clazz) metadata.getFunctionRegistry().addFunctions(functions); } + protected void registerFunctions(Plugin plugin) + { + functionAssertions.getMetadata().addFunctions(extractFunctions(plugin.getFunctions())); + } + + protected void registerTypes(Plugin plugin) + { + for (Type type : plugin.getTypes()) { + functionAssertions.getTypeRegistry().addType(type); + } + } + protected static SqlDecimal decimal(String decimalString) { DecimalParseResult parseResult = Decimals.parseIncludeLeadingZerosInPrecision(decimalString); diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java index 2bece082177a0..c8e146c2ad3a9 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayDistinct.java @@ -55,6 +55,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.relational.Expressions.field; @@ -82,7 +83,12 @@ public class BenchmarkArrayDistinct @OperationsPerInvocation(POSITIONS * ARRAY_SIZE * NUM_TYPES) public List> arrayDistinct(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java index 42e0e2e2fce9e..b1712215423dd 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayFilter.java @@ -59,6 +59,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.Signature.typeVariable; import static com.facebook.presto.operator.scalar.BenchmarkArrayFilter.ExactArrayFilterFunction.EXACT_ARRAY_FILTER_FUNCTION; import static com.facebook.presto.operator.scalar.ScalarFunctionImplementation.ArgumentProperty.valueTypeArgumentProperty; @@ -97,7 +98,12 @@ public class BenchmarkArrayFilter @OperationsPerInvocation(POSITIONS * ARRAY_SIZE * NUM_TYPES) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java index 7491a0d7eb358..9fa026e7fb714 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayHashCodeOperator.java @@ -61,6 +61,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.operator.scalar.CombineHashFunction.getHash; import static com.facebook.presto.spi.function.OperatorType.HASH_CODE; import static com.facebook.presto.spi.type.ArrayType.ARRAY_NULL_ELEMENT_MSG; @@ -90,7 +91,12 @@ public class BenchmarkArrayHashCodeOperator @OperationsPerInvocation(POSITIONS * ARRAY_SIZE * NUM_TYPES) public List> arrayHashCode(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayIntersect.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayIntersect.java new file mode 100644 index 0000000000000..f7e73e0544f58 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayIntersect.java @@ -0,0 +1,195 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.metadata.FunctionKind; +import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.DriverYieldSignal; +import com.facebook.presto.operator.project.PageProcessor; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.ArrayType; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.gen.ExpressionCompiler; +import com.facebook.presto.sql.gen.PageFunctionCompiler; +import com.facebook.presto.sql.relational.CallExpression; +import com.facebook.presto.sql.relational.RowExpression; +import com.google.common.collect.ImmutableList; +import io.airlift.slice.Slices; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.facebook.presto.sql.relational.Expressions.field; +import static com.facebook.presto.testing.TestingConnectorSession.SESSION; + +@SuppressWarnings("MethodMayBeStatic") +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(2) +@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@BenchmarkMode(Mode.AverageTime) +public class BenchmarkArrayIntersect +{ + private static final int POSITIONS = 1_000; + + @Benchmark + @OperationsPerInvocation(POSITIONS) + public List> arrayIntersect(BenchmarkData data) + { + return ImmutableList.copyOf(data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BenchmarkData + { + private String name = "array_intersect"; + + @Param({"BIGINT", "VARCHAR", "DOUBLE", "BOOLEAN"}) + private String type = "BIGINT"; + + @Param({"10", "100", "1000"}) + private int arraySize = 10; + + private Page page; + private PageProcessor pageProcessor; + + @Setup + public void setup() + { + Type elementType; + switch (type) { + case "BIGINT": + elementType = BIGINT; + break; + case "VARCHAR": + elementType = VARCHAR; + break; + case "DOUBLE": + elementType = DOUBLE; + break; + case "BOOLEAN": + elementType = BOOLEAN; + break; + default: + throw new UnsupportedOperationException(); + } + + ArrayType arrayType = new ArrayType(elementType); + Signature signature = new Signature(name, FunctionKind.SCALAR, arrayType.getTypeSignature(), arrayType.getTypeSignature(), arrayType.getTypeSignature()); + ImmutableList projections = ImmutableList.of( + new CallExpression(signature, arrayType, ImmutableList.of(field(0, arrayType), field(1, arrayType)))); + + MetadataManager metadata = MetadataManager.createTestMetadataManager(); + ExpressionCompiler compiler = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)); + pageProcessor = compiler.compilePageProcessor(Optional.empty(), projections).get(); + + page = new Page(createChannel(POSITIONS, arraySize, elementType), createChannel(POSITIONS, arraySize, elementType)); + } + + private static Block createChannel(int positionCount, int arraySize, Type elementType) + { + ArrayType arrayType = new ArrayType(elementType); + BlockBuilder blockBuilder = arrayType.createBlockBuilder(null, positionCount); + for (int position = 0; position < positionCount; position++) { + BlockBuilder entryBuilder = blockBuilder.beginBlockEntry(); + for (int i = 0; i < arraySize; i++) { + if (elementType.getJavaType() == long.class) { + elementType.writeLong(entryBuilder, ThreadLocalRandom.current().nextLong() % arraySize); + } + else if (elementType.getJavaType() == double.class) { + elementType.writeDouble(entryBuilder, ThreadLocalRandom.current().nextDouble() % arraySize); + } + else if (elementType.getJavaType() == boolean.class) { + elementType.writeBoolean(entryBuilder, ThreadLocalRandom.current().nextBoolean()); + } + else if (elementType.equals(VARCHAR)) { + // make sure the size of a varchar is rather small; otherwise the aggregated slice may overflow + elementType.writeSlice(entryBuilder, Slices.utf8Slice(Long.toString(ThreadLocalRandom.current().nextLong() % arraySize))); + } + else { + throw new UnsupportedOperationException(); + } + } + blockBuilder.closeEntry(); + } + return blockBuilder.build(); + } + + public PageProcessor getPageProcessor() + { + return pageProcessor; + } + + public Page getPage() + { + return page; + } + } + + @Test + public void verify() + { + BenchmarkData data = new BenchmarkData(); + data.setup(); + new BenchmarkArrayIntersect().arrayIntersect(data); + } + + public static void main(String[] args) + throws Throwable + { + // assure the benchmarks are valid before running + BenchmarkData data = new BenchmarkData(); + data.setup(); + new BenchmarkArrayIntersect().arrayIntersect(data); + + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkArrayIntersect.class.getSimpleName() + ".*") + .build(); + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayJoin.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayJoin.java index a90b0641fce17..499bdc7501f65 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayJoin.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayJoin.java @@ -48,6 +48,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.relational.Expressions.constant; @@ -69,7 +70,12 @@ public class BenchmarkArrayJoin @OperationsPerInvocation(POSITIONS * ARRAY_SIZE) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java index 324de49c68813..758c5ed602cee 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySort.java @@ -55,6 +55,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.sql.relational.Expressions.field; @@ -82,7 +83,12 @@ public class BenchmarkArraySort @OperationsPerInvocation(POSITIONS * ARRAY_SIZE * NUM_TYPES) public List> arraySort(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySubscript.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySubscript.java index 65e8816ebb036..d5b99b5604168 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySubscript.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArraySubscript.java @@ -56,6 +56,7 @@ import java.util.concurrent.TimeUnit; import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.function.OperatorType.SUBSCRIPT; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; @@ -80,7 +81,12 @@ public class BenchmarkArraySubscript @OperationsPerInvocation(POSITIONS) public List> arraySubscript(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") @@ -164,7 +170,7 @@ private static Block createArrayBlock(int positionCount, Block elementsBlock) for (int i = 0; i < offsets.length; i++) { offsets[i] = arraySize * i; } - return ArrayBlock.fromElementBlock(positionCount, new boolean[positionCount], offsets, elementsBlock); + return ArrayBlock.fromElementBlock(positionCount, Optional.empty(), offsets, elementsBlock); } private static Block createFixWidthValueBlock(int positionCount, int mapSize) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayTransform.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayTransform.java index 7d6076b83e9a7..fef09642f3b6f 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayTransform.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkArrayTransform.java @@ -56,6 +56,7 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.function.OperatorType.GREATER_THAN; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -84,7 +85,12 @@ public class BenchmarkArrayTransform @OperationsPerInvocation(POSITIONS * ARRAY_SIZE * NUM_TYPES) public Object benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkEqualsOperator.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkEqualsOperator.java new file mode 100644 index 0000000000000..6c25ecf035e4e --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkEqualsOperator.java @@ -0,0 +1,176 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.operator.DriverYieldSignal; +import com.facebook.presto.operator.project.PageProcessor; +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.PageBuilder; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.function.OperatorType; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.gen.ExpressionCompiler; +import com.facebook.presto.sql.gen.PageFunctionCompiler; +import com.facebook.presto.sql.relational.RowExpression; +import com.google.common.collect.ImmutableList; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.metadata.Signature.internalOperator; +import static com.facebook.presto.metadata.Signature.internalScalarFunction; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.sql.relational.Expressions.call; +import static com.facebook.presto.sql.relational.Expressions.field; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.cycle; +import static com.google.common.collect.Iterables.limit; + +@State(Scope.Thread) +@Fork(3) +@Warmup(iterations = 5, time = 10) +@Measurement(iterations = 5, time = 10) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +public class BenchmarkEqualsOperator +{ + private static final int FIELDS_COUNT = 10; + private static final int COMPARISONS_COUNT = 100; + private static final double NULLS_FRACTION = 0.05; + + private static final DriverYieldSignal SIGNAL = new DriverYieldSignal(); + private static final ConnectorSession SESSION = TEST_SESSION.toConnectorSession(); + + private PageProcessor compiledProcessor; + + @Setup + public void setup() + { + MetadataManager metadata = MetadataManager.createTestMetadataManager(); + ExpressionCompiler expressionCompiler = new ExpressionCompiler( + metadata, + new PageFunctionCompiler(metadata, 0)); + RowExpression projection = generateComplexComparisonProjection(FIELDS_COUNT, COMPARISONS_COUNT); + compiledProcessor = expressionCompiler.compilePageProcessor(Optional.empty(), ImmutableList.of(projection)).get(); + } + + private static RowExpression generateComplexComparisonProjection(int fieldsCount, int comparisonsCount) + { + checkArgument(fieldsCount > 0, "fieldsCount must be greater than zero"); + checkArgument(comparisonsCount > 0, "comparisonsCount must be greater than zero"); + + if (comparisonsCount == 1) { + return createComparison(0, 0); + } + + return createConjunction( + createComparison(0, comparisonsCount % fieldsCount), + generateComplexComparisonProjection(fieldsCount, comparisonsCount - 1)); + } + + private static RowExpression createConjunction(RowExpression left, RowExpression right) + { + return call( + internalScalarFunction("OR", BOOLEAN.getTypeSignature(), BOOLEAN.getTypeSignature(), BOOLEAN.getTypeSignature()), + BOOLEAN, + left, + right); + } + + private static RowExpression createComparison(int leftField, int rightField) + { + return call( + internalOperator(OperatorType.EQUAL, BOOLEAN.getTypeSignature(), BIGINT.getTypeSignature(), BIGINT.getTypeSignature()), + BOOLEAN, + field(leftField, BIGINT), + field(rightField, BIGINT)); + } + + @Benchmark + public List processPage(BenchmarkData data) + { + List output = new ArrayList<>(); + Iterator> pageProcessorOutput = compiledProcessor.process( + SESSION, + SIGNAL, + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.page); + while (pageProcessorOutput.hasNext()) { + pageProcessorOutput.next().ifPresent(output::add); + } + return output; + } + + @State(Scope.Thread) + public static class BenchmarkData + { + Page page; + + @Setup + public void setup() + { + List types = ImmutableList.copyOf(limit(cycle(BIGINT), FIELDS_COUNT)); + ThreadLocalRandom random = ThreadLocalRandom.current(); + PageBuilder pageBuilder = new PageBuilder(types); + while (!pageBuilder.isFull()) { + pageBuilder.declarePosition(); + for (int channel = 0; channel < FIELDS_COUNT; channel++) { + BlockBuilder blockBuilder = pageBuilder.getBlockBuilder(channel); + if (random.nextDouble() < NULLS_FRACTION) { + blockBuilder.appendNull(); + } + else { + BIGINT.writeLong(blockBuilder, random.nextLong()); + } + } + } + page = pageBuilder.build(); + } + } + + public static void main(String[] args) + throws RunnerException + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkEqualsOperator.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToArrayCast.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToArrayCast.java index 4bf9ab6d56c7f..25a58115fbcc2 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToArrayCast.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToArrayCast.java @@ -52,6 +52,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; @@ -73,7 +74,12 @@ public class BenchmarkJsonToArrayCast @OperationsPerInvocation(POSITION_COUNT) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToMapCast.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToMapCast.java index 39adb57b5e042..73bb1226c597a 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToMapCast.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkJsonToMapCast.java @@ -51,6 +51,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; @@ -73,7 +74,12 @@ public class BenchmarkJsonToMapCast @OperationsPerInvocation(POSITION_COUNT) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapConcat.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapConcat.java index e2c58b2580234..5f301b68ad57f 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapConcat.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapConcat.java @@ -53,6 +53,7 @@ import java.util.concurrent.TimeUnit; import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; import static com.facebook.presto.sql.relational.Expressions.field; @@ -75,7 +76,12 @@ public class BenchmarkMapConcat @OperationsPerInvocation(POSITIONS) public List> mapConcat(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") @@ -164,7 +170,7 @@ private static Block createMapBlock(MapType mapType, int positionCount, Block ke for (int i = 0; i < offsets.length; i++) { offsets[i] = mapSize * i; } - return mapType.createBlockFromKeyValue(new boolean[positionCount], offsets, keyBlock, valueBlock); + return mapType.createBlockFromKeyValue(Optional.empty(), offsets, keyBlock, valueBlock); } private static Block createKeyBlock(int positionCount, List keys) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapSubscript.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapSubscript.java index e4e0dd6f49074..ac299b6a691bc 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapSubscript.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapSubscript.java @@ -55,6 +55,7 @@ import java.util.concurrent.TimeUnit; import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.function.OperatorType.SUBSCRIPT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; @@ -80,7 +81,12 @@ public class BenchmarkMapSubscript @OperationsPerInvocation(POSITIONS) public List> mapSubscript(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") @@ -175,7 +181,7 @@ private static Block createMapBlock(MapType mapType, int positionCount, Block ke for (int i = 0; i < offsets.length; i++) { offsets[i] = mapSize * i; } - return mapType.createBlockFromKeyValue(new boolean[positionCount], offsets, keyBlock, valueBlock); + return mapType.createBlockFromKeyValue(Optional.empty(), offsets, keyBlock, valueBlock); } private static Block createKeyBlock(int positionCount, List keys) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapToMapCast.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapToMapCast.java index c356505998074..e541f389a1113 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapToMapCast.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkMapToMapCast.java @@ -49,6 +49,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.sql.relational.Expressions.field; @@ -72,7 +73,12 @@ public class BenchmarkMapToMapCast public List> benchmark(BenchmarkData data) throws Throwable { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") @@ -108,7 +114,7 @@ private static Block createMapBlock(MapType mapType, int positionCount, Block ke for (int i = 0; i < offsets.length; i++) { offsets[i] = mapSize * i; } - return mapType.createBlockFromKeyValue(new boolean[positionCount], offsets, keyBlock, valueBlock); + return mapType.createBlockFromKeyValue(Optional.empty(), offsets, keyBlock, valueBlock); } private static Block createKeyBlock(int positionCount, int mapSize) diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkRowToRowCast.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkRowToRowCast.java new file mode 100644 index 0000000000000..bb261dfe641de --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkRowToRowCast.java @@ -0,0 +1,166 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.metadata.Signature; +import com.facebook.presto.operator.DriverYieldSignal; +import com.facebook.presto.operator.project.PageProcessor; +import com.facebook.presto.spi.Page; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.type.RowType; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.VarcharType; +import com.facebook.presto.sql.gen.ExpressionCompiler; +import com.facebook.presto.sql.gen.PageFunctionCompiler; +import com.facebook.presto.sql.relational.CallExpression; +import com.facebook.presto.sql.relational.RowExpression; +import com.google.common.collect.ImmutableList; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.OperationsPerInvocation; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; +import org.openjdk.jmh.runner.options.WarmupMode; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; + +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.metadata.FunctionKind.SCALAR; +import static com.facebook.presto.spi.block.RowBlock.fromFieldBlocks; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.VarcharType.createVarcharType; +import static com.facebook.presto.sql.relational.Expressions.field; +import static com.facebook.presto.testing.TestingConnectorSession.SESSION; +import static io.airlift.slice.Slices.utf8Slice; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.openjdk.jmh.annotations.Mode.AverageTime; + +@SuppressWarnings("MethodMayBeStatic") +@State(Scope.Thread) +@OutputTimeUnit(NANOSECONDS) +@Fork(3) +@BenchmarkMode(AverageTime) +public class BenchmarkRowToRowCast +{ + private static final int POSITION_COUNT = 100_000; + + @Benchmark + @OperationsPerInvocation(POSITION_COUNT) + public List> benchmark(BenchmarkData data) + { + return ImmutableList.copyOf(data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BenchmarkData + { + private static final List fromFieldTypes = ImmutableList.of(createVarcharType(20), BIGINT); + private static final List toFieldTypes = ImmutableList.of(createVarcharType(30), BIGINT); + + private Page page; + private PageProcessor pageProcessor; + + @Setup + public void setup() + { + Signature signature = new Signature("$operator$CAST", SCALAR, RowType.anonymous(fromFieldTypes).getTypeSignature(), RowType.anonymous(toFieldTypes).getTypeSignature()); + + List projections = ImmutableList.of( + new CallExpression(signature, RowType.anonymous(fromFieldTypes), ImmutableList.of(field(0, RowType.anonymous(toFieldTypes))))); + + MetadataManager metadata = MetadataManager.createTestMetadataManager(); + pageProcessor = new ExpressionCompiler(metadata, new PageFunctionCompiler(metadata, 0)) + .compilePageProcessor(Optional.empty(), projections) + .get(); + + Block[] fieldBlocks = fromFieldTypes.stream() + .map(type -> createBlock(POSITION_COUNT, type)) + .toArray(Block[]::new); + Block rowBlock = fromFieldBlocks(POSITION_COUNT, Optional.empty(), fieldBlocks); + + page = new Page(rowBlock); + } + + private static Block createBlock(int positionCount, Type type) + { + BlockBuilder blockBuilder = type.createBlockBuilder(null, positionCount); + if (type instanceof VarcharType) { + for (int i = 0; i < positionCount; i++) { + type.writeSlice(blockBuilder, utf8Slice(String.valueOf(ThreadLocalRandom.current().nextInt()))); + } + } + else if (type == BIGINT) { + for (int i = 0; i < positionCount; i++) { + type.writeLong(blockBuilder, ThreadLocalRandom.current().nextLong()); + } + } + else { + throw new UnsupportedOperationException(); + } + + return blockBuilder.build(); + } + + public PageProcessor getPageProcessor() + { + return pageProcessor; + } + + public Page getPage() + { + return page; + } + } + + @Test + public void verify() + { + BenchmarkData data = new BenchmarkData(); + data.setup(); + new BenchmarkRowToRowCast().benchmark(data); + } + + public static void main(String[] args) + throws Throwable + { + // assure the benchmarks are valid before running + BenchmarkData data = new BenchmarkData(); + data.setup(); + new BenchmarkRowToRowCast().benchmark(data); + + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkRowToRowCast.class.getSimpleName() + ".*") + .warmupMode(WarmupMode.INDI) + .build(); + new Runner(options).run(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformKey.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformKey.java index 75ae982a2596b..a816dcc229291 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformKey.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformKey.java @@ -53,6 +53,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.function.OperatorType.ADD; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; @@ -81,7 +82,12 @@ public class BenchmarkTransformKey @OperationsPerInvocation(POSITIONS * NUM_TYPES) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformValue.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformValue.java index 46b6f55843664..d32cd50932662 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformValue.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/BenchmarkTransformValue.java @@ -54,6 +54,7 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.spi.function.OperatorType.GREATER_THAN; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -84,7 +85,12 @@ public class BenchmarkTransformValue @OperationsPerInvocation(POSITIONS * NUM_TYPES) public List> benchmark(BenchmarkData data) { - return ImmutableList.copyOf(data.getPageProcessor().process(SESSION, new DriverYieldSignal(), data.getPage())); + return ImmutableList.copyOf( + data.getPageProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + data.getPage())); } @SuppressWarnings("FieldMayBeFinal") diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java index 2d395a18056af..c0bcb22eae100 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/FunctionAssertions.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionListBuilder; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.Split; @@ -32,7 +33,6 @@ import com.facebook.presto.operator.project.InterpretedPageProjection; import com.facebook.presto.operator.project.PageFilter; import com.facebook.presto.operator.project.PageProcessor; -import com.facebook.presto.operator.project.PageProcessorOutput; import com.facebook.presto.operator.project.PageProjection; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorPageSource; @@ -92,6 +92,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -108,6 +109,7 @@ import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; import static com.facebook.presto.block.BlockAssertions.createStringsBlock; import static com.facebook.presto.block.BlockAssertions.createTimestampsWithTimezoneBlock; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT; import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; @@ -476,7 +478,11 @@ public void assertCachedInstanceHasBoundedRetainedSize(String projection) long maxRetainedSize = 0; int maxIterationCount = 0; for (int iterationCount = 0; iterationCount < Math.max(1000, maxIterationCount * 4); iterationCount++) { - PageProcessorOutput output = processor.process(session.toConnectorSession(), new DriverYieldSignal(), SOURCE_PAGE); + Iterator> output = processor.process( + session.toConnectorSession(), + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + SOURCE_PAGE); // consume the iterator Iterators.getOnlyElement(output); @@ -631,7 +637,8 @@ private RowExpression toRowExpression(Session session, Expression projectionExpr SQL_PARSER, INPUT_TYPES, ImmutableList.of(translatedProjection), - ImmutableList.of()); + ImmutableList.of(), + WarningCollector.NOOP); return toRowExpression(translatedProjection, expressionTypes); } @@ -749,6 +756,7 @@ public static Expression createExpression(Session session, String expression, Me symbolTypes, ImmutableList.of(parsedExpression), ImmutableList.of(), + WarningCollector.NOOP, false); Expression rewrittenExpression = ExpressionTreeRewriter.rewriteWith(new ExpressionRewriter() @@ -983,7 +991,7 @@ private static Page getAtMostOnePage(Operator operator, Page sourcePage) private static DriverContext createDriverContext(Session session) { return createTaskContext(EXECUTOR, SCHEDULED_EXECUTOR, session) - .addPipelineContext(0, true, true) + .addPipelineContext(0, true, true, false) .addDriverContext(); } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestArrayNgramsFunction.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestArrayNgramsFunction.java new file mode 100644 index 0000000000000..b19771df9fd5b --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestArrayNgramsFunction.java @@ -0,0 +1,106 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.spi.type.ArrayType; +import com.google.common.collect.ImmutableList; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.VarcharType.createVarcharType; +import static com.facebook.presto.type.UnknownType.UNKNOWN; +import static java.util.Arrays.asList; +public class TestArrayNgramsFunction + + extends AbstractTestFunctions +{ + @Test + public void testBasic() + { + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 1)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar"), + ImmutableList.of("foo"), + ImmutableList.of("baz"), + ImmutableList.of("foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 2)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo"), + ImmutableList.of("foo", "baz"), + ImmutableList.of("baz", "foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 3)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo", "baz"), + ImmutableList.of("foo", "baz", "foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 4)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo", "baz", "foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 5)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo", "baz", "foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 6)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo", "baz", "foo"))); + assertFunction("ngrams(ARRAY['bar', 'foo', 'baz', 'foo'], 100000000)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("bar", "foo", "baz", "foo"))); + assertFunction("ngrams(ARRAY['a', 'bb', 'ccc', 'dddd'], 2)", new ArrayType(new ArrayType(createVarcharType(4))), ImmutableList.of( + ImmutableList.of("a", "bb"), + ImmutableList.of("bb", "ccc"), + ImmutableList.of("ccc", "dddd"))); + } + + @Test + public void testNull() + { + assertFunction("ngrams(ARRAY['foo', NULL, 'bar'], 2)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + asList("foo", null), + asList(null, "bar"))); + + assertFunction("ngrams(ARRAY [NULL, NULL, NULL], 2)", new ArrayType(new ArrayType(UNKNOWN)), ImmutableList.of( + asList(null, null), + asList(null, null))); + + assertFunction("ngrams(ARRAY [NULL, 3, NULL], 2)", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of( + asList(null, 3), + asList(3, null))); + } + + @Test + public void testTypeCombinations() + { + assertFunction("ngrams(ARRAY[1, 2, 3], 2)", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of( + ImmutableList.of(1, 2), + ImmutableList.of(2, 3))); + assertFunction("ngrams(ARRAY[1.1E0, 2.1E0, 3.1E0], 2)", new ArrayType(new ArrayType(DOUBLE)), ImmutableList.of( + ImmutableList.of(1.1, 2.1), + ImmutableList.of(2.1, 3.1))); + assertFunction("ngrams(ARRAY[true, false, true], 2)", new ArrayType(new ArrayType(BOOLEAN)), ImmutableList.of( + ImmutableList.of(true, false), + ImmutableList.of(false, true))); + + assertFunction("ngrams(ARRAY[ARRAY['A1', 'A2'], ARRAY['B1'], ARRAY['C1', 'C2']], 2)", new ArrayType(new ArrayType(new ArrayType(createVarcharType(2)))), ImmutableList.of( + ImmutableList.of(ImmutableList.of("A1", "A2"), ImmutableList.of("B1")), + ImmutableList.of(ImmutableList.of("B1"), ImmutableList.of("C1", "C2")))); + + assertFunction("ngrams(ARRAY['\u4FE1\u5FF5\u7231', '\u5E0C\u671B', '\u671B'], 2)", new ArrayType(new ArrayType(createVarcharType(3))), ImmutableList.of( + ImmutableList.of("\u4FE1\u5FF5\u7231", "\u5E0C\u671B"), + ImmutableList.of("\u5E0C\u671B", "\u671B"))); + + assertFunction("ngrams(ARRAY[], 2)", new ArrayType(new ArrayType(UNKNOWN)), ImmutableList.of( + asList())); + assertFunction("ngrams(ARRAY[''], 2)", new ArrayType(new ArrayType(createVarcharType(0))), ImmutableList.of( + ImmutableList.of(""))); + assertFunction("ngrams(ARRAY['', ''], 2)", new ArrayType(new ArrayType(createVarcharType(0))), ImmutableList.of( + ImmutableList.of("", ""))); + + assertInvalidFunction("ngrams(ARRAY['foo','bar'], 0)", "N must be positive"); + assertInvalidFunction("ngrams(ARRAY['foo','bar'], 0)", "N must be positive"); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDataSizeFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDataSizeFunctions.java new file mode 100644 index 0000000000000..28d9747ea0965 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDataSizeFunctions.java @@ -0,0 +1,58 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.operator.scalar; + +import com.facebook.presto.spi.type.Type; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static com.facebook.presto.spi.StandardErrorCode.NUMERIC_VALUE_OUT_OF_RANGE; +import static com.facebook.presto.spi.type.DecimalType.createDecimalType; + +public class TestDataSizeFunctions + extends AbstractTestFunctions +{ + private static final Type DECIMAL = createDecimalType(38, 0); + + @Test + public void testParseDataSize() + { + assertFunction("parse_presto_data_size('0B')", DECIMAL, decimal("0")); + assertFunction("parse_presto_data_size('1B')", DECIMAL, decimal("1")); + assertFunction("parse_presto_data_size('1.2B')", DECIMAL, decimal("1")); + assertFunction("parse_presto_data_size('1.9B')", DECIMAL, decimal("1")); + assertFunction("parse_presto_data_size('2.2kB')", DECIMAL, decimal("2252")); + assertFunction("parse_presto_data_size('2.23kB')", DECIMAL, decimal("2283")); + assertFunction("parse_presto_data_size('2.23kB')", DECIMAL, decimal("2283")); + assertFunction("parse_presto_data_size('2.234kB')", DECIMAL, decimal("2287")); + assertFunction("parse_presto_data_size('3MB')", DECIMAL, decimal("3145728")); + assertFunction("parse_presto_data_size('4GB')", DECIMAL, decimal("4294967296")); + assertFunction("parse_presto_data_size('4TB')", DECIMAL, decimal("4398046511104")); + assertFunction("parse_presto_data_size('5PB')", DECIMAL, decimal("5629499534213120")); + assertFunction("parse_presto_data_size('6EB')", DECIMAL, decimal("6917529027641081856")); + assertFunction("parse_presto_data_size('7ZB')", DECIMAL, decimal("8264141345021879123968")); + assertFunction("parse_presto_data_size('8YB')", DECIMAL, decimal("9671406556917033397649408")); + assertFunction("parse_presto_data_size('6917529027641081856EB')", DECIMAL, decimal("7975367974709495237422842361682067456")); + assertFunction("parse_presto_data_size('69175290276410818560EB')", DECIMAL, decimal("79753679747094952374228423616820674560")); + + assertInvalidFunction("parse_presto_data_size('')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: ''"); + assertInvalidFunction("parse_presto_data_size('0')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: '0'"); + assertInvalidFunction("parse_presto_data_size('10KB')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: '10KB'"); + assertInvalidFunction("parse_presto_data_size('KB')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: 'KB'"); + assertInvalidFunction("parse_presto_data_size('-1B')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: '-1B'"); + assertInvalidFunction("parse_presto_data_size('12345K')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: '12345K'"); + assertInvalidFunction("parse_presto_data_size('A12345B')", INVALID_FUNCTION_ARGUMENT, "Invalid data size: 'A12345B'"); + assertInvalidFunction("parse_presto_data_size('99999999999999YB')", NUMERIC_VALUE_OUT_OF_RANGE, "Value out of range: '99999999999999YB' ('120892581961461708544797985370825293824B')"); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctions.java index 28eba3ccd583a..38469441e0133 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctions.java @@ -89,8 +89,8 @@ public void testCurrentTimestamp() .setStartTime(new DateTime(2017, 3, 1, 14, 30, 0, 0, DATE_TIME_ZONE).getMillis()) .build(); try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("CURRENT_TIMESTAMP", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 Asia/Kabul"); - localAssertion.assertFunctionString("NOW()", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 Asia/Kabul"); + localAssertion.assertFunctionString("CURRENT_TIMESTAMP", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 " + DATE_TIME_ZONE.getID()); + localAssertion.assertFunctionString("NOW()", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 " + DATE_TIME_ZONE.getID()); } } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java index a0853674b696f..f23552bfa2c1e 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsBase.java @@ -26,6 +26,7 @@ import com.facebook.presto.spi.type.TimestampType; import com.facebook.presto.spi.type.Type; import com.facebook.presto.testing.TestingConnectorSession; +import com.facebook.presto.testing.TestingSession; import com.facebook.presto.type.SqlIntervalDayTime; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -38,6 +39,7 @@ import org.joda.time.chrono.ISOChronology; import org.testng.annotations.Test; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalTime; @@ -67,6 +69,7 @@ import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static java.lang.Math.toIntExact; import static java.lang.String.format; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; import static java.util.Locale.US; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; @@ -85,10 +88,10 @@ public abstract class TestDateTimeFunctionsBase extends AbstractTestFunctions { - protected static final TimeZoneKey TIME_ZONE_KEY = getTimeZoneKey("Asia/Kabul"); + protected static final TimeZoneKey TIME_ZONE_KEY = TestingSession.DEFAULT_TIME_ZONE_KEY; protected static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); protected static final DateTimeZone UTC_TIME_ZONE = getDateTimeZone(UTC_KEY); - protected static final DateTimeZone DATE_TIME_ZONE_NUMERICAL = getDateTimeZone(getTimeZoneKey("+04:30")); + protected static final DateTimeZone DATE_TIME_ZONE_NUMERICAL = getDateTimeZone(getTimeZoneKey("-11:00")); protected static final TimeZoneKey KATHMANDU_ZONE_KEY = getTimeZoneKey("Asia/Kathmandu"); protected static final DateTimeZone KATHMANDU_ZONE = getDateTimeZone(KATHMANDU_ZONE_KEY); protected static final ZoneOffset WEIRD_ZONE = ZoneOffset.ofHoursMinutes(7, 9); @@ -107,12 +110,15 @@ public abstract class TestDateTimeFunctionsBase protected static final DateTime LEGACY_TIMESTAMP = new DateTime(2001, 8, 22, 3, 4, 5, 321, DATE_TIME_ZONE); protected static final DateTime TIMESTAMP_WITH_NUMERICAL_ZONE = new DateTime(2001, 8, 22, 3, 4, 5, 321, DATE_TIME_ZONE_NUMERICAL); protected static final String TIMESTAMP_LITERAL = "TIMESTAMP '2001-08-22 03:04:05.321'"; - protected static final String TIMESTAMP_ISO8601_STRING = "2001-08-22T03:04:05.321+04:30"; + protected static final String TIMESTAMP_ISO8601_STRING = "2001-08-22T03:04:05.321-11:00"; protected static final String TIMESTAMP_ISO8601_STRING_NO_TIME_ZONE = "2001-08-22T03:04:05.321"; protected static final DateTime WEIRD_TIMESTAMP = new DateTime(2001, 8, 22, 3, 4, 5, 321, WEIRD_DATE_TIME_ZONE); protected static final String WEIRD_TIMESTAMP_LITERAL = "TIMESTAMP '2001-08-22 03:04:05.321 +07:09'"; protected static final String WEIRD_TIMESTAMP_ISO8601_STRING = "2001-08-22T03:04:05.321+07:09"; + protected static final String INTERVAL_LITERAL = "INTERVAL '90061.234' SECOND"; + protected static final Duration DAY_TO_SECOND_INTERVAL = Duration.ofMillis(90061234); + @SuppressWarnings("MemberName") private final DateTime TIMESTAMP; @@ -154,7 +160,7 @@ public void testCurrentDateTimezone() private void assertCurrentDateAtInstant(TimeZoneKey timeZoneKey, long instant) { long expectedDays = epochDaysInZone(timeZoneKey, instant); - long dateTimeCalculation = currentDate(new TestingConnectorSession("test", "path", Optional.empty(), Optional.empty(), timeZoneKey, US, instant, ImmutableList.of(), ImmutableMap.of(), isLegacyTimestamp(session))); + long dateTimeCalculation = currentDate(new TestingConnectorSession("test", Optional.empty(), Optional.empty(), timeZoneKey, US, instant, ImmutableList.of(), ImmutableMap.of(), isLegacyTimestamp(session))); assertEquals(dateTimeCalculation, expectedDays); } @@ -264,6 +270,7 @@ public void testTimeZone() @Test public void testPartFunctions() { + assertFunction("millisecond(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMillisOfSecond()); assertFunction("second(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getSecondOfMinute()); assertFunction("minute(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMinuteOfHour()); assertFunction("hour(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getHourOfDay()); @@ -278,10 +285,13 @@ public void testPartFunctions() assertFunction("month(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear()); assertFunction("quarter(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getMonthOfYear() / 4 + 1); assertFunction("year(" + TIMESTAMP_LITERAL + ")", BIGINT, (long) TIMESTAMP.getYear()); - assertFunction("timezone_hour(" + TIMESTAMP_LITERAL + ")", BIGINT, 4L); - assertFunction("timezone_hour(localtimestamp)", BIGINT, 4L); - assertFunction("timezone_hour(current_timestamp)", BIGINT, 4L); + assertFunction("timezone_minute(" + TIMESTAMP_LITERAL + ")", BIGINT, 0L); + assertFunction("timezone_hour(" + TIMESTAMP_LITERAL + ")", BIGINT, -11L); + + assertFunction("timezone_hour(localtimestamp)", BIGINT, 14L); + assertFunction("timezone_hour(current_timestamp)", BIGINT, 14L); + assertFunction("millisecond(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMillisOfSecond()); assertFunction("second(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getSecondOfMinute()); assertFunction("minute(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getMinuteOfHour()); assertFunction("hour(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getHourOfDay()); @@ -298,6 +308,21 @@ public void testPartFunctions() assertFunction("year(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, (long) WEIRD_TIMESTAMP.getYear()); assertFunction("timezone_minute(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 9L); assertFunction("timezone_hour(" + WEIRD_TIMESTAMP_LITERAL + ")", BIGINT, 7L); + + assertFunction("millisecond(" + TIME_LITERAL + ")", BIGINT, TIME.getLong(MILLI_OF_SECOND)); + assertFunction("second(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getSecond()); + assertFunction("minute(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getMinute()); + assertFunction("hour(" + TIME_LITERAL + ")", BIGINT, (long) TIME.getHour()); + + assertFunction("millisecond(" + WEIRD_TIME_LITERAL + ")", BIGINT, WEIRD_TIME.getLong(MILLI_OF_SECOND)); + assertFunction("second(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getSecond()); + assertFunction("minute(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getMinute()); + assertFunction("hour(" + WEIRD_TIME_LITERAL + ")", BIGINT, (long) WEIRD_TIME.getHour()); + + assertFunction("millisecond(" + INTERVAL_LITERAL + ")", BIGINT, (long) DAY_TO_SECOND_INTERVAL.getNano() / 1_000_000); + assertFunction("second(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() % 60); + assertFunction("minute(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() / 60 % 60); + assertFunction("hour(" + INTERVAL_LITERAL + ")", BIGINT, DAY_TO_SECOND_INTERVAL.getSeconds() / 3600 % 24); } @Test @@ -560,6 +585,7 @@ public void testAddFieldToTimestamp() @Test public void testAddFieldToDate() { + assertFunction("date_add('day', 0, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE)); assertFunction("date_add('day', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusDays(3))); assertFunction("date_add('week', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusWeeks(3))); assertFunction("date_add('month', 3, " + DATE_LITERAL + ")", DateType.DATE, toDate(DATE.plusMonths(3))); @@ -570,6 +596,7 @@ public void testAddFieldToDate() @Test public void testAddFieldToTime() { + assertFunction("date_add('millisecond', 0, " + TIME_LITERAL + ")", TimeType.TIME, toTime(TIME)); assertFunction("date_add('millisecond', 3, " + TIME_LITERAL + ")", TimeType.TIME, toTime(TIME.plusNanos(3_000_000))); assertFunction("date_add('second', 3, " + TIME_LITERAL + ")", TimeType.TIME, toTime(TIME.plusSeconds(3))); assertFunction("date_add('minute', 3, " + TIME_LITERAL + ")", TimeType.TIME, toTime(TIME.plusMinutes(3))); @@ -730,37 +757,37 @@ public void testDateFormat() assertFunction("date_format(" + dateTimeLiteral + ", '%4')", VARCHAR, "4"); assertFunction("date_format(" + dateTimeLiteral + ", '%x %v')", VARCHAR, "2001 02"); - String wierdDateTimeLiteral = "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'"; - - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%a')", VARCHAR, "Tue"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%b')", VARCHAR, "Jan"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%c')", VARCHAR, "1"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%d')", VARCHAR, "09"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%e')", VARCHAR, "9"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%f')", VARCHAR, "321000"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%H')", VARCHAR, "13"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%h')", VARCHAR, "01"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%I')", VARCHAR, "01"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%i')", VARCHAR, "04"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%j')", VARCHAR, "009"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%k')", VARCHAR, "13"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%l')", VARCHAR, "1"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%M')", VARCHAR, "January"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%m')", VARCHAR, "01"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%p')", VARCHAR, "PM"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%r')", VARCHAR, "01:04:05 PM"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%S')", VARCHAR, "05"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%s')", VARCHAR, "05"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%T')", VARCHAR, "13:04:05"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%v')", VARCHAR, "02"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%W')", VARCHAR, "Tuesday"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%Y')", VARCHAR, "2001"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%y')", VARCHAR, "01"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%%')", VARCHAR, "%"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", 'foo')", VARCHAR, "foo"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%g')", VARCHAR, "g"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%4')", VARCHAR, "4"); - assertFunction("date_format(" + wierdDateTimeLiteral + ", '%x %v')", VARCHAR, "2001 02"); + String weirdDateTimeLiteral = "TIMESTAMP '2001-01-09 13:04:05.321 +07:09'"; + + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%a')", VARCHAR, "Tue"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%b')", VARCHAR, "Jan"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%c')", VARCHAR, "1"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%d')", VARCHAR, "09"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%e')", VARCHAR, "9"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%f')", VARCHAR, "321000"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%H')", VARCHAR, "13"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%h')", VARCHAR, "01"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%I')", VARCHAR, "01"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%i')", VARCHAR, "04"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%j')", VARCHAR, "009"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%k')", VARCHAR, "13"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%l')", VARCHAR, "1"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%M')", VARCHAR, "January"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%m')", VARCHAR, "01"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%p')", VARCHAR, "PM"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%r')", VARCHAR, "01:04:05 PM"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%S')", VARCHAR, "05"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%s')", VARCHAR, "05"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%T')", VARCHAR, "13:04:05"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%v')", VARCHAR, "02"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%W')", VARCHAR, "Tuesday"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%Y')", VARCHAR, "2001"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%y')", VARCHAR, "01"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%%')", VARCHAR, "%"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", 'foo')", VARCHAR, "foo"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%g')", VARCHAR, "g"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%4')", VARCHAR, "4"); + assertFunction("date_format(" + weirdDateTimeLiteral + ", '%x %v')", VARCHAR, "2001 02"); assertFunction("date_format(TIMESTAMP '2001-01-09 13:04:05.32', '%f')", VARCHAR, "320000"); assertFunction("date_format(TIMESTAMP '2001-01-09 00:04:05.32', '%k')", VARCHAR, "0"); @@ -778,67 +805,67 @@ public void testDateParse() { assertFunction("date_parse('2013', '%Y')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 1, 1, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 1, 1, 0, 0, 0, 0, session)); assertFunction("date_parse('2013-05', '%Y-%m')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 1, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 1, 0, 0, 0, 0, session)); assertFunction("date_parse('2013-05-17', '%Y-%m-%d')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 0, 0, 0, 0, session)); assertFunction("date_parse('2013-05-17 12:35:10', '%Y-%m-%d %h:%i:%s')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, session)); assertFunction("date_parse('2013-05-17 12:35:10 PM', '%Y-%m-%d %h:%i:%s %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 12, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 12, 35, 10, 0, session)); assertFunction("date_parse('2013-05-17 12:35:10 AM', '%Y-%m-%d %h:%i:%s %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, session)); assertFunction("date_parse('2013-05-17 00:35:10', '%Y-%m-%d %H:%i:%s')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, session)); assertFunction("date_parse('2013-05-17 23:35:10', '%Y-%m-%d %H:%i:%s')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 23, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 23, 35, 10, 0, session)); assertFunction("date_parse('abc 2013-05-17 fff 23:35:10 xyz', 'abc %Y-%m-%d fff %H:%i:%s xyz')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 23, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 5, 17, 23, 35, 10, 0, session)); assertFunction("date_parse('2013 14', '%Y %y')", TimestampType.TIMESTAMP, - sqlTimestampOf(2014, 1, 1, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2014, 1, 1, 0, 0, 0, 0, session)); assertFunction("date_parse('1998 53', '%x %v')", TimestampType.TIMESTAMP, - sqlTimestampOf(1998, 12, 28, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1998, 12, 28, 0, 0, 0, 0, session)); assertFunction("date_parse('1.1', '%s.%f')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 100, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 100, session)); assertFunction("date_parse('1.01', '%s.%f')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 10, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 10, session)); assertFunction("date_parse('1.2006', '%s.%f')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 200, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 200, session)); assertFunction("date_parse('59.123456789', '%s.%f')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 59, 123, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 0, 0, 59, 123, session)); assertFunction("date_parse('0', '%k')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 0, 0, 0, 0, session)); assertFunction("date_parse('28-JAN-16 11.45.46.421000 PM','%d-%b-%y %l.%i.%s.%f %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2016, 1, 28, 23, 45, 46, 421, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2016, 1, 28, 23, 45, 46, 421, session)); assertFunction("date_parse('11-DEC-70 11.12.13.456000 AM','%d-%b-%y %l.%i.%s.%f %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(1970, 12, 11, 11, 12, 13, 456, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 12, 11, 11, 12, 13, 456, session)); assertFunction("date_parse('31-MAY-69 04.59.59.999000 AM','%d-%b-%y %l.%i.%s.%f %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2069, 5, 31, 4, 59, 59, 999, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2069, 5, 31, 4, 59, 59, 999, session)); assertInvalidFunction("date_parse('', '%D')", "%D not supported in date format string"); assertInvalidFunction("date_parse('', '%U')", "%U not supported in date format string"); @@ -878,10 +905,10 @@ public void testLocale() localeAssertions.assertFunction("date_parse('2013-05-17 12:35:10 오후', '%Y-%m-%d %h:%i:%s %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 12, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, localeSession)); + sqlTimestampOf(2013, 5, 17, 12, 35, 10, 0, localeSession)); localeAssertions.assertFunction("date_parse('2013-05-17 12:35:10 오전', '%Y-%m-%d %h:%i:%s %p')", TimestampType.TIMESTAMP, - sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, localeSession)); + sqlTimestampOf(2013, 5, 17, 0, 35, 10, 0, localeSession)); localeAssertions.assertFunction("parse_datetime('2013-05-17 12:35:10 오후', 'yyyy-MM-dd hh:mm:ss a')", TIMESTAMP_WITH_TIME_ZONE, @@ -1022,10 +1049,11 @@ public void testTimeWithTimeZoneAtTimeZone() TIME_WITH_TIME_ZONE, new SqlTimeWithTimeZone(new DateTime(1970, 1, 1, 4, 15, 0, 0, UTC_TIME_ZONE).getMillis(), TimeZoneKey.UTC_KEY)); - // Noop on Asia/Kabul + // Noop when time zone doesn't change + TimeZoneKey kabul = TimeZoneKey.getTimeZoneKey("Asia/Kabul"); assertFunction("at_timezone(TIME '10:00 Asia/Kabul', 'Asia/Kabul')", TIME_WITH_TIME_ZONE, - new SqlTimeWithTimeZone(new DateTime(1970, 1, 1, 10, 0, 0, 0, DATE_TIME_ZONE).getMillis(), TIME_ZONE_KEY)); + new SqlTimeWithTimeZone(new DateTime(1970, 1, 1, 10, 0, 0, 0, getDateTimeZone(kabul)).getMillis(), kabul)); // This test checks if the TZ offset isn't calculated on other fixed point in time by checking if // session started in 1980 would get historical Asia/Kathmandu offset. diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsLegacy.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsLegacy.java index 6027d7f60f4d3..77cfd0699f6ff 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsLegacy.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestDateTimeFunctionsLegacy.java @@ -42,7 +42,7 @@ public void testToIso8601ForTimestampWithoutTimeZone() @Test public void testFormatDateCanImplicitlyAddTimeZoneToTimestampLiteral() { - assertFunction("format_datetime(" + TIMESTAMP_LITERAL + ", 'YYYY/MM/dd HH:mm ZZZZ')", VARCHAR, "2001/08/22 03:04 Asia/Kabul"); + assertFunction("format_datetime(" + TIMESTAMP_LITERAL + ", 'YYYY/MM/dd HH:mm ZZZZ')", VARCHAR, "2001/08/22 03:04 " + DATE_TIME_ZONE.getID()); } @Test @@ -52,7 +52,7 @@ public void testLocalTime() .setStartTime(new DateTime(2017, 3, 1, 14, 30, 0, 0, DATE_TIME_ZONE).getMillis()) .build(); try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("LOCALTIME", TimeType.TIME, "14:30:00.000"); + localAssertion.assertFunctionString("LOCALTIME", TimeType.TIME, "13:30:00.000"); } } @@ -88,8 +88,8 @@ public void testCurrentTimestamp() .setStartTime(new DateTime(2017, 3, 1, 14, 30, 0, 0, DATE_TIME_ZONE).getMillis()) .build(); try (FunctionAssertions localAssertion = new FunctionAssertions(localSession)) { - localAssertion.assertFunctionString("CURRENT_TIMESTAMP", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 Asia/Kabul"); - localAssertion.assertFunctionString("NOW()", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 Asia/Kabul"); + localAssertion.assertFunctionString("CURRENT_TIMESTAMP", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 " + DATE_TIME_ZONE.getID()); + localAssertion.assertFunctionString("NOW()", TIMESTAMP_WITH_TIME_ZONE, "2017-03-01 14:30:00.000 " + DATE_TIME_ZONE.getID()); } } } diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestMathFunctions.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestMathFunctions.java index 80152f74a3826..4efcbe60b6ed4 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestMathFunctions.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestMathFunctions.java @@ -13,19 +13,14 @@ */ package com.facebook.presto.operator.scalar; -import com.facebook.presto.Session; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.type.DecimalType; import com.facebook.presto.spi.type.SqlDecimal; import com.facebook.presto.spi.type.VarcharType; -import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.google.common.base.Joiner; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; -import static com.facebook.presto.SystemSessionProperties.LEGACY_ROUND_N_BIGINT; import static com.facebook.presto.spi.StandardErrorCode.DIVISION_BY_ZERO; import static com.facebook.presto.spi.StandardErrorCode.NUMERIC_VALUE_OUT_OF_RANGE; import static com.facebook.presto.spi.type.BigintType.BIGINT; @@ -48,25 +43,6 @@ public class TestMathFunctions private static final double[] doubleRights = {3, -3, 3.1, -3.1}; private static final double GREATEST_DOUBLE_LESS_THAN_HALF = 0x1.fffffffffffffp-2; - private static FunctionAssertions legacyRoundNBigint; - - @BeforeClass - public final void setUp() - { - legacyRoundNBigint = new FunctionAssertions( - Session.builder(session) - .setSystemProperty(LEGACY_ROUND_N_BIGINT, "true") - .build(), - new FeaturesConfig()); - } - - @AfterClass(alwaysRun = true) - public final void tearDown() - { - legacyRoundNBigint.close(); - legacyRoundNBigint = null; - } - @Test public void testAbs() { @@ -311,9 +287,6 @@ public void testTruncate() assertFunction("truncate(DECIMAL '1234.56', 3)", createDecimalType(6, 2), SqlDecimal.of("1234.56")); assertFunction("truncate(DECIMAL '-1234.56', 3)", createDecimalType(6, 2), SqlDecimal.of("-1234.56")); - assertInvalidFunction("truncate(DECIMAL '-1234.56', BIGINT '3')", "TRUNCATE(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("truncate(DECIMAL '-1234.56', BIGINT '3')", createDecimalType(6, 2), SqlDecimal.of("-1234.56")); - // TRUNCATE_N long DECIMAL -> long DECIMAL assertFunction("truncate(DECIMAL '1234567890123456789012', 1)", createDecimalType(22, 0), SqlDecimal.of("1234567890123456789012")); assertFunction("truncate(DECIMAL '1234567890123456789012', -1)", createDecimalType(22, 0), SqlDecimal.of("1234567890123456789010")); @@ -328,9 +301,6 @@ public void testTruncate() assertFunction("truncate(DECIMAL '123456789012345678901.23', 3)", createDecimalType(23, 2), SqlDecimal.of("123456789012345678901.23")); assertFunction("truncate(DECIMAL '-123456789012345678901.23', 3)", createDecimalType(23, 2), SqlDecimal.of("-123456789012345678901.23")); - assertInvalidFunction("truncate(DECIMAL '-123456789012345678901.23', BIGINT '3')", "TRUNCATE(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("truncate(DECIMAL '-123456789012345678901.23', BIGINT '3')", createDecimalType(23, 2), SqlDecimal.of("-123456789012345678901.23")); - // NULL assertFunction("truncate(CAST(NULL AS DOUBLE))", DOUBLE, null); assertFunction("truncate(CAST(NULL AS DECIMAL(1,0)), -1)", createDecimalType(1, 0), null); @@ -823,9 +793,6 @@ public void testRound() assertFunction("round(REAL '-3.5001', 1)", REAL, -3.5f); assertFunction("round(REAL '-3.99', 1)", REAL, -4.0f); - assertInvalidFunction("round(REAL '-3.5001', BIGINT '1')", "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("round(REAL '-3.5001', BIGINT '1')", REAL, -3.5f); - // ROUND short DECIMAL -> short DECIMAL assertFunction("round(DECIMAL '0')", createDecimalType(1, 0), SqlDecimal.of("0")); assertFunction("round(DECIMAL '0.1')", createDecimalType(1, 0), SqlDecimal.of("0")); @@ -916,9 +883,6 @@ public void testRound() assertFunction("round(DECIMAL '-1234.5678', -7)", createDecimalType(9, 4), SqlDecimal.of("0.0000")); assertFunction("round(DECIMAL '99', -1)", createDecimalType(3, 0), SqlDecimal.of("100")); - assertInvalidFunction("round(DECIMAL '3.450', BIGINT '1')", "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("round(DECIMAL '3.450', BIGINT '1')", createDecimalType(5, 3), SqlDecimal.of("3.500")); - // ROUND_N long DECIMAL -> long DECIMAL assertFunction("round(DECIMAL '1234567890123456789', 1)", createDecimalType(20, 0), SqlDecimal.of("1234567890123456789")); assertFunction("round(DECIMAL '-1234567890123456789', 1)", createDecimalType(20, 0), SqlDecimal.of("-1234567890123456789")); @@ -952,9 +916,6 @@ public void testRound() assertFunction("round(DECIMAL '9999999999999999999', -3)", createDecimalType(20, 0), SqlDecimal.of("10000000000000000000")); assertFunction("round(DECIMAL '-9999999999999999999', -3)", createDecimalType(20, 0), SqlDecimal.of("-10000000000000000000")); - assertInvalidFunction("round(DECIMAL '123456789012345678.45', BIGINT '1')", "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("round(DECIMAL '123456789012345678.45', BIGINT '1')", createDecimalType(21, 2), SqlDecimal.of("123456789012345678.50")); - // ROUND_N short DECIMAL -> long DECIMAL assertFunction("round(DECIMAL '9999999999999999.99', 1)", createDecimalType(19, 2), SqlDecimal.of("10000000000000000.00")); assertFunction("round(DECIMAL '-9999999999999999.99', 1)", createDecimalType(19, 2), SqlDecimal.of("-10000000000000000.00")); @@ -969,9 +930,6 @@ public void testRound() assertFunction("round(DECIMAL '999999999999999999', -3)", createDecimalType(19, 0), SqlDecimal.of("1000000000000000000")); assertFunction("round(DECIMAL '-999999999999999999', -3)", createDecimalType(19, 0), SqlDecimal.of("-1000000000000000000")); - assertInvalidFunction("round(DECIMAL '9999999999999999.99', BIGINT '1')", "ROUND(x, d) does not accept d being a BIGINT. Please cast the second argument to INTEGER"); - legacyRoundNBigint.assertFunction("round(DECIMAL '9999999999999999.99', BIGINT '1')", createDecimalType(19, 2), SqlDecimal.of("10000000000000000.00")); - // NULL assertFunction("round(CAST(NULL as DOUBLE), CAST(NULL as INTEGER))", DOUBLE, null); assertFunction("round(-3.0E0, CAST(NULL as INTEGER))", DOUBLE, null); @@ -1385,6 +1343,35 @@ public void testNormalCdf() assertInvalidFunction("normal_cdf(0, nan(), 0.1985)", "standardDeviation must > 0"); } + @Test + public void testInverseBetaCdf() + { + assertFunction("inverse_beta_cdf(3, 3.6, 0.0)", DOUBLE, 0.0); + assertFunction("inverse_beta_cdf(3, 3.6, 1.0)", DOUBLE, 1.0); + assertFunction("inverse_beta_cdf(3, 3.6, 0.3)", DOUBLE, 0.3469675485440618); + assertFunction("inverse_beta_cdf(3, 3.6, 0.95)", DOUBLE, 0.7600272463100223); + + assertInvalidFunction("inverse_beta_cdf(0, 3, 0.5)", "a, b must be > 0"); + assertInvalidFunction("inverse_beta_cdf(3, 0, 0.5)", "a, b must be > 0"); + assertInvalidFunction("inverse_beta_cdf(3, 5, -0.1)", "p must be 0 >= p >= 1"); + assertInvalidFunction("inverse_beta_cdf(3, 5, 1.1)", "p must be 0 >= p >= 1"); + } + + @Test + public void testBetaCdf() + throws Exception + { + assertFunction("beta_cdf(3, 3.6, 0.0)", DOUBLE, 0.0); + assertFunction("beta_cdf(3, 3.6, 1.0)", DOUBLE, 1.0); + assertFunction("beta_cdf(3, 3.6, 0.3)", DOUBLE, 0.21764809997679938); + assertFunction("beta_cdf(3, 3.6, 0.9)", DOUBLE, 0.9972502881611551); + + assertInvalidFunction("beta_cdf(0, 3, 0.5)", "a, b must be > 0"); + assertInvalidFunction("beta_cdf(3, 0, 0.5)", "a, b must be > 0"); + assertInvalidFunction("beta_cdf(3, 5, -0.1)", "value must be 0 >= v >= 1"); + assertInvalidFunction("beta_cdf(3, 5, 1.1)", "value must be 0 >= v >= 1"); + } + @Test public void testWilsonInterval() { diff --git a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestPageProcessorCompiler.java b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestPageProcessorCompiler.java index cee68b565394a..764950738e5ee 100644 --- a/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestPageProcessorCompiler.java +++ b/presto-main/src/test/java/com/facebook/presto/operator/scalar/TestPageProcessorCompiler.java @@ -41,9 +41,11 @@ import static com.facebook.presto.block.BlockAssertions.createLongDictionaryBlock; import static com.facebook.presto.block.BlockAssertions.createRLEBlock; import static com.facebook.presto.block.BlockAssertions.createSlicesBlock; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.metadata.Signature.internalOperator; +import static com.facebook.presto.operator.project.PageProcessor.MAX_BATCH_SIZE; import static com.facebook.presto.spi.function.OperatorType.LESS_THAN; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -93,11 +95,17 @@ public void testNoCaching() @Test public void testSanityRLE() { - PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, BIGINT), field(1, VARCHAR))).get(); + PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, BIGINT), field(1, VARCHAR)), MAX_BATCH_SIZE).get(); Slice varcharValue = Slices.utf8Slice("hello"); Page page = new Page(RunLengthEncodedBlock.create(BIGINT, 123L, 100), RunLengthEncodedBlock.create(VARCHAR, varcharValue, 100)); - Page outputPage = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertEquals(outputPage.getPositionCount(), 100); assertTrue(outputPage.getBlock(0) instanceof RunLengthEncodedBlock); @@ -118,10 +126,16 @@ public void testSanityFilterOnDictionary() Signature lessThan = internalOperator(LESS_THAN, BOOLEAN, ImmutableList.of(BIGINT, BIGINT)); CallExpression filter = new CallExpression(lessThan, BOOLEAN, ImmutableList.of(lengthVarchar, constant(10L, BIGINT))); - PageProcessor processor = compiler.compilePageProcessor(Optional.of(filter), ImmutableList.of(field(0, VARCHAR))).get(); + PageProcessor processor = compiler.compilePageProcessor(Optional.of(filter), ImmutableList.of(field(0, VARCHAR)), MAX_BATCH_SIZE).get(); Page page = new Page(createDictionaryBlock(createExpectedValues(10), 100)); - Page outputPage = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertEquals(outputPage.getPositionCount(), 100); assertTrue(outputPage.getBlock(0) instanceof DictionaryBlock); @@ -130,7 +144,12 @@ public void testSanityFilterOnDictionary() assertEquals(dictionaryBlock.getDictionary().getPositionCount(), 10); // test filter caching - Page outputPage2 = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage2 = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)).orElseThrow(() -> new AssertionError("page is not present")); assertEquals(outputPage2.getPositionCount(), 100); assertTrue(outputPage2.getBlock(0) instanceof DictionaryBlock); @@ -145,10 +164,16 @@ public void testSanityFilterOnRLE() Signature lessThan = internalOperator(LESS_THAN, BOOLEAN, ImmutableList.of(BIGINT, BIGINT)); CallExpression filter = new CallExpression(lessThan, BOOLEAN, ImmutableList.of(field(0, BIGINT), constant(10L, BIGINT))); - PageProcessor processor = compiler.compilePageProcessor(Optional.of(filter), ImmutableList.of(field(0, BIGINT))).get(); + PageProcessor processor = compiler.compilePageProcessor(Optional.of(filter), ImmutableList.of(field(0, BIGINT)), MAX_BATCH_SIZE).get(); Page page = new Page(createRLEBlock(5L, 100)); - Page outputPage = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertEquals(outputPage.getPositionCount(), 100); assertTrue(outputPage.getBlock(0) instanceof RunLengthEncodedBlock); @@ -160,10 +185,16 @@ public void testSanityFilterOnRLE() @Test public void testSanityColumnarDictionary() { - PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, VARCHAR))).get(); + PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(field(0, VARCHAR)), MAX_BATCH_SIZE).get(); Page page = new Page(createDictionaryBlock(createExpectedValues(10), 100)); - Page outputPage = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertEquals(outputPage.getPositionCount(), 100); assertTrue(outputPage.getBlock(0) instanceof DictionaryBlock); @@ -181,12 +212,18 @@ public void testNonDeterministicProject() InputReferenceExpression col0 = field(0, BIGINT); CallExpression lessThanRandomExpression = new CallExpression(lessThan, BOOLEAN, ImmutableList.of(col0, random)); - PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(lessThanRandomExpression)).get(); + PageProcessor processor = compiler.compilePageProcessor(Optional.empty(), ImmutableList.of(lessThanRandomExpression), MAX_BATCH_SIZE).get(); assertFalse(new DeterminismEvaluator(metadataManager.getFunctionRegistry()).isDeterministic(lessThanRandomExpression)); Page page = new Page(createLongDictionaryBlock(1, 100)); - Page outputPage = getOnlyElement(processor.process(null, new DriverYieldSignal(), page)).orElseThrow(() -> new AssertionError("page is not present")); + Page outputPage = getOnlyElement( + processor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + page)) + .orElseThrow(() -> new AssertionError("page is not present")); assertFalse(outputPage.getBlock(0) instanceof DictionaryBlock); } diff --git a/presto-main/src/test/java/com/facebook/presto/security/TestAccessControlManager.java b/presto-main/src/test/java/com/facebook/presto/security/TestAccessControlManager.java index ad2071699ae1a..2c1e856dc56ae 100644 --- a/presto-main/src/test/java/com/facebook/presto/security/TestAccessControlManager.java +++ b/presto-main/src/test/java/com/facebook/presto/security/TestAccessControlManager.java @@ -65,7 +65,7 @@ public class TestAccessControlManager public void testInitializing() { AccessControlManager accessControlManager = new AccessControlManager(createTestTransactionManager()); - accessControlManager.checkCanSetUser(null, "foo"); + accessControlManager.checkCanSetUser(Optional.empty(), "foo"); } @Test @@ -73,7 +73,7 @@ public void testNoneSystemAccessControl() { AccessControlManager accessControlManager = new AccessControlManager(createTestTransactionManager()); accessControlManager.setSystemAccessControl(AllowAllSystemAccessControl.NAME, ImmutableMap.of()); - accessControlManager.checkCanSetUser(null, USER_NAME); + accessControlManager.checkCanSetUser(Optional.empty(), USER_NAME); } @Test @@ -85,7 +85,7 @@ public void testReadOnlySystemAccessControl() AccessControlManager accessControlManager = new AccessControlManager(transactionManager); accessControlManager.setSystemAccessControl(ReadOnlySystemAccessControl.NAME, ImmutableMap.of()); - accessControlManager.checkCanSetUser(PRINCIPAL, USER_NAME); + accessControlManager.checkCanSetUser(Optional.of(PRINCIPAL), USER_NAME); accessControlManager.checkCanSetSystemSessionProperty(identity, "property"); transaction(transactionManager, accessControlManager) @@ -123,9 +123,9 @@ public void testSetAccessControl() accessControlManager.addSystemAccessControlFactory(accessControlFactory); accessControlManager.setSystemAccessControl("test", ImmutableMap.of()); - accessControlManager.checkCanSetUser(PRINCIPAL, USER_NAME); + accessControlManager.checkCanSetUser(Optional.of(PRINCIPAL), USER_NAME); assertEquals(accessControlFactory.getCheckedUserName(), USER_NAME); - assertEquals(accessControlFactory.getCheckedPrincipal(), PRINCIPAL); + assertEquals(accessControlFactory.getCheckedPrincipal(), Optional.of(PRINCIPAL)); } @Test @@ -214,7 +214,7 @@ private static class TestSystemAccessControlFactory private final String name; private Map config; - private Principal checkedPrincipal; + private Optional checkedPrincipal; private String checkedUserName; public TestSystemAccessControlFactory(String name) @@ -227,7 +227,7 @@ public Map getConfig() return config; } - public Principal getCheckedPrincipal() + public Optional getCheckedPrincipal() { return checkedPrincipal; } @@ -250,7 +250,7 @@ public SystemAccessControl create(Map config) return new SystemAccessControl() { @Override - public void checkCanSetUser(Principal principal, String userName) + public void checkCanSetUser(Optional principal, String userName) { checkedPrincipal = principal; checkedUserName = userName; @@ -378,7 +378,7 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { throw new UnsupportedOperationException(); } diff --git a/presto-main/src/test/java/com/facebook/presto/security/TestAllowAllSystemAccessControl.java b/presto-main/src/test/java/com/facebook/presto/security/TestAllowAllSystemAccessControl.java new file mode 100644 index 0000000000000..fec97d5657b3e --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/security/TestAllowAllSystemAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.security; + +import com.facebook.presto.spi.security.SystemAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestAllowAllSystemAccessControl +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(SystemAccessControl.class, AllowAllSystemAccessControl.class); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/security/TestFileBasedSystemAccessControl.java b/presto-main/src/test/java/com/facebook/presto/security/TestFileBasedSystemAccessControl.java index 5e1cf70f4a8fd..53aa615be951d 100644 --- a/presto-main/src/test/java/com/facebook/presto/security/TestFileBasedSystemAccessControl.java +++ b/presto-main/src/test/java/com/facebook/presto/security/TestFileBasedSystemAccessControl.java @@ -26,14 +26,20 @@ import javax.security.auth.kerberos.KerberosPrincipal; +import java.io.File; import java.util.Optional; import java.util.Set; -import static com.facebook.presto.security.FileBasedSystemAccessControl.Factory.CONFIG_FILE_NAME; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_CONFIG_FILE; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_REFRESH_PERIOD; import static com.facebook.presto.spi.security.Privilege.SELECT; +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; import static com.facebook.presto.transaction.InMemoryTransactionManager.createTestTransactionManager; import static com.facebook.presto.transaction.TransactionBuilder.transaction; +import static com.google.common.io.Files.copy; +import static java.lang.Thread.sleep; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.util.Files.newTemporaryFile; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; @@ -63,33 +69,33 @@ public void testCanSetUserOperations() AccessControlManager accessControlManager = newAccessControlManager(transactionManager, "catalog_principal.json"); try { - accessControlManager.checkCanSetUser(null, alice.getUser()); + accessControlManager.checkCanSetUser(Optional.empty(), alice.getUser()); throw new AssertionError("expected AccessDeniedExeption"); } catch (AccessDeniedException expected) { } - accessControlManager.checkCanSetUser(kerberosValidAlice.getPrincipal().get(), kerberosValidAlice.getUser()); - accessControlManager.checkCanSetUser(kerberosValidNonAsciiUser.getPrincipal().get(), kerberosValidNonAsciiUser.getUser()); + accessControlManager.checkCanSetUser(kerberosValidAlice.getPrincipal(), kerberosValidAlice.getUser()); + accessControlManager.checkCanSetUser(kerberosValidNonAsciiUser.getPrincipal(), kerberosValidNonAsciiUser.getUser()); try { - accessControlManager.checkCanSetUser(kerberosInvalidAlice.getPrincipal().get(), kerberosInvalidAlice.getUser()); + accessControlManager.checkCanSetUser(kerberosInvalidAlice.getPrincipal(), kerberosInvalidAlice.getUser()); throw new AssertionError("expected AccessDeniedExeption"); } catch (AccessDeniedException expected) { } - accessControlManager.checkCanSetUser(kerberosValidShare.getPrincipal().get(), kerberosValidShare.getUser()); + accessControlManager.checkCanSetUser(kerberosValidShare.getPrincipal(), kerberosValidShare.getUser()); try { - accessControlManager.checkCanSetUser(kerberosInValidShare.getPrincipal().get(), kerberosInValidShare.getUser()); + accessControlManager.checkCanSetUser(kerberosInValidShare.getPrincipal(), kerberosInValidShare.getUser()); throw new AssertionError("expected AccessDeniedExeption"); } catch (AccessDeniedException expected) { } - accessControlManager.checkCanSetUser(validSpecialRegexWildDot.getPrincipal().get(), validSpecialRegexWildDot.getUser()); - accessControlManager.checkCanSetUser(validSpecialRegexEndQuote.getPrincipal().get(), validSpecialRegexEndQuote.getUser()); + accessControlManager.checkCanSetUser(validSpecialRegexWildDot.getPrincipal(), validSpecialRegexWildDot.getUser()); + accessControlManager.checkCanSetUser(validSpecialRegexEndQuote.getPrincipal(), validSpecialRegexEndQuote.getUser()); try { - accessControlManager.checkCanSetUser(invalidSpecialRegex.getPrincipal().get(), invalidSpecialRegex.getUser()); + accessControlManager.checkCanSetUser(invalidSpecialRegex.getPrincipal(), invalidSpecialRegex.getUser()); throw new AssertionError("expected AccessDeniedExeption"); } catch (AccessDeniedException expected) { @@ -97,7 +103,7 @@ public void testCanSetUserOperations() TransactionManager transactionManagerNoPatterns = createTestTransactionManager(); AccessControlManager accessControlManagerNoPatterns = newAccessControlManager(transactionManager, "catalog.json"); - accessControlManagerNoPatterns.checkCanSetUser(kerberosValidAlice.getPrincipal().get(), kerberosValidAlice.getUser()); + accessControlManagerNoPatterns.checkCanSetUser(kerberosValidAlice.getPrincipal(), kerberosValidAlice.getUser()); } @Test @@ -187,16 +193,73 @@ public void testViewOperations() })); } + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(SystemAccessControl.class, FileBasedSystemAccessControl.class); + } + + @Test + public void testRefreshing() + throws Exception + { + TransactionManager transactionManager = createTestTransactionManager(); + AccessControlManager accessControlManager = new AccessControlManager(transactionManager); + File configFile = newTemporaryFile(); + configFile.deleteOnExit(); + copy(new File(getResourcePath("catalog.json")), configFile); + + accessControlManager.setSystemAccessControl(FileBasedSystemAccessControl.NAME, ImmutableMap.of( + SECURITY_CONFIG_FILE, configFile.getAbsolutePath(), + SECURITY_REFRESH_PERIOD, "1ms")); + + transaction(transactionManager, accessControlManager) + .execute(transactionId -> { + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + }); + + copy(new File(getResourcePath("security-config-file-with-unknown-rules.json")), configFile); + sleep(2); + + assertThatThrownBy(() -> transaction(transactionManager, accessControlManager) + .execute(transactionId -> { + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + })) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Invalid JSON file"); + // test if file based cached control was not cached somewhere + assertThatThrownBy(() -> transaction(transactionManager, accessControlManager) + .execute(transactionId -> { + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + })) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageStartingWith("Invalid JSON file"); + + copy(new File(getResourcePath("catalog.json")), configFile); + sleep(2); + + transaction(transactionManager, accessControlManager) + .execute(transactionId -> { + accessControlManager.checkCanCreateView(transactionId, alice, aliceView); + }); + } + private AccessControlManager newAccessControlManager(TransactionManager transactionManager, String resourceName) { AccessControlManager accessControlManager = new AccessControlManager(transactionManager); - String path = this.getClass().getClassLoader().getResource(resourceName).getPath(); - accessControlManager.setSystemAccessControl(FileBasedSystemAccessControl.NAME, ImmutableMap.of("security.config-file", path)); + accessControlManager.setSystemAccessControl(FileBasedSystemAccessControl.NAME, ImmutableMap.of("security.config-file", getResourcePath(resourceName))); return accessControlManager; } + private String getResourcePath(String resourceName) + { + return this.getClass().getClassLoader().getResource(resourceName).getPath(); + } + @Test public void parseUnknownRules() { @@ -206,6 +269,6 @@ public void parseUnknownRules() private SystemAccessControl parse(String path) { - return new FileBasedSystemAccessControl.Factory().create(ImmutableMap.of(CONFIG_FILE_NAME, path)); + return new FileBasedSystemAccessControl.Factory().create(ImmutableMap.of(SECURITY_CONFIG_FILE, path)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestBasicQueryInfo.java b/presto-main/src/test/java/com/facebook/presto/server/TestBasicQueryInfo.java index fbc5a58106f90..d734ca6ed30d0 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestBasicQueryInfo.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestBasicQueryInfo.java @@ -59,6 +59,7 @@ public void testConstructor() Duration.valueOf("8m"), Duration.valueOf("7m"), Duration.valueOf("34m"), + Duration.valueOf("44m"), Duration.valueOf("9m"), Duration.valueOf("10m"), Duration.valueOf("11m"), @@ -80,7 +81,6 @@ public void testConstructor() true, Duration.valueOf("23m"), Duration.valueOf("24m"), - Duration.valueOf("25m"), Duration.valueOf("26m"), true, ImmutableSet.of(BlockedReason.WAITING_FOR_MEMORY), @@ -113,6 +113,7 @@ public void testConstructor() Optional.empty(), null, StandardErrorCode.ABANDONED_QUERY.toErrorCode(), + ImmutableList.of(), ImmutableSet.of(), Optional.empty(), false, @@ -127,7 +128,7 @@ public void testConstructor() assertEquals(basicInfo.getQueryStats().getCreateTime(), DateTime.parse("1991-09-06T05:00-05:30")); assertEquals(basicInfo.getQueryStats().getEndTime(), DateTime.parse("1991-09-06T06:00-05:30")); assertEquals(basicInfo.getQueryStats().getElapsedTime(), Duration.valueOf("8m")); - assertEquals(basicInfo.getQueryStats().getExecutionTime(), Duration.valueOf("1m")); + assertEquals(basicInfo.getQueryStats().getExecutionTime(), Duration.valueOf("44m")); assertEquals(basicInfo.getQueryStats().getTotalDrivers(), 16); assertEquals(basicInfo.getQueryStats().getQueuedDrivers(), 17); diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestQueryProgressStats.java b/presto-main/src/test/java/com/facebook/presto/server/TestQueryProgressStats.java index 842116a53216a..3849ab82068df 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestQueryProgressStats.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestQueryProgressStats.java @@ -14,10 +14,8 @@ package com.facebook.presto.server; import io.airlift.json.JsonCodec; -import org.joda.time.DateTime; import org.testng.annotations.Test; -import java.util.Optional; import java.util.OptionalDouble; import static org.testng.Assert.assertEquals; @@ -29,12 +27,10 @@ public class TestQueryProgressStats public void testJson() { QueryProgressStats expected = new QueryProgressStats( - Optional.of(DateTime.parse("1991-09-06T05:00-05:30")), 123456, 1111, 22222, 3333, - 444, 100000, 34230492, 1000, @@ -46,12 +42,10 @@ public void testJson() String json = codec.toJson(expected); QueryProgressStats actual = codec.fromJson(json); - assertEquals(actual.getExecutionStartTime().get().getMillis(), DateTime.parse("1991-09-06T05:00-05:30").getMillis()); assertEquals(actual.getElapsedTimeMillis(), 123456); assertEquals(actual.getQueuedTimeMillis(), 1111); assertEquals(actual.getCpuTimeMillis(), 22222); assertEquals(actual.getScheduledTimeMillis(), 3333); - assertEquals(actual.getBlockedTimeMillis(), 444); assertEquals(actual.getCurrentMemoryBytes(), 100000); assertEquals(actual.getPeakMemoryBytes(), 34230492); assertEquals(actual.getInputRows(), 1000); diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestQuerySessionSupplier.java b/presto-main/src/test/java/com/facebook/presto/server/TestQuerySessionSupplier.java index db49c7b15c9eb..80e690a7f6e95 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestQuerySessionSupplier.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestQuerySessionSupplier.java @@ -18,9 +18,6 @@ import com.facebook.presto.security.AllowAllAccessControl; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; -import com.facebook.presto.spi.session.TestingSessionPropertyConfigurationManagerFactory; import com.facebook.presto.sql.SqlEnvironmentConfig; import com.facebook.presto.sql.SqlPath; import com.facebook.presto.sql.SqlPathElement; @@ -30,7 +27,6 @@ import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import io.airlift.node.NodeInfo; import org.testng.annotations.Test; import javax.servlet.http.HttpServletRequest; @@ -76,20 +72,17 @@ public class TestQuerySessionSupplier .put(PRESTO_PREPARED_STATEMENT, "query1=select * from foo,query2=select * from bar") .build(), "testRemote"); - private static final ResourceGroupId TEST_RESOURCE_GROUP_ID = new ResourceGroupId("test"); - private static final NodeInfo TEST_NODE_INFO = new NodeInfo("test"); @Test public void testCreateSession() { HttpRequestSessionContext context = new HttpRequestSessionContext(TEST_REQUEST); QuerySessionSupplier sessionSupplier = new QuerySessionSupplier( - TEST_NODE_INFO, createTestTransactionManager(), new AllowAllAccessControl(), new SessionPropertyManager(), new SqlEnvironmentConfig()); - Session session = sessionSupplier.createSession(new QueryId("test_query_id"), context, Optional.empty(), TEST_RESOURCE_GROUP_ID); + Session session = sessionSupplier.createSession(new QueryId("test_query_id"), context); assertEquals(session.getQueryId(), new QueryId("test_query_id")); assertEquals(session.getUser(), "testUser"); @@ -113,32 +106,6 @@ public void testCreateSession() .build()); } - @Test - public void testApplySessionPropertyConfigurationManager() - { - HttpRequestSessionContext context = new HttpRequestSessionContext(TEST_REQUEST); - QuerySessionSupplier sessionSupplier = new QuerySessionSupplier( - TEST_NODE_INFO, - createTestTransactionManager(), - new AllowAllAccessControl(), - new SessionPropertyManager(), - new SqlEnvironmentConfig()); - SessionPropertyConfigurationManagerFactory factory = new TestingSessionPropertyConfigurationManagerFactory( - ImmutableMap.of(QUERY_MAX_MEMORY, "10GB", "key2", "20", "key3", "3"), - ImmutableMap.of("testCatalog", ImmutableMap.of("key1", "10"))); - sessionSupplier.addConfigurationManager(factory); - sessionSupplier.setConfigurationManager(factory.getName(), ImmutableMap.of()); - Session session = sessionSupplier.createSession(new QueryId("test_query_id"), context, Optional.empty(), TEST_RESOURCE_GROUP_ID); - assertEquals(session.getSystemProperties(), ImmutableMap.builder() - .put(QUERY_MAX_MEMORY, "1GB") - .put(JOIN_DISTRIBUTION_TYPE, "partitioned") - .put(HASH_PARTITION_COUNT, "43") - .put("key2", "20") - .put("key3", "3") - .build()); - assertEquals(session.getUnprocessedCatalogProperties(), ImmutableMap.of("testCatalog", ImmutableMap.of("key1", "10"))); - } - @Test public void testEmptyClientTags() { @@ -192,12 +159,11 @@ public void testInvalidTimeZone() "testRemote"); HttpRequestSessionContext context = new HttpRequestSessionContext(request); QuerySessionSupplier sessionSupplier = new QuerySessionSupplier( - TEST_NODE_INFO, createTestTransactionManager(), new AllowAllAccessControl(), new SessionPropertyManager(), new SqlEnvironmentConfig()); - sessionSupplier.createSession(new QueryId("test_query_id"), context, Optional.empty(), TEST_RESOURCE_GROUP_ID); + sessionSupplier.createSession(new QueryId("test_query_id"), context); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfo.java b/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfo.java index 6b9b58495b726..fdf772f92976c 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfo.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfo.java @@ -63,7 +63,7 @@ public void testQueryStateInfo() // Verify QueryStateInfo for query queued on resource group root.a.y QueryStateInfo query = createQueuedQueryStateInfo( - createQueryInfo("query_root_a_x", QUEUED, "SELECT 1"), + new BasicQueryInfo(createQueryInfo("query_root_a_x", QUEUED, "SELECT 1")), Optional.of(rootAX.getId()), Optional.of(ImmutableList.of(rootAX.getInfo(), rootA.getInfo(), root.getInfo()))); @@ -110,6 +110,7 @@ private QueryInfo createQueryInfo(String queryId, QueryState state, String query Duration.valueOf("8m"), Duration.valueOf("7m"), Duration.valueOf("34m"), + Duration.valueOf("44m"), Duration.valueOf("9m"), Duration.valueOf("10m"), Duration.valueOf("11m"), @@ -131,7 +132,6 @@ private QueryInfo createQueryInfo(String queryId, QueryState state, String query true, Duration.valueOf("23m"), Duration.valueOf("24m"), - Duration.valueOf("25m"), Duration.valueOf("26m"), true, ImmutableSet.of(WAITING_FOR_MEMORY), @@ -157,6 +157,7 @@ private QueryInfo createQueryInfo(String queryId, QueryState state, String query Optional.empty(), null, null, + ImmutableList.of(), ImmutableSet.of(), Optional.empty(), false, diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfoResource.java b/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfoResource.java index c2b1ebbb70ac2..8f602941d6fd4 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfoResource.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestQueryStateInfoResource.java @@ -28,7 +28,7 @@ import java.util.List; import static com.facebook.presto.client.PrestoHeaders.PRESTO_USER; -import static com.facebook.presto.execution.QueryState.QUEUED; +import static com.facebook.presto.execution.QueryState.RUNNING; import static com.facebook.presto.testing.assertions.Assert.assertEquals; import static io.airlift.http.client.HttpUriBuilder.uriBuilderFrom; import static io.airlift.http.client.JsonResponseHandler.createJsonResponseHandler; @@ -85,7 +85,7 @@ public void setup() List queryInfos = client.execute( prepareGet().setUri(uriBuilderFrom(server.getBaseUrl()).replacePath("/v1/query").build()).build(), createJsonResponseHandler(listJsonCodec(BasicQueryInfo.class))); - if ((queryInfos.size() == 2) && queryInfos.stream().noneMatch(info -> info.getState() == QUEUED)) { + if ((queryInfos.size() == 2) && queryInfos.stream().allMatch(info -> info.getState() == RUNNING)) { break; } } diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestServer.java b/presto-main/src/test/java/com/facebook/presto/server/TestServer.java index 7985a9ed62d00..55216d2ec0dba 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/TestServer.java +++ b/presto-main/src/test/java/com/facebook/presto/server/TestServer.java @@ -15,7 +15,6 @@ import com.facebook.presto.client.QueryError; import com.facebook.presto.client.QueryResults; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.type.TimeZoneNotSupportedException; @@ -156,7 +155,7 @@ public void testQuery() assertNull(queryResults.getError()); // get the query info - QueryInfo queryInfo = server.getQueryManager().getQueryInfo(new QueryId(queryResults.getId())); + BasicQueryInfo queryInfo = server.getQueryManager().getQueryInfo(new QueryId(queryResults.getId())); // verify session properties assertEquals(queryInfo.getSession().getSystemProperties(), ImmutableMap.builder() diff --git a/presto-main/src/test/java/com/facebook/presto/server/TestSessionPropertyDefaults.java b/presto-main/src/test/java/com/facebook/presto/server/TestSessionPropertyDefaults.java new file mode 100644 index 0000000000000..b03464bca4849 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/server/TestSessionPropertyDefaults.java @@ -0,0 +1,96 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.server; + +import com.facebook.presto.Session; +import com.facebook.presto.metadata.SessionPropertyManager; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.spi.security.Identity; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; +import com.facebook.presto.spi.session.TestingSessionPropertyConfigurationManagerFactory; +import com.google.common.collect.ImmutableMap; +import io.airlift.node.NodeInfo; +import org.testng.annotations.Test; + +import java.util.Optional; + +import static com.facebook.presto.SystemSessionProperties.HASH_PARTITION_COUNT; +import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; +import static com.facebook.presto.SystemSessionProperties.QUERY_MAX_MEMORY; +import static org.testng.Assert.assertEquals; + +public class TestSessionPropertyDefaults +{ + private static final ResourceGroupId TEST_RESOURCE_GROUP_ID = new ResourceGroupId("test"); + private static final NodeInfo TEST_NODE_INFO = new NodeInfo("test"); + + @Test + public void testApplyDefaultProperties() + { + SessionPropertyDefaults sessionPropertyDefaults = new SessionPropertyDefaults(TEST_NODE_INFO); + SessionPropertyConfigurationManagerFactory factory = new TestingSessionPropertyConfigurationManagerFactory( + ImmutableMap.builder() + .put(QUERY_MAX_MEMORY, "override") + .put("system_default", "system_default") + .build(), + ImmutableMap.of( + "testCatalog", + ImmutableMap.builder() + .put("explicit_set", "override") + .put("catalog_default", "catalog_default") + .build())); + sessionPropertyDefaults.addConfigurationManagerFactory(factory); + sessionPropertyDefaults.setConfigurationManager(factory.getName(), ImmutableMap.of()); + + Session session = Session.builder(new SessionPropertyManager()) + .setQueryId(new QueryId("test_query_id")) + .setIdentity(new Identity("testUser", Optional.empty())) + .setSystemProperty(QUERY_MAX_MEMORY, "1GB") + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, "partitioned") + .setSystemProperty(HASH_PARTITION_COUNT, "43") + .setCatalogSessionProperty("testCatalog", "explicit_set", "explicit_set") + .build(); + + assertEquals(session.getSystemProperties(), ImmutableMap.builder() + .put(QUERY_MAX_MEMORY, "1GB") + .put(JOIN_DISTRIBUTION_TYPE, "partitioned") + .put(HASH_PARTITION_COUNT, "43") + .build()); + assertEquals( + session.getUnprocessedCatalogProperties(), + ImmutableMap.of( + "testCatalog", + ImmutableMap.builder() + .put("explicit_set", "explicit_set") + .build())); + + session = sessionPropertyDefaults.newSessionWithDefaultProperties(session, Optional.empty(), TEST_RESOURCE_GROUP_ID); + + assertEquals(session.getSystemProperties(), ImmutableMap.builder() + .put(QUERY_MAX_MEMORY, "1GB") + .put(JOIN_DISTRIBUTION_TYPE, "partitioned") + .put(HASH_PARTITION_COUNT, "43") + .put("system_default", "system_default") + .build()); + assertEquals( + session.getUnprocessedCatalogProperties(), + ImmutableMap.of( + "testCatalog", + ImmutableMap.builder() + .put("explicit_set", "explicit_set") + .put("catalog_default", "catalog_default") + .build())); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/server/remotetask/TestHttpRemoteTask.java b/presto-main/src/test/java/com/facebook/presto/server/remotetask/TestHttpRemoteTask.java index e552b3cfc5005..909896bbd8080 100644 --- a/presto-main/src/test/java/com/facebook/presto/server/remotetask/TestHttpRemoteTask.java +++ b/presto-main/src/test/java/com/facebook/presto/server/remotetask/TestHttpRemoteTask.java @@ -441,8 +441,7 @@ private TaskInfo buildTaskInfo() initialTaskInfo.getOutputBuffers(), initialTaskInfo.getNoMoreSplits(), initialTaskInfo.getStats(), - initialTaskInfo.isNeedsPlan(), - initialTaskInfo.isComplete()); + initialTaskInfo.isNeedsPlan()); } private TaskStatus buildTaskStatus() diff --git a/presto-main/src/test/java/com/facebook/presto/split/MockSplitSource.java b/presto-main/src/test/java/com/facebook/presto/split/MockSplitSource.java index 6c3366d038534..6d3d379617bbe 100644 --- a/presto-main/src/test/java/com/facebook/presto/split/MockSplitSource.java +++ b/presto-main/src/test/java/com/facebook/presto/split/MockSplitSource.java @@ -43,6 +43,7 @@ public class MockSplitSource { private static final Split SPLIT = new Split(new ConnectorId("test"), new ConnectorTransactionHandle() {}, new MockConnectorSplit()); private static final SettableFuture> COMPLETED_FUTURE = SettableFuture.create(); + static { COMPLETED_FUTURE.set(null); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java b/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java index 20eed5662fec7..d3324e9493121 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/TestExpressionInterpreter.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.operator.scalar.FunctionAssertions; @@ -71,6 +72,7 @@ import static com.facebook.presto.sql.planner.ExpressionInterpreter.expressionInterpreter; import static com.facebook.presto.sql.planner.ExpressionInterpreter.expressionOptimizer; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; +import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.String.format; import static java.util.Collections.emptyList; @@ -276,6 +278,9 @@ public void testNullIf() assertOptimizedEquals("nullif(9876543210.9874561203, 9876543210.9874561203)", "null"); assertOptimizedEquals("nullif(bound_decimal_short, 123.45)", "null"); assertOptimizedEquals("nullif(bound_decimal_long, 12345678901234567890.123)", "null"); + assertOptimizedEquals("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(1 AS BIGINT)]) IS NULL", "true"); + assertOptimizedEquals("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)]) IS NULL", "false"); + assertOptimizedEquals("nullif(ARRAY[CAST(NULL AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)]) IS NULL", "false"); } @Test @@ -370,7 +375,7 @@ public void testBetween() @Test public void testExtract() { - DateTime dateTime = new DateTime(2001, 8, 22, 3, 4, 5, 321, DateTimeZone.UTC); + DateTime dateTime = new DateTime(2001, 8, 22, 3, 4, 5, 321, getDateTimeZone(TEST_SESSION.getTimeZoneKey())); double seconds = dateTime.getMillis() / 1000.0; assertOptimizedEquals("extract (YEAR from from_unixtime(" + seconds + "))", "2001"); @@ -390,10 +395,10 @@ public void testExtract() assertOptimizedEquals("extract (QUARTER from bound_timestamp)", "3"); assertOptimizedEquals("extract (MONTH from bound_timestamp)", "8"); assertOptimizedEquals("extract (WEEK from bound_timestamp)", "34"); - assertOptimizedEquals("extract (DOW from bound_timestamp)", "3"); - assertOptimizedEquals("extract (DOY from bound_timestamp)", "234"); - assertOptimizedEquals("extract (DAY from bound_timestamp)", "22"); - assertOptimizedEquals("extract (HOUR from bound_timestamp)", "3"); + assertOptimizedEquals("extract (DOW from bound_timestamp)", "2"); + assertOptimizedEquals("extract (DOY from bound_timestamp)", "233"); + assertOptimizedEquals("extract (DAY from bound_timestamp)", "21"); + assertOptimizedEquals("extract (HOUR from bound_timestamp)", "16"); assertOptimizedEquals("extract (MINUTE from bound_timestamp)", "4"); assertOptimizedEquals("extract (SECOND from bound_timestamp)", "5"); // todo reenable when cast as timestamp with time zone is implemented @@ -449,6 +454,56 @@ public void testIn() assertOptimizedEquals("bound_decimal_long in (9876543210.9874561204, null, 9876543210.98745612035)", "null"); } + @Test + public void testInComplexTypes() + { + assertEvaluatedEquals("ARRAY[1] IN (ARRAY[1])", "true"); + assertEvaluatedEquals("ARRAY[1] IN (ARRAY[2])", "false"); + assertEvaluatedEquals("ARRAY[1] IN (ARRAY[2], ARRAY[1])", "true"); + assertEvaluatedEquals("ARRAY[1] IN (null)", "null"); + assertEvaluatedEquals("ARRAY[1] IN (null, ARRAY[1])", "true"); + assertEvaluatedEquals("ARRAY[1, 2, null] IN (ARRAY[2, null], ARRAY[1, null])", "false"); + assertEvaluatedEquals("ARRAY[1, null] IN (ARRAY[2, null], null)", "null"); + assertEvaluatedEquals("ARRAY[null] IN (ARRAY[null])", "null"); + assertEvaluatedEquals("ARRAY[1] IN (ARRAY[null])", "null"); + assertEvaluatedEquals("ARRAY[null] IN (ARRAY[1])", "null"); + assertEvaluatedEquals("ARRAY[1, null] IN (ARRAY[1, null])", "null"); + assertEvaluatedEquals("ARRAY[1, null] IN (ARRAY[2, null])", "false"); + assertEvaluatedEquals("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null])", "null"); + assertEvaluatedEquals("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null], ARRAY[1, null])", "null"); + assertEvaluatedEquals("ARRAY[ARRAY[1, 2], ARRAY[3, 4]] in (ARRAY[ARRAY[1, 2], ARRAY[3, NULL]])", "null"); + + assertEvaluatedEquals("ROW(1) IN (ROW(1))", "true"); + assertEvaluatedEquals("ROW(1) IN (ROW(2))", "false"); + assertEvaluatedEquals("ROW(1) IN (ROW(2), ROW(1), ROW(2))", "true"); + assertEvaluatedEquals("ROW(1) IN (null)", "null"); + assertEvaluatedEquals("ROW(1) IN (null, ROW(1))", "true"); + assertEvaluatedEquals("ROW(1, null) IN (ROW(2, null), null)", "null"); + assertEvaluatedEquals("ROW(null) IN (ROW(null))", "null"); + assertEvaluatedEquals("ROW(1) IN (ROW(null))", "null"); + assertEvaluatedEquals("ROW(null) IN (ROW(1))", "null"); + assertEvaluatedEquals("ROW(1, null) IN (ROW(1, null))", "null"); + assertEvaluatedEquals("ROW(1, null) IN (ROW(2, null))", "false"); + assertEvaluatedEquals("ROW(1, null) IN (ROW(1, null), ROW(2, null))", "null"); + assertEvaluatedEquals("ROW(1, null) IN (ROW(1, null), ROW(2, null), ROW(1, null))", "null"); + + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[1]))", "true"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[1]) IN (null)", "null"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[1]) IN (null, MAP(ARRAY[1], ARRAY[1]))", "true"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", "false"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]), null)", "null"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", "false"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[null]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[null]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[1]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", "false"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]))", "false"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]))", "null"); + assertEvaluatedEquals("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]), MAP(ARRAY[1, 2], ARRAY[1, null]))", "null"); + } + @Test public void testCurrentTimestamp() { @@ -829,6 +884,10 @@ public void testSearchCase() "when true then 2.2 " + "end", "2.2"); + + assertOptimizedEquals("case when ARRAY[CAST(1 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'matched'"); + assertOptimizedEquals("case when ARRAY[CAST(2 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'not_matched'"); + assertOptimizedEquals("case when ARRAY[CAST(null AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'not_matched'"); } @Test @@ -1061,6 +1120,10 @@ public void testSimpleCase() "when true then 2.2 " + "end", "2.2"); + + assertOptimizedEquals("case ARRAY[CAST(1 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'matched'"); + assertOptimizedEquals("case ARRAY[CAST(2 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'not_matched'"); + assertOptimizedEquals("case ARRAY[CAST(null AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", "'not_matched'"); } @Test @@ -1378,7 +1441,7 @@ private static Object optimize(@Language("SQL") String expression) Expression parsedExpression = FunctionAssertions.createExpression(expression, METADATA, SYMBOL_TYPES); - Map, Type> expressionTypes = getExpressionTypes(TEST_SESSION, METADATA, SQL_PARSER, SYMBOL_TYPES, parsedExpression, emptyList()); + Map, Type> expressionTypes = getExpressionTypes(TEST_SESSION, METADATA, SQL_PARSER, SYMBOL_TYPES, parsedExpression, emptyList(), WarningCollector.NOOP); ExpressionInterpreter interpreter = expressionOptimizer(parsedExpression, METADATA, TEST_SESSION, expressionTypes); return interpreter.optimize(symbol -> { switch (symbol.getName().toLowerCase(ENGLISH)) { @@ -1412,6 +1475,11 @@ private static Object optimize(@Language("SQL") String expression) }); } + private static void assertEvaluatedEquals(@Language("SQL") String actual, @Language("SQL") String expected) + { + assertEquals(evaluate(actual), evaluate(expected)); + } + private static Object evaluate(String expression) { assertRoundTrip(expression); @@ -1430,7 +1498,7 @@ private static void assertRoundTrip(String expression) private static Object evaluate(Expression expression) { - Map, Type> expressionTypes = getExpressionTypes(TEST_SESSION, METADATA, SQL_PARSER, SYMBOL_TYPES, expression, emptyList()); + Map, Type> expressionTypes = getExpressionTypes(TEST_SESSION, METADATA, SQL_PARSER, SYMBOL_TYPES, expression, emptyList(), WarningCollector.NOOP); ExpressionInterpreter interpreter = expressionInterpreter(expression, METADATA, TEST_SESSION, expressionTypes); return interpreter.evaluate(); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/TestSqlEnvironmentConfig.java b/presto-main/src/test/java/com/facebook/presto/sql/TestSqlEnvironmentConfig.java index ff4a077948bc6..953af44d2fca5 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/TestSqlEnvironmentConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/TestSqlEnvironmentConfig.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql; +import com.facebook.presto.sql.parser.ParsingException; import com.google.common.collect.ImmutableMap; import org.testng.annotations.Test; @@ -44,7 +45,7 @@ public void testExplicitPropertyMappings() assertFullMapping(properties, expected); } - @Test (expectedExceptions = RuntimeException.class) + @Test(expectedExceptions = ParsingException.class, expectedExceptionsMessageRegExp = "\\Qline 1:9: mismatched input '.'. Expecting: ',', \\E") public void testInvalidPath() { SqlEnvironmentConfig config = new SqlEnvironmentConfig().setPath("too.many.qualifiers"); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/TestSqlToRowExpressionTranslator.java b/presto-main/src/test/java/com/facebook/presto/sql/TestSqlToRowExpressionTranslator.java index 4315268533e93..1f2bcdaa6ae4b 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/TestSqlToRowExpressionTranslator.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/TestSqlToRowExpressionTranslator.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.spi.type.Type; @@ -112,7 +113,8 @@ private Map, Type> getExpressionTypes(Expression expression) TEST_SESSION, TypeProvider.empty(), emptyList(), - node -> new IllegalStateException("Expected node: %s" + node), + node -> new IllegalStateException("Unexpected node: %s" + node), + WarningCollector.NOOP, false); expressionAnalyzer.analyze(expression, Scope.create()); return expressionAnalyzer.getExpressionTypes(); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java index d5033e21e94ba..81a7dbee7b755 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestAnalyzer.java @@ -21,6 +21,7 @@ import com.facebook.presto.connector.system.SystemConnector; import com.facebook.presto.execution.QueryManagerConfig; import com.facebook.presto.execution.TaskManagerConfig; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.MemoryManagerConfig; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.CatalogManager; @@ -1459,6 +1460,10 @@ public void testQuantifiedComparisonExpression() // HLL is neither orderable nor comparable assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS HyperLogLog) < ALL (VALUES cast(NULL AS HyperLogLog))"); assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS HyperLogLog) = ANY (VALUES cast(NULL AS HyperLogLog))"); + + // qdigest is neither orderable nor comparable + assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS qdigest(double)) < ALL (VALUES cast(NULL AS qdigest(double)))"); + assertFails(TYPE_MISMATCH, "SELECT cast(NULL AS qdigest(double)) = ANY (VALUES cast(NULL AS qdigest(double)))"); } @Test @@ -1504,8 +1509,8 @@ public void setup() catalogManager.registerCatalog(createTestingCatalog(THIRD_CATALOG, THIRD_CONNECTOR_ID)); SchemaTableName table1 = new SchemaTableName("s1", "t1"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table1, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table1, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT), new ColumnMetadata("c", BIGINT), @@ -1513,15 +1518,15 @@ public void setup() false)); SchemaTableName table2 = new SchemaTableName("s1", "t2"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table2, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table2, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT))), false)); SchemaTableName table3 = new SchemaTableName("s1", "t3"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table3, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table3, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT), new ColumnMetadata("x", BIGINT, null, true))), @@ -1529,23 +1534,23 @@ public void setup() // table in different catalog SchemaTableName table4 = new SchemaTableName("s2", "t4"); - inSetupTransaction(session -> metadata.createTable(session, SECOND_CATALOG, new ConnectorTableMetadata(table4, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, SECOND_CATALOG, + new ConnectorTableMetadata(table4, ImmutableList.of( new ColumnMetadata("a", BIGINT))), false)); // table with a hidden column SchemaTableName table5 = new SchemaTableName("s1", "t5"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table5, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table5, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT, null, true))), false)); // table with a varchar column SchemaTableName table6 = new SchemaTableName("s1", "t6"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table6, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table6, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", VARCHAR), new ColumnMetadata("c", BIGINT), @@ -1554,8 +1559,8 @@ public void setup() // table with bigint, double, array of bigints and array of doubles column SchemaTableName table7 = new SchemaTableName("s1", "t7"); - inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, new ConnectorTableMetadata(table7, - ImmutableList.of( + inSetupTransaction(session -> metadata.createTable(session, TPCH_CATALOG, + new ConnectorTableMetadata(table7, ImmutableList.of( new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", DOUBLE), new ColumnMetadata("c", new ArrayType(BIGINT)), @@ -1629,7 +1634,8 @@ private static Analyzer createAnalyzer(Session session, Metadata metadata) SQL_PARSER, new AllowAllAccessControl(), Optional.empty(), - emptyList()); + emptyList(), + WarningCollector.NOOP); } private void analyze(@Language("SQL") String query) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java index 1afd736040d78..45c68e3d62ae7 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestFeaturesConfig.java @@ -15,6 +15,7 @@ import com.facebook.presto.operator.aggregation.arrayagg.ArrayAggGroupImplementation; import com.facebook.presto.operator.aggregation.histogram.HistogramGroupImplementation; +import com.facebook.presto.operator.aggregation.multimapagg.MultimapAggGroupImplementation; import com.google.common.collect.ImmutableMap; import io.airlift.configuration.ConfigurationFactory; import io.airlift.configuration.testing.ConfigAssertions; @@ -37,7 +38,6 @@ import static io.airlift.units.DataSize.Unit.GIGABYTE; import static io.airlift.units.DataSize.Unit.KILOBYTE; import static io.airlift.units.DataSize.Unit.MEGABYTE; -import static io.airlift.units.DataSize.succinctBytes; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -52,7 +52,9 @@ public void testDefaults() .setNetworkCostWeight(15) .setDistributedIndexJoinsEnabled(false) .setJoinDistributionType(PARTITIONED) + .setJoinMaxBroadcastTableSize(null) .setGroupedExecutionForAggregationEnabled(false) + .setDynamicScheduleForGroupedExecutionEnabled(false) .setConcurrentLifespansPerTask(0) .setFastInequalityJoins(true) .setColocatedJoinsEnabled(false) @@ -83,10 +85,11 @@ public void testDefaults() .setLegacyLogFunction(false) .setIterativeOptimizerEnabled(true) .setIterativeOptimizerTimeout(new Duration(3, MINUTES)) - .setEnableNewStatsCalculator(true) + .setEnableStatsCalculator(true) + .setIgnoreStatsCalculatorFailures(true) + .setDefaultFilterFactorEnabled(false) .setExchangeCompressionEnabled(false) .setLegacyTimestamp(true) - .setLegacyRoundNBigint(false) .setLegacyRowFieldOrdinalAccess(false) .setLegacyCharToVarcharCoercion(false) .setEnableIntermediateAggregations(false) @@ -98,12 +101,13 @@ public void testDefaults() .setFilterAndProjectMinOutputPageRowCount(256) .setUseMarkDistinct(true) .setPreferPartialAggregation(true) + .setOptimizeTopNRowNumber(true) .setHistogramGroupImplementation(HistogramGroupImplementation.NEW) .setArrayAggGroupImplementation(ArrayAggGroupImplementation.NEW) + .setMultimapAggGroupImplementation(MultimapAggGroupImplementation.NEW) .setDistributedSortEnabled(true) .setMaxGroupingSets(2048) - .setLegacyUnnestArrayRows(false) - .setPreAllocateMemoryThreshold(succinctBytes(0))); + .setLegacyUnnestArrayRows(false)); } @Test @@ -115,17 +119,20 @@ public void testExplicitPropertyMappings() .put("network-cost-weight", "0.2") .put("experimental.iterative-optimizer-enabled", "false") .put("experimental.iterative-optimizer-timeout", "10s") - .put("experimental.enable-new-stats-calculator", "false") + .put("experimental.enable-stats-calculator", "false") + .put("optimizer.ignore-stats-calculator-failures", "false") + .put("optimizer.default-filter-factor-enabled", "true") .put("deprecated.legacy-array-agg", "true") .put("deprecated.legacy-log-function", "true") .put("deprecated.group-by-uses-equal", "true") .put("deprecated.legacy-map-subscript", "true") - .put("deprecated.legacy-round-n-bigint", "true") .put("deprecated.legacy-row-field-ordinal-access", "true") .put("deprecated.legacy-char-to-varchar-coercion", "true") .put("distributed-index-joins-enabled", "true") .put("join-distribution-type", "BROADCAST") + .put("join-max-broadcast-table-size", "42GB") .put("grouped-execution-for-aggregation-enabled", "true") + .put("dynamic-schedule-for-grouped-execution", "true") .put("concurrent-lifespans-per-task", "1") .put("fast-inequality-joins", "false") .put("colocated-joins-enabled", "true") @@ -161,12 +168,13 @@ public void testExplicitPropertyMappings() .put("experimental.filter-and-project-min-output-page-row-count", "2048") .put("histogram.implementation", "LEGACY") .put("arrayagg.implementation", "LEGACY") + .put("multimapagg.implementation", "LEGACY") .put("optimizer.use-mark-distinct", "false") .put("optimizer.prefer-partial-aggregation", "false") + .put("optimizer.optimize-top-n-row-number", "false") .put("distributed-sort", "false") .put("analyzer.max-grouping-sets", "2047") .put("deprecated.legacy-unnest-array-rows", "true") - .put("experimental.preallocate-memory-threshold", "5TB") .build(); FeaturesConfig expected = new FeaturesConfig() @@ -175,10 +183,13 @@ public void testExplicitPropertyMappings() .setNetworkCostWeight(0.2) .setIterativeOptimizerEnabled(false) .setIterativeOptimizerTimeout(new Duration(10, SECONDS)) - .setEnableNewStatsCalculator(false) + .setEnableStatsCalculator(false) + .setIgnoreStatsCalculatorFailures(false) .setDistributedIndexJoinsEnabled(true) .setJoinDistributionType(BROADCAST) + .setJoinMaxBroadcastTableSize(new DataSize(42, GIGABYTE)) .setGroupedExecutionForAggregationEnabled(true) + .setDynamicScheduleForGroupedExecutionEnabled(true) .setConcurrentLifespansPerTask(1) .setFastInequalityJoins(false) .setColocatedJoinsEnabled(true) @@ -210,7 +221,6 @@ public void testExplicitPropertyMappings() .setLegacyLogFunction(true) .setExchangeCompressionEnabled(true) .setLegacyTimestamp(false) - .setLegacyRoundNBigint(true) .setLegacyRowFieldOrdinalAccess(true) .setLegacyCharToVarcharCoercion(true) .setEnableIntermediateAggregations(true) @@ -221,12 +231,14 @@ public void testExplicitPropertyMappings() .setFilterAndProjectMinOutputPageRowCount(2048) .setUseMarkDistinct(false) .setPreferPartialAggregation(false) + .setOptimizeTopNRowNumber(false) .setHistogramGroupImplementation(HistogramGroupImplementation.LEGACY) .setArrayAggGroupImplementation(ArrayAggGroupImplementation.LEGACY) + .setMultimapAggGroupImplementation(MultimapAggGroupImplementation.LEGACY) .setDistributedSortEnabled(false) .setMaxGroupingSets(2047) .setLegacyUnnestArrayRows(true) - .setPreAllocateMemoryThreshold(DataSize.valueOf("5TB")); + .setDefaultFilterFactorEnabled(true); assertFullMapping(properties, expected); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestScope.java b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestScope.java index 0bd2008529dcb..416df9c1ab63e 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestScope.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/analyzer/TestScope.java @@ -32,12 +32,12 @@ public void test() { Scope root = Scope.create(); - Field outerColumn1 = Field.newQualified(QualifiedName.of("outer", "column1"), Optional.of("c1"), BIGINT, false, Optional.empty(), false); - Field outerColumn2 = Field.newQualified(QualifiedName.of("outer", "column2"), Optional.of("c2"), BIGINT, false, Optional.empty(), false); + Field outerColumn1 = Field.newQualified(QualifiedName.of("outer", "column1"), Optional.of("c1"), BIGINT, false, Optional.empty(), Optional.empty(), false); + Field outerColumn2 = Field.newQualified(QualifiedName.of("outer", "column2"), Optional.of("c2"), BIGINT, false, Optional.empty(), Optional.empty(), false); Scope outer = Scope.builder().withParent(root).withRelationType(RelationId.anonymous(), new RelationType(outerColumn1, outerColumn2)).build(); - Field innerColumn2 = Field.newQualified(QualifiedName.of("inner", "column2"), Optional.of("c2"), BIGINT, false, Optional.empty(), false); - Field innerColumn3 = Field.newQualified(QualifiedName.of("inner", "column3"), Optional.of("c3"), BIGINT, false, Optional.empty(), false); + Field innerColumn2 = Field.newQualified(QualifiedName.of("inner", "column2"), Optional.of("c2"), BIGINT, false, Optional.empty(), Optional.empty(), false); + Field innerColumn3 = Field.newQualified(QualifiedName.of("inner", "column3"), Optional.of("c3"), BIGINT, false, Optional.empty(), Optional.empty(), false); Scope inner = Scope.builder().withOuterQueryParent(outer).withRelationType(RelationId.anonymous(), new RelationType(innerColumn2, innerColumn3)).build(); Expression c1 = name("c1"); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/gen/BenchmarkPageProcessor.java b/presto-main/src/test/java/com/facebook/presto/sql/gen/BenchmarkPageProcessor.java index b2d7c534674fc..10a504df5d449 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/gen/BenchmarkPageProcessor.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/gen/BenchmarkPageProcessor.java @@ -46,6 +46,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.Signature.internalOperator; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -98,7 +99,12 @@ public Page handCoded() @Benchmark public List> compiled() { - return ImmutableList.copyOf(compiledProcessor.process(null, new DriverYieldSignal(), inputPage)); + return ImmutableList.copyOf( + compiledProcessor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + inputPage)); } public static void main(String[] args) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/gen/InCodeGeneratorBenchmark.java b/presto-main/src/test/java/com/facebook/presto/sql/gen/InCodeGeneratorBenchmark.java index 70dfae4f34761..6345193d1c022 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/gen/InCodeGeneratorBenchmark.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/gen/InCodeGeneratorBenchmark.java @@ -45,6 +45,7 @@ import java.util.Random; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; @@ -138,7 +139,12 @@ public void setup() @Benchmark public List> benchmark() { - return ImmutableList.copyOf(processor.process(SESSION, new DriverYieldSignal(), inputPage)); + return ImmutableList.copyOf( + processor.process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + inputPage)); } public static void main(String[] args) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/gen/PageProcessorBenchmark.java b/presto-main/src/test/java/com/facebook/presto/sql/gen/PageProcessorBenchmark.java index 2cb7094b5bd75..fd1843c982c5a 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/gen/PageProcessorBenchmark.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/gen/PageProcessorBenchmark.java @@ -15,6 +15,7 @@ import com.facebook.presto.SequencePageBuilder; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.operator.DriverYieldSignal; @@ -59,6 +60,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.operator.scalar.FunctionAssertions.createExpression; @@ -138,7 +140,12 @@ public Page rowOriented() @Benchmark public List> columnOriented() { - return ImmutableList.copyOf(pageProcessor.process(null, new DriverYieldSignal(), inputPage)); + return ImmutableList.copyOf( + pageProcessor.process( + null, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + inputPage)); } private RowExpression getFilter(Type type) @@ -181,7 +188,7 @@ private RowExpression rowExpression(String expression, Type type) } Map types = builder.build(); - Map, Type> expressionTypes = getExpressionTypesFromInput(TEST_SESSION, METADATA, SQL_PARSER, types, inputReferenceExpression, emptyList()); + Map, Type> expressionTypes = getExpressionTypesFromInput(TEST_SESSION, METADATA, SQL_PARSER, types, inputReferenceExpression, emptyList(), WarningCollector.NOOP); return SqlToRowExpressionTranslator.translate(inputReferenceExpression, SCALAR, expressionTypes, METADATA.getFunctionRegistry(), METADATA.getTypeManager(), TEST_SESSION, true); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/gen/TestExpressionCompiler.java b/presto-main/src/test/java/com/facebook/presto/sql/gen/TestExpressionCompiler.java index 893e198311dd7..cd9bc6a05e127 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/gen/TestExpressionCompiler.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/gen/TestExpressionCompiler.java @@ -22,6 +22,7 @@ import com.facebook.presto.operator.scalar.MathFunctions; import com.facebook.presto.operator.scalar.StringFunctions; import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.type.ArrayType; import com.facebook.presto.spi.type.SqlDecimal; import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; import com.facebook.presto.spi.type.SqlVarbinary; @@ -74,7 +75,6 @@ import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; @@ -84,6 +84,7 @@ import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; import static com.facebook.presto.type.JsonType.JSON; import static com.facebook.presto.type.UnknownType.UNKNOWN; +import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static com.facebook.presto.util.StructuralTestUtil.mapType; import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService; @@ -93,7 +94,9 @@ import static java.lang.Math.cos; import static java.lang.Runtime.getRuntime; import static java.lang.String.format; +import static java.util.Collections.singletonList; import static java.util.concurrent.Executors.newFixedThreadPool; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.stream.Collectors.joining; import static java.util.stream.IntStream.range; import static org.joda.time.DateTimeZone.UTC; @@ -823,7 +826,7 @@ public void testTryCast() assertExecute("try_cast('foo' as varchar)", VARCHAR, "foo"); assertExecute("try_cast('foo' as bigint)", BIGINT, null); assertExecute("try_cast('foo' as integer)", INTEGER, null); - assertExecute("try_cast('2001-08-22' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 8, 22, 0, 0, 0, 0, UTC, UTC_KEY, TEST_SESSION)); + assertExecute("try_cast('2001-08-22' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 8, 22, 0, 0, 0, 0, TEST_SESSION)); assertExecute("try_cast(bound_string as bigint)", BIGINT, null); assertExecute("try_cast(cast(null as varchar) as bigint)", BIGINT, null); assertExecute("try_cast(bound_long / 13 as bigint)", BIGINT, 94L); @@ -984,6 +987,10 @@ else if (secondTest != null && secondTest.equals(value)) { } } + assertExecute("case ARRAY[CAST(1 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "matched"); + assertExecute("case ARRAY[CAST(2 AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + assertExecute("case ARRAY[CAST(null AS BIGINT)] when ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + Futures.allAsList(futures).get(); } @@ -1040,6 +1047,10 @@ else if (secondTest != null && value == secondTest.doubleValue()) { } } + assertExecute("case when ARRAY[CAST(1 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "matched"); + assertExecute("case when ARRAY[CAST(2 AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + assertExecute("case when ARRAY[CAST(null AS BIGINT)] = ARRAY[CAST(1 AS BIGINT)] then 'matched' else 'not_matched' end", createVarcharType(11), "not_matched"); + Futures.allAsList(futures).get(); } @@ -1266,6 +1277,55 @@ public void testHugeIn() Futures.allAsList(futures).get(); } + @Test + public void testInComplexTypes() + { + assertExecute("ARRAY[1] IN (ARRAY[1])", BOOLEAN, true); + assertExecute("ARRAY[1] IN (ARRAY[2])", BOOLEAN, false); + assertExecute("ARRAY[1] IN (ARRAY[2], ARRAY[1])", BOOLEAN, true); + assertExecute("ARRAY[1] IN (null)", BOOLEAN, null); + assertExecute("ARRAY[1] IN (null, ARRAY[1])", BOOLEAN, true); + assertExecute("ARRAY[1, 2, null] IN (ARRAY[2, null], ARRAY[1, null])", BOOLEAN, false); + assertExecute("ARRAY[1, null] IN (ARRAY[2, null], null)", BOOLEAN, null); + assertExecute("ARRAY[null] IN (ARRAY[null])", BOOLEAN, null); + assertExecute("ARRAY[1] IN (ARRAY[null])", BOOLEAN, null); + assertExecute("ARRAY[null] IN (ARRAY[1])", BOOLEAN, null); + assertExecute("ARRAY[1, null] IN (ARRAY[1, null])", BOOLEAN, null); + assertExecute("ARRAY[1, null] IN (ARRAY[2, null])", BOOLEAN, false); + assertExecute("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null])", BOOLEAN, null); + assertExecute("ARRAY[1, null] IN (ARRAY[1, null], ARRAY[2, null], ARRAY[1, null])", BOOLEAN, null); + + assertExecute("ROW(1) IN (ROW(1))", BOOLEAN, true); + assertExecute("ROW(1) IN (ROW(2))", BOOLEAN, false); + assertExecute("ROW(1) IN (ROW(2), ROW(1), ROW(2))", BOOLEAN, true); + assertExecute("ROW(1) IN (null)", BOOLEAN, null); + assertExecute("ROW(1) IN (null, ROW(1))", BOOLEAN, true); + assertExecute("ROW(1, null) IN (ROW(2, null), null)", BOOLEAN, null); + assertExecute("ROW(null) IN (ROW(null))", BOOLEAN, null); + assertExecute("ROW(1) IN (ROW(null))", BOOLEAN, null); + assertExecute("ROW(null) IN (ROW(1))", BOOLEAN, null); + assertExecute("ROW(1, null) IN (ROW(1, null))", BOOLEAN, null); + assertExecute("ROW(1, null) IN (ROW(2, null))", BOOLEAN, false); + assertExecute("ROW(1, null) IN (ROW(1, null), ROW(2, null))", BOOLEAN, null); + assertExecute("ROW(1, null) IN (ROW(1, null), ROW(2, null), ROW(1, null))", BOOLEAN, null); + + assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, true); + assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (null)", BOOLEAN, null); + assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (null, MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, true); + assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, false); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]), null)", BOOLEAN, null); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", BOOLEAN, false); + assertExecute("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[null]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1], ARRAY[1]) IN (MAP(ARRAY[1], ARRAY[null]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1], ARRAY[null]) IN (MAP(ARRAY[1], ARRAY[1]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 3], ARRAY[1, null]))", BOOLEAN, false); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[2, null]))", BOOLEAN, false); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]))", BOOLEAN, null); + assertExecute("MAP(ARRAY[1, 2], ARRAY[1, null]) IN (MAP(ARRAY[1, 2], ARRAY[1, null]), MAP(ARRAY[1, 2], ARRAY[2, null]), MAP(ARRAY[1, 2], ARRAY[1, null]))", BOOLEAN, null); + } + @Test public void testFunctionCall() throws Exception @@ -1402,7 +1462,14 @@ public void testExtract() millis = left.getMillis(); expected = callExtractFunction(TEST_SESSION.toConnectorSession(), millis, field); } - assertExecute(generateExpression("extract(" + field.toString() + " from from_unixtime(%s / 1000.0E0, 0, 0))", millis), BIGINT, expected); + DateTimeZone zone = getDateTimeZone(TEST_SESSION.getTimeZoneKey()); + long zoneOffsetMinutes = millis != null ? MILLISECONDS.toMinutes(zone.getOffset(millis)) : 0; + String expressionPattern = format( + "extract(%s from from_unixtime(%%s / 1000.0E0, %s, %s))", + field, + zoneOffsetMinutes / 60, + zoneOffsetMinutes % 60); + assertExecute(generateExpression(expressionPattern, millis), BIGINT, expected); } } @@ -1519,6 +1586,9 @@ public void testNullif() assertExecute("nullif(NULL, 2)", UNKNOWN, null); assertExecute("nullif(2, NULL)", INTEGER, 2); assertExecute("nullif(BIGINT '2', NULL)", BIGINT, 2L); + assertExecute("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(1 AS BIGINT)])", new ArrayType(BIGINT), null); + assertExecute("nullif(ARRAY[CAST(1 AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)])", new ArrayType(BIGINT), ImmutableList.of(1L)); + assertExecute("nullif(ARRAY[CAST(NULL AS BIGINT)], ARRAY[CAST(NULL AS BIGINT)])", new ArrayType(BIGINT), singletonList(null)); // Test coercion in which the CAST function takes ConnectorSession (e.g. MapToMapCast) assertExecute("nullif(" + diff --git a/presto-main/src/test/java/com/facebook/presto/sql/gen/TestPageFunctionCompiler.java b/presto-main/src/test/java/com/facebook/presto/sql/gen/TestPageFunctionCompiler.java index f7adef19977c7..83a9a03d7ce1e 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/gen/TestPageFunctionCompiler.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/gen/TestPageFunctionCompiler.java @@ -24,11 +24,9 @@ import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.sql.relational.CallExpression; import com.google.common.collect.ImmutableList; -import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Optional; -import java.util.concurrent.ScheduledExecutorService; import java.util.function.Supplier; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; @@ -39,8 +37,6 @@ import static com.facebook.presto.sql.relational.Expressions.constant; import static com.facebook.presto.sql.relational.Expressions.field; import static com.facebook.presto.testing.TestingConnectorSession.SESSION; -import static io.airlift.concurrent.Threads.daemonThreadsNamed; -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotSame; import static org.testng.Assert.assertSame; @@ -55,16 +51,8 @@ public class TestPageFunctionCompiler field(0, BIGINT), constant(10L, BIGINT)); - private final ScheduledExecutorService executor = newSingleThreadScheduledExecutor(daemonThreadsNamed("test-%s")); - - @DataProvider(name = "forceYield") - public static Object[][] forceYield() - { - return new Object[][] {{true}, {false}}; - } - - @Test(dataProvider = "forceYield") - public void testFailureDoesNotCorruptFutureResults(boolean forceYield) + @Test + public void testFailureDoesNotCorruptFutureResults() { PageFunctionCompiler functionCompiler = new PageFunctionCompiler(createTestMetadataManager(), 0); @@ -73,26 +61,13 @@ public void testFailureDoesNotCorruptFutureResults(boolean forceYield) // process good page and verify we got the expected number of result rows Page goodPage = createLongBlockPage(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); - // yield 10 times - Block goodResult; - if (forceYield) { - goodResult = projectWithYield(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount()), 10); - } - else { - goodResult = projectWithoutYield(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount())); - } + Block goodResult = project(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount())); assertEquals(goodPage.getPositionCount(), goodResult.getPositionCount()); // addition will throw due to integer overflow Page badPage = createLongBlockPage(0, 1, 2, 3, 4, Long.MAX_VALUE); try { - // yield 6 times then fail - if (forceYield) { - projectWithYield(projection, badPage, SelectedPositions.positionsRange(0, 100), 6); - } - else { - projectWithoutYield(projection, badPage, SelectedPositions.positionsRange(0, 100)); - } + project(projection, badPage, SelectedPositions.positionsRange(0, 100)); fail("expected exception"); } catch (PrestoException e) { @@ -101,12 +76,7 @@ public void testFailureDoesNotCorruptFutureResults(boolean forceYield) // running the good page should still work // if block builder in generated code was not reset properly, we could get junk results after the failure - if (forceYield) { - goodResult = projectWithYield(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount()), 10); - } - else { - goodResult = projectWithoutYield(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount())); - } + goodResult = project(projection, goodPage, SelectedPositions.positionsRange(0, goodPage.getPositionCount())); assertEquals(goodPage.getPositionCount(), goodResult.getPositionCount()); } @@ -157,29 +127,7 @@ public void testCache() noCacheCompiler.compileProjection(ADD_10_EXPRESSION, Optional.of("hint2"))); } - private Block projectWithYield(PageProjection projection, Page page, SelectedPositions selectedPositions, int expectedYields) - { - DriverYieldSignal yieldSignal = new DriverYieldSignal(); - Work work = projection.project(SESSION, yieldSignal, page, selectedPositions); - - boolean processed = false; - for (int i = 0; i < 1000; i++) { - yieldSignal.setWithDelay(1, executor); - yieldSignal.forceYieldForTesting(); - if (work.process()) { - processed = true; - assertEquals(i, expectedYields); - break; - } - yieldSignal.reset(); - } - if (!processed) { - fail("result is not present"); - } - return work.getResult(); - } - - private Block projectWithoutYield(PageProjection projection, Page page, SelectedPositions selectedPositions) + private Block project(PageProjection projection, Page page, SelectedPositions selectedPositions) { Work work = projection.project(SESSION, new DriverYieldSignal(), page, selectedPositions); assertTrue(work.process()); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/BenchmarkPlanner.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/BenchmarkPlanner.java index 01a62cf2d0bac..ab64cfad0198e 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/BenchmarkPlanner.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/BenchmarkPlanner.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.testing.LocalQueryRunner; import com.facebook.presto.tpch.ColumnNaming; import com.facebook.presto.tpch.TpchConnectorFactory; @@ -118,7 +119,7 @@ public List planQueries(BenchmarkData benchmarkData) return benchmarkData.queryRunner.inTransaction(transactionSession -> { LogicalPlanner.Stage stage = LogicalPlanner.Stage.valueOf(benchmarkData.stage.toUpperCase()); return benchmarkData.queries.stream() - .map(query -> benchmarkData.queryRunner.createPlan(transactionSession, query, stage, false)) + .map(query -> benchmarkData.queryRunner.createPlan(transactionSession, query, stage, false, WarningCollector.NOOP)) .collect(toImmutableList()); }); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestEffectivePredicateExtractor.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestEffectivePredicateExtractor.java index 2a989348e33b2..206f0ee482448 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestEffectivePredicateExtractor.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestEffectivePredicateExtractor.java @@ -134,10 +134,7 @@ public void setUp() newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), - assignments, - Optional.empty(), - TupleDomain.all(), - null); + assignments); expressionNormalizer = new ExpressionIdentityNormalizer(); } @@ -330,10 +327,7 @@ public void testTableScan() newId(), DUAL_TABLE_HANDLE, ImmutableList.copyOf(assignments.keySet()), - assignments, - Optional.empty(), - TupleDomain.all(), - null); + assignments); Expression effectivePredicate = effectivePredicateExtractor.extract(node); assertEquals(effectivePredicate, BooleanLiteral.TRUE_LITERAL); @@ -344,7 +338,7 @@ public void testTableScan() assignments, Optional.of(TESTING_TABLE_LAYOUT), TupleDomain.none(), - null); + TupleDomain.all()); effectivePredicate = effectivePredicateExtractor.extract(node); assertEquals(effectivePredicate, FALSE_LITERAL); @@ -355,7 +349,7 @@ public void testTableScan() assignments, Optional.of(TESTING_TABLE_LAYOUT), TupleDomain.withColumnDomains(ImmutableMap.of(scanAssignments.get(A), Domain.singleValue(BIGINT, 1L))), - null); + TupleDomain.all()); effectivePredicate = effectivePredicateExtractor.extract(node); assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts(equals(bigintLiteral(1L), AE))); @@ -368,7 +362,7 @@ public void testTableScan() TupleDomain.withColumnDomains(ImmutableMap.of( scanAssignments.get(A), Domain.singleValue(BIGINT, 1L), scanAssignments.get(B), Domain.singleValue(BIGINT, 2L))), - null); + TupleDomain.all()); effectivePredicate = effectivePredicateExtractor.extract(node); assertEquals(normalizeConjuncts(effectivePredicate), normalizeConjuncts(equals(bigintLiteral(2L), BE), equals(bigintLiteral(1L), AE))); @@ -379,7 +373,7 @@ public void testTableScan() assignments, Optional.empty(), TupleDomain.all(), - null); + TupleDomain.all()); effectivePredicate = effectivePredicateExtractor.extract(node); assertEquals(effectivePredicate, BooleanLiteral.TRUE_LITERAL); } @@ -412,24 +406,10 @@ public void testInnerJoin() List criteria = criteriaBuilder.build(); Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); - TableScanNode leftScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(leftAssignments.keySet()), - leftAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode leftScan = tableScanNode(leftAssignments); Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); - TableScanNode rightScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(rightAssignments.keySet()), - rightAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode rightScan = tableScanNode(rightAssignments); FilterNode left = filter(leftScan, and( @@ -450,7 +430,7 @@ public void testInnerJoin() .addAll(left.getOutputSymbols()) .addAll(right.getOutputSymbols()) .build(), - Optional.empty(), + Optional.of(lessThanOrEqual(BE, EE)), Optional.empty(), Optional.empty(), Optional.empty()); @@ -464,7 +444,68 @@ public void testInnerJoin() equals(DE, EE), lessThan(FE, bigintLiteral(100)), equals(AE, DE), - equals(BE, EE))); + equals(BE, EE), + lessThanOrEqual(BE, EE))); + } + + @Test + public void testInnerJoinPropagatesPredicatesViaEquiConditions() + { + Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); + TableScanNode leftScan = tableScanNode(leftAssignments); + + Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); + TableScanNode rightScan = tableScanNode(rightAssignments); + + FilterNode left = filter(leftScan, equals(AE, bigintLiteral(10))); + + // predicates on "a" column should be propagated to output symbols via join equi conditions + PlanNode node = new JoinNode(newId(), + JoinNode.Type.INNER, + left, + rightScan, + ImmutableList.of(new JoinNode.EquiJoinClause(A, D)), + ImmutableList.builder() + .addAll(rightScan.getOutputSymbols()) + .build(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty()); + + Expression effectivePredicate = effectivePredicateExtractor.extract(node); + + assertEquals( + normalizeConjuncts(effectivePredicate), + normalizeConjuncts(equals(DE, bigintLiteral(10)))); + } + + @Test + public void testInnerJoinWithFalseFilter() + { + Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); + TableScanNode leftScan = tableScanNode(leftAssignments); + + Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); + TableScanNode rightScan = tableScanNode(rightAssignments); + + PlanNode node = new JoinNode(newId(), + JoinNode.Type.INNER, + leftScan, + rightScan, + ImmutableList.of(new JoinNode.EquiJoinClause(A, D)), + ImmutableList.builder() + .addAll(leftScan.getOutputSymbols()) + .addAll(rightScan.getOutputSymbols()) + .build(), + Optional.of(FALSE_LITERAL), + Optional.empty(), + Optional.empty(), + Optional.empty()); + + Expression effectivePredicate = effectivePredicateExtractor.extract(node); + + assertEquals(effectivePredicate, FALSE_LITERAL); } @Test @@ -476,24 +517,10 @@ public void testLeftJoin() List criteria = criteriaBuilder.build(); Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); - TableScanNode leftScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(leftAssignments.keySet()), - leftAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode leftScan = tableScanNode(leftAssignments); Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); - TableScanNode rightScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(rightAssignments.keySet()), - rightAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode rightScan = tableScanNode(rightAssignments); FilterNode left = filter(leftScan, and( @@ -536,24 +563,10 @@ public void testLeftJoinWithFalseInner() List criteria = ImmutableList.of(new JoinNode.EquiJoinClause(A, D)); Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); - TableScanNode leftScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(leftAssignments.keySet()), - leftAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode leftScan = tableScanNode(leftAssignments); Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); - TableScanNode rightScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(rightAssignments.keySet()), - rightAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode rightScan = tableScanNode(rightAssignments); FilterNode left = filter(leftScan, and( @@ -593,24 +606,10 @@ public void testRightJoin() List criteria = criteriaBuilder.build(); Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); - TableScanNode leftScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(leftAssignments.keySet()), - leftAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode leftScan = tableScanNode(leftAssignments); Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); - TableScanNode rightScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(rightAssignments.keySet()), - rightAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode rightScan = tableScanNode(rightAssignments); FilterNode left = filter(leftScan, and( @@ -653,24 +652,10 @@ public void testRightJoinWithFalseInner() List criteria = ImmutableList.of(new JoinNode.EquiJoinClause(A, D)); Map leftAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(A, B, C))); - TableScanNode leftScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(leftAssignments.keySet()), - leftAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode leftScan = tableScanNode(leftAssignments); Map rightAssignments = Maps.filterKeys(scanAssignments, Predicates.in(ImmutableList.of(D, E, F))); - TableScanNode rightScan = new TableScanNode( - newId(), - DUAL_TABLE_HANDLE, - ImmutableList.copyOf(rightAssignments.keySet()), - rightAssignments, - Optional.empty(), - TupleDomain.all(), - null); + TableScanNode rightScan = tableScanNode(rightAssignments); FilterNode left = filter(leftScan, FALSE_LITERAL); FilterNode right = filter(rightScan, @@ -718,6 +703,18 @@ public void testSemiJoin() normalizeConjuncts(and(greaterThan(AE, bigintLiteral(10)), lessThan(AE, bigintLiteral(100))))); } + private static TableScanNode tableScanNode(Map scanAssignments) + { + return new TableScanNode( + newId(), + DUAL_TABLE_HANDLE, + ImmutableList.copyOf(scanAssignments.keySet()), + scanAssignments, + Optional.empty(), + TupleDomain.all(), + TupleDomain.all()); + } + private static PlanNodeId newId() { return new PlanNodeId(UUID.randomUUID().toString()); @@ -746,6 +743,11 @@ private static ComparisonExpression lessThan(Expression expression1, Expression return new ComparisonExpression(ComparisonExpression.Operator.LESS_THAN, expression1, expression2); } + private static ComparisonExpression lessThanOrEqual(Expression expression1, Expression expression2) + { + return new ComparisonExpression(ComparisonExpression.Operator.LESS_THAN_OR_EQUAL, expression1, expression2); + } + private static ComparisonExpression greaterThan(Expression expression1, Expression expression2) { return new ComparisonExpression(ComparisonExpression.Operator.GREATER_THAN, expression1, expression2); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestGroupingOperationRewriter.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestGroupingOperationRewriter.java index ed191a2138f64..8c7e46abc6b2c 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestGroupingOperationRewriter.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestGroupingOperationRewriter.java @@ -14,9 +14,11 @@ package com.facebook.presto.sql.planner; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import org.testng.annotations.Test; import java.util.List; +import java.util.Set; import static com.facebook.presto.sql.planner.GroupingOperationRewriter.calculateGrouping; import static org.testng.Assert.assertEquals; @@ -32,10 +34,10 @@ public class TestGroupingOperationRewriter public void testGroupingOperationAllBitsSet() { List groupingOrdinals = ImmutableList.of(0, 4, 8); - List> groupingSetOrdinals = ImmutableList.of(ImmutableList.of(1), ImmutableList.of(7, 3, 1), ImmutableList.of(9, 1)); + List> groupingSetOrdinals = ImmutableList.of(ImmutableSet.of(1), ImmutableSet.of(7, 3, 1), ImmutableSet.of(9, 1)); - for (int groupId = 0; groupId < groupingSetOrdinals.size(); groupId++) { - assertEquals(calculateGrouping(groupId, groupingOrdinals, groupingSetOrdinals), 7L); + for (Set groupingSet : groupingSetOrdinals) { + assertEquals(calculateGrouping(groupingSet, groupingOrdinals), 7L); } } @@ -43,10 +45,10 @@ public void testGroupingOperationAllBitsSet() public void testGroupingOperationNoBitsSet() { List groupingOrdinals = ImmutableList.of(4, 6); - List> groupingSetOrdinals = ImmutableList.of(ImmutableList.of(4, 6)); + List> groupingSetOrdinals = ImmutableList.of(ImmutableSet.of(4, 6)); - for (int groupId = 0; groupId < groupingSetOrdinals.size(); groupId++) { - assertEquals(calculateGrouping(groupId, groupingOrdinals, groupingSetOrdinals), 0L); + for (Set groupingSet : groupingSetOrdinals) { + assertEquals(calculateGrouping(groupingSet, groupingOrdinals), 0L); } } @@ -54,22 +56,24 @@ public void testGroupingOperationNoBitsSet() public void testGroupingOperationSomeBitsSet() { List groupingOrdinals = ImmutableList.of(7, 2, 9, 3, 5); - List> groupingSetOrdinals = ImmutableList.of(ImmutableList.of(4, 2), ImmutableList.of(9, 7, 14), ImmutableList.of(5, 2, 7), ImmutableList.of(3)); + List> groupingSetOrdinals = ImmutableList.of(ImmutableSet.of(4, 2), ImmutableSet.of(9, 7, 14), ImmutableSet.of(5, 2, 7), ImmutableSet.of(3)); List expectedResults = ImmutableList.of(23L, 11L, 6L, 29L); for (int groupId = 0; groupId < groupingSetOrdinals.size(); groupId++) { - assertEquals(Long.valueOf(calculateGrouping(groupId, groupingOrdinals, groupingSetOrdinals)), expectedResults.get(groupId)); + Set groupingSet = groupingSetOrdinals.get(groupId); + assertEquals(Long.valueOf(calculateGrouping(groupingSet, groupingOrdinals)), expectedResults.get(groupId)); } } @Test public void testMoreThanThirtyTwoArguments() { - List> groupingSetOrdinals = ImmutableList.of(ImmutableList.of(20, 2, 13, 33, 40, 9, 14), ImmutableList.of(28, 4, 5, 29, 31, 10)); + List> groupingSetOrdinals = ImmutableList.of(ImmutableSet.of(20, 2, 13, 33, 40, 9, 14), ImmutableSet.of(28, 4, 5, 29, 31, 10)); List expectedResults = ImmutableList.of(822283861886L, 995358664191L); for (int groupId = 0; groupId < groupingSetOrdinals.size(); groupId++) { - assertEquals(Long.valueOf(calculateGrouping(groupId, fortyIntegers, groupingSetOrdinals)), expectedResults.get(groupId)); + Set groupingSet = groupingSetOrdinals.get(groupId); + assertEquals(Long.valueOf(calculateGrouping(groupingSet, fortyIntegers)), expectedResults.get(groupId)); } } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java index 79c82d2e95be3..81dc1e058b940 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestLogicalPlanner.java @@ -14,7 +14,6 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.Session; -import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; import com.facebook.presto.sql.planner.assertions.BasePlanTest; import com.facebook.presto.sql.planner.assertions.PlanMatchPattern; @@ -42,7 +41,6 @@ import org.testng.annotations.Test; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Predicate; @@ -53,7 +51,6 @@ import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_HASH_GENERATION; import static com.facebook.presto.spi.StandardErrorCode.SUBQUERY_MULTIPLE_ROWS; import static com.facebook.presto.spi.predicate.Domain.singleValue; -import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.createVarcharType; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.aggregation; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.any; @@ -166,13 +163,15 @@ public void testDistinctLimitOverInequalityJoin() @Test public void testDistinctOverConstants() { - assertPlan("SELECT count(*), count(distinct orderkey) FROM (SELECT * FROM orders WHERE orderkey = 1)", + assertPlan("SELECT count(*), count(distinct orderstatus) FROM (SELECT * FROM orders WHERE orderstatus = 'F')", anyTree( - markDistinct("is_distinct", ImmutableList.of("orderkey"), "hash", + markDistinct( + "is_distinct", + ImmutableList.of("orderstatus"), + "hash", anyTree( - project(ImmutableMap.of("hash", expression("combine_hash(bigint '0', coalesce(\"$operator$hash_code\"(orderkey), 0))")), - filter("orderkey = BIGINT '1'", - tableScan("orders", ImmutableMap.of("orderkey", "orderkey")))))))); + project(ImmutableMap.of("hash", expression("combine_hash(bigint '0', coalesce(\"$operator$hash_code\"(orderstatus), 0))")), + tableScan("orders", ImmutableMap.of("orderstatus", "orderstatus"))))))); } @Test @@ -277,23 +276,27 @@ public void testUncorrelatedSubqueries() @Test public void testPushDownJoinConditionConjunctsToInnerSideBasedOnInheritedPredicate() { - Map tableScanConstraint = ImmutableMap.builder() - .put("name", singleValue(createVarcharType(25), utf8Slice("blah"))) - .build(); - assertPlan( "SELECT nationkey FROM nation LEFT OUTER JOIN region " + "ON nation.regionkey = region.regionkey and nation.name = region.name WHERE nation.name = 'blah'", anyTree( join(LEFT, ImmutableList.of(equiJoinClause("NATION_NAME", "REGION_NAME"), equiJoinClause("NATION_REGIONKEY", "REGION_REGIONKEY")), anyTree( - constrainedTableScan("nation", tableScanConstraint, ImmutableMap.of( - "NATION_NAME", "name", - "NATION_REGIONKEY", "regionkey"))), + filter("NATION_NAME = CAST ('blah' AS VARCHAR(25))", + constrainedTableScan( + "nation", + ImmutableMap.of(), + ImmutableMap.of( + "NATION_NAME", "name", + "NATION_REGIONKEY", "regionkey")))), anyTree( - constrainedTableScan("region", tableScanConstraint, ImmutableMap.of( - "REGION_NAME", "name", - "REGION_REGIONKEY", "regionkey")))))); + filter("REGION_NAME = CAST ('blah' AS VARCHAR(25))", + constrainedTableScan( + "region", + ImmutableMap.of(), + ImmutableMap.of( + "REGION_NAME", "name", + "REGION_REGIONKEY", "regionkey"))))))); } @Test @@ -372,8 +375,9 @@ public void testSubqueryPruning() .replaceAll(subqueries) .forEach(this::assertPlanContainsNoApplyOrAnyJoin); - // TODO enable when pruning apply nodes works for this kind of query - // assertPlanContainsNoApplyOrAnyJoin("SELECT * FROM orders WHERE true OR " + subquery); + queryTemplate("SELECT * FROM orders WHERE true OR %subquery%") + .replaceAll(subqueries) + .forEach(this::assertPlanContainsNoApplyOrAnyJoin); } @Test @@ -417,14 +421,13 @@ public void testCorrelatedScalarSubqueryInSelect() assertDistributedPlan("SELECT name, (SELECT name FROM region WHERE regionkey = nation.regionkey) FROM nation", anyTree( filter(format("CASE \"is_distinct\" WHEN true THEN true ELSE CAST(fail(%s, 'Scalar sub-query has returned multiple rows') AS boolean) END", SUBQUERY_MULTIPLE_ROWS.toErrorCode().getCode()), - project( - markDistinct("is_distinct", ImmutableList.of("unique"), "hash", - project(ImmutableMap.of("hash", expression("combine_hash(bigint '0', coalesce(\"$operator$hash_code\"(unique), 0))")), - join(LEFT, ImmutableList.of(equiJoinClause("n_regionkey", "r_regionkey")), - assignUniqueId("unique", - exchange(REMOTE, REPARTITION, - anyTree(tableScan("nation", ImmutableMap.of("n_regionkey", "regionkey"))))), - anyTree(tableScan("region", ImmutableMap.of("r_regionkey", "regionkey")))))))))); + markDistinct("is_distinct", ImmutableList.of("unique"), + join(LEFT, ImmutableList.of(equiJoinClause("n_regionkey", "r_regionkey")), + assignUniqueId("unique", + exchange(REMOTE, REPARTITION, + anyTree(tableScan("nation", ImmutableMap.of("n_regionkey", "regionkey"))))), + anyTree( + tableScan("region", ImmutableMap.of("r_regionkey", "regionkey")))))))); } @Test @@ -627,14 +630,21 @@ public void testPruneCountAggregationOverScalar() @Test public void testPickTableLayoutWithFilter() { - Map filterConstraint = ImmutableMap.builder() - .put("orderkey", singleValue(BIGINT, 5L)) - .build(); assertPlan( "SELECT orderkey FROM orders WHERE orderkey=5", output( filter("orderkey = BIGINT '5'", - constrainedTableScanWithTableLayout("orders", filterConstraint, ImmutableMap.of("orderkey", "orderkey"))))); + constrainedTableScanWithTableLayout( + "orders", + ImmutableMap.of(), + ImmutableMap.of("orderkey", "orderkey"))))); + assertPlan( + "SELECT orderkey FROM orders WHERE orderstatus='F'", + output( + constrainedTableScanWithTableLayout( + "orders", + ImmutableMap.of("orderstatus", singleValue(createVarcharType(1), utf8Slice("F"))), + ImmutableMap.of("orderkey", "orderkey")))); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestPredicatePushdown.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestPredicatePushdown.java index 01d18f910989d..1c167300730f4 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestPredicatePushdown.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestPredicatePushdown.java @@ -14,8 +14,10 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.sql.planner.assertions.BasePlanTest; +import com.facebook.presto.sql.planner.assertions.PlanMatchPattern; import com.facebook.presto.sql.planner.optimizations.PlanOptimizer; import com.facebook.presto.sql.planner.plan.ExchangeNode; +import com.facebook.presto.sql.planner.plan.WindowNode; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.testng.annotations.Test; @@ -138,12 +140,12 @@ public void testEqualsPredicateFromFilterSidePropagatesToSourceSideOfSemiJoin() anyTree( semiJoin("LINE_ORDER_KEY", "ORDERS_ORDER_KEY", "SEMI_JOIN_RESULT", anyTree( - filter("LINE_ORDER_KEY = BIGINT '2' AND BIGINT '2' = LINE_ORDER_KEY", // TODO this should be simplified + filter("LINE_ORDER_KEY = BIGINT '2'", tableScan("lineitem", ImmutableMap.of( "LINE_ORDER_KEY", "orderkey", "LINE_QUANTITY", "quantity")))), anyTree( - filter("ORDERS_ORDER_KEY = BIGINT '2' AND BIGINT '2' = ORDERS_ORDER_KEY", // TODO this should be simplified + filter("ORDERS_ORDER_KEY = BIGINT '2'", tableScan("orders", ImmutableMap.of("ORDERS_ORDER_KEY", "orderkey"))))))); } @@ -187,12 +189,12 @@ public void testEqualPredicateFromSourceSidePropagatesToFilterSideOfSemiJoin() anyTree( semiJoin("LINE_ORDER_KEY", "ORDERS_ORDER_KEY", "SEMI_JOIN_RESULT", anyTree( - filter("LINE_ORDER_KEY = BIGINT '2' AND BIGINT '2' = LINE_ORDER_KEY", // TODO this should be simplified + filter("LINE_ORDER_KEY = BIGINT '2'", tableScan("lineitem", ImmutableMap.of( "LINE_ORDER_KEY", "orderkey", "LINE_QUANTITY", "quantity")))), anyTree( - filter("ORDERS_ORDER_KEY = BIGINT '2' AND BIGINT '2' = ORDERS_ORDER_KEY", // TODO this should be simplified + filter("ORDERS_ORDER_KEY = BIGINT '2'", tableScan("orders", ImmutableMap.of("ORDERS_ORDER_KEY", "orderkey"))))))); } @@ -330,4 +332,84 @@ public void testPredicatePushDownOverProjection() tableScan("orders", ImmutableMap.of( "ORDERKEY", "orderkey")))))); } + + @Test + public void testConjunctsOrder() + { + assertPlan( + "select partkey " + + "from (" + + " select" + + " partkey," + + " 100/(size-1) x" + + " from part" + + " where size <> 1" + + ") " + + "where x = 2", + anyTree( + // Order matters: size<>1 should be before 100/(size-1)=2. + // In this particular example, reversing the order leads to div-by-zero error. + filter("size <> 1 AND 100/(size - 1) = 2", + tableScan("part", ImmutableMap.of( + "partkey", "partkey", + "size", "size"))))); + } + + @Test + public void testPredicateOnPartitionSymbolsPushedThroughWindow() + { + PlanMatchPattern tableScan = tableScan( + "orders", + ImmutableMap.of( + "CUST_KEY", "custkey", + "ORDER_KEY", "orderkey")); + assertPlan( + "SELECT * FROM (" + + "SELECT custkey, orderkey, rank() OVER (PARTITION BY custkey ORDER BY orderdate ASC)" + + "FROM orders" + + ") WHERE custkey = 0 AND orderkey > 0", + anyTree( + filter("ORDER_KEY > BIGINT '0'", + anyTree( + node(WindowNode.class, + anyTree( + filter("CUST_KEY = BIGINT '0'", + tableScan))))))); + } + + @Test + public void testPredicateOnNonDeterministicSymbolsPushedDown() + { + assertPlan( + "SELECT * FROM (" + + "SELECT random_column, orderkey, rank() OVER (PARTITION BY random_column ORDER BY orderdate ASC)" + + "FROM (select round(custkey*rand()) random_column, * from orders) " + + ") WHERE random_column > 100", + anyTree( + node(WindowNode.class, + anyTree( + filter("\"ROUND\" > 1E2", + project(ImmutableMap.of("ROUND", expression("round(CAST(CUST_KEY AS double) * rand())")), + tableScan( + "orders", + ImmutableMap.of("CUST_KEY", "custkey")))))))); + } + + @Test + public void testNonDeterministicPredicateNotPushedDown() + { + assertPlan( + "SELECT * FROM (" + + "SELECT custkey, orderkey, rank() OVER (PARTITION BY custkey ORDER BY orderdate ASC)" + + "FROM orders" + + ") WHERE custkey > 100*rand()", + anyTree( + filter("CAST(\"CUST_KEY\" AS double) > (1E2 * \"rand\"())", + anyTree( + node(WindowNode.class, + anyTree( + tableScan( + "orders", + ImmutableMap.of("CUST_KEY", "custkey")))))))); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestTypeValidator.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestTypeValidator.java index 8bec69ec682f3..09796e1bbb349 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/TestTypeValidator.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/TestTypeValidator.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.FunctionKind; import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.TableHandle; @@ -102,7 +103,7 @@ public void setUp() assignments, Optional.empty(), TupleDomain.all(), - null); + TupleDomain.all()); } @Test @@ -391,7 +392,7 @@ public void testInvalidUnion() private void assertTypesValid(PlanNode node) { - TYPE_VALIDATOR.validate(node, TEST_SESSION, createTestMetadataManager(), SQL_PARSER, symbolAllocator.getTypes()); + TYPE_VALIDATOR.validate(node, TEST_SESSION, createTestMetadataManager(), SQL_PARSER, symbolAllocator.getTypes(), WarningCollector.NOOP); } private static PlanNodeId newId() diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java index bac6f414ebdaf..b0fe4e9a6f707 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/BasePlanTest.java @@ -15,6 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.LogicalPlanner; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.RuleStatsRecorder; @@ -132,7 +133,7 @@ protected void assertPlan(String sql, LogicalPlanner.Stage stage, PlanMatchPatte protected void assertPlan(String sql, LogicalPlanner.Stage stage, PlanMatchPattern pattern, List optimizers) { queryRunner.inTransaction(transactionSession -> { - Plan actualPlan = queryRunner.createPlan(transactionSession, sql, optimizers, stage); + Plan actualPlan = queryRunner.createPlan(transactionSession, sql, optimizers, stage, WarningCollector.NOOP); PlanAssert.assertPlan(transactionSession, queryRunner.getMetadata(), queryRunner.getStatsCalculator(), actualPlan, pattern); return null; }); @@ -165,7 +166,7 @@ protected void assertMinimallyOptimizedPlan(@Language("SQL") String sql, PlanMat protected void assertPlanWithSession(@Language("SQL") String sql, Session session, boolean forceSingleNode, PlanMatchPattern pattern) { queryRunner.inTransaction(session, transactionSession -> { - Plan actualPlan = queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, forceSingleNode); + Plan actualPlan = queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, forceSingleNode, WarningCollector.NOOP); PlanAssert.assertPlan(transactionSession, queryRunner.getMetadata(), queryRunner.getStatsCalculator(), actualPlan, pattern); return null; }); @@ -174,7 +175,7 @@ protected void assertPlanWithSession(@Language("SQL") String sql, Session sessio protected void assertPlanWithSession(@Language("SQL") String sql, Session session, boolean forceSingleNode, PlanMatchPattern pattern, Consumer planValidator) { queryRunner.inTransaction(session, transactionSession -> { - Plan actualPlan = queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, forceSingleNode); + Plan actualPlan = queryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, forceSingleNode, WarningCollector.NOOP); PlanAssert.assertPlan(transactionSession, queryRunner.getMetadata(), queryRunner.getStatsCalculator(), actualPlan, pattern); planValidator.accept(actualPlan); return null; @@ -194,7 +195,7 @@ protected Plan plan(String sql, LogicalPlanner.Stage stage) protected Plan plan(String sql, LogicalPlanner.Stage stage, boolean forceSingleNode) { try { - return queryRunner.inTransaction(transactionSession -> queryRunner.createPlan(transactionSession, sql, stage, forceSingleNode)); + return queryRunner.inTransaction(transactionSession -> queryRunner.createPlan(transactionSession, sql, stage, forceSingleNode, WarningCollector.NOOP)); } catch (RuntimeException e) { throw new AssertionError("Planning failed for SQL: " + sql, e); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanAssert.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanAssert.java index 6e6f96b01d81f..b0afd6352f9bc 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanAssert.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanAssert.java @@ -15,7 +15,7 @@ import com.facebook.presto.Session; import com.facebook.presto.cost.CachingStatsProvider; -import com.facebook.presto.cost.CostProvider; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.cost.StatsProvider; import com.facebook.presto.metadata.Metadata; @@ -23,7 +23,6 @@ import com.facebook.presto.sql.planner.iterative.Lookup; import com.facebook.presto.sql.planner.plan.PlanNode; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.facebook.presto.sql.planner.iterative.Plans.resolveGroupReferences; import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textLogicalPlan; @@ -46,12 +45,11 @@ public static void assertPlan(Session session, Metadata metadata, StatsCalculato public static void assertPlan(Session session, Metadata metadata, StatsProvider statsProvider, Plan actual, Lookup lookup, PlanMatchPattern pattern) { - CostProvider costProvider = node -> UNKNOWN_COST; MatchResult matches = actual.getRoot().accept(new PlanMatchingVisitor(session, metadata, statsProvider, lookup), pattern); if (!matches.isMatch()) { - String formattedPlan = textLogicalPlan(actual.getRoot(), actual.getTypes(), metadata.getFunctionRegistry(), statsProvider, costProvider, session, 0); + String formattedPlan = textLogicalPlan(actual.getRoot(), actual.getTypes(), metadata.getFunctionRegistry(), StatsAndCosts.empty(), session, 0); PlanNode resolvedPlan = resolveGroupReferences(actual.getRoot(), lookup); - String resolvedFormattedPlan = textLogicalPlan(resolvedPlan, actual.getTypes(), metadata.getFunctionRegistry(), statsProvider, costProvider, session, 0); + String resolvedFormattedPlan = textLogicalPlan(resolvedPlan, actual.getTypes(), metadata.getFunctionRegistry(), StatsAndCosts.empty(), session, 0); throw new AssertionError(format( "Plan does not match, expected [\n\n%s\n] but found [\n\n%s\n] which resolves to [\n\n%s\n]", pattern, diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanMatchPattern.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanMatchPattern.java index b97361c365e54..8e6078a63cf92 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanMatchPattern.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/PlanMatchPattern.java @@ -42,9 +42,11 @@ import com.facebook.presto.sql.planner.plan.ProjectNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.SortNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; import com.facebook.presto.sql.planner.plan.TableWriterNode; import com.facebook.presto.sql.planner.plan.TopNNode; import com.facebook.presto.sql.planner.plan.UnionNode; +import com.facebook.presto.sql.planner.plan.UnnestNode; import com.facebook.presto.sql.planner.plan.ValuesNode; import com.facebook.presto.sql.planner.plan.WindowNode; import com.facebook.presto.sql.tree.Expression; @@ -77,8 +79,6 @@ import static com.facebook.presto.sql.planner.assertions.MatchResult.match; import static com.facebook.presto.sql.planner.assertions.StrictAssignedSymbolsMatcher.actualAssignments; import static com.facebook.presto.sql.planner.assertions.StrictSymbolsMatcher.actualOutputs; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; -import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; import static com.facebook.presto.sql.tree.SortItem.NullOrdering.FIRST; import static com.facebook.presto.sql.tree.SortItem.NullOrdering.UNDEFINED; import static com.facebook.presto.sql.tree.SortItem.Ordering.ASCENDING; @@ -129,14 +129,6 @@ public static PlanMatchPattern tableScan(String expectedTableName) return TableScanMatcher.create(expectedTableName); } - public static PlanMatchPattern tableScan(String expectedTableName, String originalConstraint) - { - Expression expectedOriginalConstraint = rewriteIdentifiersToSymbolReferences(new SqlParser().createExpression(originalConstraint)); - return TableScanMatcher.builder(expectedTableName) - .expectedOriginalConstraint(expectedOriginalConstraint) - .build(); - } - public static PlanMatchPattern tableScan(String expectedTableName, Map columnReferences) { PlanMatchPattern result = tableScan(expectedTableName); @@ -363,14 +355,24 @@ public static PlanMatchPattern join(JoinNode.Type joinType, List kdbTree, PlanMatchPattern left, PlanMatchPattern right) + { + return node(SpatialJoinNode.class, left, right).with( + new SpatialJoinMatcher(SpatialJoinNode.Type.INNER, rewriteIdentifiersToSymbolReferences(new SqlParser().createExpression(expectedFilter, new ParsingOptions())), kdbTree)); } public static PlanMatchPattern spatialLeftJoin(String expectedFilter, PlanMatchPattern left, PlanMatchPattern right) { - return node(JoinNode.class, left, right).with( - new SpatialJoinMatcher(LEFT, rewriteIdentifiersToSymbolReferences(new SqlParser().createExpression(expectedFilter, new ParsingOptions())))); + return node(SpatialJoinNode.class, left, right).with( + new SpatialJoinMatcher(SpatialJoinNode.Type.LEFT, rewriteIdentifiersToSymbolReferences(new SqlParser().createExpression(expectedFilter, new ParsingOptions())), Optional.empty())); + } + + public static PlanMatchPattern unnest(PlanMatchPattern source) + { + return node(UnnestNode.class, source); } public static PlanMatchPattern exchange(PlanMatchPattern... sources) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/SpatialJoinMatcher.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/SpatialJoinMatcher.java index 40bdb85979681..d6d31b92aabd0 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/SpatialJoinMatcher.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/SpatialJoinMatcher.java @@ -16,11 +16,13 @@ import com.facebook.presto.Session; import com.facebook.presto.cost.StatsProvider; import com.facebook.presto.metadata.Metadata; -import com.facebook.presto.sql.planner.plan.JoinNode; -import com.facebook.presto.sql.planner.plan.JoinNode.Type; import com.facebook.presto.sql.planner.plan.PlanNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode; +import com.facebook.presto.sql.planner.plan.SpatialJoinNode.Type; import com.facebook.presto.sql.tree.Expression; +import java.util.Optional; + import static com.facebook.presto.sql.planner.assertions.MatchResult.NO_MATCH; import static com.facebook.presto.sql.planner.assertions.MatchResult.match; import static com.google.common.base.MoreObjects.toStringHelper; @@ -32,22 +34,24 @@ public class SpatialJoinMatcher { private final Type type; private final Expression filter; + private final Optional kdbTree; - public SpatialJoinMatcher(Type type, Expression filter) + public SpatialJoinMatcher(Type type, Expression filter, Optional kdbTree) { this.type = type; this.filter = requireNonNull(filter, "filter can not be null"); + this.kdbTree = requireNonNull(kdbTree, "kdbTree can not be null"); } @Override public boolean shapeMatches(PlanNode node) { - if (!(node instanceof JoinNode)) { + if (!(node instanceof SpatialJoinNode)) { return false; } - JoinNode joinNode = (JoinNode) node; - return joinNode.getType() == type && joinNode.isSpatialJoin(); + SpatialJoinNode joinNode = (SpatialJoinNode) node; + return joinNode.getType() == type; } @Override @@ -55,11 +59,11 @@ public MatchResult detailMatches(PlanNode node, StatsProvider stats, Session ses { checkState(shapeMatches(node), "Plan testing framework error: shapeMatches returned false in detailMatches in %s", this.getClass().getName()); - JoinNode joinNode = (JoinNode) node; - if (!joinNode.getFilter().isPresent()) { + SpatialJoinNode joinNode = (SpatialJoinNode) node; + if (!new ExpressionVerifier(symbolAliases).process(joinNode.getFilter(), filter)) { return NO_MATCH; } - if (!new ExpressionVerifier(symbolAliases).process(joinNode.getFilter().get(), filter)) { + if (!joinNode.getKdbTree().equals(kdbTree)) { return NO_MATCH; } return match(); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/TableScanMatcher.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/TableScanMatcher.java index 9588d474f6060..079d74e1e7cc4 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/TableScanMatcher.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/assertions/TableScanMatcher.java @@ -18,11 +18,8 @@ import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableMetadata; import com.facebook.presto.spi.predicate.Domain; -import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.TableScanNode; -import com.facebook.presto.sql.tree.Expression; -import com.facebook.presto.sql.tree.SymbolReference; import java.util.Map; import java.util.Optional; @@ -32,21 +29,18 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toMap; final class TableScanMatcher implements Matcher { private final String expectedTableName; private final Optional> expectedConstraint; - private final Optional expectedOriginalConstraint; private final Optional hasTableLayout; - private TableScanMatcher(String expectedTableName, Optional> expectedConstraint, Optional originalConstraint, Optional hasTableLayout) + private TableScanMatcher(String expectedTableName, Optional> expectedConstraint, Optional hasTableLayout) { this.expectedTableName = requireNonNull(expectedTableName, "expectedTableName is null"); this.expectedConstraint = requireNonNull(expectedConstraint, "expectedConstraint is null"); - this.expectedOriginalConstraint = requireNonNull(originalConstraint, "expectedOriginalConstraint is null"); this.hasTableLayout = requireNonNull(hasTableLayout, "hasTableLayout is null"); } @@ -66,25 +60,11 @@ public MatchResult detailMatches(PlanNode node, StatsProvider stats, Session ses String actualTableName = tableMetadata.getTable().getTableName(); return new MatchResult( expectedTableName.equalsIgnoreCase(actualTableName) && - originalConstraintMatches(tableScanNode) && ((!expectedConstraint.isPresent()) || domainsMatch(expectedConstraint, tableScanNode.getCurrentConstraint(), tableScanNode.getTable(), session, metadata)) && hasTableLayout(tableScanNode)); } - private boolean originalConstraintMatches(TableScanNode node) - { - return expectedOriginalConstraint - .map(expected -> { - Map assignments = node.getOutputSymbols().stream() - .collect(toMap(Symbol::getName, Symbol::toSymbolReference)); - SymbolAliases symbolAliases = SymbolAliases.builder().putAll(assignments).build(); - ExpressionVerifier verifier = new ExpressionVerifier(symbolAliases); - return verifier.process(node.getOriginalConstraint(), expected); - }) - .orElse(true); - } - private boolean hasTableLayout(TableScanNode tableScanNode) { return !hasTableLayout.isPresent() || hasTableLayout.get() == tableScanNode.getLayout().isPresent(); @@ -97,7 +77,6 @@ public String toString() .omitNullValues() .add("expectedTableName", expectedTableName) .add("expectedConstraint", expectedConstraint.orElse(null)) - .add("expectedOriginalConstraint", expectedOriginalConstraint.orElse(null)) .add("hasTableLayout", hasTableLayout.orElse(null)) .toString(); } @@ -116,7 +95,6 @@ public static class Builder { private final String expectedTableName; private Optional> expectedConstraint = Optional.empty(); - private Optional expectedOriginalConstraint = Optional.empty(); private Optional hasTableLayout = Optional.empty(); private Builder(String expectedTableName) @@ -130,12 +108,6 @@ public Builder expectedConstraint(Map expectedConstraint) return this; } - public Builder expectedOriginalConstraint(Expression expression) - { - this.expectedOriginalConstraint = Optional.of(expression); - return this; - } - public Builder hasTableLayout() { this.hasTableLayout = Optional.of(true); @@ -148,7 +120,6 @@ PlanMatchPattern build() new TableScanMatcher( expectedTableName, expectedConstraint, - expectedOriginalConstraint, hasTableLayout)); return result; } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/TestIterativeOptimizer.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/TestIterativeOptimizer.java index 304146e223a4a..cd16340416e61 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/TestIterativeOptimizer.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/TestIterativeOptimizer.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.iterative; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.matching.Captures; import com.facebook.presto.matching.Pattern; import com.facebook.presto.spi.PrestoException; @@ -78,7 +79,7 @@ public void optimizerTimeoutsOnNonConvergingPlan() try { queryRunner.inTransaction(transactionSession -> { - queryRunner.createPlan(transactionSession, "SELECT * FROM nation", ImmutableList.of(optimizer)); + queryRunner.createPlan(transactionSession, "SELECT * FROM nation", ImmutableList.of(optimizer), WarningCollector.NOOP); fail("The optimizer should not converge"); return null; }); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestCanonicalizeExpressions.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestCanonicalizeExpressions.java index a424dbfdb39fc..a98e4c59d1229 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestCanonicalizeExpressions.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestCanonicalizeExpressions.java @@ -18,8 +18,6 @@ import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.tree.BooleanLiteral.FALSE_LITERAL; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; public class TestCanonicalizeExpressions extends BaseRuleTest @@ -49,20 +47,4 @@ public void testDoesNotFireForCanonicalExpressions() .on(p -> p.join(INNER, p.values(), p.values(), FALSE_LITERAL)) .doesNotFire(); } - - @Test - public void testDoesNotFireForUnfilteredTableScan() - { - tester().assertThat(canonicalizeExpressions.tableScanExpressionRewrite()) - .on(p -> p.tableScan(emptyList(), emptyMap())) - .doesNotFire(); - } - - @Test - public void testDoesNotFireForFilterInCanonicalForm() - { - tester().assertThat(canonicalizeExpressions.tableScanExpressionRewrite()) - .on(p -> p.tableScan(emptyList(), emptyMap(), FALSE_LITERAL)) - .doesNotFire(); - } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestDetermineJoinDistributionType.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestDetermineJoinDistributionType.java index 260fee9ff3be8..1f8a342a68a7a 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestDetermineJoinDistributionType.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestDetermineJoinDistributionType.java @@ -13,18 +13,28 @@ */ package com.facebook.presto.sql.planner.iterative.rule; +import com.facebook.presto.cost.CostComparator; +import com.facebook.presto.cost.PlanNodeStatsEstimate; +import com.facebook.presto.cost.SymbolStatsEstimate; +import com.facebook.presto.cost.TaskCountEstimator; import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; -import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; +import com.facebook.presto.sql.planner.Symbol; +import com.facebook.presto.sql.planner.iterative.rule.test.RuleAssert; +import com.facebook.presto.sql.planner.iterative.rule.test.RuleTester; import com.facebook.presto.sql.planner.plan.JoinNode; import com.facebook.presto.sql.planner.plan.JoinNode.DistributionType; import com.facebook.presto.sql.planner.plan.JoinNode.Type; +import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.util.Optional; import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; +import static com.facebook.presto.SystemSessionProperties.JOIN_MAX_BROADCAST_TABLE_SIZE; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.enforceSingleRow; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.equiJoinClause; @@ -32,6 +42,8 @@ import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.values; import static com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder.expression; import static com.facebook.presto.sql.planner.iterative.rule.test.PlanBuilder.expressions; +import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.PARTITIONED; +import static com.facebook.presto.sql.planner.plan.JoinNode.DistributionType.REPLICATED; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.FULL; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.INNER; import static com.facebook.presto.sql.planner.plan.JoinNode.Type.LEFT; @@ -39,8 +51,25 @@ @Test(singleThreaded = true) public class TestDetermineJoinDistributionType - extends BaseRuleTest { + private static final CostComparator COST_COMPARATOR = new CostComparator(1, 1, 1); + private static final int NODES_COUNT = 4; + + private RuleTester tester; + + @BeforeClass + public void setUp() + { + tester = new RuleTester(ImmutableList.of(), ImmutableMap.of(), Optional.of(NODES_COUNT)); + } + + @AfterClass(alwaysRun = true) + public void tearDown() + { + tester.close(); + tester = null; + } + @Test public void testDetermineDistributionType() { @@ -59,7 +88,7 @@ public void testDetermineDistributionTypeForLeftOuter() private void testDetermineDistributionType(JoinDistributionType sessionDistributedJoin, Type joinType, DistributionType expectedDistribution) { - tester().assertThat(new DetermineJoinDistributionType()) + assertDetermineJoinDistributionType() .on(p -> p.join( joinType, @@ -91,7 +120,7 @@ public void testRepartitionRightOuter() private void testRepartitionRightOuter(JoinDistributionType sessionDistributedJoin, Type joinType) { - tester().assertThat(new DetermineJoinDistributionType()) + assertDetermineJoinDistributionType() .on(p -> p.join( joinType, @@ -113,7 +142,7 @@ private void testRepartitionRightOuter(JoinDistributionType sessionDistributedJo @Test public void testReplicateScalar() { - tester().assertThat(new DetermineJoinDistributionType()) + assertDetermineJoinDistributionType() .on(p -> p.join( INNER, @@ -142,7 +171,7 @@ public void testReplicateNoEquiCriteria() private void testReplicateNoEquiCriteria(Type joinType) { - tester().assertThat(new DetermineJoinDistributionType()) + assertDetermineJoinDistributionType() .on(p -> p.join( joinType, @@ -164,7 +193,7 @@ private void testReplicateNoEquiCriteria(Type joinType) @Test public void testRetainDistributionType() { - tester().assertThat(new DetermineJoinDistributionType()) + assertDetermineJoinDistributionType() .on(p -> p.join( INNER, @@ -178,4 +207,409 @@ public void testRetainDistributionType() Optional.of(DistributionType.REPLICATED))) .doesNotFire(); } + + @Test + public void testFlipAndReplicateWhenOneTableMuchSmaller() + { + int aRows = 100; + int bRows = 10_000; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 6400, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("B1", "A1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("B1", 0)), + values(ImmutableMap.of("A1", 0)))); + } + + @Test + public void testFlipAndReplicateWhenOneTableMuchSmallerAndJoinCardinalityUnknown() + { + int aRows = 100; + int bRows = 10_000; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + // set symbol stats to unknown, so the join cardinality cannot be estimated + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), SymbolStatsEstimate.unknown())) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + // set symbol stats to unknown, so the join cardinality cannot be estimated + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), SymbolStatsEstimate.unknown())) + .build()) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("B1", "A1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("B1", 0)), + values(ImmutableMap.of("A1", 0)))); + } + + @Test + public void testPartitionWhenRequiredBySession() + { + int aRows = 100; + int bRows = 10_000; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 6400, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.PARTITIONED.name()) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("B1", "A1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("B1", 0)), + values(ImmutableMap.of("A1", 0)))); + } + + @Test + public void testPartitionWhenBothTablesEqual() + { + int aRows = 10_000; + int bRows = 10_000; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testReplicatesWhenRequiredBySession() + { + int aRows = 10_000; + int bRows = 10_000; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.BROADCAST.name()) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testPartitionFullOuterJoin() + { + int aRows = 10_000; + int bRows = 10; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + FULL, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + FULL, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testPartitionRightOuterJoin() + { + int aRows = 10_000; + int bRows = 10; + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + RIGHT, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + RIGHT, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testReplicateLeftOuterJoin() + { + int aRows = 10_000; + int bRows = 10; + assertDetermineJoinDistributionType(new CostComparator(75, 10, 15)) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + LEFT, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + LEFT, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testFlipAndReplicateRightOuterJoin() + { + int aRows = 10; + int bRows = 1_000_000; + assertDetermineJoinDistributionType(new CostComparator(75, 10, 15)) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 100))) + .build()) + .on(p -> + p.join( + RIGHT, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + LEFT, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testFlipAndReplicateRightOuterJoinWhenJoinCardinalityUnknown() + { + int aRows = 10; + int bRows = 1_000_000; + assertDetermineJoinDistributionType(new CostComparator(75, 10, 15)) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .overrideStats("valuesA", PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + // set symbol stats to unknown, so the join cardinality cannot be estimated + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), SymbolStatsEstimate.unknown())) + .build()) + .overrideStats("valuesB", PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + // set symbol stats to unknown, so the join cardinality cannot be estimated + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), SymbolStatsEstimate.unknown())) + .build()) + .on(p -> + p.join( + RIGHT, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + LEFT, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + @Test + public void testReplicatesWhenNotRestricted() + { + int aRows = 10_000; + int bRows = 10; + + PlanNodeStatsEstimate probeSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 10))) + .build(); + PlanNodeStatsEstimate buildSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 10))) + .build(); + + // B table is small enough to be replicated in AUTOMATIC_RESTRICTED mode + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .setSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, "100MB") + .overrideStats("valuesA", probeSideStatsEstimate) + .overrideStats("valuesB", buildSideStatsEstimate) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + + probeSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000d * 10000, 10))) + .build(); + buildSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000d * 10000, 10))) + .build(); + + // B table exceeds AUTOMATIC_RESTRICTED limit therefore it is partitioned + assertDetermineJoinDistributionType() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.AUTOMATIC.name()) + .setSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, "100MB") + .overrideStats("valuesA", probeSideStatsEstimate) + .overrideStats("valuesB", buildSideStatsEstimate) + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1", BIGINT)), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1", BIGINT)), + ImmutableList.of(new JoinNode.EquiJoinClause(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT))), + ImmutableList.of(p.symbol("A1", BIGINT), p.symbol("B1", BIGINT)), + Optional.empty())) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + + private RuleAssert assertDetermineJoinDistributionType() + { + return assertDetermineJoinDistributionType(COST_COMPARATOR); + } + + private RuleAssert assertDetermineJoinDistributionType(CostComparator costComparator) + { + return tester.assertThat(new DetermineJoinDistributionType(costComparator, new TaskCountEstimator(() -> NODES_COUNT))); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestJoinEnumerator.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestJoinEnumerator.java index be4810558f84e..23943e9605ae4 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestJoinEnumerator.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestJoinEnumerator.java @@ -19,7 +19,9 @@ import com.facebook.presto.cost.CachingStatsProvider; import com.facebook.presto.cost.CostComparator; import com.facebook.presto.cost.CostProvider; +import com.facebook.presto.cost.PlanNodeCostEstimate; import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.SymbolAllocator; @@ -39,7 +41,6 @@ import java.util.LinkedHashSet; import java.util.Optional; -import static com.facebook.presto.cost.PlanNodeCostEstimate.INFINITE_COST; import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.facebook.presto.sql.planner.iterative.rule.ReorderJoins.JoinEnumerator.generatePartitions; import static com.facebook.presto.sql.tree.BooleanLiteral.TRUE_LITERAL; @@ -102,7 +103,7 @@ public void testDoesNotCreateJoinWhenPartitionedOnCrossJoin() createContext()); JoinEnumerationResult actual = joinEnumerator.createJoinAccordingToPartitioning(multiJoinNode.getSources(), multiJoinNode.getOutputSymbols(), ImmutableSet.of(0)); assertFalse(actual.getPlanNode().isPresent()); - assertEquals(actual.getCost(), INFINITE_COST); + assertEquals(actual.getCost(), PlanNodeCostEstimate.infinite()); } private Rule.Context createContext() @@ -119,7 +120,6 @@ private Rule.Context createContext() queryRunner.getCostCalculator(), statsProvider, Optional.empty(), - noLookup(), queryRunner.getDefaultSession(), symbolAllocator.getTypes()); @@ -163,6 +163,12 @@ public CostProvider getCostProvider() @Override public void checkTimeoutNotExhausted() {} + + @Override + public WarningCollector getWarningCollector() + { + return WarningCollector.NOOP; + } }; } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestLambdaCaptureDesugaringRewriter.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestLambdaCaptureDesugaringRewriter.java index 36ec398d22670..acc192a98e572 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestLambdaCaptureDesugaringRewriter.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestLambdaCaptureDesugaringRewriter.java @@ -42,13 +42,13 @@ public void testRewriteBasicLambda() final SymbolAllocator allocator = new SymbolAllocator(symbols); assertEquals(rewrite(expression("x -> a + x"), allocator.getTypes(), allocator), - new BindExpression( - ImmutableList.of(expression("a")), - new LambdaExpression( - Stream.of("a_0", "x") - .map(Identifier::new) - .map(LambdaArgumentDeclaration::new) - .collect(toList()), - expression("a_0 + x")))); + new BindExpression( + ImmutableList.of(expression("a")), + new LambdaExpression( + Stream.of("a_0", "x") + .map(Identifier::new) + .map(LambdaArgumentDeclaration::new) + .collect(toList()), + expression("a_0 + x")))); } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPickTableLayout.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPickTableLayout.java index 23fefc4234a24..28d63205c9dee 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPickTableLayout.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPickTableLayout.java @@ -20,6 +20,7 @@ import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.Type; +import com.facebook.presto.sql.parser.SqlParser; import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.sql.planner.iterative.rule.test.BaseRuleTest; import com.facebook.presto.testing.TestingTransactionHandle; @@ -48,22 +49,32 @@ public class TestPickTableLayout { private PickTableLayout pickTableLayout; private TableHandle nationTableHandle; + private TableHandle ordersTableHandle; private TableLayoutHandle nationTableLayoutHandle; + private TableLayoutHandle ordersTableLayoutHandle; private ConnectorId connectorId; @BeforeClass public void setUpBeforeClass() { - pickTableLayout = new PickTableLayout(tester().getMetadata()); + pickTableLayout = new PickTableLayout(tester().getMetadata(), new SqlParser()); connectorId = tester().getCurrentConnectorId(); nationTableHandle = new TableHandle( connectorId, - new TpchTableHandle(connectorId.toString(), "nation", 1.0)); + new TpchTableHandle("nation", 1.0)); + ordersTableHandle = new TableHandle( + connectorId, + new TpchTableHandle("orders", 1.0)); - nationTableLayoutHandle = new TableLayoutHandle(connectorId, + nationTableLayoutHandle = new TableLayoutHandle( + connectorId, TestingTransactionHandle.create(), new TpchTableLayoutHandle((TpchTableHandle) nationTableHandle.getConnectorHandle(), TupleDomain.all())); + ordersTableLayoutHandle = new TableLayoutHandle( + connectorId, + TestingTransactionHandle.create(), + new TpchTableLayoutHandle((TpchTableHandle) ordersTableHandle.getConnectorHandle(), TupleDomain.all())); } @Test @@ -92,18 +103,13 @@ public void doesNotFireIfTableScanHasTableLayout() public void eliminateTableScanWhenNoLayoutExist() { tester().assertThat(pickTableLayout.pickTableLayoutForPredicate()) - .on(p -> p.filter(expression("nationkey = BIGINT '44'"), + .on(p -> p.filter(expression("orderstatus = 'G'"), p.tableScan( - nationTableHandle, - ImmutableList.of(p.symbol("nationkey", BIGINT)), - ImmutableMap.of(p.symbol("nationkey", BIGINT), new TpchColumnHandle("nationkey", BIGINT)), - Optional.of(nationTableLayoutHandle)))) - .matches( - filter("nationkey = BIGINT '44'", - constrainedTableScanWithTableLayout( - "nation", - ImmutableMap.of("nationkey", singleValue(BIGINT, 44L)), - ImmutableMap.of("nationkey", "nationkey")))); + ordersTableHandle, + ImmutableList.of(p.symbol("orderstatus", createVarcharType(1))), + ImmutableMap.of(p.symbol("orderstatus", createVarcharType(1)), new TpchColumnHandle("orderstatus", createVarcharType(1))), + Optional.of(ordersTableLayoutHandle)))) + .matches(values("A")); } @Test @@ -117,6 +123,7 @@ public void replaceWithExistsWhenNoLayoutExist() ImmutableList.of(p.symbol("nationkey", BIGINT)), ImmutableMap.of(p.symbol("nationkey", BIGINT), columnHandle), Optional.of(nationTableLayoutHandle), + TupleDomain.none(), TupleDomain.none()))) .matches(values("A")); } @@ -131,6 +138,7 @@ public void doesNotFireIfRuleNotChangePlan() ImmutableList.of(p.symbol("nationkey", BIGINT)), ImmutableMap.of(p.symbol("nationkey", BIGINT), new TpchColumnHandle("nationkey", BIGINT)), Optional.of(nationTableLayoutHandle), + TupleDomain.all(), TupleDomain.all()))) .doesNotFire(); } @@ -151,43 +159,38 @@ public void ruleAddedTableLayoutToTableScan() public void ruleAddedTableLayoutToFilterTableScan() { Map filterConstraint = ImmutableMap.builder() - .put("nationkey", singleValue(BIGINT, 44L)) + .put("orderstatus", singleValue(createVarcharType(1), utf8Slice("F"))) .build(); tester().assertThat(pickTableLayout.pickTableLayoutForPredicate()) - .on(p -> p.filter(expression("nationkey = BIGINT '44'"), + .on(p -> p.filter(expression("orderstatus = CAST ('F' AS VARCHAR(1))"), p.tableScan( - nationTableHandle, - ImmutableList.of(p.symbol("nationkey", BIGINT)), - ImmutableMap.of(p.symbol("nationkey", BIGINT), new TpchColumnHandle("nationkey", BIGINT))))) + ordersTableHandle, + ImmutableList.of(p.symbol("orderstatus", createVarcharType(1))), + ImmutableMap.of(p.symbol("orderstatus", createVarcharType(1)), new TpchColumnHandle("orderstatus", createVarcharType(1)))))) .matches( - filter("nationkey = BIGINT '44'", - constrainedTableScanWithTableLayout("nation", filterConstraint, ImmutableMap.of("nationkey", "nationkey")))); + constrainedTableScanWithTableLayout("orders", filterConstraint, ImmutableMap.of("orderstatus", "orderstatus"))); } @Test public void ruleAddedNewTableLayoutIfTableScanHasEmptyConstraint() { tester().assertThat(pickTableLayout.pickTableLayoutForPredicate()) - .on(p -> p.filter(expression("nationkey = BIGINT '44'"), + .on(p -> p.filter(expression("orderstatus = 'F'"), p.tableScan( - nationTableHandle, - ImmutableList.of(p.symbol("nationkey", BIGINT)), - ImmutableMap.of(p.symbol("nationkey", BIGINT), new TpchColumnHandle("nationkey", BIGINT)), - Optional.of(nationTableLayoutHandle)))) + ordersTableHandle, + ImmutableList.of(p.symbol("orderstatus", createVarcharType(1))), + ImmutableMap.of(p.symbol("orderstatus", createVarcharType(1)), new TpchColumnHandle("orderstatus", createVarcharType(1))), + Optional.of(ordersTableLayoutHandle)))) .matches( - filter("nationkey = BIGINT '44'", - constrainedTableScanWithTableLayout( - "nation", - ImmutableMap.of("nationkey", singleValue(BIGINT, 44L)), - ImmutableMap.of("nationkey", "nationkey")))); + constrainedTableScanWithTableLayout( + "orders", + ImmutableMap.of("orderstatus", singleValue(createVarcharType(1), utf8Slice("F"))), + ImmutableMap.of("orderstatus", "orderstatus"))); } @Test public void ruleWithPushdownableToTableLayoutPredicate() { - TableHandle ordersTableHandle = new TableHandle( - connectorId, - new TpchTableHandle(connectorId.toString(), "orders", 1.0)); Type orderStatusType = createVarcharType(1); tester().assertThat(pickTableLayout.pickTableLayoutForPredicate()) .on(p -> p.filter(expression("orderstatus = 'O'"), @@ -204,9 +207,6 @@ public void ruleWithPushdownableToTableLayoutPredicate() @Test public void nonDeterministicPredicate() { - TableHandle ordersTableHandle = new TableHandle( - connectorId, - new TpchTableHandle(connectorId.toString(), "orders", 1.0)); Type orderStatusType = createVarcharType(1); tester().assertThat(pickTableLayout.pickTableLayoutForPredicate()) .on(p -> p.filter(expression("orderstatus = 'O' AND rand() = 0"), diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneCountAggregationOverScalar.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneCountAggregationOverScalar.java index 0f25bda2e5371..f4a8f02df022d 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneCountAggregationOverScalar.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneCountAggregationOverScalar.java @@ -119,9 +119,9 @@ public void testDoesNotFireOnNestedCountAggregateWithNonEmptyGroupBy() .source( p.aggregation(aggregationBuilder -> { aggregationBuilder - .source(p.tableScan(ImmutableList.of(), ImmutableMap.of())).groupingSets(singleGroupingSet(ImmutableList.of(p.symbol("orderkey")))); + .source(p.tableScan(ImmutableList.of(), ImmutableMap.of())).groupingSets(singleGroupingSet(ImmutableList.of(p.symbol("orderkey")))); aggregationBuilder - .source(p.tableScan(ImmutableList.of(), ImmutableMap.of())); + .source(p.tableScan(ImmutableList.of(), ImmutableMap.of())); })))) .doesNotFire(); } @@ -143,7 +143,7 @@ public void testDoesNotFireOnNestedNonCountAggregate() p.tableScan( new TableHandle( new ConnectorId("local"), - new TpchTableHandle("local", "orders", TINY_SCALE_FACTOR)), + new TpchTableHandle("orders", TINY_SCALE_FACTOR)), ImmutableList.of(totalPrice), ImmutableMap.of(totalPrice, new TpchColumnHandle(totalPrice.getName(), DOUBLE)))))); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneIndexSourceColumns.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneIndexSourceColumns.java index f52cdab58eb67..c49d2707f28fb 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneIndexSourceColumns.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestPruneIndexSourceColumns.java @@ -85,7 +85,7 @@ private static PlanNode buildProjectedIndexSource(PlanBuilder p, Predicate p.tableDelete( new SchemaTableName("sch", "tab"), p.tableScan( - new TableHandle(CONNECTOR_ID, new TpchTableHandle(CATALOG_ID, "nation", 1.0)), + new TableHandle(CONNECTOR_ID, new TpchTableHandle("nation", 1.0)), ImmutableList.of(), ImmutableMap.of()), p.symbol("a", BigintType.BIGINT))) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestReorderJoins.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestReorderJoins.java index e69b60c256c93..190821457c7d2 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestReorderJoins.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestReorderJoins.java @@ -38,7 +38,9 @@ import java.util.Optional; import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; +import static com.facebook.presto.SystemSessionProperties.JOIN_MAX_BROADCAST_TABLE_SIZE; import static com.facebook.presto.SystemSessionProperties.JOIN_REORDERING_STRATEGY; +import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.AUTOMATIC; import static com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType.BROADCAST; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.equiJoinClause; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.join; @@ -196,7 +198,7 @@ public void testRepartitionsWhenBothTablesEqual() } @Test - public void testReplicatesWhenRequiredBySession() + public void testReplicatesUnrestrictedWhenRequiredBySession() { assertReorderJoins() .on(p -> @@ -207,6 +209,7 @@ public void testReplicatesWhenRequiredBySession() ImmutableList.of(new EquiJoinClause(p.symbol("A1"), p.symbol("B1"))), ImmutableList.of(p.symbol("A1"), p.symbol("B1")), Optional.empty())) + .setSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, "1kB") .setSystemProperty(JOIN_DISTRIBUTION_TYPE, BROADCAST.name()) .overrideStats("valuesA", PlanNodeStatsEstimate.builder() .setOutputRowCount(10000) @@ -309,7 +312,7 @@ public void testDoesNotFireWithNoStats() ImmutableList.of(new EquiJoinClause(p.symbol("A1"), p.symbol("B1"))), ImmutableList.of(p.symbol("A1")), Optional.empty())) - .overrideStats("valuesA", PlanNodeStatsEstimate.UNKNOWN_STATS) + .overrideStats("valuesA", PlanNodeStatsEstimate.unknown()) .doesNotFire(); } @@ -418,6 +421,75 @@ public void testSmallerJoinFirst() values("B1", "B2")))); } + @Test + public void testReplicatesWhenNotRestricted() + { + int aRows = 10_000; + int bRows = 10; + + PlanNodeStatsEstimate probeSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000, 10))) + .build(); + PlanNodeStatsEstimate buildSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000, 10))) + .build(); + + // B table is small enough to be replicated in AUTOMATIC_RESTRICTED mode + assertReorderJoins() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, AUTOMATIC.name()) + .setSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, "100MB") + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1")), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1")), + ImmutableList.of(new EquiJoinClause(p.symbol("A1"), p.symbol("B1"))), + ImmutableList.of(p.symbol("A1"), p.symbol("B1")), + Optional.empty())) + .overrideStats("valuesA", probeSideStatsEstimate) + .overrideStats("valuesB", buildSideStatsEstimate) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(REPLICATED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + + probeSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(aRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("A1"), new SymbolStatsEstimate(0, 100, 0, 640000d * 10000, 10))) + .build(); + buildSideStatsEstimate = PlanNodeStatsEstimate.builder() + .setOutputRowCount(bRows) + .addSymbolStatistics(ImmutableMap.of(new Symbol("B1"), new SymbolStatsEstimate(0, 100, 0, 640000d * 10000, 10))) + .build(); + + // B table exceeds AUTOMATIC_RESTRICTED limit therefore it is partitioned + assertReorderJoins() + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, AUTOMATIC.name()) + .setSystemProperty(JOIN_MAX_BROADCAST_TABLE_SIZE, "100MB") + .on(p -> + p.join( + INNER, + p.values(new PlanNodeId("valuesA"), aRows, p.symbol("A1")), + p.values(new PlanNodeId("valuesB"), bRows, p.symbol("B1")), + ImmutableList.of(new EquiJoinClause(p.symbol("A1"), p.symbol("B1"))), + ImmutableList.of(p.symbol("A1"), p.symbol("B1")), + Optional.empty())) + .overrideStats("valuesA", probeSideStatsEstimate) + .overrideStats("valuesB", buildSideStatsEstimate) + .matches(join( + INNER, + ImmutableList.of(equiJoinClause("A1", "B1")), + Optional.empty(), + Optional.of(PARTITIONED), + values(ImmutableMap.of("A1", 0)), + values(ImmutableMap.of("B1", 0)))); + } + private RuleAssert assertReorderJoins() { return tester.assertThat(new ReorderJoins(new CostComparator(1, 1, 1))); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestTransformCorrelatedSingleRowSubqueryToProject.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestTransformCorrelatedSingleRowSubqueryToProject.java index 40b1eb971665b..5de55887c0969 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestTransformCorrelatedSingleRowSubqueryToProject.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/TestTransformCorrelatedSingleRowSubqueryToProject.java @@ -50,7 +50,7 @@ public void testRewrite() ImmutableList.of(p.symbol("l_nationkey")), p.tableScan(new TableHandle( new ConnectorId("local"), - new TpchTableHandle("local", "nation", TINY_SCALE_FACTOR)), ImmutableList.of(p.symbol("l_nationkey")), + new TpchTableHandle("nation", TINY_SCALE_FACTOR)), ImmutableList.of(p.symbol("l_nationkey")), ImmutableMap.of(p.symbol("l_nationkey"), new TpchColumnHandle("nationkey", BIGINT))), p.project( diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java index 799a6eb8dcf31..51f523a035814 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/PlanBuilder.java @@ -53,9 +53,12 @@ import com.facebook.presto.sql.planner.plan.LimitNode; import com.facebook.presto.sql.planner.plan.MarkDistinctNode; import com.facebook.presto.sql.planner.plan.OutputNode; +import com.facebook.presto.sql.planner.plan.PlanFragmentId; import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.sql.planner.plan.PlanNodeId; import com.facebook.presto.sql.planner.plan.ProjectNode; +import com.facebook.presto.sql.planner.plan.RemoteSourceNode; +import com.facebook.presto.sql.planner.plan.RowNumberNode; import com.facebook.presto.sql.planner.plan.SampleNode; import com.facebook.presto.sql.planner.plan.SemiJoinNode; import com.facebook.presto.sql.planner.plan.TableFinishNode; @@ -90,6 +93,7 @@ import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.FIXED_HASH_DISTRIBUTION; import static com.facebook.presto.sql.planner.SystemPartitioningHandle.SINGLE_DISTRIBUTION; +import static com.facebook.presto.util.MoreLists.nElements; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; @@ -161,7 +165,15 @@ public ValuesNode values(Symbol... columns) public ValuesNode values(PlanNodeId id, Symbol... columns) { - return values(id, ImmutableList.copyOf(columns), ImmutableList.of()); + return values(id, 0, columns); + } + + public ValuesNode values(PlanNodeId id, int rows, Symbol... columns) + { + return values( + id, + ImmutableList.copyOf(columns), + nElements(rows, row -> nElements(columns.length, cell -> (Expression) new NullLiteral()))); } public ValuesNode values(List columns, List> rows) @@ -345,14 +357,9 @@ public LateralJoinNode lateral(List correlation, PlanNode input, PlanNod } public TableScanNode tableScan(List symbols, Map assignments) - { - return tableScan(symbols, assignments, null); - } - - public TableScanNode tableScan(List symbols, Map assignments, Expression originalConstraint) { TableHandle tableHandle = new TableHandle(new ConnectorId("testConnector"), new TestingTableHandle()); - return tableScan(tableHandle, symbols, assignments, Optional.empty(), TupleDomain.all(), originalConstraint); + return tableScan(tableHandle, symbols, assignments, Optional.empty(), TupleDomain.all(), TupleDomain.all()); } public TableScanNode tableScan(TableHandle tableHandle, List symbols, Map assignments) @@ -366,7 +373,7 @@ public TableScanNode tableScan( Map assignments, Optional tableLayout) { - return tableScan(tableHandle, symbols, assignments, tableLayout, TupleDomain.all()); + return tableScan(tableHandle, symbols, assignments, tableLayout, TupleDomain.all(), TupleDomain.all()); } public TableScanNode tableScan( @@ -374,18 +381,8 @@ public TableScanNode tableScan( List symbols, Map assignments, Optional tableLayout, - TupleDomain tupleDomain) - { - return tableScan(tableHandle, symbols, assignments, tableLayout, tupleDomain, null); - } - - public TableScanNode tableScan( - TableHandle tableHandle, - List symbols, - Map assignments, - Optional tableLayout, - TupleDomain tupleDomain, - Expression originalConstraint) + TupleDomain currentConstraint, + TupleDomain enforcedConstraint) { return new TableScanNode( idAllocator.getNextId(), @@ -393,8 +390,8 @@ public TableScanNode tableScan( symbols, assignments, tableLayout, - tupleDomain, - originalConstraint); + currentConstraint, + enforcedConstraint); } public TableFinishNode tableDelete(SchemaTableName schemaTableName, PlanNode deleteSource, Symbol deleteRowId) @@ -719,6 +716,22 @@ public WindowNode window(WindowNode.Specification specification, Map partitionBy, Optional maxRowCountPerPartition, Symbol rowNumberSymbol, PlanNode source) + { + return new RowNumberNode( + idAllocator.getNextId(), + source, + partitionBy, + rowNumberSymbol, + maxRowCountPerPartition, + Optional.empty()); + } + + public RemoteSourceNode remoteSourceNode(List fragmentIds, List symbols, ExchangeNode.Type exchangeType) + { + return new RemoteSourceNode(idAllocator.getNextId(), fragmentIds, symbols, Optional.empty(), exchangeType); + } + public static Expression expression(String sql) { return ExpressionUtils.rewriteIdentifiersToSymbolReferences(new SqlParser().createExpression(sql)); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleAssert.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleAssert.java index 35a0975885c1b..a90e2b53cb60e 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleAssert.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleAssert.java @@ -19,8 +19,10 @@ import com.facebook.presto.cost.CostCalculator; import com.facebook.presto.cost.CostProvider; import com.facebook.presto.cost.PlanNodeStatsEstimate; +import com.facebook.presto.cost.StatsAndCosts; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.cost.StatsProvider; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.matching.Match; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; @@ -44,7 +46,6 @@ import java.util.function.Function; import java.util.stream.Stream; -import static com.facebook.presto.cost.PlanNodeCostEstimate.UNKNOWN_COST; import static com.facebook.presto.sql.planner.assertions.PlanAssert.assertPlan; import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textLogicalPlan; import static com.facebook.presto.transaction.TransactionBuilder.transaction; @@ -115,7 +116,7 @@ public void doesNotFire() fail(String.format( "Expected %s to not fire for:\n%s", rule.getClass().getName(), - inTransaction(session -> textLogicalPlan(plan, ruleApplication.types, metadata.getFunctionRegistry(), ruleApplication.statsProvider, node -> UNKNOWN_COST, session, 2)))); + inTransaction(session -> textLogicalPlan(plan, ruleApplication.types, metadata.getFunctionRegistry(), StatsAndCosts.empty(), session, 2)))); } } @@ -151,7 +152,7 @@ public void matches(PlanMatchPattern pattern) } inTransaction(session -> { - assertPlan(session, metadata, ruleApplication.statsProvider, new Plan(actual, types), ruleApplication.lookup, pattern); + assertPlan(session, metadata, ruleApplication.statsProvider, new Plan(actual, types, StatsAndCosts.empty()), ruleApplication.lookup, pattern); return null; }); } @@ -185,7 +186,9 @@ private static RuleApplication applyRule(Rule rule, PlanNode planNode, Ru private String formatPlan(PlanNode plan, TypeProvider types) { - return inTransaction(session -> textLogicalPlan(plan, types, metadata.getFunctionRegistry(), statsCalculator, costCalculator, session, 2, false)); + StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, session, types); + CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, session, types); + return inTransaction(session -> textLogicalPlan(plan, types, metadata.getFunctionRegistry(), StatsAndCosts.create(plan, statsProvider, costProvider), session, 2, false)); } private T inTransaction(Function transactionSessionConsumer) @@ -202,7 +205,7 @@ private T inTransaction(Function transactionSessionConsumer) private Rule.Context ruleContext(StatsCalculator statsCalculator, CostCalculator costCalculator, SymbolAllocator symbolAllocator, Memo memo, Lookup lookup, Session session) { StatsProvider statsProvider = new CachingStatsProvider(statsCalculator, Optional.of(memo), lookup, session, symbolAllocator.getTypes()); - CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.of(memo), lookup, session, symbolAllocator.getTypes()); + CostProvider costProvider = new CachingCostProvider(costCalculator, statsProvider, Optional.of(memo), session, symbolAllocator.getTypes()); return new Rule.Context() { @@ -244,6 +247,12 @@ public CostProvider getCostProvider() @Override public void checkTimeoutNotExhausted() {} + + @Override + public WarningCollector getWarningCollector() + { + return WarningCollector.NOOP; + } }; } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleTester.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleTester.java index 6c7fe31fdc451..3435642462355 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleTester.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/iterative/rule/test/RuleTester.java @@ -18,6 +18,8 @@ import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; import com.facebook.presto.spi.Plugin; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.planner.iterative.Rule; import com.facebook.presto.testing.LocalQueryRunner; import com.facebook.presto.tpch.TpchConnectorFactory; @@ -42,6 +44,8 @@ public class RuleTester private final Session session; private final LocalQueryRunner queryRunner; private final TransactionManager transactionManager; + private final SplitManager splitManager; + private final PageSourceManager pageSourceManager; private final AccessControl accessControl; public RuleTester() @@ -82,6 +86,8 @@ public RuleTester(List plugins, Map sessionProperties, O this.metadata = queryRunner.getMetadata(); this.transactionManager = queryRunner.getTransactionManager(); + this.splitManager = queryRunner.getSplitManager(); + this.pageSourceManager = queryRunner.getPageSourceManager(); this.accessControl = queryRunner.getAccessControl(); } @@ -101,6 +107,16 @@ public Metadata getMetadata() return metadata; } + public SplitManager getSplitManager() + { + return splitManager; + } + + public PageSourceManager getPageSourceManager() + { + return pageSourceManager; + } + public ConnectorId getCurrentConnectorId() { return queryRunner.inTransaction(transactionSession -> metadata.getCatalogHandle(transactionSession, session.getCatalog().get())).get(); diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestExpressionEquivalence.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestExpressionEquivalence.java index d6179da8b53d9..2199e88805868 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestExpressionEquivalence.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestExpressionEquivalence.java @@ -46,6 +46,7 @@ public class TestExpressionEquivalence @Test public void testEquivalent() { + assertEquivalent("CAST(null AS BIGINT)", "CAST(null as BIGINT)"); assertEquivalent("a_bigint < b_double", "b_double > a_bigint"); assertEquivalent("true", "true"); assertEquivalent("4", "4"); @@ -93,6 +94,10 @@ public void testEquivalent() assertEquivalent( "(a_boolean and b_boolean and c_boolean) or (d_boolean and e_boolean) or (f_boolean and g_boolean and h_boolean)", "(h_boolean and g_boolean and f_boolean) or (b_boolean and a_boolean and c_boolean) or (e_boolean and d_boolean)"); + + assertEquivalent( + "reduce(ARRAY [b_boolean], false, (s, x) -> s AND x, s -> s)", + "reduce(ARRAY [b_boolean], false, (s, x) -> x AND s, s -> s)"); } private static void assertEquivalent(@Language("SQL") String left, @Language("SQL") String right) @@ -116,6 +121,8 @@ private static void assertEquivalent(@Language("SQL") String left, @Language("SQ @Test public void testNotEquivalent() { + assertNotEquivalent("CAST(null AS BOOLEAN)", "false"); + assertNotEquivalent("false", "CAST(null AS BOOLEAN)"); assertNotEquivalent("true", "false"); assertNotEquivalent("4", "5"); assertNotEquivalent("4.4", "5.5"); @@ -139,6 +146,10 @@ public void testNotEquivalent() assertNotEquivalent("4 <= 5 or 6 < 7", "7 > 6 or 5 >= 6"); assertNotEquivalent("a_bigint <= b_bigint and c_bigint < d_bigint", "d_bigint > c_bigint and b_bigint >= c_bigint"); assertNotEquivalent("a_bigint <= b_bigint or c_bigint < d_bigint", "d_bigint > c_bigint or b_bigint >= c_bigint"); + + assertNotEquivalent( + "reduce(ARRAY [b_boolean], false, (s, x) -> s AND x, s -> s)", + "reduce(ARRAY [b_boolean], false, (s, x) -> s OR x, s -> s)"); } private static void assertNotEquivalent(@Language("SQL") String left, @Language("SQL") String right) diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestReorderWindows.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestReorderWindows.java index 0e8e3153d19d1..a91bae8be13a4 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestReorderWindows.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestReorderWindows.java @@ -228,7 +228,7 @@ public void testReorderAcrossProjectNodes() } @Test - public void testNotReorderAcrossFilter() + public void testNotReorderAcrossNonPartitionFilter() { @Language("SQL") String sql = "" + "SELECT " + @@ -246,13 +246,38 @@ public void testNotReorderAcrossFilter() window(windowMatcherBuilder -> windowMatcherBuilder .specification(windowA) .addFunction(functionCall("avg", commonFrame, ImmutableList.of(QUANTITY_ALIAS))), - filter( - RECEIPTDATE_ALIAS + " IS NOT NULL", - project( + project( + filter(RECEIPTDATE_ALIAS + " IS NOT NULL", window(windowMatcherBuilder -> windowMatcherBuilder .specification(windowApp) .addFunction(functionCall("avg", commonFrame, ImmutableList.of(DISCOUNT_ALIAS))), - LINEITEM_TABLESCAN_DOQRST)))))); // should be anyTree(LINEITEM_TABLESCAN_DOQRST) but anyTree does not handle zero nodes case correctly + LINEITEM_TABLESCAN_DOQRST)))))); // should be anyTree(LINEITEM_TABLESCAN_DOQPRSST) but anyTree does not handle zero nodes case correctly + } + + @Test + public void testReorderAcrossPartitionFilter() + { + @Language("SQL") String sql = "" + + "SELECT " + + " avg_discount_APP, " + + " AVG(quantity) OVER(PARTITION BY suppkey ORDER BY orderkey ASC NULLS LAST) avg_quantity_A " + + "FROM ( " + + " SELECT " + + " *, " + + " AVG(discount) OVER(PARTITION BY suppkey, tax ORDER BY receiptdate ASC NULLS LAST) avg_discount_APP " + + " FROM lineitem) " + + "WHERE suppkey > 0"; + + assertUnitPlan(sql, + anyTree( + window(windowMatcherBuilder -> windowMatcherBuilder + .specification(windowApp) + .addFunction(functionCall("avg", commonFrame, ImmutableList.of(DISCOUNT_ALIAS))), + window(windowMatcherBuilder -> windowMatcherBuilder + .specification(windowA) + .addFunction(functionCall("avg", commonFrame, ImmutableList.of(QUANTITY_ALIAS))), + filter("SUPPKEY > BIGINT '0'", + LINEITEM_TABLESCAN_DOQRST))))); } @Test @@ -297,6 +322,7 @@ private void assertUnitPlan(@Language("SQL") String sql, PlanMatchPattern patter { List optimizers = ImmutableList.of( new UnaliasSymbolReferences(), + new PredicatePushDown(getQueryRunner().getMetadata(), getQueryRunner().getSqlParser()), new IterativeOptimizer( new RuleStatsRecorder(), getQueryRunner().getStatsCalculator(), diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestWindowFilterPushDown.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestWindowFilterPushDown.java new file mode 100644 index 0000000000000..b0955575c3428 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestWindowFilterPushDown.java @@ -0,0 +1,96 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.planner.optimizations; + +import com.facebook.presto.Session; +import com.facebook.presto.sql.planner.assertions.BasePlanTest; +import com.facebook.presto.sql.planner.plan.FilterNode; +import com.facebook.presto.sql.planner.plan.TopNRowNumberNode; +import com.facebook.presto.sql.planner.plan.WindowNode; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.Test; + +import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_TOP_N_ROW_NUMBER; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.anyNot; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.anyTree; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.limit; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.node; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.tableScan; + +public class TestWindowFilterPushDown + extends BasePlanTest +{ + @Test + public void testLimitAboveWindow() + { + @Language("SQL") String sql = "SELECT " + + "row_number() OVER (PARTITION BY suppkey ORDER BY orderkey) partition_row_number FROM lineitem LIMIT 10"; + + assertPlanWithSession( + sql, + optimizeTopNRowNumber(true), + true, + anyTree( + limit(10, anyTree( + node(TopNRowNumberNode.class, + anyTree( + tableScan("lineitem"))))))); + + assertPlanWithSession( + sql, + optimizeTopNRowNumber(false), + true, + anyTree( + limit(10, anyTree( + node(WindowNode.class, + anyTree( + tableScan("lineitem"))))))); + } + + @Test + public void testFilterAboveWindow() + { + @Language("SQL") String sql = "SELECT * FROM " + + "(SELECT row_number() OVER (PARTITION BY suppkey ORDER BY orderkey) partition_row_number FROM lineitem) " + + "WHERE partition_row_number < 10"; + + assertPlanWithSession( + sql, + optimizeTopNRowNumber(true), + true, + anyTree( + anyNot(FilterNode.class, + node(TopNRowNumberNode.class, + anyTree( + tableScan("lineitem")))))); + + assertPlanWithSession( + sql, + optimizeTopNRowNumber(false), + true, + anyTree( + node(FilterNode.class, + anyTree( + node(WindowNode.class, + anyTree( + tableScan("lineitem"))))))); + } + + private Session optimizeTopNRowNumber(boolean enabled) + { + return Session.builder(this.getQueryRunner().getDefaultSession()) + .setSystemProperty(OPTIMIZE_TOP_N_ROW_NUMBER, Boolean.toString(enabled)) + .build(); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateAggregationsWithDefaultValues.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateAggregationsWithDefaultValues.java index af214e75d78af..dafbfc8344aeb 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateAggregationsWithDefaultValues.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateAggregationsWithDefaultValues.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.metadata.TableLayoutHandle; @@ -65,7 +66,7 @@ public void setup() ConnectorId connectorId = getCurrentConnectorId(); TableHandle nationTableHandle = new TableHandle( connectorId, - new TpchTableHandle(connectorId.toString(), "nation", 1.0)); + new TpchTableHandle("nation", 1.0)); TableLayoutHandle nationTableLayoutHandle = new TableLayoutHandle(connectorId, TestingTransactionHandle.create(), new TpchTableLayoutHandle((TpchTableHandle) nationTableHandle.getConnectorHandle(), TupleDomain.all())); @@ -195,7 +196,7 @@ private void validatePlan(PlanNode root, boolean forceSingleNode) getQueryRunner().inTransaction(session -> { // metadata.getCatalogHandle() registers the catalog for the transaction session.getCatalog().ifPresent(catalog -> metadata.getCatalogHandle(session, catalog)); - new ValidateAggregationsWithDefaultValues(forceSingleNode).validate(root, session, metadata, SQL_PARSER, TypeProvider.empty()); + new ValidateAggregationsWithDefaultValues(forceSingleNode).validate(root, session, metadata, SQL_PARSER, TypeProvider.empty(), WarningCollector.NOOP); return null; }); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateStreamingAggregations.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateStreamingAggregations.java index 1dcde69066dfb..cbafcc96bcb03 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateStreamingAggregations.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestValidateStreamingAggregations.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.planner.sanity; import com.facebook.presto.connector.ConnectorId; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.metadata.TableLayoutHandle; @@ -57,7 +58,7 @@ public void setup() ConnectorId connectorId = getCurrentConnectorId(); nationTableHandle = new TableHandle( connectorId, - new TpchTableHandle(connectorId.toString(), "nation", 1.0)); + new TpchTableHandle("nation", 1.0)); nationTableLayoutHandle = new TableLayoutHandle(connectorId, TestingTransactionHandle.create(), @@ -117,7 +118,7 @@ private void validatePlan(Function planProvider) getQueryRunner().inTransaction(session -> { // metadata.getCatalogHandle() registers the catalog for the transaction session.getCatalog().ifPresent(catalog -> metadata.getCatalogHandle(session, catalog)); - new ValidateStreamingAggregations().validate(planNode, session, metadata, sqlParser, types); + new ValidateStreamingAggregations().validate(planNode, session, metadata, sqlParser, types, WarningCollector.NOOP); return null; }); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestVerifyOnlyOneOutputNode.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestVerifyOnlyOneOutputNode.java index 71ea81cd195db..f1f448d45b1af 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestVerifyOnlyOneOutputNode.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/sanity/TestVerifyOnlyOneOutputNode.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.sql.planner.sanity; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.PlanNodeIdAllocator; import com.facebook.presto.sql.planner.Symbol; import com.facebook.presto.sql.planner.plan.Assignments; @@ -39,7 +40,7 @@ public void testValidateSuccessful() idAllocator.getNextId(), ImmutableList.of(), ImmutableList.of()), Assignments.of() ), ImmutableList.of(), ImmutableList.of()); - new VerifyOnlyOneOutputNode().validate(root, null, null, null, null); + new VerifyOnlyOneOutputNode().validate(root, null, null, null, null, WarningCollector.NOOP); } @Test(expectedExceptions = IllegalStateException.class) @@ -58,6 +59,6 @@ public void testValidateFailed() ), new Symbol("a"), false), ImmutableList.of(), ImmutableList.of()); - new VerifyOnlyOneOutputNode().validate(root, null, null, null, null); + new VerifyOnlyOneOutputNode().validate(root, null, null, null, null, WarningCollector.NOOP); } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/query/QueryAssertions.java b/presto-main/src/test/java/com/facebook/presto/sql/query/QueryAssertions.java index f659fbf2b2f4c..6d87df4b8719a 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/query/QueryAssertions.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/query/QueryAssertions.java @@ -14,6 +14,7 @@ package com.facebook.presto.sql.query; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.assertions.PlanAssert; import com.facebook.presto.sql.planner.assertions.PlanMatchPattern; @@ -72,7 +73,7 @@ public void assertQueryAndPlan( Consumer planValidator) { assertQuery(actual, expected); - Plan plan = runner.createPlan(runner.getDefaultSession(), actual); + Plan plan = runner.createPlan(runner.getDefaultSession(), actual, WarningCollector.NOOP); PlanAssert.assertPlan(runner.getDefaultSession(), runner.getMetadata(), runner.getStatsCalculator(), plan, pattern); planValidator.accept(plan); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/query/TestFilteredAggregations.java b/presto-main/src/test/java/com/facebook/presto/sql/query/TestFilteredAggregations.java index 89df920de2ac1..5b7c2749defbe 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/query/TestFilteredAggregations.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/query/TestFilteredAggregations.java @@ -13,11 +13,23 @@ */ package com.facebook.presto.sql.query; +import com.facebook.presto.sql.planner.LogicalPlanner; +import com.facebook.presto.sql.planner.assertions.BasePlanTest; +import com.facebook.presto.sql.planner.plan.FilterNode; +import com.google.common.collect.ImmutableMap; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.anyTree; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.filter; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.tableScan; +import static com.facebook.presto.sql.planner.optimizations.PlanNodeSearcher.searchFrom; +import static com.facebook.presto.util.MorePredicates.isInstanceOfAny; +import static org.testng.Assert.assertFalse; + public class TestFilteredAggregations + extends BasePlanTest { private QueryAssertions assertions; @@ -34,6 +46,22 @@ public void teardown() assertions = null; } + @Test + public void testAddPredicateForFilterClauses() + { + assertions.assertQuery( + "SELECT sum(x) FILTER(WHERE x > 0) FROM (VALUES 1, 1, 0, 2, 3, 3) t(x)", + "VALUES (BIGINT '10')"); + + assertions.assertQuery( + "SELECT sum(x) FILTER(WHERE x > 0), sum(x) FILTER(WHERE x < 3) FROM (VALUES 1, 1, 0, 5, 3, 8) t(x)", + "VALUES (BIGINT '18', BIGINT '2')"); + + assertions.assertQuery( + "SELECT sum(x) FILTER(WHERE x > 1), sum(x) FROM (VALUES 1, 1, 0, 2, 3, 3) t(x)", + "VALUES (BIGINT '8', BIGINT '10')"); + } + @Test public void testGroupAll() { @@ -86,4 +114,48 @@ public void testGroupingSets() "(2, BIGINT '4', BIGINT '1'), " + "(CAST(NULL AS INTEGER), BIGINT '5', BIGINT '2')"); } + + @Test + public void rewriteAddFilterWithMultipleFilters() + { + assertPlan( + "SELECT sum(totalprice) FILTER(WHERE totalprice > 0), sum(custkey) FILTER(WHERE custkey > 0) FROM orders", + anyTree( + filter( + "(\"totalprice\" > 0E0 OR \"custkey\" > BIGINT '0')", + tableScan( + "orders", ImmutableMap.of("totalprice", "totalprice", + "custkey", "custkey"))))); + } + + @Test + public void testDoNotPushdownPredicateIfNonFilteredAggregateIsPresent() + { + assertPlanContainsNoFilter("SELECT sum(totalprice) FILTER(WHERE totalprice > 0), sum(custkey) FROM orders"); + } + + @Test + public void testPushDownConstantFilterPredicate() + { + assertPlanContainsNoFilter("SELECT sum(totalprice) FILTER(WHERE FALSE) FROM orders"); + + assertPlanContainsNoFilter("SELECT sum(totalprice) FILTER(WHERE TRUE) FROM orders"); + } + + @Test + public void testNoFilterAddedForConstantValueFilters() + { + assertPlanContainsNoFilter("SELECT sum(x) FILTER(WHERE x > 0) FROM (VALUES 1, 1, 0, 2, 3, 3) t(x) GROUP BY x"); + + assertPlanContainsNoFilter("SELECT sum(totalprice) FILTER(WHERE totalprice > 0) FROM orders GROUP BY totalprice"); + } + + private void assertPlanContainsNoFilter(String sql) + { + assertFalse( + searchFrom(plan(sql, LogicalPlanner.Stage.OPTIMIZED).getRoot()) + .where(isInstanceOfAny(FilterNode.class)) + .matches(), + "Unexpected node for query: " + sql); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/query/TestGroupingSets.java b/presto-main/src/test/java/com/facebook/presto/sql/query/TestGroupingSets.java index af8202473e940..8bebe725e66b3 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/query/TestGroupingSets.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/query/TestGroupingSets.java @@ -50,4 +50,26 @@ public void testPredicateOverGroupingKeysWithEmptyGroupingSet() "WHERE a IS NOT NULL", "VALUES 1, 2"); } + + @Test + public void testDistinctWithMixedReferences() + { + assertions.assertQuery("" + + "SELECT a " + + "FROM (VALUES 1) t(a) " + + "GROUP BY DISTINCT ROLLUP(a, t.a)", + "VALUES (1), (NULL)"); + + assertions.assertQuery("" + + "SELECT a " + + "FROM (VALUES 1) t(a) " + + "GROUP BY DISTINCT GROUPING SETS ((a), (t.a))", + "VALUES 1"); + + assertions.assertQuery("" + + "SELECT a " + + "FROM (VALUES 1) t(a) " + + "GROUP BY DISTINCT a, GROUPING SETS ((), (t.a))", + "VALUES 1"); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/query/TestPrecomputedHashes.java b/presto-main/src/test/java/com/facebook/presto/sql/query/TestPrecomputedHashes.java new file mode 100644 index 0000000000000..02d0436fe567f --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/sql/query/TestPrecomputedHashes.java @@ -0,0 +1,61 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.sql.query; + +import com.facebook.presto.Session; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static com.facebook.presto.SystemSessionProperties.OPTIMIZE_HASH_GENERATION; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; + +public class TestPrecomputedHashes +{ + private QueryAssertions assertions; + + @BeforeClass + public void init() + { + Session session = testSessionBuilder() + .setSystemProperty(OPTIMIZE_HASH_GENERATION, "true") + .build(); + + assertions = new QueryAssertions(session); + } + + @AfterClass(alwaysRun = true) + public void teardown() + { + assertions.close(); + assertions = null; + } + + @Test + public void testDistinctLimit() + { + // issue #11593 + assertions.assertQuery( + "SELECT a " + + "FROM (" + + " SELECT a, b" + + " FROM (VALUES (1, 2)) t(a, b)" + + " WHERE a = 1" + + " GROUP BY a, b" + + " LIMIT 1" + + ")" + + "GROUP BY a", + "VALUES (1)"); + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/type/BenchmarkDecimalOperators.java b/presto-main/src/test/java/com/facebook/presto/type/BenchmarkDecimalOperators.java index 4357af5743662..e5a04341977ae 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/BenchmarkDecimalOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/BenchmarkDecimalOperators.java @@ -15,6 +15,7 @@ import com.facebook.presto.RowPagesBuilder; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.operator.DriverYieldSignal; import com.facebook.presto.operator.project.PageProcessor; @@ -62,6 +63,7 @@ import static com.facebook.presto.RowPagesBuilder.rowPagesBuilder; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; import static com.facebook.presto.metadata.FunctionKind.SCALAR; import static com.facebook.presto.metadata.MetadataManager.createTestMetadataManager; import static com.facebook.presto.operator.scalar.FunctionAssertions.createExpression; @@ -535,7 +537,12 @@ public void testDecimalToShortDecimalCastBenchmark() private Object execute(BaseState state) { - return ImmutableList.copyOf(state.getProcessor().process(SESSION, new DriverYieldSignal(), state.getInputPage())); + return ImmutableList.copyOf( + state.getProcessor().process( + SESSION, + new DriverYieldSignal(), + newSimpleAggregatedMemoryContext().newLocalMemoryContext(PageProcessor.class.getSimpleName()), + state.getInputPage())); } private static class BaseState @@ -611,7 +618,7 @@ private RowExpression rowExpression(String expression) Map types = sourceLayout.entrySet().stream() .collect(toMap(Map.Entry::getValue, entry -> symbolTypes.get(entry.getKey()))); - Map, Type> expressionTypes = getExpressionTypesFromInput(TEST_SESSION, metadata, SQL_PARSER, types, inputReferenceExpression, emptyList()); + Map, Type> expressionTypes = getExpressionTypesFromInput(TEST_SESSION, metadata, SQL_PARSER, types, inputReferenceExpression, emptyList(), WarningCollector.NOOP); return SqlToRowExpressionTranslator.translate(inputReferenceExpression, SCALAR, expressionTypes, metadata.getFunctionRegistry(), metadata.getTypeManager(), TEST_SESSION, true); } diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java index af06046729e44..e5474b99491c3 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestArrayOperators.java @@ -65,7 +65,6 @@ import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; @@ -88,7 +87,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -213,7 +211,7 @@ public void testArrayToJson() assertFunction( "CAST(ARRAY[TIMESTAMP '1970-01-01 00:00:01', null] AS JSON)", JSON, - format("[\"%s\",null]", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + format("[\"%s\",null]", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction( "CAST(ARRAY[DATE '2001-08-22', DATE '2001-08-23', null] AS JSON)", JSON, @@ -377,8 +375,8 @@ public void testConstructor() "ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01']", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION))); assertFunction("ARRAY [sqrt(-1)]", new ArrayType(DOUBLE), ImmutableList.of(NaN)); assertFunction("ARRAY [pow(infinity(), 2)]", new ArrayType(DOUBLE), ImmutableList.of(POSITIVE_INFINITY)); assertFunction("ARRAY [pow(-infinity(), 1)]", new ArrayType(DOUBLE), ImmutableList.of(NEGATIVE_INFINITY)); @@ -413,8 +411,8 @@ public void testArrayToArrayConcat() assertFunction("ARRAY [TRUE] || ARRAY [FALSE]", new ArrayType(BOOLEAN), ImmutableList.of(true, false)); assertFunction("concat(ARRAY [1] , ARRAY[2,3])", new ArrayType(INTEGER), ImmutableList.of(1, 2, 3)); assertFunction("ARRAY [TIMESTAMP '1970-01-01 00:00:01'] || ARRAY[TIMESTAMP '1973-07-08 22:00:01']", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION))); assertFunction("ARRAY [ARRAY[ARRAY[1]]] || ARRAY [ARRAY[ARRAY[2]]]", new ArrayType(new ArrayType(new ArrayType(INTEGER))), asList(singletonList(Ints.asList(1)), singletonList(Ints.asList(2)))); @@ -475,11 +473,11 @@ public void testElementArrayConcat() assertFunction("'puppies' || ARRAY ['kittens']", new ArrayType(createVarcharType(7)), Lists.newArrayList("puppies", "kittens")); assertFunction("ARRAY ['kittens'] || 'puppies'", new ArrayType(createVarcharType(7)), Lists.newArrayList("kittens", "puppies")); assertFunction("ARRAY [TIMESTAMP '1970-01-01 00:00:01'] || TIMESTAMP '1973-07-08 22:00:01'", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION))); assertFunction("TIMESTAMP '1973-07-08 22:00:01' || ARRAY [TIMESTAMP '1970-01-01 00:00:01']", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction("ARRAY [2, 8] || ARRAY[ARRAY[3, 6], ARRAY[4]]", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(2, 8), ImmutableList.of(3, 6), ImmutableList.of(4))); assertFunction("ARRAY [ARRAY [1], ARRAY [2, 8]] || ARRAY [3, 6]", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1), ImmutableList.of(2, 8), ImmutableList.of(3, 6))); assertFunction( @@ -533,6 +531,10 @@ public void testArrayContains() assertFunction("CONTAINS(ARRAY [2.2, 1.1], 0000000000001.100)", BOOLEAN, true); assertFunction("CONTAINS(ARRAY [2.2, 001.20], 1.2)", BOOLEAN, true); assertFunction("CONTAINS(ARRAY [ARRAY [1.1, 2.2], ARRAY [3.3, 4.3]], ARRAY [3.3, 4.300])", BOOLEAN, true); + assertFunction("CONTAINS(ARRAY [ARRAY [1.1, 2.2], ARRAY [3.3, 4.3]], ARRAY [1.3, null])", BOOLEAN, false); + + assertInvalidFunction("CONTAINS(ARRAY [ARRAY [1.1, 2.2], ARRAY [3.3, 4.3]], ARRAY [1.1, null])", NOT_SUPPORTED); + assertInvalidFunction("CONTAINS(ARRAY [ARRAY [1.1, null], ARRAY [3.3, 4.3]], ARRAY [1.1, null])", NOT_SUPPORTED); } @Test @@ -555,16 +557,16 @@ public void testArrayJoin() assertFunction("ARRAY_JOIN(ARRAY [sqrt(-1), infinity()], ',')", VARCHAR, "NaN,Infinity"); assertFunction("ARRAY_JOIN(ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'], '|')", VARCHAR, format( "%s|%s", - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION))); assertFunction( "ARRAY_JOIN(ARRAY [null, TIMESTAMP '1970-01-01 00:00:01'], '|')", VARCHAR, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION).toString()); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION).toString()); assertFunction( "ARRAY_JOIN(ARRAY [null, TIMESTAMP '1970-01-01 00:00:01'], '|', 'XYZ')", VARCHAR, - "XYZ|" + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION).toString()); + "XYZ|" + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION).toString()); assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.1, 3.3], 'x')", VARCHAR, "1.0x2.1x3.3"); assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.100, 3.3], 'x')", VARCHAR, "1.000x2.100x3.300"); assertFunction("ARRAY_JOIN(ARRAY [1.0, 2.100, NULL], 'x', 'N/A')", VARCHAR, "1.000x2.100xN/A"); @@ -678,6 +680,10 @@ public void testArrayPosition() assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3.0, 4.0], 000000000000000000000003.000)", BIGINT, 3L); assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3.0, 4.0], 3)", BIGINT, 3L); assertFunction("ARRAY_POSITION(ARRAY [1.0, 2.0, 3, 4.0], 4.0)", BIGINT, 4L); + assertFunction("ARRAY_POSITION(ARRAY [ARRAY[1]], ARRAY[1])", BIGINT, 1L); + + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[1])", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_POSITION(ARRAY [ARRAY[null]], ARRAY[null])", NOT_SUPPORTED); } @Test @@ -715,7 +721,7 @@ public void testSubscript() assertFunction( "ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'][1]", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION)); assertFunction("ARRAY [infinity()][1]", DOUBLE, POSITIVE_INFINITY); assertFunction("ARRAY [-infinity()][1]", DOUBLE, NEGATIVE_INFINITY); assertFunction("ARRAY [sqrt(-1)][1]", DOUBLE, NaN); @@ -767,11 +773,11 @@ public void testElementAt() assertFunction( "ELEMENT_AT(ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'], 1)", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION)); assertFunction( "ELEMENT_AT(ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'], -2)", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION)); assertFunction("ELEMENT_AT(ARRAY [infinity()], 1)", DOUBLE, POSITIVE_INFINITY); assertFunction("ELEMENT_AT(ARRAY [infinity()], -1)", DOUBLE, POSITIVE_INFINITY); assertFunction("ELEMENT_AT(ARRAY [-infinity()], 1)", DOUBLE, NEGATIVE_INFINITY); @@ -812,9 +818,9 @@ public void testSort() "ARRAY_SORT(ARRAY [TIMESTAMP '1973-07-08 22:00:01', TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1989-02-06 12:00:00'])", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1989, 2, 6, 12, 0, 0, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1989, 2, 6, 12, 0, 0, 0, TEST_SESSION))); assertFunction("ARRAY_SORT(ARRAY [ARRAY [1], ARRAY [2]])", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(1), ImmutableList.of(2))); @@ -895,9 +901,9 @@ public void testSort() asList( null, null, - sqlTimestampOf(1989, 2, 6, 12, 0, 0, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1989, 2, 6, 12, 0, 0, 0, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction( "ARRAY_SORT(ARRAY[ARRAY[2, 3, 1], null, ARRAY[4, null, 2, 1, 4], ARRAY[1, 2], null], (x, y) -> CASE " + "WHEN x IS NULL THEN -1 " + @@ -963,16 +969,21 @@ public void testDistinct() assertFunction("ARRAY_DISTINCT(ARRAY [])", new ArrayType(UNKNOWN), ImmutableList.of()); // Order matters here. Result should be stable. + assertFunction("ARRAY_DISTINCT(ARRAY [0, NULL])", new ArrayType(INTEGER), asList(0, null)); + assertFunction("ARRAY_DISTINCT(ARRAY [0, NULL, 0, NULL])", new ArrayType(INTEGER), asList(0, null)); assertFunction("ARRAY_DISTINCT(ARRAY [2, 3, 4, 3, 1, 2, 3])", new ArrayType(INTEGER), ImmutableList.of(2, 3, 4, 1)); + assertFunction("ARRAY_DISTINCT(ARRAY [0.0E0, NULL])", new ArrayType(DOUBLE), asList(0.0, null)); assertFunction("ARRAY_DISTINCT(ARRAY [2.2E0, 3.3E0, 4.4E0, 3.3E0, 1, 2.2E0, 3.3E0])", new ArrayType(DOUBLE), ImmutableList.of(2.2, 3.3, 4.4, 1.0)); + assertFunction("ARRAY_DISTINCT(ARRAY [FALSE, NULL])", new ArrayType(BOOLEAN), asList(false, null)); + assertFunction("ARRAY_DISTINCT(ARRAY [FALSE, TRUE, NULL])", new ArrayType(BOOLEAN), asList(false, true, null)); assertFunction("ARRAY_DISTINCT(ARRAY [TRUE, TRUE, TRUE])", new ArrayType(BOOLEAN), ImmutableList.of(true)); assertFunction("ARRAY_DISTINCT(ARRAY [TRUE, FALSE, FALSE, TRUE])", new ArrayType(BOOLEAN), ImmutableList.of(true, false)); assertFunction( "ARRAY_DISTINCT(ARRAY [TIMESTAMP '1973-07-08 22:00:01', TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'])", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION), + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction("ARRAY_DISTINCT(ARRAY ['2', '3', '2'])", new ArrayType(createVarcharType(1)), ImmutableList.of("2", "3")); assertFunction("ARRAY_DISTINCT(ARRAY ['BB', 'CCC', 'BB'])", new ArrayType(createVarcharType(3)), ImmutableList.of("BB", "CCC")); assertFunction( @@ -1097,29 +1108,99 @@ public void testArraysOverlap() @Test public void testArrayIntersect() { - assertFunction("ARRAY_INTERSECT(ARRAY [12], ARRAY [10])", new ArrayType(INTEGER), ImmutableList.of()); - assertFunction("ARRAY_INTERSECT(ARRAY ['foo', 'bar', 'baz'], ARRAY ['foo', 'test', 'bar'])", new ArrayType(createVarcharType(4)), ImmutableList.of("bar", "foo")); - assertFunction("ARRAY_INTERSECT(ARRAY [NULL], ARRAY [NULL, NULL])", new ArrayType(UNKNOWN), asList((Object) null)); - assertFunction("ARRAY_INTERSECT(ARRAY ['abc', NULL, 'xyz', NULL], ARRAY [NULL, 'abc', NULL, NULL])", new ArrayType(createVarcharType(3)), asList(null, "abc")); - assertFunction("ARRAY_INTERSECT(ARRAY [1, 5], ARRAY [1])", new ArrayType(INTEGER), ImmutableList.of(1)); - assertFunction("ARRAY_INTERSECT(ARRAY [1, 1, 2, 4], ARRAY [1, 1, 4, 4])", new ArrayType(INTEGER), ImmutableList.of(1, 4)); - assertFunction("ARRAY_INTERSECT(ARRAY [2, 8], ARRAY [8, 3])", new ArrayType(INTEGER), ImmutableList.of(8)); + // test basic + assertFunction("ARRAY_INTERSECT(ARRAY [5], ARRAY [5])", new ArrayType(INTEGER), ImmutableList.of(5)); + assertFunction("ARRAY_INTERSECT(ARRAY [1, 2, 5, 5, 6], ARRAY [5, 5, 6, 6, 7, 8])", new ArrayType(INTEGER), ImmutableList.of(5, 6)); assertFunction("ARRAY_INTERSECT(ARRAY [IF (RAND() < 1.0E0, 7, 1) , 2], ARRAY [7])", new ArrayType(INTEGER), ImmutableList.of(7)); + assertFunction("ARRAY_INTERSECT(ARRAY [CAST(5 AS BIGINT), CAST(5 AS BIGINT)], ARRAY [CAST(1 AS BIGINT), CAST(5 AS BIGINT)])", new ArrayType(BIGINT), ImmutableList.of(5L)); assertFunction("ARRAY_INTERSECT(ARRAY [1, 5], ARRAY [1.0E0])", new ArrayType(DOUBLE), ImmutableList.of(1.0)); + assertFunction("ARRAY_INTERSECT(ARRAY [1.0E0, 5.0E0], ARRAY [5.0E0, 5.0E0, 6.0E0])", new ArrayType(DOUBLE), ImmutableList.of(5.0)); assertFunction("ARRAY_INTERSECT(ARRAY [8.3E0, 1.6E0, 4.1E0, 5.2E0], ARRAY [4.0E0, 5.2E0, 8.3E0, 9.7E0, 3.5E0])", new ArrayType(DOUBLE), ImmutableList.of(5.2, 8.3)); - assertFunction("ARRAY_INTERSECT(ARRAY [5.1E0, 7, 3.0E0, 4.8E0, 10], ARRAY [6.5E0, 10.0E0, 1.9E0, 5.1E0, 3.9E0, 4.8E0])", new ArrayType(DOUBLE), ImmutableList.of(4.8, 5.1, 10.0)); - assertFunction("ARRAY_INTERSECT(ARRAY [ARRAY [4, 5], ARRAY [6, 7]], ARRAY [ARRAY [4, 5], ARRAY [6, 8]])", new ArrayType(new ArrayType(INTEGER)), ImmutableList.of(ImmutableList.of(4, 5))); - - assertCachedInstanceHasBoundedRetainedSize("ARRAY_INTERSECT(ARRAY ['foo', 'bar', 'baz'], ARRAY ['foo', 'test', 'bar'])"); - + assertFunction("ARRAY_INTERSECT(ARRAY [5.1E0, 7, 3.0E0, 4.8E0, 10], ARRAY [6.5E0, 10.0E0, 1.9E0, 5.1E0, 3.9E0, 4.8E0])", new ArrayType(DOUBLE), ImmutableList.of(10.0, 5.1, 4.8)); assertFunction( "ARRAY_INTERSECT(ARRAY [2.3, 2.3, 2.2], ARRAY[2.2, 2.3])", new ArrayType(createDecimalType(2, 1)), - ImmutableList.of(decimal("2.2"), decimal("2.3"))); + ImmutableList.of(decimal("2.3"), decimal("2.2"))); assertFunction("ARRAY_INTERSECT(ARRAY [2.330, 1.900, 2.330], ARRAY [2.3300, 1.9000])", new ArrayType(createDecimalType(5, 4)), - ImmutableList.of(decimal("1.9000"), decimal("2.3300"))); + ImmutableList.of(decimal("2.3300"), decimal("1.9000"))); assertFunction("ARRAY_INTERSECT(ARRAY [2, 3], ARRAY[2.0, 3.0])", new ArrayType(createDecimalType(11, 1)), ImmutableList.of(decimal("00000000002.0"), decimal("00000000003.0"))); + assertFunction("ARRAY_INTERSECT(ARRAY [true], ARRAY [true])", new ArrayType(BOOLEAN), ImmutableList.of(true)); + assertFunction("ARRAY_INTERSECT(ARRAY [true, false], ARRAY [true])", new ArrayType(BOOLEAN), ImmutableList.of(true)); + assertFunction("ARRAY_INTERSECT(ARRAY [true, true], ARRAY [true, true])", new ArrayType(BOOLEAN), ImmutableList.of(true)); + assertFunction("ARRAY_INTERSECT(ARRAY ['abc'], ARRAY ['abc', 'bcd'])", new ArrayType(createVarcharType(3)), ImmutableList.of("abc")); + assertFunction("ARRAY_INTERSECT(ARRAY ['abc', 'abc'], ARRAY ['abc', 'abc'])", new ArrayType(createVarcharType(3)), ImmutableList.of("abc")); + assertFunction("ARRAY_INTERSECT(ARRAY ['foo', 'bar', 'baz'], ARRAY ['foo', 'test', 'bar'])", new ArrayType(createVarcharType(4)), ImmutableList.of("foo", "bar")); + + // test empty results + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY [5])", new ArrayType(INTEGER), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [5, 6], ARRAY [])", new ArrayType(INTEGER), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [1], ARRAY [5])", new ArrayType(INTEGER), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [CAST(1 AS BIGINT)], ARRAY [CAST(5 AS BIGINT)])", new ArrayType(BIGINT), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [true, true], ARRAY [false])", new ArrayType(BOOLEAN), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY [false])", new ArrayType(BOOLEAN), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [5], ARRAY [1.0E0])", new ArrayType(DOUBLE), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY ['abc'], ARRAY [])", new ArrayType(createVarcharType(3)), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY ['abc', 'bcd'])", new ArrayType(createVarcharType(3)), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY [])", new ArrayType(UNKNOWN), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY [NULL])", new ArrayType(UNKNOWN), ImmutableList.of()); + + // test nulls + assertFunction("ARRAY_INTERSECT(ARRAY [NULL], ARRAY [NULL, NULL])", new ArrayType(UNKNOWN), asList((Object) null)); + assertFunction("ARRAY_INTERSECT(ARRAY [0, 0, 1, NULL], ARRAY [0, 0, 1, NULL])", new ArrayType(INTEGER), asList(0, 1, null)); + assertFunction("ARRAY_INTERSECT(ARRAY [0, 0], ARRAY [0, 0, NULL])", new ArrayType(INTEGER), ImmutableList.of(0)); + assertFunction("ARRAY_INTERSECT(ARRAY [CAST(0 AS BIGINT), CAST(0 AS BIGINT)], ARRAY [CAST(0 AS BIGINT), NULL])", new ArrayType(BIGINT), ImmutableList.of(0L)); + assertFunction("ARRAY_INTERSECT(ARRAY [0.0E0], ARRAY [NULL])", new ArrayType(DOUBLE), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [0.0E0, NULL], ARRAY [0.0E0, NULL])", new ArrayType(DOUBLE), asList(0.0, null)); + assertFunction("ARRAY_INTERSECT(ARRAY [true, true, false, false, NULL], ARRAY [true, false, false, NULL])", new ArrayType(BOOLEAN), asList(true, false, null)); + assertFunction("ARRAY_INTERSECT(ARRAY [false, false], ARRAY [false, false, NULL])", new ArrayType(BOOLEAN), ImmutableList.of(false)); + assertFunction("ARRAY_INTERSECT(ARRAY ['abc'], ARRAY [NULL])", new ArrayType(createVarcharType(3)), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [''], ARRAY ['', NULL])", new ArrayType(createVarcharType(0)), ImmutableList.of("")); + assertFunction("ARRAY_INTERSECT(ARRAY ['', NULL], ARRAY ['', NULL])", new ArrayType(createVarcharType(0)), asList("", null)); + assertFunction("ARRAY_INTERSECT(ARRAY [NULL], ARRAY ['abc', NULL])", new ArrayType(createVarcharType(3)), singletonList(null)); + assertFunction("ARRAY_INTERSECT(ARRAY ['abc', NULL, 'xyz', NULL], ARRAY [NULL, 'abc', NULL, NULL])", new ArrayType(createVarcharType(3)), asList("abc", null)); + assertFunction("ARRAY_INTERSECT(ARRAY [], ARRAY [NULL])", new ArrayType(UNKNOWN), ImmutableList.of()); + assertFunction("ARRAY_INTERSECT(ARRAY [NULL], ARRAY [NULL])", new ArrayType(UNKNOWN), singletonList(null)); + + // test composite types + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 456), (123, 789)], ARRAY[(123, 456), (123, 456), (123, 789)])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, INTEGER))), + ImmutableList.of(asList(123, 456), asList(123, 789))); + assertFunction( + "ARRAY_INTERSECT(ARRAY[ARRAY[123, 456], ARRAY[123, 789]], ARRAY[ARRAY[123, 456], ARRAY[123, 456], ARRAY[123, 789]])", + new ArrayType(new ArrayType((INTEGER))), + ImmutableList.of(asList(123, 456), asList(123, 789))); + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 'abc'), (123, 'cde')], ARRAY[(123, 'abc'), (123, 'cde')])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, createVarcharType(3)))), + ImmutableList.of(asList(123, "abc"), asList(123, "cde"))); + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 'abc'), (123, 'cde'), NULL], ARRAY[(123, 'abc'), (123, 'cde')])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, createVarcharType(3)))), + ImmutableList.of(asList(123, "abc"), asList(123, "cde"))); + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 'abc'), (123, 'cde'), NULL, NULL], ARRAY[(123, 'abc'), (123, 'cde'), NULL])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, createVarcharType(3)))), + asList(asList(123, "abc"), asList(123, "cde"), null)); + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 'abc'), (123, 'abc')], ARRAY[(123, 'abc'), (123, NULL)])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, createVarcharType(3)))), + ImmutableList.of(asList(123, "abc"))); + assertFunction( + "ARRAY_INTERSECT(ARRAY[(123, 'abc')], ARRAY[(123, NULL)])", + new ArrayType(RowType.anonymous(ImmutableList.of(INTEGER, createVarcharType(3)))), + ImmutableList.of()); + + // test unsupported + assertNotSupported( + "ARRAY_INTERSECT(ARRAY[(123, 'abc'), (123, NULL)], ARRAY[(123, 'abc'), (123, NULL)])", + "ROW comparison not supported for fields with null elements"); + assertNotSupported( + "ARRAY_INTERSECT(ARRAY[(NULL, 'abc'), (123, 'abc')], ARRAY[(123, 'abc'),(NULL, 'abc')])", + "ROW comparison not supported for fields with null elements"); + + assertCachedInstanceHasBoundedRetainedSize("ARRAY_INTERSECT(ARRAY ['foo', 'bar', 'baz'], ARRAY ['foo', 'test', 'bar'])"); } @Test @@ -1169,6 +1250,19 @@ public void testComparison() "!= ARRAY [1234567890.1234567890, 9876543210.9876543210, 123123123456.6549876543]", BOOLEAN, false); assertFunction("ARRAY [1234567890.1234567890, 9876543210.9876543210, 123123123456.6549876543] " + "!= ARRAY [1234567890.1234567890, 9876543210.9876543210, 0]", BOOLEAN, true); + assertFunction("ARRAY [1, 2, null] = ARRAY [1, null]", BOOLEAN, false); + assertFunction("ARRAY ['1', '2', null] = ARRAY ['1', null]", BOOLEAN, false); + assertFunction("ARRAY [1.0, 2.0, null] = ARRAY [1.0, null]", BOOLEAN, false); + assertFunction("ARRAY [1.0E0, 2.0E0, null] = ARRAY [1.0E0, null]", BOOLEAN, false); + assertFunction("ARRAY [1, 2, null] = ARRAY [1, 2, null]", BOOLEAN, null); + assertFunction("ARRAY ['1', '2', null] = ARRAY ['1', '2', null]", BOOLEAN, null); + assertFunction("ARRAY [1.0, 2.0, null] = ARRAY [1.0, 2.0, null]", BOOLEAN, null); + assertFunction("ARRAY [1.0E0, 2.0E0, null] = ARRAY [1.0E0, 2.0E0, null]", BOOLEAN, null); + assertFunction("ARRAY [1, 3, null] = ARRAY [1, 2, null]", BOOLEAN, false); + assertFunction("ARRAY [1E0, 3E0, null] = ARRAY [1E0, 2E0, null]", BOOLEAN, false); + assertFunction("ARRAY ['1', '3', null] = ARRAY ['1', '2', null]", BOOLEAN, false); + assertFunction("ARRAY [ARRAY[1], ARRAY[null], ARRAY[2]] = ARRAY [ARRAY[1], ARRAY[2], ARRAY[3]]", BOOLEAN, false); + assertFunction("ARRAY [ARRAY[1], ARRAY[null], ARRAY[3]] = ARRAY [ARRAY[1], ARRAY[2], ARRAY[3]]", BOOLEAN, null); assertFunction("ARRAY [10, 20, 30] != ARRAY [5]", BOOLEAN, true); assertFunction("ARRAY [10, 20, 30] = ARRAY [5]", BOOLEAN, false); @@ -1196,6 +1290,11 @@ public void testComparison() assertFunction("ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]] = ARRAY [ARRAY [1, 2, 3], ARRAY [4, 5]]", BOOLEAN, false); assertFunction("ARRAY [1.0, 2.0, 3.0] = ARRAY [1.0, 2.0]", BOOLEAN, false); assertFunction("ARRAY [1.0, 2.0, 3.0] != ARRAY [1.0, 2.0]", BOOLEAN, true); + assertFunction("ARRAY [1, 2, null] != ARRAY [1, 2, null]", BOOLEAN, null); + assertFunction("ARRAY [1, 2, null] != ARRAY [1, null]", BOOLEAN, true); + assertFunction("ARRAY [1, 3, null] != ARRAY [1, 2, null]", BOOLEAN, true); + assertFunction("ARRAY [ARRAY[1], ARRAY[null], ARRAY[2]] != ARRAY [ARRAY[1], ARRAY[2], ARRAY[3]]", BOOLEAN, true); + assertFunction("ARRAY [ARRAY[1], ARRAY[null], ARRAY[3]] != ARRAY [ARRAY[1], ARRAY[2], ARRAY[3]]", BOOLEAN, null); assertFunction("ARRAY [10, 20, 30] < ARRAY [10, 20, 40, 50]", BOOLEAN, true); assertFunction("ARRAY [10, 20, 30] >= ARRAY [10, 20, 40, 50]", BOOLEAN, false); @@ -1318,8 +1417,6 @@ public void testComparison() assertFunction("ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]] < ARRAY [ARRAY [1, 2], ARRAY [3, 4]]", BOOLEAN, false); assertFunction("ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]] >= ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]]", BOOLEAN, true); assertFunction("ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]] < ARRAY [ARRAY [1, 2], ARRAY [3, 4, 5]]", BOOLEAN, false); - - assertInvalidFunction("ARRAY [1, NULL] = ARRAY [1, 2]", NOT_SUPPORTED.toErrorCode()); } @Test @@ -1336,9 +1433,15 @@ public void testDistinctFrom() assertFunction("ARRAY [1, NULL] IS DISTINCT FROM ARRAY [1, NULL]", BOOLEAN, false); assertFunction("ARRAY [1, NULL] IS DISTINCT FROM ARRAY [1, NULL]", BOOLEAN, false); assertFunction("ARRAY [1, 2, NULL] IS DISTINCT FROM ARRAY [1, 2]", BOOLEAN, true); + assertFunction("ARRAY [TRUE, FALSE] IS DISTINCT FROM ARRAY [TRUE, FALSE]", BOOLEAN, false); + assertFunction("ARRAY [TRUE, NULL] IS DISTINCT FROM ARRAY [TRUE, FALSE]", BOOLEAN, true); + assertFunction("ARRAY [FALSE, NULL] IS DISTINCT FROM ARRAY [NULL, FALSE]", BOOLEAN, true); assertFunction("ARRAY ['puppies', 'kittens'] IS DISTINCT FROM ARRAY ['puppies', 'kittens']", BOOLEAN, false); assertFunction("ARRAY ['puppies', NULL] IS DISTINCT FROM ARRAY ['puppies', 'kittens']", BOOLEAN, true); assertFunction("ARRAY ['puppies', NULL] IS DISTINCT FROM ARRAY [NULL, 'kittens']", BOOLEAN, true); + assertFunction("ARRAY [ARRAY ['puppies'], ARRAY ['kittens']] IS DISTINCT FROM ARRAY [ARRAY ['puppies'], ARRAY ['kittens']]", BOOLEAN, false); + assertFunction("ARRAY [ARRAY ['puppies'], NULL] IS DISTINCT FROM ARRAY [ARRAY ['puppies'], ARRAY ['kittens']]", BOOLEAN, true); + assertFunction("ARRAY [ARRAY ['puppies'], NULL] IS DISTINCT FROM ARRAY [NULL, ARRAY ['kittens']]", BOOLEAN, true); } @Test @@ -1387,6 +1490,10 @@ public void testArrayRemove() new ArrayType(createDecimalType(22, 10)), ImmutableList.of(decimal("1234567890.1234567890"), decimal("9876543210.9876543210"), decimal("123123123456.6549876543"))); assertCachedInstanceHasBoundedRetainedSize("ARRAY_REMOVE(ARRAY ['foo', 'bar', 'baz'], 'foo')"); + + assertInvalidFunction("ARRAY_REMOVE(ARRAY [ARRAY[CAST(null AS BIGINT)]], ARRAY[CAST(1 AS BIGINT)])", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_REMOVE(ARRAY [ARRAY[CAST(null AS BIGINT)]], ARRAY[CAST(null AS BIGINT)])", NOT_SUPPORTED); + assertInvalidFunction("ARRAY_REMOVE(ARRAY [ARRAY[CAST(1 AS BIGINT)]], ARRAY[CAST(null AS BIGINT)])", NOT_SUPPORTED); } @Test @@ -1527,46 +1634,46 @@ public void testSequenceDateTimeDayToSecond() "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2016-04-16 01:07:00', interval '3' minute)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 3, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 6, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 3, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 6, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:10:10', timestamp '2016-04-16 01:03:00', interval '-3' minute)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 10, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 7, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 4, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 10, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 7, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 4, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2016-04-16 01:01:00', interval '20' second)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 0, 30, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 0, 50, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 0, 30, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 0, 50, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:01:10', timestamp '2016-04-16 01:00:20', interval '-20' second)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 1, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 0, 50, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 1, 0, 30, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 1, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 0, 50, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 1, 0, 30, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2016-04-18 01:01:00', interval '19' hour)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 16, 20, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 17, 15, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 16, 20, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 17, 15, 0, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2016-04-14 01:00:20', interval '-19' hour)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 15, 6, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 4, 14, 11, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 15, 6, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 4, 14, 11, 0, 10, 0, TEST_SESSION))); // failure modes assertInvalidFunction( @@ -1634,31 +1741,31 @@ public void testSequenceDateTimeYearToMonth() "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2016-09-16 01:10:00', interval '2' month)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 6, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 8, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 6, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 8, 16, 1, 0, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-09-16 01:10:10', timestamp '2016-04-16 01:00:00', interval '-2' month)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 9, 16, 1, 10, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 7, 16, 1, 10, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2016, 5, 16, 1, 10, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 9, 16, 1, 10, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 7, 16, 1, 10, 10, 0, TEST_SESSION), + sqlTimestampOf(2016, 5, 16, 1, 10, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:00:10', timestamp '2021-04-16 01:01:00', interval '2' year)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2018, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2020, 4, 16, 1, 0, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2018, 4, 16, 1, 0, 10, 0, TEST_SESSION), + sqlTimestampOf(2020, 4, 16, 1, 0, 10, 0, TEST_SESSION))); assertFunction( "SEQUENCE(timestamp '2016-04-16 01:01:10', timestamp '2011-04-16 01:00:00', interval '-2' year)", new ArrayType(TIMESTAMP), ImmutableList.of( - sqlTimestampOf(2016, 4, 16, 1, 1, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2014, 4, 16, 1, 1, 10, 0, UTC, UTC_KEY, TEST_SESSION), - sqlTimestampOf(2012, 4, 16, 1, 1, 10, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(2016, 4, 16, 1, 1, 10, 0, TEST_SESSION), + sqlTimestampOf(2014, 4, 16, 1, 1, 10, 0, TEST_SESSION), + sqlTimestampOf(2012, 4, 16, 1, 1, 10, 0, TEST_SESSION))); // failure modes assertInvalidFunction( diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestConventionDependencies.java b/presto-main/src/test/java/com/facebook/presto/type/TestConventionDependencies.java index 792dfb4562a13..70303de488f19 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestConventionDependencies.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestConventionDependencies.java @@ -28,10 +28,10 @@ import java.lang.invoke.MethodHandle; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; -import static com.facebook.presto.spi.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; -import static com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.BLOCK_POSITION; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.google.common.base.Throwables.throwIfInstanceOf; diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestDateBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestDateBase.java index a72e909c62949..67212fb114393 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestDateBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestDateBase.java @@ -129,7 +129,7 @@ public void testCastToTimestamp() { assertFunction("cast(DATE '2001-1-22' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, session)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperators.java index 0f3dfd00b6171..a6b15fdbaa49a 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperators.java @@ -50,28 +50,28 @@ public void testTimeZoneGap() assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 2, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 2, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 3, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 3, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-03-31 04:05' - INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-03-31 03:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-03-31 01:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); } @Test @@ -80,42 +80,42 @@ public void testDaylightTimeSaving() assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 3, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 3, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '4' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 4, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 4, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '4' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 26, 23, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 26, 23, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 02:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 01:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); + sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session.toConnectorSession())); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsBase.java index 41591d5ad1911..40650815126dd 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsBase.java @@ -43,7 +43,7 @@ public abstract class TestDateTimeOperatorsBase extends AbstractTestFunctions { protected static final TimeZoneKey TIME_ZONE_KEY = getTimeZoneKey("Europe/Berlin"); - protected static final DateTimeZone TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); + protected static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); protected static final DateTimeZone WEIRD_TIME_ZONE = DateTimeZone.forOffsetHoursMinutes(5, 9); protected static final TimeZoneKey WEIRD_TIME_ZONE_KEY = getTimeZoneKeyForOffset(5 * 60 + 9); @@ -122,28 +122,28 @@ public void testTimestampPlusInterval() { assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' + INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 6, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 6, 4, 5, 321, session)); assertFunction("INTERVAL '3' hour + TIMESTAMP '2001-1-22 03:04:05.321'", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 6, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 6, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' + INTERVAL '3' day", TIMESTAMP, - sqlTimestampOf(2001, 1, 25, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 25, 3, 4, 5, 321, session)); assertFunction("INTERVAL '3' day + TIMESTAMP '2001-1-22 03:04:05.321'", TIMESTAMP, - sqlTimestampOf(2001, 1, 25, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 25, 3, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' + INTERVAL '3' month", TIMESTAMP, - sqlTimestampOf(2001, 4, 22, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 4, 22, 3, 4, 5, 321, session)); assertFunction("INTERVAL '3' month + TIMESTAMP '2001-1-22 03:04:05.321'", TIMESTAMP, - sqlTimestampOf(2001, 4, 22, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 4, 22, 3, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' + INTERVAL '3' year", TIMESTAMP, - sqlTimestampOf(2004, 1, 22, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2004, 1, 22, 3, 4, 5, 321, session)); assertFunction("INTERVAL '3' year + TIMESTAMP '2001-1-22 03:04:05.321'", TIMESTAMP, - sqlTimestampOf(2004, 1, 22, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2004, 1, 22, 3, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321 +05:09' + INTERVAL '3' hour", TIMESTAMP_WITH_TIME_ZONE, @@ -211,13 +211,13 @@ public void testTimestampMinusInterval() { assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' - INTERVAL '3' day", TIMESTAMP, - sqlTimestampOf(2001, 1, 19, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 19, 3, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321 +05:09' - INTERVAL '3' day", TIMESTAMP_WITH_TIME_ZONE, new SqlTimestampWithTimeZone(new DateTime(2001, 1, 19, 3, 4, 5, 321, WEIRD_TIME_ZONE).getMillis(), WEIRD_TIME_ZONE_KEY)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321' - INTERVAL '3' month", TIMESTAMP, - sqlTimestampOf(2000, 10, 22, 3, 4, 5, 321, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2000, 10, 22, 3, 4, 5, 321, session)); assertFunction("TIMESTAMP '2001-1-22 03:04:05.321 +05:09' - INTERVAL '3' month", TIMESTAMP_WITH_TIME_ZONE, new SqlTimestampWithTimeZone(new DateTime(2000, 10, 22, 3, 4, 5, 321, WEIRD_TIME_ZONE).getMillis(), WEIRD_TIME_ZONE_KEY)); diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsLegacy.java b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsLegacy.java index ac17ac3c718bc..44cfa8e8d7ab5 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsLegacy.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestDateTimeOperatorsLegacy.java @@ -51,28 +51,28 @@ public void testTimeZoneGap() assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 1, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 3, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 3, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-03-31 00:05' + INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 4, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 4, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-03-31 04:05' - INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-03-31 03:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-03-31 01:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 31, 0, 5, 0, 0, session)); } @Test @@ -82,42 +82,42 @@ public void testDaylightTimeSaving() assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 1, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, session)); // we need to manipulate millis directly here because 2 am has two representations in out time zone, and we need the second one assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '3' hour", TIMESTAMP, - sqlTimestampOf(new DateTime(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE).plus(HOURS.toMillis(3)), session)); + sqlTimestampOf(new DateTime(2013, 10, 27, 0, 5, 0, 0, DATE_TIME_ZONE).plus(HOURS.toMillis(3)), session)); assertFunction( "TIMESTAMP '2013-10-27 00:05' + INTERVAL '4' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 3, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 3, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '4' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-10-27 02:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-10-27 01:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 0, 5, 0, 0, session)); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '1' hour", TIMESTAMP, - sqlTimestampOf(new DateTime(2013, 10, 27, 0, 5, 0, 0, TIME_ZONE).plus(HOURS.toMillis(3)), session)); + sqlTimestampOf(new DateTime(2013, 10, 27, 0, 5, 0, 0, DATE_TIME_ZONE).plus(HOURS.toMillis(3)), session)); assertFunction( "TIMESTAMP '2013-10-27 03:05' - INTERVAL '2' hour", TIMESTAMP, - sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 10, 27, 2, 5, 0, 0, session)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestDecimalOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestDecimalOperators.java index 2a90ab08f6437..580ce6f997c4d 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestDecimalOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestDecimalOperators.java @@ -787,6 +787,14 @@ public void testIsDistinctFrom() assertFunction("DECIMAL '-2' IS DISTINCT FROM NULL", BOOLEAN, true); assertFunction("NULL IS DISTINCT FROM DECIMAL '12345678901234567.89012345678901234567'", BOOLEAN, true); assertFunction("DECIMAL '12345678901234567.89012345678901234567' IS DISTINCT FROM NULL", BOOLEAN, true); + + // delegation from other operator (exercises block-position convention implementation) + assertFunction("ARRAY [1.23, 4.56] IS DISTINCT FROM ARRAY [1.23, 4.56]", BOOLEAN, false); + assertFunction("ARRAY [1.23, NULL] IS DISTINCT FROM ARRAY [1.23, 4.56]", BOOLEAN, true); + assertFunction("ARRAY [1.23, NULL] IS DISTINCT FROM ARRAY [NULL, 4.56]", BOOLEAN, true); + assertFunction("ARRAY [1234567890.123456789, 9876543210.987654321] IS DISTINCT FROM ARRAY [1234567890.123456789, 9876543210.987654321]", BOOLEAN, false); + assertFunction("ARRAY [1234567890.123456789, NULL] IS DISTINCT FROM ARRAY [1234567890.123456789, 9876543210.987654321]", BOOLEAN, true); + assertFunction("ARRAY [1234567890.123456789, NULL] IS DISTINCT FROM ARRAY [NULL, 9876543210.987654321]", BOOLEAN, true); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestJsonOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestJsonOperators.java index 166cfecb42847..f2a72cd672c4d 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestJsonOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestJsonOperators.java @@ -36,7 +36,6 @@ import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; @@ -45,7 +44,6 @@ import static java.lang.Double.NEGATIVE_INFINITY; import static java.lang.Double.POSITIVE_INFINITY; import static java.lang.String.format; -import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -176,6 +174,10 @@ public void testTypeConstructor() assertFunction("JSON '123'", JSON, "123"); assertFunction("JSON '[4,5,6]'", JSON, "[4,5,6]"); assertFunction("JSON '{ \"a\": 789 }'", JSON, "{\"a\":789}"); + assertFunction("JSON 'null'", JSON, "null"); + assertFunction("JSON '[null]'", JSON, "[null]"); + assertFunction("JSON '[13,null,42]'", JSON, "[13,null,42]"); + assertFunction("JSON '{\"x\": null}'", JSON, "{\"x\":null}"); } @Test @@ -391,7 +393,7 @@ public void testCastFromVarchar() public void testCastFromTimestamp() { assertFunction("cast(cast (null as timestamp) as JSON)", JSON, null); - assertFunction("CAST(TIMESTAMP '1970-01-01 00:00:01' AS JSON)", JSON, format("\"%s\"", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + assertFunction("CAST(TIMESTAMP '1970-01-01 00:00:01' AS JSON)", JSON, format("\"%s\"", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestMapOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestMapOperators.java index 8e5d17352a441..13f181ac78518 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestMapOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestMapOperators.java @@ -46,7 +46,6 @@ import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; @@ -65,7 +64,6 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static org.joda.time.DateTimeZone.UTC; public class TestMapOperators extends AbstractTestFunctions @@ -107,16 +105,16 @@ public void testConstructor() mapType(createVarcharType(3), TIMESTAMP), ImmutableMap.of( "1", - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), "100", - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION))); assertFunction( "MAP(ARRAY[TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'], ARRAY[1.0E0, 100.0E0])", mapType(TIMESTAMP, DOUBLE), ImmutableMap.of( - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION), 1.0, - sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION), + sqlTimestampOf(1973, 7, 8, 22, 0, 1, 0, TEST_SESSION), 100.0)); assertInvalidFunction("MAP(ARRAY [1], ARRAY [2, 4])", "Key and value arrays must be the same length"); @@ -249,7 +247,7 @@ public void testMapToJson() assertFunction( "CAST(MAP(ARRAY[1, 2], ARRAY[TIMESTAMP '1970-01-01 00:00:01', null]) AS JSON)", JSON, - format("{\"1\":\"%s\",\"2\":null}", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION).toString())); + format("{\"1\":\"%s\",\"2\":null}", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION).toString())); assertFunction( "CAST(MAP(ARRAY[2, 5, 3], ARRAY[DATE '2001-08-22', DATE '2001-08-23', null]) AS JSON)", JSON, @@ -521,7 +519,7 @@ public void testElementAt() assertFunction( "element_at(MAP(ARRAY ['1', '100'], ARRAY [TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '2005-09-10 13:00:00']), '1')", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION)); assertFunction("element_at(MAP(ARRAY [from_unixtime(1), from_unixtime(100)], ARRAY [1.0E0, 100.0E0]), from_unixtime(1))", DOUBLE, 1.0); } @@ -546,7 +544,7 @@ public void testSubscript() assertFunction( "MAP(ARRAY['1', '100'], ARRAY[TIMESTAMP '1970-01-01 00:00:01', TIMESTAMP '1973-07-08 22:00:01'])['1']", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION)); + sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION)); assertFunction("MAP(ARRAY[from_unixtime(1), from_unixtime(100)], ARRAY[1.0E0, 100.0E0])[from_unixtime(1)]", DOUBLE, 1.0); assertInvalidFunction("MAP(ARRAY [BIGINT '1'], ARRAY [BIGINT '2'])[3]", "Key not present in map: 3"); assertInvalidFunction("MAP(ARRAY ['hi'], ARRAY [2])['missing']", "Key not present in map: missing"); @@ -566,7 +564,7 @@ public void testMapKeys() assertFunction( "MAP_KEYS(MAP(ARRAY[TIMESTAMP '1970-01-01 00:00:01'], ARRAY[1.0E0]))", new ArrayType(TIMESTAMP), - ImmutableList.of(sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + ImmutableList.of(sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction("MAP_KEYS(MAP(ARRAY[CAST('puppies' as varbinary)], ARRAY['kittens']))", new ArrayType(VARBINARY), ImmutableList.of(new SqlVarbinary("puppies".getBytes(UTF_8)))); assertFunction("MAP_KEYS(MAP(ARRAY[1,2], ARRAY[ARRAY[1, 2], ARRAY[3]]))", new ArrayType(INTEGER), ImmutableList.of(1, 2)); assertFunction("MAP_KEYS(MAP(ARRAY[1,4], ARRAY[MAP(ARRAY[2], ARRAY[3]), MAP(ARRAY[5], ARRAY[6])]))", new ArrayType(INTEGER), ImmutableList.of(1, 4)); @@ -662,7 +660,9 @@ public void testEquals() assertFunction("MAP(ARRAY [1.0, 383838383838383.12324234234234], ARRAY [2.2, 3.3]) = MAP(ARRAY [1.0, 383838383838383.12324234234234], ARRAY [2.2, 3.2])", BOOLEAN, false); // nulls - assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) = MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 2])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) = MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[3, 3]) = MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) = MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 2])", BOOLEAN, false); assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, NULL]) = MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, NULL])", BOOLEAN, null); assertFunction("MAP(ARRAY[from_unixtime(1), from_unixtime(100)], ARRAY[NULL, FALSE]) = MAP(ARRAY[from_unixtime(100), from_unixtime(1)], ARRAY[FALSE, NULL])", BOOLEAN, null); assertFunction("MAP(ARRAY[from_unixtime(1), from_unixtime(100)], ARRAY[TRUE, NULL]) = MAP(ARRAY[from_unixtime(100), from_unixtime(1)], ARRAY[TRUE, NULL])", BOOLEAN, null); @@ -705,7 +705,9 @@ public void testNotEquals() assertFunction("MAP(ARRAY [1.0, 383838383838383.12324234234234], ARRAY [2.2, 3.3]) != MAP(ARRAY [1.0, 383838383838383.12324234234234], ARRAY [2.2, 3.2])", BOOLEAN, true); // nulls - assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) != MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 2])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) != MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[3, 3]) != MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3])", BOOLEAN, null); + assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 3]) != MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, 2])", BOOLEAN, true); assertFunction("MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, NULL]) != MAP(ARRAY['kittens', 'puppies'], ARRAY[NULL, NULL])", BOOLEAN, null); assertFunction("MAP(ARRAY[from_unixtime(1), from_unixtime(100)], ARRAY[NULL, FALSE]) != MAP(ARRAY[from_unixtime(100), from_unixtime(1)], ARRAY[FALSE, NULL])", BOOLEAN, null); assertFunction("MAP(ARRAY[from_unixtime(1), from_unixtime(100)], ARRAY[TRUE, NULL]) != MAP(ARRAY[from_unixtime(100), from_unixtime(1)], ARRAY[TRUE, NULL])", BOOLEAN, null); diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestRowOperators.java b/presto-main/src/test/java/com/facebook/presto/type/TestRowOperators.java index 9ea6ed97b0e12..4a69b8b681aac 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestRowOperators.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestRowOperators.java @@ -52,7 +52,6 @@ import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; @@ -67,7 +66,6 @@ import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; public class TestRowOperators @@ -142,7 +140,7 @@ public void testRowToJson() assertFunction( "CAST(ROW(TIMESTAMP '1970-01-01 00:00:01', cast(null as TIMESTAMP)) AS JSON)", JSON, - format("[\"%s\",null]", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, UTC, UTC_KEY, TEST_SESSION))); + format("[\"%s\",null]", sqlTimestampOf(1970, 1, 1, 0, 0, 1, 0, TEST_SESSION))); assertFunction( "cast(ROW(ARRAY[1, 2], ARRAY[3, null], ARRAY[], ARRAY[null, null], CAST(null AS ARRAY)) AS JSON)", @@ -505,10 +503,18 @@ public void testRowComparison() assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog)) > cast(row(cast(cast ('' as varbinary) as hyperloglog)) as row(col0 hyperloglog))", SemanticErrorCode.TYPE_MISMATCH, "line 1:81: '>' cannot be applied to row(col0 HyperLogLog), row(col0 HyperLogLog)"); + assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) = cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))", + SemanticErrorCode.TYPE_MISMATCH, "line 1:89: '=' cannot be applied to row(col0 qdigest(double)), row(col0 qdigest(double))"); + assertInvalidFunction("cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double))) > cast(row(cast(cast ('' as varbinary) as qdigest(double))) as row(col0 qdigest(double)))", + SemanticErrorCode.TYPE_MISMATCH, "line 1:89: '>' cannot be applied to row(col0 qdigest(double)), row(col0 qdigest(double))"); + assertFunction("row(TRUE, ARRAY [1], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) = row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", BOOLEAN, false); assertFunction("row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) = row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", BOOLEAN, true); - assertInvalidFunction("row(1, CAST(NULL AS INTEGER)) = row(1, 2)", StandardErrorCode.NOT_SUPPORTED); + assertFunction("row(1, CAST(NULL AS INTEGER)) = row(1, 2)", BOOLEAN, null); + assertFunction("row(1, CAST(NULL AS INTEGER)) != row(1, 2)", BOOLEAN, null); + assertFunction("row(2, CAST(NULL AS INTEGER)) = row(1, 2)", BOOLEAN, false); + assertFunction("row(2, CAST(NULL AS INTEGER)) != row(1, 2)", BOOLEAN, true); assertInvalidFunction("row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0])) > row(TRUE, ARRAY [1, 2], MAP(ARRAY[1, 3], ARRAY[2.0E0, 4.0E0]))", SemanticErrorCode.TYPE_MISMATCH, "line 1:64: '>' cannot be applied to row(boolean,array(integer),map(integer,double)), row(boolean,array(integer),map(integer,double))"); diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimeBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimeBase.java index a5ed57d26d63f..036ca96ba7e6e 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimeBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimeBase.java @@ -20,6 +20,7 @@ import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; import com.facebook.presto.spi.type.TimeZoneKey; import com.facebook.presto.sql.analyzer.SemanticErrorCode; +import com.facebook.presto.testing.TestingSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.testng.annotations.Test; @@ -41,7 +42,7 @@ public abstract class TestTimeBase extends AbstractTestFunctions { - protected static final TimeZoneKey TIME_ZONE_KEY = getTimeZoneKey("Europe/Berlin"); + protected static final TimeZoneKey TIME_ZONE_KEY = TestingSession.DEFAULT_TIME_ZONE_KEY; protected static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); public TestTimeBase(boolean legacyTimestamp) @@ -177,7 +178,7 @@ public void testCastToTimestamp() { assertFunction("cast(TIME '03:04:05.321' as timestamp)", TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(1970, 1, 1, 3, 4, 5, 321, session)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZone.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZone.java index ca0f285114b9a..b8d061229fee3 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZone.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZone.java @@ -16,7 +16,9 @@ import org.testng.annotations.Test; import static com.facebook.presto.spi.type.TimeType.TIME; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimeOf; +import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; public class TestTimeWithTimeZone extends TestTimeWithTimeZoneBase @@ -34,4 +36,13 @@ public void testCastToTime() TIME, sqlTimeOf(3, 4, 5, 321, session)); } + + @Test + @Override + public void testCastToTimestamp() + { + assertFunction("cast(TIME '03:04:05.321 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(1970, 1, 1, 3, 4, 5, 321, session)); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneBase.java index d36a3afb13c4f..a407fe7fd8ac8 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneBase.java @@ -26,10 +26,8 @@ import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKey; import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKeyForOffset; -import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; -import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; @@ -203,12 +201,7 @@ public void testBetween() public abstract void testCastToTime(); @Test - public void testCastToTimestamp() - { - assertFunction("cast(TIME '03:04:05.321 +07:09' as timestamp)", - TIMESTAMP, - sqlTimestampOf(1970, 1, 1, 3, 4, 5, 321, WEIRD_ZONE, session.getTimeZoneKey(), session)); - } + public abstract void testCastToTimestamp(); @Test public void testCastToTimestampWithTimeZone() diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneLegacy.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneLegacy.java index 9bcd01aed5a04..bc66169c182e9 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneLegacy.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimeWithTimeZoneLegacy.java @@ -16,7 +16,9 @@ import org.testng.annotations.Test; import static com.facebook.presto.spi.type.TimeType.TIME; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimeOf; +import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; public class TestTimeWithTimeZoneLegacy extends TestTimeWithTimeZoneBase @@ -32,6 +34,15 @@ public void testCastToTime() { assertFunction("cast(TIME '03:04:05.321 +07:09' as time)", TIME, - sqlTimeOf(2, 4, 5, 321, session)); + sqlTimeOf(2 /* not 3 */, 4, 5, 321, session)); + } + + @Test + @Override + public void testCastToTimestamp() + { + assertFunction("cast(TIME '03:04:05.321 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(1970, 1, 1, 2 /* not 3 */, 4, 5, 321, session)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampBase.java index c0f3d66eb0965..cf4634ab669cc 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampBase.java @@ -19,6 +19,7 @@ import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; import com.facebook.presto.spi.type.TimeZoneKey; import com.facebook.presto.sql.analyzer.SemanticErrorCode; +import com.facebook.presto.testing.TestingSession; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.testng.annotations.Test; @@ -45,7 +46,7 @@ public abstract class TestTimestampBase extends AbstractTestFunctions { - protected static final TimeZoneKey TIME_ZONE_KEY = getTimeZoneKey("Europe/Berlin"); + protected static final TimeZoneKey TIME_ZONE_KEY = TestingSession.DEFAULT_TIME_ZONE_KEY; protected static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); protected static final TimeZoneKey WEIRD_TIME_ZONE_KEY = getTimeZoneKeyForOffset(7 * 60 + 9); protected static final DateTimeZone WEIRD_ZONE = getDateTimeZone(WEIRD_TIME_ZONE_KEY); @@ -75,19 +76,19 @@ public void testSubtract() @Test public void testLiteral() { - assertFunction("TIMESTAMP '2013-03-30 01:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2013-03-30 02:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 2, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2013-03-30 03:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 3, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + assertFunction("TIMESTAMP '2013-03-30 01:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, session)); + assertFunction("TIMESTAMP '2013-03-30 02:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 2, 5, 0, 0, session)); + assertFunction("TIMESTAMP '2013-03-30 03:05'", TIMESTAMP, sqlTimestampOf(2013, 3, 30, 3, 5, 0, 0, session)); - assertFunction("TIMESTAMP '2001-01-22 03:04:05.321'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-01-22 03:04:05'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-01-22 03:04'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-01-22'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + assertFunction("TIMESTAMP '2001-01-22 03:04:05.321'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); + assertFunction("TIMESTAMP '2001-01-22 03:04:05'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, session)); + assertFunction("TIMESTAMP '2001-01-22 03:04'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, session)); + assertFunction("TIMESTAMP '2001-01-22'", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, session)); - assertFunction("TIMESTAMP '2001-1-2 3:4:5.321'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-1-2 3:4:5'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 5, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-1-2 3:4'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); - assertFunction("TIMESTAMP '2001-1-2'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + assertFunction("TIMESTAMP '2001-1-2 3:4:5.321'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 5, 321, session)); + assertFunction("TIMESTAMP '2001-1-2 3:4:5'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 5, 0, session)); + assertFunction("TIMESTAMP '2001-1-2 3:4'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 3, 4, 0, 0, session)); + assertFunction("TIMESTAMP '2001-1-2'", TIMESTAMP, sqlTimestampOf(2001, 1, 2, 0, 0, 0, 0, session)); assertInvalidFunction("TIMESTAMP 'text'", SemanticErrorCode.INVALID_LITERAL, "line 1:1: 'text' is not a valid timestamp literal"); } @@ -194,7 +195,7 @@ public void testCastToTimeWithTimeZone() new SqlTimeWithTimeZone(new DateTime(1970, 1, 1, 3, 4, 5, 321, DATE_TIME_ZONE).getMillis(), TIME_ZONE_KEY)); functionAssertions.assertFunctionString("cast(TIMESTAMP '2001-1-22 03:04:05.321' as time with time zone)", TIME_WITH_TIME_ZONE, - "03:04:05.321 Europe/Berlin"); + "03:04:05.321 " + DATE_TIME_ZONE.getID()); } @Test @@ -205,7 +206,7 @@ public void testCastToTimestampWithTimeZone() new SqlTimestampWithTimeZone(new DateTime(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE).getMillis(), DATE_TIME_ZONE.toTimeZone())); functionAssertions.assertFunctionString("cast(TIMESTAMP '2001-1-22 03:04:05.321' as timestamp with time zone)", TIMESTAMP_WITH_TIME_ZONE, - "2001-01-22 03:04:05.321 Europe/Berlin"); + "2001-01-22 03:04:05.321 " + DATE_TIME_ZONE.getID()); } @Test @@ -222,25 +223,25 @@ public void testCastFromSlice() { assertFunction("cast('2001-1-22 03:04:05.321' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); assertFunction("cast('2001-1-22 03:04:05' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, session)); assertFunction("cast('2001-1-22 03:04' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, session)); assertFunction("cast('2001-1-22' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, session)); assertFunction("cast('\n\t 2001-1-22 03:04:05.321' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); assertFunction("cast('2001-1-22 03:04:05.321 \t\n' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); assertFunction("cast('\n\t 2001-1-22 03:04:05.321 \t\n' as timestamp)", TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); } @Test @@ -248,10 +249,10 @@ public void testGreatest() { assertFunction("greatest(TIMESTAMP '2013-03-30 01:05', TIMESTAMP '2012-03-30 01:05')", TIMESTAMP, - sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, session)); assertFunction("greatest(TIMESTAMP '2013-03-30 01:05', TIMESTAMP '2012-03-30 01:05', TIMESTAMP '2012-05-01 01:05')", TIMESTAMP, - sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2013, 3, 30, 1, 5, 0, 0, session)); } @Test @@ -259,10 +260,10 @@ public void testLeast() { assertFunction("least(TIMESTAMP '2013-03-30 01:05', TIMESTAMP '2012-03-30 01:05')", TIMESTAMP, - sqlTimestampOf(2012, 3, 30, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2012, 3, 30, 1, 5, 0, 0, session)); assertFunction("least(TIMESTAMP '2013-03-30 01:05', TIMESTAMP '2012-03-30 01:05', TIMESTAMP '2012-05-01 01:05')", TIMESTAMP, - sqlTimestampOf(2012, 3, 30, 1, 5, 0, 0, DATE_TIME_ZONE, TIME_ZONE_KEY, session)); + sqlTimestampOf(2012, 3, 30, 1, 5, 0, 0, session)); } @Test diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampLegacy.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampLegacy.java index 031d4461580e5..cfab5201f2f56 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampLegacy.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampLegacy.java @@ -28,14 +28,38 @@ public TestTimestampLegacy() public void testCastFromSlice() { super.testCastFromSlice(); - assertFunction("cast('2001-1-22 03:04:05.321 +07:09' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, WEIRD_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 03:04:05 +07:09' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, WEIRD_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 03:04 +07:09' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, WEIRD_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 +07:09' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, WEIRD_ZONE, TIME_ZONE_KEY, session)); + assertFunction( + "cast('2001-1-22 03:04:05.321 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 8, 55, 5, 321, session)); + assertFunction( + "cast('2001-1-22 03:04:05 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 8, 55, 5, 0, session)); + assertFunction( + "cast('2001-1-22 03:04 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 8, 55, 0, 0, session)); + assertFunction( + "cast('2001-1-22 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 5, 51, 0, 0, session)); - assertFunction("cast('2001-1-22 03:04:05.321 Asia/Oral' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, ORAL_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 03:04:05 Asia/Oral' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 5, 0, ORAL_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 03:04 Asia/Oral' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 3, 4, 0, 0, ORAL_ZONE, TIME_ZONE_KEY, session)); - assertFunction("cast('2001-1-22 Asia/Oral' as timestamp)", TIMESTAMP, sqlTimestampOf(2001, 1, 22, 0, 0, 0, 0, ORAL_ZONE, TIME_ZONE_KEY, session)); + assertFunction( + "cast('2001-1-22 03:04:05.321 Asia/Oral' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 12, 4, 5, 321, session)); + assertFunction( + "cast('2001-1-22 03:04:05 Asia/Oral' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 12, 4, 5, 0, session)); + assertFunction( + "cast('2001-1-22 03:04 Asia/Oral' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 12, 4, 0, 0, session)); + assertFunction( + "cast('2001-1-22 Asia/Oral' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21, 9, 0, 0, 0, session)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZone.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZone.java index feee20cac567e..acfa1b231d348 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZone.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZone.java @@ -17,7 +17,9 @@ import static com.facebook.presto.spi.type.TimeType.TIME; import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimeOf; +import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; public class TestTimestampWithTimeZone extends TestTimestampWithTimeZoneBase @@ -41,6 +43,7 @@ public void testCastToTime() } @Test + @Override public void testCastToTimeWithTimeZone() { super.testCastToTimeWithTimeZone(); @@ -52,4 +55,18 @@ public void testCastToTimeWithTimeZone() TIME_WITH_TIME_ZONE, "10:00:00.000 Asia/Kathmandu"); } + + @Test + @Override + public void testCastToTimestamp() + { + assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); + + // This TZ had switch in 2014, so if we test for 2014 and used unpacked value we would use wrong shift + assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 Pacific/Bougainville' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, session)); + } } diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneBase.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneBase.java index bb634efa8eb4e..f99e50629e08f 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneBase.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneBase.java @@ -30,10 +30,8 @@ import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKey; import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKeyForOffset; -import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; -import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.type.IntervalDayTimeType.INTERVAL_DAY_TIME; import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; @@ -266,17 +264,7 @@ public void testCastToTimeWithTimeZone() } @Test - public void testCastToTimestamp() - { - assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 +07:09' as timestamp)", - TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, WEIRD_ZONE, session.getTimeZoneKey(), session)); - - // This TZ had switch in 2014, so if we test for 2014 and used unpacked value we would use wrong shift - assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 Pacific/Bougainville' as timestamp)", - TIMESTAMP, - sqlTimestampOf(2001, 1, 22, 3, 4, 5, 321, getDateTimeZone(getTimeZoneKey("Pacific/Bougainville")), session.getTimeZoneKey(), session)); - } + public abstract void testCastToTimestamp(); @Test public void testCastToSlice() diff --git a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneLegacy.java b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneLegacy.java index e01307fd3f236..53728f21971f9 100644 --- a/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneLegacy.java +++ b/presto-main/src/test/java/com/facebook/presto/type/TestTimestampWithTimeZoneLegacy.java @@ -16,7 +16,9 @@ import org.testng.annotations.Test; import static com.facebook.presto.spi.type.TimeType.TIME; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimeOf; +import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; public class TestTimestampWithTimeZoneLegacy extends TestTimestampWithTimeZoneBase @@ -32,6 +34,20 @@ public void testCastToTime() { assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 +07:09' as time)", TIME, - sqlTimeOf(2, 4, 5, 321, session)); + sqlTimeOf(2 /* not 3 */, 4, 5, 321, session)); + } + + @Test + @Override + public void testCastToTimestamp() + { + assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 +07:09' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 22, 2 /* not 3 */, 4, 5, 321, session)); + + // This TZ had switch in 2014 + assertFunction("cast(TIMESTAMP '2001-1-22 03:04:05.321 Pacific/Bougainville' as timestamp)", + TIMESTAMP, + sqlTimestampOf(2001, 1, 21 /* not 22 */, 23 /* not 3 */, 13, 5, 321, session)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/util/TestMergeSortedPages.java b/presto-main/src/test/java/com/facebook/presto/util/TestMergeSortedPages.java index 819bd9b82df9d..7775479bc7128 100644 --- a/presto-main/src/test/java/com/facebook/presto/util/TestMergeSortedPages.java +++ b/presto-main/src/test/java/com/facebook/presto/util/TestMergeSortedPages.java @@ -355,6 +355,29 @@ public void testSortingYields() assertTrue(mergedPages.isFinished()); } + @Test + public void testMergeSortYieldingProgresses() + throws Exception + { + DriverYieldSignal yieldSignal = new DriverYieldSignal(); + yieldSignal.forceYieldForTesting(); + List types = ImmutableList.of(INTEGER); + WorkProcessor mergedPages = MergeSortedPages.mergeSortedPages( + ImmutableList.of(WorkProcessor.fromIterable(rowPagesBuilder(types).build())), + new SimplePageWithPositionComparator(types, ImmutableList.of(0), ImmutableList.of(DESC_NULLS_LAST)), + ImmutableList.of(0), + types, + (pageBuilder, pageWithPosition) -> pageBuilder.isFull(), + false, + newSimpleAggregatedMemoryContext().newAggregatedMemoryContext(), + yieldSignal); + // yield signal is on + assertFalse(mergedPages.process()); + // processor finishes computations (yield signal is still on, but previous process() call yielded) + assertTrue(mergedPages.process()); + assertTrue(mergedPages.isFinished()); + } + private static MaterializedResult mergeSortedPages( List types, List sortChannels, diff --git a/presto-main/src/test/java/com/facebook/presto/util/TestTimeZoneUtils.java b/presto-main/src/test/java/com/facebook/presto/util/TestTimeZoneUtils.java index 2d4d9ce8dd4c9..3d95f47be7256 100644 --- a/presto-main/src/test/java/com/facebook/presto/util/TestTimeZoneUtils.java +++ b/presto-main/src/test/java/com/facebook/presto/util/TestTimeZoneUtils.java @@ -13,15 +13,14 @@ */ package com.facebook.presto.util; -import com.facebook.presto.server.JavaVersion; import com.facebook.presto.spi.type.TimeZoneKey; -import com.google.common.collect.Sets; +import io.airlift.jodabridge.JdkBasedZoneInfoProvider; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import java.util.Arrays; -import java.util.TimeZone; +import java.time.ZoneId; import java.util.TreeSet; import static com.facebook.presto.spi.type.DateTimeEncoding.packDateTimeWithZone; @@ -29,26 +28,41 @@ import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static com.facebook.presto.util.DateTimeZoneIndex.packDateTimeWithZone; import static com.facebook.presto.util.DateTimeZoneIndex.unpackDateTimeZone; -import static java.util.Locale.ENGLISH; import static org.testng.Assert.assertEquals; public class TestTimeZoneUtils { + @BeforeClass + protected void validateJodaZoneInfoProvider() + { + try { + JdkBasedZoneInfoProvider.registerAsJodaZoneInfoProvider(); + } + catch (RuntimeException e) { + throw new RuntimeException("Set the following system property to JVM running the test: -Dorg.joda.time.DateTimeZone.Provider=com.facebook.presto.tz.JdkBasedZoneInfoProvider"); + } + } + @Test public void test() { TimeZoneKey.getTimeZoneKey("GMT-13:00"); TreeSet jodaZones = new TreeSet<>(DateTimeZone.getAvailableIDs()); - TreeSet jdkZones = new TreeSet<>(Arrays.asList(TimeZone.getAvailableIDs())); + TreeSet jdkZones = new TreeSet<>(ZoneId.getAvailableZoneIds()); + // We use JdkBasedZoneInfoProvider for joda + assertEquals(jodaZones, jdkZones); - for (String zoneId : new TreeSet<>(Sets.intersection(jodaZones, jdkZones))) { - if (zoneId.toLowerCase(ENGLISH).startsWith("etc/") || zoneId.toLowerCase(ENGLISH).startsWith("gmt")) { + for (String zoneId : new TreeSet<>(jdkZones)) { + if (zoneId.startsWith("Etc/") || zoneId.startsWith("GMT") || zoneId.startsWith("SystemV/")) { continue; } - // Known bug in Joda(https://github.com/JodaOrg/joda-time/issues/427) - // We will skip this timezone in test - if (JavaVersion.current().getMajor() == 8 && JavaVersion.current().getUpdate().orElse(0) < 121 && zoneId.equals("Asia/Rangoon")) { + + if (zoneId.equals("Canada/East-Saskatchewan")) { + // TODO: remove once minimum Java version is increased to 8u161 and 9.0.4, see PrestoSystemRequirement. + // Removed from tzdata since 2017c. + // Java updated to 2017c since 8u161, 9.0.4. + // All Java 10+ are on later versions continue; } diff --git a/presto-matching/pom.xml b/presto-matching/pom.xml index 0006ca08dd9de..03c76136a1b96 100644 --- a/presto-matching/pom.xml +++ b/presto-matching/pom.xml @@ -18,7 +18,7 @@ presto-root com.facebook.presto - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-matching diff --git a/presto-memory-context/pom.xml b/presto-memory-context/pom.xml index 620097a8c023e..a731ccb8b5c17 100644 --- a/presto-memory-context/pom.xml +++ b/presto-memory-context/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-memory-context diff --git a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/LocalMemoryContext.java b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/LocalMemoryContext.java index 40fd27d9a9473..33fb5a674484f 100644 --- a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/LocalMemoryContext.java +++ b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/LocalMemoryContext.java @@ -34,6 +34,7 @@ public interface LocalMemoryContext * This method can return false when there is not enough memory available to satisfy a positive delta allocation * ({@code bytes} is greater than the bytes tracked by this LocalMemoryContext). *

    + * * @return true if the bytes tracked by this LocalMemoryContext can be set to {@code bytes}. */ boolean trySetBytes(long bytes); diff --git a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryReservationHandler.java b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryReservationHandler.java index 768bc898daade..9dcab1312f969 100644 --- a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryReservationHandler.java +++ b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryReservationHandler.java @@ -24,6 +24,7 @@ public interface MemoryReservationHandler /** * Try reserving the given number of bytes. + * * @return true if reservation is successful, false otherwise. */ boolean tryReserveMemory(String allocationTag, long delta); diff --git a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryTrackingContext.java b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryTrackingContext.java index 3d9e756a3dc8b..80189a364e436 100644 --- a/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryTrackingContext.java +++ b/presto-memory-context/src/main/java/com/facebook/presto/memory/context/MemoryTrackingContext.java @@ -25,20 +25,20 @@ /** * This class is used to track memory usage at all levels (operator, driver, pipeline, etc.). - * + *

    * At every level we have three aggregate and three local memory contexts. The local memory contexts * track the allocations in the current level while the aggregate memory contexts aggregate the memory * allocated by the leaf levels and the current level. - * + *

    * The reason we have local memory contexts at every level is that not all the * allocations are done by the leaf levels (e.g., at the pipeline level exchange clients * can do system allocations directly, see the ExchangeOperator, another example is the buffers * doing system allocations at the task context level, etc.). - * + *

    * As another example, at the pipeline level there will be system allocations initiated by the operator context * and there will be system allocations initiated by the exchange clients (local allocations). All these system * allocations will be visible in the systemAggregateMemoryContext. - * + *

    * To perform local allocations clients should use localUserMemoryContext()/localSystemMemoryContext() * and get a reference to the local memory contexts. Clients can also use updateUserMemory()/tryReserveUserMemory() * to allocate memory (non-local allocations), which will be reflected to all ancestors of this context in the hierarchy. diff --git a/presto-memory/pom.xml b/presto-memory/pom.xml index e2b4b9d5d36c4..ff3efa38badc0 100644 --- a/presto-memory/pom.xml +++ b/presto-memory/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-memory diff --git a/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryConnectorFactory.java b/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryConnectorFactory.java index bc2d2af18087d..407b495a01226 100644 --- a/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryConnectorFactory.java +++ b/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryConnectorFactory.java @@ -42,14 +42,14 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map requiredConfig, ConnectorContext context) + public Connector create(String catalogName, Map requiredConfig, ConnectorContext context) { requireNonNull(requiredConfig, "requiredConfig is null"); try { // A plugin is not required to use Guice; it is just very convenient Bootstrap app = new Bootstrap( new JsonModule(), - new MemoryModule(connectorId, context.getTypeManager(), context.getNodeManager())); + new MemoryModule(catalogName, context.getTypeManager(), context.getNodeManager())); Injector injector = app .strictConfig() diff --git a/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryPagesStore.java b/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryPagesStore.java index 3fa9d4093d30a..3c680ff840a4b 100644 --- a/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryPagesStore.java +++ b/presto-memory/src/main/java/com/facebook/presto/plugin/memory/MemoryPagesStore.java @@ -63,6 +63,8 @@ public synchronized void add(Long tableId, Page page) throw new PrestoException(MISSING_DATA, "Failed to find table on a worker."); } + page.compact(); + long newSize = currentBytes + page.getRetainedSizeInBytes(); if (maxBytes < newSize) { throw new PrestoException(MEMORY_LIMIT_EXCEEDED, format("Memory limit [%d] for memory connector exceeded", maxBytes)); diff --git a/presto-ml/pom.xml b/presto-ml/pom.xml index e32e0bac62f28..49cbf6a2b5e47 100644 --- a/presto-ml/pom.xml +++ b/presto-ml/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-ml @@ -26,11 +26,6 @@ libsvm - - javax.validation - validation-api - - it.unimi.dsi fastutil diff --git a/presto-ml/src/main/java/com/facebook/presto/ml/LearnState.java b/presto-ml/src/main/java/com/facebook/presto/ml/LearnState.java index ffb2d45467341..f8578e951c2bc 100644 --- a/presto-ml/src/main/java/com/facebook/presto/ml/LearnState.java +++ b/presto-ml/src/main/java/com/facebook/presto/ml/LearnState.java @@ -18,8 +18,6 @@ import com.google.common.collect.BiMap; import io.airlift.slice.Slice; -import javax.validation.constraints.NotNull; - import java.util.List; @AccumulatorStateMetadata(stateSerializerClass = LearnStateSerializer.class, stateFactoryClass = LearnStateFactory.class) @@ -27,15 +25,12 @@ public interface LearnState extends AccumulatorState { // Mapping of string labels for classifiers that use strings instead of doubles - @NotNull BiMap getLabelEnumeration(); int enumerateLabel(String label); - @NotNull List getLabels(); - @NotNull List getFeatureVectors(); Slice getParameters(); diff --git a/presto-mongodb/pom.xml b/presto-mongodb/pom.xml index a7f1b51604aea..c7cd26f42fd7d 100644 --- a/presto-mongodb/pom.xml +++ b/presto-mongodb/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-mongodb @@ -30,6 +30,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.validation validation-api @@ -176,36 +182,4 @@ test - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - **/TestMongoDistributedQueries.java - - - - - - - - - ci - - - - org.apache.maven.plugins - maven-surefire-plugin - - - - - - - - diff --git a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoConnectorFactory.java b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoConnectorFactory.java index da2cb80b3c6f0..a567e9fc2f7c5 100644 --- a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoConnectorFactory.java +++ b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoConnectorFactory.java @@ -53,7 +53,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { requireNonNull(config, "config is null"); diff --git a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoSession.java b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoSession.java index f2d8c202e97df..d1f1915754186 100644 --- a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoSession.java +++ b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/MongoSession.java @@ -526,10 +526,10 @@ else if (value instanceof List) { return Optional.empty(); } - Set signatures = subTypes.stream().map(t -> t.get()).collect(toSet()); + Set signatures = subTypes.stream().map(Optional::get).collect(toSet()); if (signatures.size() == 1) { typeSignature = new TypeSignature(StandardTypes.ARRAY, signatures.stream() - .map(s -> TypeSignatureParameter.of(s)) + .map(TypeSignatureParameter::of) .collect(Collectors.toList())); } else { diff --git a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/ObjectIdFunctions.java b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/ObjectIdFunctions.java index 342e9c4a44520..f9ba5020ebb57 100644 --- a/presto-mongodb/src/main/java/com/facebook/presto/mongodb/ObjectIdFunctions.java +++ b/presto-mongodb/src/main/java/com/facebook/presto/mongodb/ObjectIdFunctions.java @@ -16,6 +16,7 @@ import com.facebook.presto.spi.function.Description; import com.facebook.presto.spi.function.ScalarFunction; import com.facebook.presto.spi.function.ScalarOperator; +import com.facebook.presto.spi.function.SqlNullable; import com.facebook.presto.spi.function.SqlType; import com.facebook.presto.spi.type.StandardTypes; import com.google.common.base.CharMatcher; @@ -54,14 +55,16 @@ public static Slice ObjectId(@SqlType(StandardTypes.VARCHAR) Slice value) @ScalarOperator(EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean equal(@SqlType("ObjectId") Slice left, @SqlType("ObjectId") Slice right) + @SqlNullable + public static Boolean equal(@SqlType("ObjectId") Slice left, @SqlType("ObjectId") Slice right) { return left.equals(right); } @ScalarOperator(NOT_EQUAL) @SqlType(StandardTypes.BOOLEAN) - public static boolean notEqual(@SqlType("ObjectId") Slice left, @SqlType("ObjectId") Slice right) + @SqlNullable + public static Boolean notEqual(@SqlType("ObjectId") Slice left, @SqlType("ObjectId") Slice right) { return !left.equals(right); } diff --git a/presto-mongodb/src/test/java/com/facebook/presto/mongodb/MongoQueryRunner.java b/presto-mongodb/src/test/java/com/facebook/presto/mongodb/MongoQueryRunner.java index d777a7b2805aa..bfba7bc110f1f 100644 --- a/presto-mongodb/src/test/java/com/facebook/presto/mongodb/MongoQueryRunner.java +++ b/presto-mongodb/src/test/java/com/facebook/presto/mongodb/MongoQueryRunner.java @@ -26,12 +26,10 @@ import java.net.InetSocketAddress; import java.util.Map; -import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.tests.QueryAssertions.copyTpchTables; import static com.facebook.presto.tpch.TpchMetadata.TINY_SCHEMA_NAME; import static io.airlift.testing.Closeables.closeAllSuppress; -import static java.util.Locale.ENGLISH; public class MongoQueryRunner extends DistributedQueryRunner @@ -90,8 +88,6 @@ public static Session createSession() return testSessionBuilder() .setCatalog("mongodb") .setSchema(TPCH_SCHEMA) - .setTimeZoneKey(UTC_KEY) - .setLocale(ENGLISH) .build(); } diff --git a/presto-mysql/pom.xml b/presto-mysql/pom.xml index 6960308d5f218..f70086258234a 100644 --- a/presto-mysql/pom.xml +++ b/presto-mysql/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-mysql @@ -14,14 +14,6 @@ ${project.parent.basedir} - - America/Bahia_Banderas diff --git a/presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/TestMySqlIntegrationSmokeTest.java b/presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/TestMySqlIntegrationSmokeTest.java index e3fba4f46e190..441d12aca9367 100644 --- a/presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/TestMySqlIntegrationSmokeTest.java +++ b/presto-mysql/src/test/java/com/facebook/presto/plugin/mysql/TestMySqlIntegrationSmokeTest.java @@ -16,7 +16,10 @@ import com.facebook.presto.Session; import com.facebook.presto.testing.MaterializedResult; import com.facebook.presto.testing.MaterializedRow; +import com.facebook.presto.testing.QueryRunner; import com.facebook.presto.tests.AbstractTestIntegrationSmokeTest; +import com.facebook.presto.tests.DistributedQueryRunner; +import com.google.common.collect.ImmutableMap; import io.airlift.testing.mysql.TestingMySqlServer; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -25,6 +28,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.util.Map; import static com.facebook.presto.plugin.mysql.MySqlQueryRunner.createMySqlQueryRunner; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; @@ -151,6 +155,38 @@ public void testMySqlTinyint1() assertUpdate("DROP TABLE mysql_test_tinyint1"); } + @Test + public void testCharTrailingSpace() + throws Exception + { + execute("CREATE TABLE tpch.char_trailing_space (x char(10))"); + assertUpdate("INSERT INTO char_trailing_space VALUES ('test')", 1); + + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test'", "VALUES 'test'"); + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test '", "VALUES 'test'"); + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test '", "VALUES 'test'"); + + assertEquals(getQueryRunner().execute("SELECT * FROM char_trailing_space WHERE x = char ' test'").getRowCount(), 0); + + Map properties = ImmutableMap.of("deprecated.legacy-char-to-varchar-coercion", "true"); + Map connectorProperties = ImmutableMap.of("connection-url", mysqlServer.getJdbcUrl()); + + try (QueryRunner queryRunner = new DistributedQueryRunner(getSession(), 3, properties);) { + queryRunner.installPlugin(new MySqlPlugin()); + queryRunner.createCatalog("mysql", "mysql", connectorProperties); + + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test'").getRowCount(), 0); + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '").getRowCount(), 0); + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '").getRowCount(), 0); + + MaterializedResult result = queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '"); + assertEquals(result.getRowCount(), 1); + assertEquals(result.getMaterializedRows().get(0).getField(0), "test "); + } + + assertUpdate("DROP TABLE char_trailing_space"); + } + private void execute(String sql) throws SQLException { diff --git a/presto-orc/pom.xml b/presto-orc/pom.xml index ba4addf9e00f8..ad3a85d20c8ef 100644 --- a/presto-orc/pom.xml +++ b/presto-orc/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-orc @@ -71,6 +71,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + com.google.guava guava @@ -91,6 +97,11 @@ jmxutils + + com.github.luben + zstd-jni + + com.google.code.findbugs jsr305 diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/ChainedSliceLoader.java b/presto-orc/src/main/java/com/facebook/presto/orc/ChainedSliceLoader.java deleted file mode 100644 index e9239345d86c9..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/ChainedSliceLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc; - -import com.google.common.collect.ImmutableList; -import io.airlift.slice.ChunkedSliceInput.BufferReference; -import io.airlift.slice.ChunkedSliceInput.SliceLoader; -import io.airlift.slice.Slice; -import io.airlift.slice.Slices; - -import java.util.List; - -import static java.lang.Math.toIntExact; -import static java.util.Objects.requireNonNull; - -public class ChainedSliceLoader - implements SliceLoader -{ - private final List slices; - private final long totalLength; - - public ChainedSliceLoader(List slices) - { - this.slices = ImmutableList.copyOf(requireNonNull(slices)); - this.totalLength = slices.stream().mapToLong(Slice::length).sum(); - } - - @Override - public BufferReference createBuffer(int bufferSize) - { - Slice slice = Slices.allocate(bufferSize); - return () -> slice; - } - - @Override - public long getSize() - { - return totalLength; - } - - @Override - public void load(long position, BufferReference bufferReference, int length) - { - if (position < 0 || position + length > totalLength) { - throw new IllegalArgumentException(); - } - - int currentSliceIndex = 0; - long globalOffset = 0; - while (globalOffset + slices.get(currentSliceIndex).length() <= position) { - globalOffset += slices.get(currentSliceIndex).length(); - currentSliceIndex++; - } - - int currentSliceOffset = toIntExact(position - globalOffset); - Slice outputSlice = bufferReference.getSlice(); - int outputSliceOffset = 0; - while (length > 0) { - Slice currentSlice = slices.get(currentSliceIndex); - int loadSize = Math.min(currentSlice.length() - currentSliceOffset, length); - - currentSlice.getBytes(currentSliceOffset, outputSlice, outputSliceOffset, loadSize); - length -= loadSize; - outputSliceOffset += loadSize; - - // advance to the next slice - currentSliceIndex++; - currentSliceOffset = 0; - } - } - - @Override - public void close() - { - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/DictionaryCompressionOptimizer.java b/presto-orc/src/main/java/com/facebook/presto/orc/DictionaryCompressionOptimizer.java index a00cc84f7b25e..dbe6ed9533cbe 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/DictionaryCompressionOptimizer.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/DictionaryCompressionOptimizer.java @@ -20,24 +20,27 @@ import io.airlift.units.DataSize.Unit; import java.util.HashSet; +import java.util.OptionalInt; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toSet; public class DictionaryCompressionOptimizer { private static final double DICTIONARY_MIN_COMPRESSION_RATIO = 1.25; - private static final double DICTIONARY_ALWAYS_KEEP_COMPRESSION_RATIO = 3.0; // Instead of waiting for the dictionary to fill completely, which would force a column into // direct mode, close the stripe early assuming it has hit the minimum row count. static final DataSize DICTIONARY_MEMORY_MAX_RANGE = new DataSize(4, Unit.MEGABYTE); + static final DataSize DIRECT_COLUMN_SIZE_RANGE = new DataSize(4, Unit.MEGABYTE); + private final Set allWriters; - private final Set dictionaryWriters = new HashSet<>(); + private final Set directConversionCandidates = new HashSet<>(); private final int stripeMinBytes; private final int stripeMaxBytes; @@ -72,7 +75,7 @@ public DictionaryCompressionOptimizer( this.dictionaryMemoryMaxBytesHigh = dictionaryMemoryMaxBytes; this.dictionaryMemoryMaxBytesLow = (int) Math.max(dictionaryMemoryMaxBytes - DICTIONARY_MEMORY_MAX_RANGE.toBytes(), 0); - dictionaryWriters.addAll(allWriters); + directConversionCandidates.addAll(allWriters); } public int getDictionaryMemoryBytes() @@ -93,93 +96,126 @@ public boolean isFull(long bufferedBytes) public void reset() { - dictionaryWriters.clear(); - dictionaryWriters.addAll(allWriters); + directConversionCandidates.clear(); + directConversionCandidates.addAll(allWriters); dictionaryMemoryBytes = 0; allWriters.forEach(DictionaryColumnManager::reset); } - public void finalOptimize() + public void finalOptimize(int bufferedBytes) { - convertLowCompressionStreams(); + convertLowCompressionStreams(bufferedBytes); } public void optimize(int bufferedBytes, int stripeRowCount) { // recompute the dictionary memory usage - dictionaryMemoryBytes = dictionaryWriters.stream() + dictionaryMemoryBytes = allWriters.stream() + .filter(writer -> !writer.isDirectEncoded()) .mapToInt(DictionaryColumnManager::getDictionaryBytes) .sum(); // update the dictionary growth history - dictionaryWriters.forEach(column -> column.updateHistory(stripeRowCount)); + allWriters.stream() + .filter(writer -> !writer.isDirectEncoded()) + .forEach(column -> column.updateHistory(stripeRowCount)); if (dictionaryMemoryBytes <= dictionaryMemoryMaxBytesLow) { return; } // before any further checks, convert all low compression streams - convertLowCompressionStreams(); + bufferedBytes = convertLowCompressionStreams(bufferedBytes); - if (dictionaryMemoryBytes <= dictionaryMemoryMaxBytesLow) { + if (dictionaryMemoryBytes <= dictionaryMemoryMaxBytesLow || bufferedBytes >= stripeMaxBytes) { return; } // calculate size of non-dictionary columns by removing the buffered size of dictionary columns int nonDictionaryBufferedBytes = bufferedBytes; - for (DictionaryColumnManager dictionaryWriter : dictionaryWriters) { - nonDictionaryBufferedBytes -= dictionaryWriter.getBufferedBytes(); + for (DictionaryColumnManager dictionaryWriter : allWriters) { + if (!dictionaryWriter.isDirectEncoded()) { + nonDictionaryBufferedBytes -= dictionaryWriter.getBufferedBytes(); + } } - // convert dictionary columns to direct until we are blow the high memory limit - while (dictionaryMemoryBytes > dictionaryMemoryMaxBytesHigh) { + // convert dictionary columns to direct until we are below the high memory limit + while (!directConversionCandidates.isEmpty() && dictionaryMemoryBytes > dictionaryMemoryMaxBytesHigh && bufferedBytes < stripeMaxBytes) { DictionaryCompressionProjection projection = selectDictionaryColumnToConvert(nonDictionaryBufferedBytes, stripeRowCount); - if (projection.getColumnToConvert().getCompressionRatio() >= DICTIONARY_ALWAYS_KEEP_COMPRESSION_RATIO) { - return; + int selectDictionaryColumnBufferedBytes = toIntExact(projection.getColumnToConvert().getBufferedBytes()); + + OptionalInt directBytes = tryConvertToDirect(projection.getColumnToConvert(), getMaxDirectBytes(bufferedBytes)); + if (directBytes.isPresent()) { + bufferedBytes = bufferedBytes + directBytes.getAsInt() - selectDictionaryColumnBufferedBytes; + nonDictionaryBufferedBytes += directBytes.getAsInt(); } - nonDictionaryBufferedBytes += convertToDirect(projection.getColumnToConvert()); } - // if the stripe is larger then the minimum row count, we are not required to convert any more dictionary columns to direct - if (nonDictionaryBufferedBytes + dictionaryMemoryBytes >= stripeMinBytes) { + if (bufferedBytes >= stripeMaxBytes) { + return; + } + + // if the stripe is larger then the minimum stripe size, we are not required to convert any more dictionary columns to direct + if (bufferedBytes >= stripeMinBytes) { // check if we can get better compression by converting a dictionary column to direct. This can happen when then there are multiple // dictionary columns and one does not compress well, so if we convert it to direct we can continue to use the existing dictionaries // for the other columns. double currentCompressionRatio = currentCompressionRatio(nonDictionaryBufferedBytes); - while (!dictionaryWriters.isEmpty()) { + while (!directConversionCandidates.isEmpty() && bufferedBytes < stripeMaxBytes) { DictionaryCompressionProjection projection = selectDictionaryColumnToConvert(nonDictionaryBufferedBytes, stripeRowCount); if (projection.getPredictedFileCompressionRatio() < currentCompressionRatio) { return; } - nonDictionaryBufferedBytes += convertToDirect(projection.getColumnToConvert()); + + int selectDictionaryColumnBufferedBytes = toIntExact(projection.getColumnToConvert().getBufferedBytes()); + OptionalInt directBytes = tryConvertToDirect(projection.getColumnToConvert(), getMaxDirectBytes(bufferedBytes)); + if (directBytes.isPresent()) { + bufferedBytes = bufferedBytes + directBytes.getAsInt() - selectDictionaryColumnBufferedBytes; + nonDictionaryBufferedBytes += directBytes.getAsInt(); + } } } } - private void convertLowCompressionStreams() + private int convertLowCompressionStreams(int bufferedBytes) { // convert all low compression column to direct - ImmutableList.copyOf(dictionaryWriters).stream() - .filter(dictionaryWriter -> dictionaryWriter.getCompressionRatio() < DICTIONARY_MIN_COMPRESSION_RATIO) - .forEach(this::convertToDirect); + for (DictionaryColumnManager dictionaryWriter : ImmutableList.copyOf(directConversionCandidates)) { + if (dictionaryWriter.getCompressionRatio() < DICTIONARY_MIN_COMPRESSION_RATIO) { + int columnBufferedBytes = toIntExact(dictionaryWriter.getBufferedBytes()); + OptionalInt directBytes = tryConvertToDirect(dictionaryWriter, getMaxDirectBytes(bufferedBytes)); + if (directBytes.isPresent()) { + bufferedBytes = bufferedBytes + directBytes.getAsInt() - columnBufferedBytes; + if (bufferedBytes >= stripeMaxBytes) { + return bufferedBytes; + } + } + } + } + return bufferedBytes; } - private long convertToDirect(DictionaryColumnManager dictionaryWriter) + private OptionalInt tryConvertToDirect(DictionaryColumnManager dictionaryWriter, int maxDirectBytes) { - dictionaryMemoryBytes -= dictionaryWriter.getDictionaryBytes(); - long directBytes = dictionaryWriter.convertToDirect(); - dictionaryWriters.remove(dictionaryWriter); + int dictionaryBytes = dictionaryWriter.getDictionaryBytes(); + OptionalInt directBytes = dictionaryWriter.tryConvertToDirect(maxDirectBytes); + if (directBytes.isPresent()) { + dictionaryMemoryBytes -= dictionaryBytes; + } + directConversionCandidates.remove(dictionaryWriter); return directBytes; } - private double currentCompressionRatio(int allOtherBytes) + private double currentCompressionRatio(int totalNonDictionaryBytes) { - long uncompressedBytes = allOtherBytes; - long compressedBytes = allOtherBytes; + long uncompressedBytes = totalNonDictionaryBytes; + long compressedBytes = totalNonDictionaryBytes; - for (DictionaryColumnManager column : dictionaryWriters) { - uncompressedBytes += column.getRawBytes(); - compressedBytes += column.getDictionaryBytes(); + for (DictionaryColumnManager column : allWriters) { + if (!column.isDirectEncoded()) { + uncompressedBytes += column.getRawBytes(); + compressedBytes += column.getDictionaryBytes(); + } } return 1.0 * uncompressedBytes / compressedBytes; } @@ -196,7 +232,7 @@ private double currentCompressionRatio(int allOtherBytes) */ private DictionaryCompressionProjection selectDictionaryColumnToConvert(int totalNonDictionaryBytes, int stripeRowCount) { - checkState(!dictionaryWriters.isEmpty()); + checkState(!directConversionCandidates.isEmpty()); int totalNonDictionaryBytesPerRow = totalNonDictionaryBytes / stripeRowCount; @@ -212,20 +248,22 @@ private DictionaryCompressionProjection selectDictionaryColumnToConvert(int tota long totalDictionaryBytesPerNewRow = 0; long totalDictionaryIndexBytesPerRow = 0; - for (DictionaryColumnManager column : dictionaryWriters) { - totalDictionaryRawBytes += column.getRawBytes(); - totalDictionaryBytes += column.getDictionaryBytes(); - totalDictionaryIndexBytes += column.getIndexBytes(); + for (DictionaryColumnManager column : allWriters) { + if (!column.isDirectEncoded()) { + totalDictionaryRawBytes += column.getRawBytes(); + totalDictionaryBytes += column.getDictionaryBytes(); + totalDictionaryIndexBytes += column.getIndexBytes(); - totalDictionaryRawBytesPerRow += column.getRawBytesPerRow(); - totalDictionaryBytesPerNewRow += column.getDictionaryBytesPerFutureRow(); - totalDictionaryIndexBytesPerRow += column.getIndexBytesPerRow(); + totalDictionaryRawBytesPerRow += column.getRawBytesPerRow(); + totalDictionaryBytesPerNewRow += column.getDictionaryBytesPerFutureRow(); + totalDictionaryIndexBytesPerRow += column.getIndexBytesPerRow(); + } } long totalUncompressedBytesPerRow = totalNonDictionaryBytesPerRow + totalDictionaryRawBytesPerRow; DictionaryCompressionProjection maxProjectedCompression = null; - for (DictionaryColumnManager column : dictionaryWriters) { + for (DictionaryColumnManager column : directConversionCandidates) { // determine the size of the currently written stripe if we were convert this column to direct long currentRawBytes = totalNonDictionaryBytes + column.getRawBytes(); long currentDictionaryBytes = totalDictionaryBytes - column.getDictionaryBytes(); @@ -257,6 +295,11 @@ private DictionaryCompressionProjection selectDictionaryColumnToConvert(int tota return maxProjectedCompression; } + private int getMaxDirectBytes(int bufferedBytes) + { + return toIntExact(Math.min(stripeMaxBytes, stripeMaxBytes - bufferedBytes + DIRECT_COLUMN_SIZE_RANGE.toBytes())); + } + public static int estimateIndexBytesPerValue(int dictionaryEntries) { // assume basic byte packing @@ -274,9 +317,9 @@ public static int estimateIndexBytesPerValue(int dictionaryEntries) public interface DictionaryColumn { - int getValueCount(); + long getValueCount(); - int getNonNullValueCount(); + long getNonNullValueCount(); long getRawBytes(); @@ -284,7 +327,11 @@ public interface DictionaryColumn int getDictionaryBytes(); - long convertToDirect(); + int getIndexBytes(); + + OptionalInt tryConvertToDirect(int maxDirectBytes); + + long getBufferedBytes(); } private static class DictionaryColumnManager @@ -294,10 +341,10 @@ private static class DictionaryColumnManager private int rowCount; - private int pastValueCount; + private long pastValueCount; private int pastDictionaryEntries; - private int pendingPastValueCount; + private long pendingPastValueCount; private int pendingPastDictionaryEntries; public DictionaryColumnManager(DictionaryColumn dictionaryColumn) @@ -305,10 +352,13 @@ public DictionaryColumnManager(DictionaryColumn dictionaryColumn) this.dictionaryColumn = dictionaryColumn; } - long convertToDirect() + OptionalInt tryConvertToDirect(int maxDirectBytes) { - directEncoded = true; - return dictionaryColumn.convertToDirect(); + OptionalInt directBytes = dictionaryColumn.tryConvertToDirect(maxDirectBytes); + if (directBytes.isPresent()) { + directEncoded = true; + } + return directBytes; } void reset() @@ -325,7 +375,7 @@ void reset() public void updateHistory(int rowCount) { this.rowCount = rowCount; - int currentValueCount = dictionaryColumn.getValueCount(); + long currentValueCount = dictionaryColumn.getValueCount(); if (currentValueCount - pendingPastValueCount >= 1024) { pastValueCount = pendingPastValueCount; pastDictionaryEntries = pendingPastDictionaryEntries; @@ -358,7 +408,7 @@ public double getDictionaryBytesPerFutureRow() checkState(!directEncoded); int currentDictionaryEntries = dictionaryColumn.getDictionaryEntries(); - int currentValueCount = dictionaryColumn.getValueCount(); + long currentValueCount = dictionaryColumn.getValueCount(); // average size of a dictionary entry double dictionaryBytesPerEntry = 1.0 * dictionaryColumn.getDictionaryBytes() / currentDictionaryEntries; @@ -373,7 +423,7 @@ public double getDictionaryBytesPerFutureRow() public int getIndexBytes() { checkState(!directEncoded); - return estimateIndexBytesPerValue(dictionaryColumn.getDictionaryEntries()) * dictionaryColumn.getNonNullValueCount(); + return dictionaryColumn.getIndexBytes(); } public double getIndexBytesPerRow() @@ -382,26 +432,20 @@ public double getIndexBytesPerRow() return 1.0 * getIndexBytes() / rowCount; } - public int getNullsBytes() + public double getCompressionRatio() { checkState(!directEncoded); - return (dictionaryColumn.getValueCount() - dictionaryColumn.getNonNullValueCount() + 7) / 8; + return 1.0 * getRawBytes() / getBufferedBytes(); } - public int getCompressedBytes() - { - return getDictionaryBytes() + getIndexBytes() + getNullsBytes(); - } - - public double getCompressionRatio() + public long getBufferedBytes() { - checkState(!directEncoded); - return 1.0 * getRawBytes() / getCompressedBytes(); + return dictionaryColumn.getBufferedBytes(); } - public long getBufferedBytes() + public boolean isDirectEncoded() { - return getIndexBytes() + getDictionaryBytes(); + return directEncoded; } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcEncoding.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcEncoding.java index f3f7a344c9ae0..30df8e391198d 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcEncoding.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcEncoding.java @@ -50,5 +50,6 @@ public MetadataWriter createMetadataWriter() }; public abstract MetadataReader createMetadataReader(); + public abstract MetadataWriter createMetadataWriter(); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcOutputBuffer.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcOutputBuffer.java index 47b36014ed978..c84e8904ac405 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcOutputBuffer.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcOutputBuffer.java @@ -15,6 +15,7 @@ import com.facebook.presto.orc.checkpoint.InputStreamCheckpoint; import com.facebook.presto.orc.metadata.CompressionKind; +import com.facebook.presto.orc.zstd.ZstdJniCompressor; import com.google.common.annotations.VisibleForTesting; import io.airlift.compress.Compressor; import io.airlift.compress.lz4.Lz4Compressor; @@ -30,7 +31,6 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.util.Arrays; -import java.util.List; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -98,6 +98,9 @@ else if (compression == CompressionKind.ZLIB) { else if (compression == CompressionKind.LZ4) { this.compressor = new Lz4Compressor(); } + else if (compression == CompressionKind.ZSTD) { + this.compressor = new ZstdJniCompressor(); + } else { throw new IllegalArgumentException("Unsupported compression " + compression); } @@ -365,12 +368,6 @@ public Slice getUnderlyingSlice() throw new UnsupportedOperationException(); } - public List getCompressedSlices() - { - flushBufferToOutputStream(); - return compressedOutputStream.getSlices(); - } - @Override public String toString(Charset charset) { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcReader.java index 5a418f52690d0..39a21fe62b70e 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcReader.java @@ -184,6 +184,9 @@ public OrcReader(OrcDataSource orcDataSource, OrcEncoding orcEncoding, DataSize try (InputStream footerInputStream = new OrcInputStream(orcDataSource.getId(), footerSlice.getInput(), decompressor, newSimpleAggregatedMemoryContext(), footerSize)) { this.footer = metadataReader.readFooter(hiveWriterVersion, footerInputStream); } + if (footer.getTypes().size() == 0) { + throw new OrcCorruptionException(orcDataSource.getId(), "File has no columns"); + } validateWrite(validation -> validation.getColumnNames().equals(getColumnNames()), "Unexpected column names"); validateWrite(validation -> validation.getRowGroupMaxRowCount() == footer.getRowsInRowGroup(), "Unexpected rows in group"); diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java index b8382753b0b4e..5acce537e675c 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java @@ -56,8 +56,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; import java.util.SortedMap; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.IntStream; import static com.facebook.presto.orc.OrcWriteValidation.OrcWriteValidationMode.BOTH; @@ -92,7 +94,8 @@ public class OrcWriteValidation { - public enum OrcWriteValidationMode { + public enum OrcWriteValidationMode + { HASHED, DETAILED, BOTH } @@ -216,7 +219,7 @@ public void validateStripeStatistics(OrcDataSourceId orcDataSourceId, long strip validateColumnStatisticsEquivalent(orcDataSourceId, "Stripe at " + stripeOffset, actual, expected.getColumnStatistics()); } - public void validateRowGroupStatistics(OrcDataSourceId orcDataSourceId, long stripeOffset, Map> actualRowGroupStatistics) + public void validateRowGroupStatistics(OrcDataSourceId orcDataSourceId, long stripeOffset, Map> actualRowGroupStatistics) throws OrcCorruptionException { requireNonNull(actualRowGroupStatistics, "actualRowGroupStatistics is null"); @@ -226,7 +229,11 @@ public void validateRowGroupStatistics(OrcDataSourceId orcDataSourceId, long str } int rowGroupCount = expectedRowGroupStatistics.size(); - for (Entry> entry : actualRowGroupStatistics.entrySet()) { + for (Entry> entry : actualRowGroupStatistics.entrySet()) { + // TODO: Remove once the Presto writer supports flat map + if (entry.getKey().getSequence() > 0) { + throw new OrcCorruptionException(orcDataSourceId, "Unexpected sequence ID for column %s at offset %s", entry.getKey().getColumn(), stripeOffset); + } if (entry.getValue().size() != rowGroupCount) { throw new OrcCorruptionException(orcDataSourceId, "Unexpected row group count stripe in at offset %s", stripeOffset); } @@ -236,14 +243,15 @@ public void validateRowGroupStatistics(OrcDataSourceId orcDataSourceId, long str RowGroupStatistics expectedRowGroup = expectedRowGroupStatistics.get(rowGroupIndex); if (expectedRowGroup.getValidationMode() != HASHED) { Map expectedStatistics = expectedRowGroup.getColumnStatistics(); - if (!expectedStatistics.keySet().equals(actualRowGroupStatistics.keySet())) { + Set actualColumns = actualRowGroupStatistics.keySet().stream() + .map(StreamId::getColumn) + .collect(Collectors.toSet()); + if (!expectedStatistics.keySet().equals(actualColumns)) { throw new OrcCorruptionException(orcDataSourceId, "Unexpected column in row group %s in stripe at offset %s", rowGroupIndex, stripeOffset); } - for (Entry entry : expectedStatistics.entrySet()) { - int columnIndex = entry.getKey(); - List actualRowGroup = actualRowGroupStatistics.get(columnIndex); - ColumnStatistics actual = actualRowGroup.get(rowGroupIndex).getColumnStatistics(); - ColumnStatistics expected = entry.getValue(); + for (Entry> entry : actualRowGroupStatistics.entrySet()) { + ColumnStatistics actual = entry.getValue().get(rowGroupIndex).getColumnStatistics(); + ColumnStatistics expected = expectedStatistics.get(entry.getKey().getColumn()); validateColumnStatisticsEquivalent(orcDataSourceId, "Row group " + rowGroupIndex + " in stripe at offset " + stripeOffset, actual, expected); } } @@ -257,13 +265,13 @@ public void validateRowGroupStatistics(OrcDataSourceId orcDataSourceId, long str } } - private static RowGroupStatistics buildActualRowGroupStatistics(int rowGroupIndex, Map> actualRowGroupStatistics) + private static RowGroupStatistics buildActualRowGroupStatistics(int rowGroupIndex, Map> actualRowGroupStatistics) { return new RowGroupStatistics( - BOTH, - IntStream.range(1, actualRowGroupStatistics.size() + 1) - .boxed() - .collect(toImmutableMap(identity(), columnIndex -> actualRowGroupStatistics.get(columnIndex).get(rowGroupIndex).getColumnStatistics()))); + BOTH, + actualRowGroupStatistics.entrySet() + .stream() + .collect(Collectors.toMap(entry -> entry.getKey().getColumn(), entry -> entry.getValue().get(rowGroupIndex).getColumnStatistics()))); } public void validateRowGroupStatistics( diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriter.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriter.java index 7603d24da162d..c82278d2c026c 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriter.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriter.java @@ -94,6 +94,7 @@ public class OrcWriter private final CompressionKind compression; private final int stripeMinBytes; private final int stripeMaxBytes; + private final int chunkMaxLogicalBytes; private final int stripeMaxRowCount; private final int rowGroupMaxRowCount; private final int maxCompressionBufferSize; @@ -142,6 +143,7 @@ public OrcWriter( checkArgument(options.getStripeMaxSize().compareTo(options.getStripeMinSize()) >= 0, "stripeMaxSize must be greater than stripeMinSize"); this.stripeMinBytes = toIntExact(requireNonNull(options.getStripeMinSize(), "stripeMinSize is null").toBytes()); this.stripeMaxBytes = toIntExact(requireNonNull(options.getStripeMaxSize(), "stripeMaxSize is null").toBytes()); + this.chunkMaxLogicalBytes = Math.max(1, stripeMaxBytes / 2); this.stripeMaxRowCount = options.getStripeMaxRowCount(); this.rowGroupMaxRowCount = options.getRowGroupMaxRowCount(); recordValidation(validation -> validation.setRowGroupMaxRowCount(rowGroupMaxRowCount)); @@ -238,16 +240,22 @@ public void write(Page page) while (page != null) { // align page to row group boundaries - Page chunk; - if (rowGroupRowCount + page.getPositionCount() > rowGroupMaxRowCount || stripeRowCount + page.getPositionCount() > stripeMaxRowCount) { - int chunkRows = min(rowGroupMaxRowCount - rowGroupRowCount, stripeMaxRowCount - stripeRowCount); - chunk = page.getRegion(0, chunkRows); + int chunkRows = min(page.getPositionCount(), min(rowGroupMaxRowCount - rowGroupRowCount, stripeMaxRowCount - stripeRowCount)); + Page chunk = page.getRegion(0, chunkRows); + + // avoid chunk with huge logical size + while (chunkRows > 1 && chunk.getLogicalSizeInBytes() > chunkMaxLogicalBytes) { + chunkRows /= 2; + chunk = chunk.getRegion(0, chunkRows); + } + + if (chunkRows < page.getPositionCount()) { page = page.getRegion(chunkRows, page.getPositionCount() - chunkRows); } else { - chunk = page; page = null; } + writeChunk(chunk); } @@ -354,7 +362,7 @@ private List bufferStripeData(long stripeStartOffset, FlushReason } // convert any dictionary encoded column with a low compression ratio to direct - dictionaryCompressionOptimizer.finalOptimize(); + dictionaryCompressionOptimizer.finalOptimize(bufferedBytes); columnWriters.forEach(ColumnWriter::close); @@ -416,10 +424,6 @@ private List bufferStripeData(long stripeStartOffset, FlushReason recordValidation(validation -> validation.addStripe(stripeInformation.getNumberOfRows())); stats.recordStripeWritten(flushReason, stripeInformation.getTotalLength(), stripeInformation.getNumberOfRows(), dictionaryCompressionOptimizer.getDictionaryMemoryBytes()); - if (flushReason == MAX_BYTES && stripeInformation.getTotalLength() < stripeMaxBytes / 2) { - log.debug("Small stripe with size %s get MAX_BYTES flush. QueryId: %s", stripeInformation.getTotalLength(), userMetadata.get("presto_query_id")); - } - return outputData; } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriterOptions.java b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriterOptions.java index faf508d713ac5..1124a8102b148 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriterOptions.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriterOptions.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.orc; +import com.google.common.annotations.VisibleForTesting; import io.airlift.units.DataSize; import static com.google.common.base.MoreObjects.toStringHelper; @@ -29,8 +30,12 @@ public class OrcWriterOptions private static final int DEFAULT_STRIPE_MAX_ROW_COUNT = 10_000_000; private static final int DEFAULT_ROW_GROUP_MAX_ROW_COUNT = 10_000; private static final DataSize DEFAULT_DICTIONARY_MAX_MEMORY = new DataSize(16, MEGABYTE); - public static final DataSize DEFAULT_MAX_STRING_STATISTICS_LIMIT = new DataSize(64, BYTE); - private static final DataSize DEFAULT_MAX_COMPRESSION_BUFFER_SIZE = new DataSize(256, KILOBYTE); + + @VisibleForTesting + static final DataSize DEFAULT_MAX_STRING_STATISTICS_LIMIT = new DataSize(64, BYTE); + + @VisibleForTesting + static final DataSize DEFAULT_MAX_COMPRESSION_BUFFER_SIZE = new DataSize(256, KILOBYTE); private final DataSize stripeMinSize; private final DataSize stripeMaxSize; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/StreamDescriptor.java b/presto-orc/src/main/java/com/facebook/presto/orc/StreamDescriptor.java index c1842afdc8521..9d01b3bb67bc1 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/StreamDescriptor.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/StreamDescriptor.java @@ -18,6 +18,7 @@ import java.util.List; +import static com.facebook.presto.orc.metadata.ColumnEncoding.DEFAULT_SEQUENCE_ID; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -25,15 +26,22 @@ public final class StreamDescriptor { private final String streamName; private final int streamId; + private final int sequence; private final OrcTypeKind streamType; private final String fieldName; private final OrcDataSource orcDataSource; private final List nestedStreams; public StreamDescriptor(String streamName, int streamId, String fieldName, OrcTypeKind streamType, OrcDataSource orcDataSource, List nestedStreams) + { + this(streamName, streamId, fieldName, streamType, orcDataSource, nestedStreams, DEFAULT_SEQUENCE_ID); + } + + public StreamDescriptor(String streamName, int streamId, String fieldName, OrcTypeKind streamType, OrcDataSource orcDataSource, List nestedStreams, int sequence) { this.streamName = requireNonNull(streamName, "streamName is null"); this.streamId = streamId; + this.sequence = sequence; this.fieldName = requireNonNull(fieldName, "fieldName is null"); this.streamType = requireNonNull(streamType, "type is null"); this.orcDataSource = requireNonNull(orcDataSource, "orcDataSource is null"); @@ -50,6 +58,11 @@ public int getStreamId() return streamId; } + public int getSequence() + { + return sequence; + } + public OrcTypeKind getStreamType() { return streamType; @@ -81,6 +94,7 @@ public String toString() return toStringHelper(this) .add("streamName", streamName) .add("streamId", streamId) + .add("sequence", sequence) .add("streamType", streamType) .add("dataSource", orcDataSource.getId()) .toString(); diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/StreamId.java b/presto-orc/src/main/java/com/facebook/presto/orc/StreamId.java index 6ffe1b1a8faf2..e6296541780f0 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/StreamId.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/StreamId.java @@ -16,22 +16,27 @@ import com.facebook.presto.orc.metadata.Stream; import com.facebook.presto.orc.metadata.Stream.StreamKind; +import java.util.Objects; + import static com.google.common.base.MoreObjects.toStringHelper; public final class StreamId { private final int column; + private final int sequence; private final StreamKind streamKind; public StreamId(Stream stream) { this.column = stream.getColumn(); + this.sequence = stream.getSequence(); this.streamKind = stream.getStreamKind(); } - public StreamId(int column, StreamKind streamKind) + public StreamId(int column, int sequence, StreamKind streamKind) { this.column = column; + this.sequence = sequence; this.streamKind = streamKind; } @@ -40,6 +45,11 @@ public int getColumn() return column; } + public int getSequence() + { + return sequence; + } + public StreamKind getStreamKind() { return streamKind; @@ -48,7 +58,7 @@ public StreamKind getStreamKind() @Override public int hashCode() { - return 31 * column + streamKind.hashCode(); + return Objects.hash(column, sequence, streamKind); } @Override @@ -62,7 +72,7 @@ public boolean equals(Object obj) } StreamId other = (StreamId) obj; - return column == other.column && streamKind == other.streamKind; + return column == other.column && sequence == other.sequence && streamKind == other.streamKind; } @Override @@ -70,6 +80,7 @@ public String toString() { return toStringHelper(this) .add("column", column) + .add("sequence", sequence) .add("streamKind", streamKind) .toString(); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/StripeReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/StripeReader.java index 34ad886882238..fd8e0d9293a14 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/StripeReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/StripeReader.java @@ -18,6 +18,7 @@ import com.facebook.presto.orc.checkpoint.StreamCheckpoint; import com.facebook.presto.orc.metadata.ColumnEncoding; import com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind; +import com.facebook.presto.orc.metadata.DwrfSequenceEncoding; import com.facebook.presto.orc.metadata.MetadataReader; import com.facebook.presto.orc.metadata.OrcType; import com.facebook.presto.orc.metadata.OrcType.OrcTypeKind; @@ -44,6 +45,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; @@ -62,9 +64,11 @@ import static com.facebook.presto.orc.metadata.Stream.StreamKind.DICTIONARY_DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; import static com.facebook.presto.orc.metadata.Stream.StreamKind.ROW_INDEX; +import static com.facebook.presto.orc.metadata.statistics.ColumnStatistics.mergeColumnStatistics; import static com.facebook.presto.orc.stream.CheckpointInputStreamSource.createCheckpointStreamSource; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; @@ -115,9 +119,20 @@ public Stripe readStripe(StripeInformation stripe, AggregatedMemoryContext syste if (includedOrcColumns.contains(stream.getColumn())) { streams.put(new StreamId(stream), stream); - ColumnEncodingKind columnEncoding = columnEncodings.get(stream.getColumn()).getColumnEncodingKind(); - if (columnEncoding == DICTIONARY && stream.getStreamKind() == StreamKind.IN_DICTIONARY) { - hasRowGroupDictionary = true; + if (stream.getStreamKind() == StreamKind.IN_DICTIONARY) { + ColumnEncoding columnEncoding = columnEncodings.get(stream.getColumn()); + + if (columnEncoding.getColumnEncodingKind() == DICTIONARY) { + hasRowGroupDictionary = true; + } + + Optional> additionalSequenceEncodings = columnEncoding.getAdditionalSequenceEncodings(); + if (additionalSequenceEncodings.isPresent() + && additionalSequenceEncodings.get().stream() + .map(DwrfSequenceEncoding::getValueEncoding) + .anyMatch(encoding -> encoding.getColumnEncodingKind() == DICTIONARY)) { + hasRowGroupDictionary = true; + } } } } @@ -133,10 +148,10 @@ public Stripe readStripe(StripeInformation stripe, AggregatedMemoryContext syste Map streamsData = readDiskRanges(stripe.getOffset(), diskRanges, systemMemoryUsage); // read the bloom filter for each column - Map> bloomFilterIndexes = readBloomFilterIndexes(streams, streamsData); + Map> bloomFilterIndexes = readBloomFilterIndexes(streams, streamsData); // read the row index for each column - Map> columnIndexes = readColumnIndexes(streams, streamsData, bloomFilterIndexes); + Map> columnIndexes = readColumnIndexes(streams, streamsData, bloomFilterIndexes); if (writeValidation.isPresent()) { writeValidation.get().validateRowGroupStatistics(orcDataSource.getId(), stripe.getOffset(), columnIndexes); } @@ -262,7 +277,9 @@ private Map> createValueStreams(Map entry : streams.entrySet()) { StreamId streamId = entry.getKey(); Stream stream = entry.getValue(); - ColumnEncodingKind columnEncoding = columnEncodings.get(stream.getColumn()).getColumnEncodingKind(); + ColumnEncodingKind columnEncoding = columnEncodings.get(stream.getColumn()) + .getColumnEncoding(stream.getSequence()) + .getColumnEncodingKind(); // skip index and empty streams if (isIndexStream(stream) || stream.getLength() == 0) { @@ -286,7 +303,9 @@ public InputStreamSources createDictionaryStreamSources(Map st int column = stream.getColumn(); // only process dictionary streams - ColumnEncodingKind columnEncoding = columnEncodings.get(column).getColumnEncodingKind(); + ColumnEncodingKind columnEncoding = columnEncodings.get(column) + .getColumnEncoding(stream.getSequence()) + .getColumnEncodingKind(); if (!isDictionary(stream, columnEncoding)) { continue; } @@ -310,7 +329,7 @@ private List createRowGroups( int rowsInStripe, Map streams, Map> valueStreams, - Map> columnIndexes, + Map> columnIndexes, Set selectedRowGroups, List encodings) throws InvalidCheckpointException @@ -373,30 +392,30 @@ static boolean isIndexStream(Stream stream) return stream.getStreamKind() == ROW_INDEX || stream.getStreamKind() == DICTIONARY_COUNT || stream.getStreamKind() == BLOOM_FILTER || stream.getStreamKind() == BLOOM_FILTER_UTF8; } - private Map> readBloomFilterIndexes(Map streams, Map streamsData) + private Map> readBloomFilterIndexes(Map streams, Map streamsData) throws IOException { - ImmutableMap.Builder> bloomFilters = ImmutableMap.builder(); + ImmutableMap.Builder> bloomFilters = ImmutableMap.builder(); for (Entry entry : streams.entrySet()) { Stream stream = entry.getValue(); if (stream.getStreamKind() == BLOOM_FILTER) { OrcInputStream inputStream = streamsData.get(entry.getKey()); - bloomFilters.put(stream.getColumn(), metadataReader.readBloomFilterIndexes(inputStream)); + bloomFilters.put(entry.getKey(), metadataReader.readBloomFilterIndexes(inputStream)); } // TODO: add support for BLOOM_FILTER_UTF8 } return bloomFilters.build(); } - private Map> readColumnIndexes(Map streams, Map streamsData, Map> bloomFilterIndexes) + private Map> readColumnIndexes(Map streams, Map streamsData, Map> bloomFilterIndexes) throws IOException { - ImmutableMap.Builder> columnIndexes = ImmutableMap.builder(); + ImmutableMap.Builder> columnIndexes = ImmutableMap.builder(); for (Entry entry : streams.entrySet()) { Stream stream = entry.getValue(); if (stream.getStreamKind() == ROW_INDEX) { OrcInputStream inputStream = streamsData.get(entry.getKey()); - List bloomFilters = bloomFilterIndexes.get(stream.getColumn()); + List bloomFilters = bloomFilterIndexes.get(entry.getKey()); List rowGroupIndexes = metadataReader.readRowIndexes(hiveWriterVersion, inputStream); if (bloomFilters != null && !bloomFilters.isEmpty()) { ImmutableList.Builder newRowGroupIndexes = ImmutableList.builder(); @@ -408,13 +427,13 @@ private Map> readColumnIndexes(Map selectRowGroups(StripeInformation stripe, Map> columnIndexes) + private Set selectRowGroups(StripeInformation stripe, Map> columnIndexes) { int rowsInStripe = toIntExact(stripe.getNumberOfRows()); int groupsInStripe = ceil(rowsInStripe, rowsInRowGroup); @@ -432,18 +451,31 @@ private Set selectRowGroups(StripeInformation stripe, Map getRowGroupStatistics(OrcType rootStructType, Map> columnIndexes, int rowGroup) + private static Map getRowGroupStatistics(OrcType rootStructType, Map> columnIndexes, int rowGroup) { requireNonNull(rootStructType, "rootStructType is null"); checkArgument(rootStructType.getOrcTypeKind() == OrcTypeKind.STRUCT); requireNonNull(columnIndexes, "columnIndexes is null"); checkArgument(rowGroup >= 0, "rowGroup is negative"); + Map> groupedColumnStatistics = new HashMap<>(); + for (Entry> entry : columnIndexes.entrySet()) { + groupedColumnStatistics.computeIfAbsent(entry.getKey().getColumn(), key -> new ArrayList<>()) + .add(entry.getValue().get(rowGroup).getColumnStatistics()); + } + ImmutableMap.Builder statistics = ImmutableMap.builder(); for (int ordinal = 0; ordinal < rootStructType.getFieldCount(); ordinal++) { - List rowGroupIndexes = columnIndexes.get(rootStructType.getFieldTypeIndex(ordinal)); - if (rowGroupIndexes != null) { - statistics.put(ordinal, rowGroupIndexes.get(rowGroup).getColumnStatistics()); + List columnStatistics = groupedColumnStatistics.get(rootStructType.getFieldTypeIndex(ordinal)); + if (columnStatistics != null) { + if (columnStatistics.size() == 1) { + statistics.put(ordinal, getOnlyElement(columnStatistics)); + } + else { + // Merge statistics from different streams + // This can happen if map is represented as struct (DWRF only) + statistics.put(ordinal, mergeColumnStatistics(columnStatistics)); + } } } return statistics.build(); diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/checkpoint/Checkpoints.java b/presto-orc/src/main/java/com/facebook/presto/orc/checkpoint/Checkpoints.java index 391e47bae7c05..3ce51542eae32 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/checkpoint/Checkpoints.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/checkpoint/Checkpoints.java @@ -40,6 +40,7 @@ import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DICTIONARY_DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_DICTIONARY; +import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_MAP; import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.metadata.Stream.StreamKind.ROW_GROUP_DICTIONARY; @@ -62,7 +63,7 @@ public static Map getStreamCheckpoints( int rowGroupId, List columnEncodings, Map streams, - Map> columnIndexes) + Map> columnIndexes) throws InvalidCheckpointException { ImmutableSetMultimap.Builder streamKindsBuilder = ImmutableSetMultimap.builder(); @@ -72,53 +73,60 @@ public static Map getStreamCheckpoints( SetMultimap streamKinds = streamKindsBuilder.build(); ImmutableMap.Builder checkpoints = ImmutableMap.builder(); - for (int column : columns) { - List positionsList = columnIndexes.get(column).get(rowGroupId).getPositions(); + for (Map.Entry> entry : columnIndexes.entrySet()) { + int column = entry.getKey().getColumn(); + + if (!columns.contains(column)) { + continue; + } + + int sequence = entry.getKey().getSequence(); + List positionsList = entry.getValue().get(rowGroupId).getPositions(); ColumnEncodingKind columnEncoding = columnEncodings.get(column).getColumnEncodingKind(); OrcTypeKind columnType = columnTypes.get(column).getOrcTypeKind(); Set availableStreams = streamKinds.get(column); - ColumnPositionsList columnPositionsList = new ColumnPositionsList(column, columnType, positionsList); + ColumnPositionsList columnPositionsList = new ColumnPositionsList(column, sequence, columnType, positionsList); switch (columnType) { case BOOLEAN: - checkpoints.putAll(getBooleanColumnCheckpoints(column, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getBooleanColumnCheckpoints(column, sequence, compressed, availableStreams, columnPositionsList)); break; case BYTE: - checkpoints.putAll(getByteColumnCheckpoints(column, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getByteColumnCheckpoints(column, sequence, compressed, availableStreams, columnPositionsList)); break; case SHORT: case INT: case LONG: case DATE: - checkpoints.putAll(getLongColumnCheckpoints(column, columnEncoding, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getLongColumnCheckpoints(column, sequence, columnEncoding, compressed, availableStreams, columnPositionsList)); break; case FLOAT: - checkpoints.putAll(getFloatColumnCheckpoints(column, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getFloatColumnCheckpoints(column, sequence, compressed, availableStreams, columnPositionsList)); break; case DOUBLE: - checkpoints.putAll(getDoubleColumnCheckpoints(column, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getDoubleColumnCheckpoints(column, sequence, compressed, availableStreams, columnPositionsList)); break; case TIMESTAMP: - checkpoints.putAll(getTimestampColumnCheckpoints(column, columnEncoding, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getTimestampColumnCheckpoints(column, sequence, columnEncoding, compressed, availableStreams, columnPositionsList)); break; case BINARY: case STRING: case VARCHAR: case CHAR: - checkpoints.putAll(getSliceColumnCheckpoints(column, columnEncoding, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getSliceColumnCheckpoints(column, sequence, columnEncoding, compressed, availableStreams, columnPositionsList)); break; case LIST: case MAP: - checkpoints.putAll(getListOrMapColumnCheckpoints(column, columnEncoding, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getListOrMapColumnCheckpoints(column, sequence, columnEncoding, compressed, availableStreams, columnPositionsList)); break; case STRUCT: - checkpoints.putAll(getStructColumnCheckpoints(column, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getStructColumnCheckpoints(column, sequence, compressed, availableStreams, columnPositionsList)); break; case DECIMAL: - checkpoints.putAll(getDecimalColumnCheckpoints(column, columnEncoding, compressed, availableStreams, columnPositionsList)); + checkpoints.putAll(getDecimalColumnCheckpoints(column, sequence, columnEncoding, compressed, availableStreams, columnPositionsList)); break; - case UNION: + default: throw new IllegalArgumentException("Unsupported column type " + columnType); } @@ -167,18 +175,23 @@ else if (columnEncoding == DICTIONARY) { private static Map getBooleanColumnCheckpoints( int column, + int sequence, boolean compressed, Set availableStreams, ColumnPositionsList positionsList) { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new BooleanStreamCheckpoint(compressed, positionsList)); } return checkpoints.build(); @@ -186,18 +199,23 @@ private static Map getBooleanColumnCheckpoints( private static Map getByteColumnCheckpoints( int column, + int sequence, boolean compressed, Set availableStreams, ColumnPositionsList positionsList) { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new ByteStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new ByteStreamCheckpoint(compressed, positionsList)); } return checkpoints.build(); @@ -205,6 +223,7 @@ private static Map getByteColumnCheckpoints( private static Map getLongColumnCheckpoints( int column, + int sequence, ColumnEncodingKind encoding, boolean compressed, Set availableStreams, @@ -212,16 +231,20 @@ private static Map getLongColumnCheckpoints( { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(IN_DICTIONARY)) { - checkpoints.put(new StreamId(column, IN_DICTIONARY), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, IN_DICTIONARY), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); } return checkpoints.build(); @@ -229,18 +252,23 @@ private static Map getLongColumnCheckpoints( private static Map getFloatColumnCheckpoints( int column, + int sequence, boolean compressed, Set availableStreams, ColumnPositionsList positionsList) { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new FloatStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new FloatStreamCheckpoint(compressed, positionsList)); } return checkpoints.build(); @@ -248,18 +276,23 @@ private static Map getFloatColumnCheckpoints( private static Map getDoubleColumnCheckpoints( int column, + int sequence, boolean compressed, Set availableStreams, ColumnPositionsList positionsList) { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new DoubleStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new DoubleStreamCheckpoint(compressed, positionsList)); } return checkpoints.build(); @@ -267,6 +300,7 @@ private static Map getDoubleColumnCheckpoints( private static Map getTimestampColumnCheckpoints( int column, + int sequence, ColumnEncodingKind encoding, boolean compressed, Set availableStreams, @@ -274,16 +308,20 @@ private static Map getTimestampColumnCheckpoints( { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); } if (availableStreams.contains(SECONDARY)) { - checkpoints.put(new StreamId(column, SECONDARY), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, SECONDARY), createLongStreamCheckpoint(encoding, compressed, positionsList)); } return checkpoints.build(); @@ -291,6 +329,7 @@ private static Map getTimestampColumnCheckpoints( private static Map getSliceColumnCheckpoints( int column, + int sequence, ColumnEncodingKind encoding, boolean compressed, Set availableStreams, @@ -298,37 +337,41 @@ private static Map getSliceColumnCheckpoints( { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (encoding == DIRECT || encoding == DIRECT_V2) { if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new ByteArrayStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new ByteArrayStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(LENGTH)) { - checkpoints.put(new StreamId(column, LENGTH), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, LENGTH), createLongStreamCheckpoint(encoding, compressed, positionsList)); } } else if (encoding == DICTIONARY || encoding == DICTIONARY_V2) { // DWRF has rules inconsistent with the ORC style if (availableStreams.contains(IN_DICTIONARY)) { if (availableStreams.contains(ROW_GROUP_DICTIONARY)) { - checkpoints.put(new StreamId(column, ROW_GROUP_DICTIONARY), new ByteArrayStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, ROW_GROUP_DICTIONARY), new ByteArrayStreamCheckpoint(compressed, positionsList)); } - checkpoints.put(new StreamId(column, ROW_GROUP_DICTIONARY_LENGTH), new RowGroupDictionaryLengthStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, ROW_GROUP_DICTIONARY_LENGTH), new RowGroupDictionaryLengthStreamCheckpoint(compressed, positionsList)); if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); } - checkpoints.put(new StreamId(column, IN_DICTIONARY), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, IN_DICTIONARY), new BooleanStreamCheckpoint(compressed, positionsList)); } else { if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), createLongStreamCheckpoint(encoding, compressed, positionsList)); } } } @@ -341,6 +384,7 @@ else if (encoding == DICTIONARY || encoding == DICTIONARY_V2) { private static Map getListOrMapColumnCheckpoints( int column, + int sequence, ColumnEncodingKind encoding, boolean compressed, Set availableStreams, @@ -348,12 +392,16 @@ private static Map getListOrMapColumnCheckpoints( { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(LENGTH)) { - checkpoints.put(new StreamId(column, LENGTH), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, LENGTH), createLongStreamCheckpoint(encoding, compressed, positionsList)); } return checkpoints.build(); @@ -361,14 +409,19 @@ private static Map getListOrMapColumnCheckpoints( private static Map getStructColumnCheckpoints( int column, + int sequence, boolean compressed, Set availableStreams, ColumnPositionsList positionsList) { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } return checkpoints.build(); @@ -376,6 +429,7 @@ private static Map getStructColumnCheckpoints( private static Map getDecimalColumnCheckpoints( int column, + int sequence, ColumnEncodingKind encoding, boolean compressed, Set availableStreams, @@ -383,16 +437,20 @@ private static Map getDecimalColumnCheckpoints( { ImmutableMap.Builder checkpoints = ImmutableMap.builder(); + if (availableStreams.contains(IN_MAP)) { + checkpoints.put(new StreamId(column, sequence, IN_MAP), new BooleanStreamCheckpoint(compressed, positionsList)); + } + if (availableStreams.contains(PRESENT)) { - checkpoints.put(new StreamId(column, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, PRESENT), new BooleanStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(DATA)) { - checkpoints.put(new StreamId(column, DATA), new DecimalStreamCheckpoint(compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, DATA), new DecimalStreamCheckpoint(compressed, positionsList)); } if (availableStreams.contains(SECONDARY)) { - checkpoints.put(new StreamId(column, SECONDARY), createLongStreamCheckpoint(encoding, compressed, positionsList)); + checkpoints.put(new StreamId(column, sequence, SECONDARY), createLongStreamCheckpoint(encoding, compressed, positionsList)); } return checkpoints.build(); @@ -418,13 +476,15 @@ private static StreamCheckpoint createLongStreamCheckpoint(ColumnEncodingKind en public static class ColumnPositionsList { private final int column; + private final int sequence; private final OrcTypeKind columnType; private final List positionsList; private int index; - private ColumnPositionsList(int column, OrcTypeKind columnType, List positionsList) + private ColumnPositionsList(int column, int sequence, OrcTypeKind columnType, List positionsList) { this.column = column; + this.sequence = sequence; this.columnType = requireNonNull(columnType, "columnType is null"); this.positionsList = ImmutableList.copyOf(requireNonNull(positionsList, "positionsList is null")); } @@ -442,8 +502,9 @@ public boolean hasNextPosition() public int nextPosition() { if (!hasNextPosition()) { - throw new InvalidCheckpointException("Not enough positions for column %s, of type %s, checkpoints", + throw new InvalidCheckpointException("Not enough positions for column %s and sequence %s, of type %s, checkpoints", column, + sequence, columnType); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/ColumnEncoding.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/ColumnEncoding.java index 9992d92f74e07..ccff96a29e827 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/ColumnEncoding.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/ColumnEncoding.java @@ -13,7 +13,11 @@ */ package com.facebook.presto.orc.metadata; +import java.util.List; +import java.util.Optional; + import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class ColumnEncoding @@ -25,15 +29,30 @@ public enum ColumnEncodingKind DIRECT_V2, DICTIONARY_V2, DWRF_DIRECT, + DWRF_MAP_FLAT, } + public static final int DEFAULT_SEQUENCE_ID = 0; + private final ColumnEncodingKind columnEncodingKind; private final int dictionarySize; + // DWRF supports the concept of sequences. + // A column can be modeled as multiple sequences that are independently encoded. + // For example, for a flat map column, each key will have a + // separate value stream with its own column encoding. + private final Optional> additionalSequenceEncodings; + public ColumnEncoding(ColumnEncodingKind columnEncodingKind, int dictionarySize) + { + this(columnEncodingKind, dictionarySize, Optional.empty()); + } + + public ColumnEncoding(ColumnEncodingKind columnEncodingKind, int dictionarySize, Optional> additionalSequenceEncodings) { this.columnEncodingKind = requireNonNull(columnEncodingKind, "columnEncodingKind is null"); this.dictionarySize = dictionarySize; + this.additionalSequenceEncodings = additionalSequenceEncodings; } public ColumnEncodingKind getColumnEncodingKind() @@ -46,12 +65,31 @@ public int getDictionarySize() return dictionarySize; } + public Optional> getAdditionalSequenceEncodings() + { + return additionalSequenceEncodings; + } + + public ColumnEncoding getColumnEncoding(int sequence) + { + if (sequence == 0) { + return this; + } + + checkState( + additionalSequenceEncodings.isPresent(), + "Got non-zero sequence: %d, but there are no additional sequence encodings: %s", sequence, this); + + return additionalSequenceEncodings.get().get(sequence - 1).getValueEncoding(); + } + @Override public String toString() { return toStringHelper(this) .add("columnEncodingKind", columnEncodingKind) .add("dictionarySize", dictionarySize) + .add("additionalSequenceEncodings", additionalSequenceEncodings) .toString(); } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataReader.java index ae583046578e1..377c536a8680b 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataReader.java @@ -34,6 +34,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -54,7 +56,6 @@ import static com.facebook.presto.orc.metadata.statistics.DoubleStatistics.DOUBLE_VALUE_BYTES; import static com.facebook.presto.orc.metadata.statistics.IntegerStatistics.INTEGER_VALUE_BYTES; import static com.facebook.presto.orc.metadata.statistics.StringStatistics.STRING_VALUE_BYTES_OVERHEAD; -import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.lang.Math.toIntExact; @@ -131,7 +132,7 @@ public StripeFooter readStripeFooter(List types, InputStream inputStrea private static Stream toStream(DwrfProto.Stream stream) { - return new Stream(stream.getColumn(), toStreamKind(stream.getKind()), toIntExact(stream.getLength()), stream.getUseVInts()); + return new Stream(stream.getColumn(), toStreamKind(stream.getKind()), toIntExact(stream.getLength()), stream.getUseVInts(), stream.getSequence()); } private static List toStream(List streams) @@ -139,21 +140,60 @@ private static List toStream(List streams) return ImmutableList.copyOf(Iterables.transform(streams, DwrfMetadataReader::toStream)); } - private static ColumnEncoding toColumnEncoding(OrcTypeKind type, DwrfProto.ColumnEncoding columnEncoding) + private static DwrfSequenceEncoding toSequenceEncoding(OrcType type, DwrfProto.ColumnEncoding columnEncoding) { - return new ColumnEncoding(toColumnEncodingKind(type, columnEncoding.getKind()), columnEncoding.getDictionarySize()); + return new DwrfSequenceEncoding( + columnEncoding.getKey(), + new ColumnEncoding( + toColumnEncodingKind(type.getOrcTypeKind(), columnEncoding.getKind()), + columnEncoding.getDictionarySize())); + } + + private static Optional> toAdditionalSequenceEncodings(List columnEncodings, OrcType type) + { + if (columnEncodings.size() == 1) { + return Optional.empty(); + } + + ImmutableList.Builder additionalSequenceEncodings = ImmutableList.builder(); + + for (int i = 1; i < columnEncodings.size(); i++) { + additionalSequenceEncodings.add(toSequenceEncoding(type, columnEncodings.get(i))); + } + + return Optional.of(additionalSequenceEncodings.build()); } private static List toColumnEncoding(List types, List columnEncodings) { - checkArgument(types.size() == columnEncodings.size()); + Map> groupedColumnEncodings = new HashMap<>(columnEncodings.size()); - ImmutableList.Builder encodings = ImmutableList.builder(); - for (int i = 0; i < types.size(); i++) { - OrcType type = types.get(i); - encodings.add(toColumnEncoding(type.getOrcTypeKind(), columnEncodings.get(i))); + for (int i = 0; i < columnEncodings.size(); i++) { + DwrfProto.ColumnEncoding columnEncoding = columnEncodings.get(i); + int column = columnEncoding.getColumn(); + + // DWRF prior to version 6.0.8 doesn't set the value of column, infer it from the index + if (!columnEncoding.hasColumn()) { + column = i; + } + + groupedColumnEncodings.computeIfAbsent(column, key -> new ArrayList<>()).add(columnEncoding); + } + + ImmutableList.Builder resultBuilder = ImmutableList.builder(); + + for (Map.Entry> entry : groupedColumnEncodings.entrySet()) { + OrcType type = types.get(entry.getKey()); + + DwrfProto.ColumnEncoding columnEncoding = entry.getValue().get(0); + resultBuilder.add( + new ColumnEncoding( + toColumnEncodingKind(type.getOrcTypeKind(), columnEncoding.getKind()), + columnEncoding.getDictionarySize(), + toAdditionalSequenceEncodings(entry.getValue(), type))); } - return encodings.build(); + + return resultBuilder.build(); } @Override @@ -372,6 +412,8 @@ private static StreamKind toStreamKind(DwrfProto.Stream.Kind kind) return StreamKind.ROW_GROUP_DICTIONARY; case STRIDE_DICTIONARY_LENGTH: return StreamKind.ROW_GROUP_DICTIONARY_LENGTH; + case IN_MAP: + return StreamKind.IN_MAP; default: throw new IllegalArgumentException(kind + " stream type not implemented yet"); } @@ -389,6 +431,8 @@ private static ColumnEncodingKind toColumnEncodingKind(OrcTypeKind type, DwrfPro } case DICTIONARY: return ColumnEncodingKind.DICTIONARY; + case MAP_FLAT: + return ColumnEncodingKind.DWRF_MAP_FLAT; default: throw new IllegalArgumentException(kind + " stream encoding not implemented yet"); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataWriter.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataWriter.java index 3bb71dbe5955b..1eff5b9e4c060 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataWriter.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfMetadataWriter.java @@ -36,6 +36,7 @@ import java.util.Map; import java.util.Map.Entry; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static io.airlift.slice.Slices.utf8Slice; import static java.lang.Math.toIntExact; @@ -269,6 +270,10 @@ private static DwrfProto.Stream.Kind toStreamKind(StreamKind streamKind) private static DwrfProto.ColumnEncoding toColumnEncoding(ColumnEncoding columnEncodings) { + checkArgument( + !columnEncodings.getAdditionalSequenceEncodings().isPresent(), + "DWRF writer doesn't support writing columns with non-zero sequence IDs: " + columnEncodings); + return DwrfProto.ColumnEncoding.newBuilder() .setKind(toColumnEncoding(columnEncodings.getColumnEncodingKind())) .setDictionarySize(columnEncodings.getDictionarySize()) @@ -319,6 +324,8 @@ private static DwrfProto.CompressionKind toCompression(CompressionKind compressi return DwrfProto.CompressionKind.SNAPPY; case LZ4: return DwrfProto.CompressionKind.LZ4; + case ZSTD: + return DwrfProto.CompressionKind.ZSTD; } throw new IllegalArgumentException("Unsupported compression kind: " + compressionKind); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfSequenceEncoding.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfSequenceEncoding.java new file mode 100644 index 0000000000000..8da54bcaedb89 --- /dev/null +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/DwrfSequenceEncoding.java @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc.metadata; + +import com.facebook.presto.orc.proto.DwrfProto; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class DwrfSequenceEncoding +{ + private final DwrfProto.KeyInfo key; + private final ColumnEncoding valueEncoding; + + public DwrfSequenceEncoding(DwrfProto.KeyInfo key, ColumnEncoding valueEncoding) + { + this.key = requireNonNull(key, "key is null"); + this.valueEncoding = requireNonNull(valueEncoding, "valueEncoding is null"); + } + + public DwrfProto.KeyInfo getKey() + { + return key; + } + + public ColumnEncoding getValueEncoding() + { + return valueEncoding; + } + + @Override + public String toString() + { + return toStringHelper(this) + .add("key", key) + .add("valueEncoding", valueEncoding) + .toString(); + } +} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/OrcMetadataWriter.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/OrcMetadataWriter.java index 806833fbb1e27..d15d22ce0b955 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/OrcMetadataWriter.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/OrcMetadataWriter.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map.Entry; +import static com.google.common.base.Preconditions.checkArgument; import static java.lang.Math.toIntExact; import static java.util.stream.Collectors.toList; @@ -311,6 +312,10 @@ private static OrcProto.Stream.Kind toStreamKind(StreamKind streamKind) private static OrcProto.ColumnEncoding toColumnEncoding(ColumnEncoding columnEncodings) { + checkArgument( + !columnEncodings.getAdditionalSequenceEncodings().isPresent(), + "Writing columns with non-zero sequence IDs is not supported in ORC: " + columnEncodings); + return OrcProto.ColumnEncoding.newBuilder() .setKind(toColumnEncoding(columnEncodings.getColumnEncodingKind())) .setDictionarySize(columnEncodings.getDictionarySize()) @@ -365,6 +370,8 @@ private static OrcProto.CompressionKind toCompression(CompressionKind compressio return OrcProto.CompressionKind.SNAPPY; case LZ4: return OrcProto.CompressionKind.LZ4; + case ZSTD: + return OrcProto.CompressionKind.ZSTD; } throw new IllegalArgumentException("Unsupported compression kind: " + compressionKind); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/Stream.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/Stream.java index 4b28d64de2211..0d52940de33f8 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/Stream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/Stream.java @@ -32,19 +32,27 @@ public enum StreamKind IN_DICTIONARY, ROW_GROUP_DICTIONARY, ROW_GROUP_DICTIONARY_LENGTH, + IN_MAP, } private final int column; private final StreamKind streamKind; private final int length; private final boolean useVInts; + private final int sequence; public Stream(int column, StreamKind streamKind, int length, boolean useVInts) + { + this(column, streamKind, length, useVInts, ColumnEncoding.DEFAULT_SEQUENCE_ID); + } + + public Stream(int column, StreamKind streamKind, int length, boolean useVInts, int sequence) { this.column = column; this.streamKind = requireNonNull(streamKind, "streamKind is null"); this.length = length; this.useVInts = useVInts; + this.sequence = sequence; } public int getColumn() @@ -67,6 +75,11 @@ public boolean isUseVInts() return useVInts; } + public int getSequence() + { + return sequence; + } + @Override public String toString() { @@ -75,6 +88,7 @@ public String toString() .add("streamKind", streamKind) .add("length", length) .add("useVInts", useVInts) + .add("sequence", sequence) .toString(); } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/StripeInformation.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/StripeInformation.java index c48492bcc2775..4b4ba05278740 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/StripeInformation.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/StripeInformation.java @@ -26,8 +26,8 @@ public class StripeInformation public StripeInformation(int numberOfRows, long offset, long indexLength, long dataLength, long footerLength) { + // dataLength can be zero when the stripe only contains empty flat maps. checkArgument(numberOfRows > 0, "Stripe must have at least one row"); - checkArgument(dataLength > 0, "Stripe must have a data section"); checkArgument(footerLength > 0, "Stripe must have a footer section"); this.numberOfRows = numberOfRows; this.offset = offset; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/statistics/StringStatisticsBuilder.java b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/statistics/StringStatisticsBuilder.java index 10244a40344d7..d3ca2fdc3e79c 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/metadata/statistics/StringStatisticsBuilder.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/metadata/statistics/StringStatisticsBuilder.java @@ -150,7 +150,7 @@ public static Optional mergeStringStatistics(List 0) { - if (partialStatistics == null) { + if (partialStatistics == null || (partialStatistics.getMin() == null && partialStatistics.getMax() == null)) { // there are non null values but no statistics, so we can not say anything about the data return Optional.empty(); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/BooleanStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/BooleanStreamReader.java index 59864833d7ed5..9cc744c88a00d 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/BooleanStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/BooleanStreamReader.java @@ -25,19 +25,17 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class BooleanStreamReader @@ -50,13 +48,11 @@ public class BooleanStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream dataStream; @@ -108,23 +104,14 @@ public Block readBlock(Type type) dataStream.getSetBits(type, nextBatchSize, builder); } else { - assureVectorSize(); - - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - dataStream.getSetBits(type, subBatchSize, builder, nullVector); + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(dataStream != null); + type.writeBoolean(builder, dataStream.nextBit()); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } - nextBatchSize -= subBatchSize; } } @@ -134,15 +121,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/ByteStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/ByteStreamReader.java index 455b3a254f9fa..a2de7400873aa 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/ByteStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/ByteStreamReader.java @@ -26,19 +26,17 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class ByteStreamReader @@ -51,13 +49,11 @@ public class ByteStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(ByteInputStream.class); @Nullable private ByteInputStream dataStream; @@ -109,23 +105,14 @@ public Block readBlock(Type type) dataStream.nextVector(type, nextBatchSize, builder); } else { - assureVectorSize(); - - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - dataStream.nextVector(type, subBatchSize, builder, nullVector); + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(dataStream != null); + type.writeLong(builder, dataStream.next()); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } - nextBatchSize -= subBatchSize; } } @@ -135,15 +122,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/DecimalStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/DecimalStreamReader.java index b4ffb9686d6c1..3aa14366cb314 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/DecimalStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/DecimalStreamReader.java @@ -25,23 +25,25 @@ import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic; +import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.metadata.Stream.StreamKind.SECONDARY; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; +import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.rescale; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class DecimalStreamReader @@ -57,17 +59,14 @@ public class DecimalStreamReader private boolean[] nullVector = new boolean[0]; private long[] scaleVector = new long[0]; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; - @Nonnull private InputStreamSource decimalStreamSource = missingStreamSource(DecimalInputStream.class); @Nullable private DecimalInputStream decimalStream; - @Nonnull private InputStreamSource scaleStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream scaleStream; @@ -94,58 +93,64 @@ public Block readBlock(Type type) throws IOException { DecimalType decimalType = (DecimalType) type; + int targetScale = decimalType.getScale(); if (!rowGroupOpen) { openRowGroup(); } seekToOffset(); - assureVectorSize(); BlockBuilder builder = decimalType.createBlockBuilder(null, nextBatchSize); - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - if (presentStream == null) { - if (decimalStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but decimal stream is not present"); - } - if (scaleStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but scale stream is not present"); - } - scaleStream.nextLongVector(subBatchSize, scaleVector); + + if (presentStream == null) { + if (decimalStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but decimal stream is not present"); + } + if (scaleStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but scale stream is not present"); + } + + for (int i = 0; i < nextBatchSize; i++) { + long sourceScale = scaleStream.next(); if (decimalType.isShort()) { - decimalStream.nextShortDecimalVector(subBatchSize, builder, decimalType, scaleVector); + long rescaledDecimal = Decimals.rescale(decimalStream.nextLong(), (int) sourceScale, decimalType.getScale()); + decimalType.writeLong(builder, rescaledDecimal); } else { - decimalStream.nextLongDecimalVector(subBatchSize, builder, decimalType, scaleVector); + Slice decimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); + Slice rescaledDecimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); + + decimalStream.nextLongDecimal(decimal); + rescale(decimal, (int) (decimalType.getScale() - sourceScale), rescaledDecimal); + decimalType.writeSlice(builder, rescaledDecimal); } } - else { - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (decimalStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but decimal stream is not present"); - } - if (scaleStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but scale stream is not present"); - } - - scaleStream.nextLongVector(subBatchSize, scaleVector, nullVector); - + } + else { + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + // The current row is not null + verify(decimalStream != null); + verify(scaleStream != null); + long sourceScale = scaleStream.next(); if (decimalType.isShort()) { - decimalStream.nextShortDecimalVector(subBatchSize, builder, decimalType, scaleVector, nullVector); + long rescaledDecimal = Decimals.rescale(decimalStream.nextLong(), (int) sourceScale, decimalType.getScale()); + decimalType.writeLong(builder, rescaledDecimal); } else { - decimalStream.nextLongDecimalVector(subBatchSize, builder, decimalType, scaleVector, nullVector); + Slice decimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); + Slice rescaledDecimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); + + decimalStream.nextLongDecimal(decimal); + rescale(decimal, (int) (decimalType.getScale() - sourceScale), rescaledDecimal); + decimalType.writeSlice(builder, rescaledDecimal); } } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } } - nextBatchSize -= subBatchSize; } readOffset = 0; @@ -154,16 +159,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - scaleVector = new long[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/DoubleStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/DoubleStreamReader.java index 323f3a96a69f6..71caa8c149711 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/DoubleStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/DoubleStreamReader.java @@ -26,19 +26,17 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class DoubleStreamReader @@ -51,13 +49,11 @@ public class DoubleStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(DoubleInputStream.class); @Nullable private DoubleInputStream dataStream; @@ -109,23 +105,14 @@ public Block readBlock(Type type) dataStream.nextVector(type, nextBatchSize, builder); } else { - assureVectorSize(); - - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - dataStream.nextVector(type, subBatchSize, builder, nullVector); + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(dataStream != null); + type.writeDouble(builder, dataStream.next()); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } - nextBatchSize -= subBatchSize; } } @@ -135,15 +122,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/FloatStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/FloatStreamReader.java index 108d3e15d5c56..b785c90c9ba60 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/FloatStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/FloatStreamReader.java @@ -26,19 +26,18 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; +import static java.lang.Float.floatToRawIntBits; import static java.util.Objects.requireNonNull; public class FloatStreamReader @@ -51,13 +50,11 @@ public class FloatStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(FloatInputStream.class); @Nullable private FloatInputStream dataStream; @@ -109,23 +106,14 @@ public Block readBlock(Type type) dataStream.nextVector(type, nextBatchSize, builder); } else { - assureVectorSize(); - - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - dataStream.nextVector(type, subBatchSize, builder, nullVector); + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(dataStream != null); + type.writeLong(builder, floatToRawIntBits(dataStream.next())); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } - nextBatchSize -= subBatchSize; } } @@ -135,15 +123,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/ListStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/ListStreamReader.java index b941b2077d0bc..f5386ae51cb17 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/ListStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/ListStreamReader.java @@ -28,12 +28,12 @@ import org.joda.time.DateTimeZone; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; +import java.util.Optional; import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; @@ -55,12 +55,10 @@ public class ListStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; - @Nonnull private InputStreamSource lengthStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream lengthStream; @@ -106,7 +104,7 @@ public Block readBlock(Type type) // We will use the offsetVector as the buffer to read the length values from lengthStream, // and the length values will be converted in-place to an offset vector. int[] offsetVector = new int[nextBatchSize + 1]; - boolean[] nullVector = new boolean[nextBatchSize]; + boolean[] nullVector = null; if (presentStream == null) { if (lengthStream == null) { throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); @@ -114,6 +112,7 @@ public Block readBlock(Type type) lengthStream.nextIntVector(nextBatchSize, offsetVector, 0); } else { + nullVector = new boolean[nextBatchSize]; int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector); if (nullValues != nextBatchSize) { if (lengthStream == null) { @@ -143,7 +142,7 @@ public Block readBlock(Type type) else { elements = elementType.createBlockBuilder(null, 0).build(); } - Block arrayBlock = ArrayBlock.fromElementBlock(nextBatchSize, nullVector, offsetVector, elements); + Block arrayBlock = ArrayBlock.fromElementBlock(nextBatchSize, Optional.ofNullable(nullVector), offsetVector, elements); readOffset = 0; nextBatchSize = 0; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDictionaryStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDictionaryStreamReader.java index 2bad4b4bebab4..2f51603bd94af 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDictionaryStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDictionaryStreamReader.java @@ -26,14 +26,11 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; -import java.util.Arrays; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DICTIONARY_DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_DICTIONARY; @@ -41,7 +38,6 @@ import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class LongDictionaryStreamReader @@ -54,25 +50,20 @@ public class LongDictionaryStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dictionaryDataStreamSource = missingStreamSource(LongInputStream.class); private int dictionarySize; - @Nonnull private long[] dictionary = new long[0]; - @Nonnull private InputStreamSource inDictionaryStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream inDictionaryStream; private boolean[] inDictionaryVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource; @Nullable private LongInputStream dataStream; @@ -123,66 +114,64 @@ public Block readBlock(Type type) } } - assureVectorSize(); - BlockBuilder builder = type.createBlockBuilder(null, nextBatchSize); - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - if (presentStream == null) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + + if (presentStream == null) { + // Data doesn't have nulls + if (dataStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } + if (inDictionaryStream == null) { + for (int i = 0; i < nextBatchSize; i++) { + type.writeLong(builder, dictionary[((int) dataStream.next())]); } - Arrays.fill(nullVector, false); - dataStream.nextLongVector(subBatchSize, dataVector); } else { - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + for (int i = 0; i < nextBatchSize; i++) { + long id = dataStream.next(); + if (inDictionaryStream.nextBit()) { + type.writeLong(builder, dictionary[(int) id]); + } + else { + type.writeLong(builder, id); } - dataStream.nextLongVector(subBatchSize, dataVector, nullVector); } } - - if (inDictionaryStream == null) { - Arrays.fill(inDictionaryVector, true); - } - else { - inDictionaryStream.getSetBits(subBatchSize, inDictionaryVector, nullVector); - } - - for (int i = 0; i < subBatchSize; i++) { - if (nullVector[i]) { - builder.appendNull(); + } + else { + // Data has nulls + if (dataStream == null) { + // The only valid case for dataStream is null when data has nulls is that all values are nulls. + int nullValues = presentStream.getUnsetBits(nextBatchSize); + if (nullValues != nextBatchSize) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); } - else if (inDictionaryVector[i]) { - type.writeLong(builder, dictionary[((int) dataVector[i])]); + for (int i = 0; i < nextBatchSize; i++) { + builder.appendNull(); } - else { - type.writeLong(builder, dataVector[i]); + } + else { + for (int i = 0; i < nextBatchSize; i++) { + if (!presentStream.nextBit()) { + builder.appendNull(); + } + else { + long id = dataStream.next(); + if (inDictionaryStream == null || inDictionaryStream.nextBit()) { + type.writeLong(builder, dictionary[(int) id]); + } + else { + type.writeLong(builder, id); + } + } } } - nextBatchSize -= subBatchSize; } readOffset = 0; nextBatchSize = 0; - return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - // nullVector, dataVector and inDictionary should be of the same length - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - dataVector = new long[requiredVectorLength]; - inDictionaryVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { @@ -211,7 +200,9 @@ private void openRowGroup() public void startStripe(InputStreamSources dictionaryStreamSources, List encoding) { dictionaryDataStreamSource = dictionaryStreamSources.getInputStreamSource(streamDescriptor, DICTIONARY_DATA, LongInputStream.class); - dictionarySize = encoding.get(streamDescriptor.getStreamId()).getDictionarySize(); + dictionarySize = encoding.get(streamDescriptor.getStreamId()) + .getColumnEncoding(streamDescriptor.getSequence()) + .getDictionarySize(); dictionaryOpen = false; inDictionaryStreamSource = missingStreamSource(BooleanInputStream.class); diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDirectStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDirectStreamReader.java index a9e36cb426b70..950493ed00d38 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDirectStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongDirectStreamReader.java @@ -26,19 +26,17 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class LongDirectStreamReader @@ -51,13 +49,11 @@ public class LongDirectStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream dataStream; @@ -109,23 +105,14 @@ public Block readBlock(Type type) dataStream.nextLongVector(type, nextBatchSize, builder); } else { - assureVectorSize(); - - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - dataStream.nextLongVector(type, subBatchSize, builder, nullVector); + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(dataStream != null); + type.writeLong(builder, dataStream.next()); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } - nextBatchSize -= subBatchSize; } } @@ -135,15 +122,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (nullVector.length < requiredVectorLength) { - nullVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongStreamReader.java index 76d777088c342..3d9ade34fdc83 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/LongStreamReader.java @@ -68,7 +68,9 @@ public Block readBlock(Type type) public void startStripe(InputStreamSources dictionaryStreamSources, List encoding) throws IOException { - ColumnEncodingKind kind = encoding.get(streamDescriptor.getStreamId()).getColumnEncodingKind(); + ColumnEncodingKind kind = encoding.get(streamDescriptor.getStreamId()) + .getColumnEncoding(streamDescriptor.getSequence()) + .getColumnEncodingKind(); if (kind == DIRECT || kind == DIRECT_V2 || kind == DWRF_DIRECT) { currentReader = directReader; } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapDirectStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapDirectStreamReader.java new file mode 100644 index 0000000000000..b0cb68a5b94f3 --- /dev/null +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapDirectStreamReader.java @@ -0,0 +1,284 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc.reader; + +import com.facebook.presto.memory.context.AggregatedMemoryContext; +import com.facebook.presto.orc.OrcCorruptionException; +import com.facebook.presto.orc.StreamDescriptor; +import com.facebook.presto.orc.metadata.ColumnEncoding; +import com.facebook.presto.orc.stream.BooleanInputStream; +import com.facebook.presto.orc.stream.InputStreamSource; +import com.facebook.presto.orc.stream.InputStreamSources; +import com.facebook.presto.orc.stream.LongInputStream; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.MapType; +import com.facebook.presto.spi.type.Type; +import com.google.common.io.Closer; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import org.joda.time.DateTimeZone; +import org.openjdk.jol.info.ClassLayout; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; +import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; +import static com.facebook.presto.orc.reader.StreamReaders.createStreamReader; +import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.lang.Math.toIntExact; +import static java.util.Objects.requireNonNull; + +public class MapDirectStreamReader + implements StreamReader +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(MapDirectStreamReader.class).instanceSize(); + + private final StreamDescriptor streamDescriptor; + + private final StreamReader keyStreamReader; + private final StreamReader valueStreamReader; + + private int readOffset; + private int nextBatchSize; + + private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); + @Nullable + private BooleanInputStream presentStream; + + private InputStreamSource lengthStreamSource = missingStreamSource(LongInputStream.class); + @Nullable + private LongInputStream lengthStream; + + private boolean rowGroupOpen; + + public MapDirectStreamReader(StreamDescriptor streamDescriptor, DateTimeZone hiveStorageTimeZone, AggregatedMemoryContext systemMemoryContext) + { + this.streamDescriptor = requireNonNull(streamDescriptor, "stream is null"); + this.keyStreamReader = createStreamReader(streamDescriptor.getNestedStreams().get(0), hiveStorageTimeZone, systemMemoryContext); + this.valueStreamReader = createStreamReader(streamDescriptor.getNestedStreams().get(1), hiveStorageTimeZone, systemMemoryContext); + } + + @Override + public void prepareNextRead(int batchSize) + { + readOffset += nextBatchSize; + nextBatchSize = batchSize; + } + + @Override + public Block readBlock(Type type) + throws IOException + { + if (!rowGroupOpen) { + openRowGroup(); + } + + if (readOffset > 0) { + if (presentStream != null) { + // skip ahead the present bit reader, but count the set bits + // and use this as the skip size for the data reader + readOffset = presentStream.countBitsSet(readOffset); + } + if (readOffset > 0) { + if (lengthStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } + long entrySkipSize = lengthStream.sum(readOffset); + keyStreamReader.prepareNextRead(toIntExact(entrySkipSize)); + valueStreamReader.prepareNextRead(toIntExact(entrySkipSize)); + } + } + + // We will use the offsetVector as the buffer to read the length values from lengthStream, + // and the length values will be converted in-place to an offset vector. + int[] offsetVector = new int[nextBatchSize + 1]; + boolean[] nullVector = null; + + if (presentStream == null) { + if (lengthStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } + lengthStream.nextIntVector(nextBatchSize, offsetVector, 0); + } + else { + nullVector = new boolean[nextBatchSize]; + int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector); + if (nullValues != nextBatchSize) { + if (lengthStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } + lengthStream.nextIntVector(nextBatchSize, offsetVector, 0, nullVector); + } + } + + MapType mapType = (MapType) type; + Type keyType = mapType.getKeyType(); + Type valueType = mapType.getValueType(); + + // Calculate the entryCount. Note that the values in the offsetVector are still length values now. + int entryCount = 0; + for (int i = 0; i < offsetVector.length - 1; i++) { + entryCount += offsetVector[i]; + } + + Block keys; + Block values; + if (entryCount > 0) { + keyStreamReader.prepareNextRead(entryCount); + valueStreamReader.prepareNextRead(entryCount); + keys = keyStreamReader.readBlock(keyType); + values = valueStreamReader.readBlock(valueType); + } + else { + keys = keyType.createBlockBuilder(null, 0).build(); + values = valueType.createBlockBuilder(null, 1).build(); + } + + Block[] keyValueBlock = createKeyValueBlock(nextBatchSize, keys, values, offsetVector); + + // Convert the length values in the offsetVector to offset values in-place + int currentLength = offsetVector[0]; + offsetVector[0] = 0; + for (int i = 1; i < offsetVector.length; i++) { + int lastLength = offsetVector[i]; + offsetVector[i] = offsetVector[i - 1] + currentLength; + currentLength = lastLength; + } + + readOffset = 0; + nextBatchSize = 0; + + return mapType.createBlockFromKeyValue(Optional.ofNullable(nullVector), offsetVector, keyValueBlock[0], keyValueBlock[1]); + } + + private static Block[] createKeyValueBlock(int positionCount, Block keys, Block values, int[] lengths) + { + if (!hasNull(keys)) { + return new Block[] {keys, values}; + } + + // + // Map entries with a null key are skipped in the Hive ORC reader, so skip them here also + // + + IntArrayList nonNullPositions = new IntArrayList(keys.getPositionCount()); + + int position = 0; + for (int mapIndex = 0; mapIndex < positionCount; mapIndex++) { + int length = lengths[mapIndex]; + for (int entryIndex = 0; entryIndex < length; entryIndex++) { + if (keys.isNull(position)) { + // key is null, so remove this entry from the map + lengths[mapIndex]--; + } + else { + nonNullPositions.add(position); + } + position++; + } + } + + Block newKeys = keys.copyPositions(nonNullPositions.elements(), 0, nonNullPositions.size()); + Block newValues = values.copyPositions(nonNullPositions.elements(), 0, nonNullPositions.size()); + return new Block[] {newKeys, newValues}; + } + + private static boolean hasNull(Block keys) + { + for (int position = 0; position < keys.getPositionCount(); position++) { + if (keys.isNull(position)) { + return true; + } + } + return false; + } + + private void openRowGroup() + throws IOException + { + presentStream = presentStreamSource.openStream(); + lengthStream = lengthStreamSource.openStream(); + + rowGroupOpen = true; + } + + @Override + public void startStripe(InputStreamSources dictionaryStreamSources, List encoding) + throws IOException + { + presentStreamSource = missingStreamSource(BooleanInputStream.class); + lengthStreamSource = missingStreamSource(LongInputStream.class); + + readOffset = 0; + nextBatchSize = 0; + + presentStream = null; + lengthStream = null; + + rowGroupOpen = false; + + keyStreamReader.startStripe(dictionaryStreamSources, encoding); + valueStreamReader.startStripe(dictionaryStreamSources, encoding); + } + + @Override + public void startRowGroup(InputStreamSources dataStreamSources) + throws IOException + { + presentStreamSource = dataStreamSources.getInputStreamSource(streamDescriptor, PRESENT, BooleanInputStream.class); + lengthStreamSource = dataStreamSources.getInputStreamSource(streamDescriptor, LENGTH, LongInputStream.class); + + readOffset = 0; + nextBatchSize = 0; + + presentStream = null; + lengthStream = null; + + rowGroupOpen = false; + + keyStreamReader.startRowGroup(dataStreamSources); + valueStreamReader.startRowGroup(dataStreamSources); + } + + @Override + public String toString() + { + return toStringHelper(this) + .addValue(streamDescriptor) + .toString(); + } + + @Override + public void close() + { + try (Closer closer = Closer.create()) { + closer.register(keyStreamReader::close); + closer.register(valueStreamReader::close); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public long getRetainedSizeInBytes() + { + return INSTANCE_SIZE + keyStreamReader.getRetainedSizeInBytes() + valueStreamReader.getRetainedSizeInBytes(); + } +} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapFlatStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapFlatStreamReader.java new file mode 100644 index 0000000000000..234300f5da7db --- /dev/null +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapFlatStreamReader.java @@ -0,0 +1,400 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc.reader; + +import com.facebook.presto.memory.context.AggregatedMemoryContext; +import com.facebook.presto.orc.StreamDescriptor; +import com.facebook.presto.orc.metadata.ColumnEncoding; +import com.facebook.presto.orc.metadata.DwrfSequenceEncoding; +import com.facebook.presto.orc.metadata.OrcType; +import com.facebook.presto.orc.stream.BooleanInputStream; +import com.facebook.presto.orc.stream.InputStreamSource; +import com.facebook.presto.orc.stream.InputStreamSources; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.block.DictionaryBlock; +import com.facebook.presto.spi.block.VariableWidthBlockBuilder; +import com.facebook.presto.spi.type.BigintType; +import com.facebook.presto.spi.type.IntegerType; +import com.facebook.presto.spi.type.MapType; +import com.facebook.presto.spi.type.SmallintType; +import com.facebook.presto.spi.type.TinyintType; +import com.facebook.presto.spi.type.Type; +import com.google.common.io.Closer; +import io.airlift.slice.Slice; +import io.airlift.slice.Slices; +import org.joda.time.DateTimeZone; +import org.openjdk.jol.info.ClassLayout; + +import javax.annotation.Nullable; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_MAP; +import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; +import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.Objects.requireNonNull; + +/** + * Flat Maps are a layout of maps supported in DWRF. + *

    + * Values associated with different keys are stored in separate streams rather than having a single set of value streams for the map. + *

    + * There is a ColumnEncoding associated with the value streams for a given key. All the ColumnEncodings for values have the same + * columnId, and use a sequenceId to distinguish them. The ColumnEncoding also contains the key it is associated with as metadata. + *

    + * Note that the ColumnEncoding with sequenceId 0 for the values has no data associated with it, only statistics. Similarly there + * is a ColumnEncoding for the key stream which has no data associated with it, only statistics, so it is not used in this class. + */ +public class MapFlatStreamReader + implements StreamReader +{ + private static final int INSTANCE_SIZE = ClassLayout.parseClass(MapFlatStreamReader.class).instanceSize(); + + private final StreamDescriptor streamDescriptor; + private final DateTimeZone hiveStorageTimeZone; + private final AggregatedMemoryContext systemMemoryContext; + + // This is the StreamDescriptor for the value stream with sequence ID 0, it is used to derive StreamDescriptors for the + // value streams with other sequence IDs + private final StreamDescriptor baseValueStreamDescriptor; + private final OrcType.OrcTypeKind keyOrcType; + + private final List> inMapStreamSources = new ArrayList<>(); + private final List inMapStreams = new ArrayList<>(); + private final List valueStreamReaders = new ArrayList<>(); + private final List valueStreamDescriptors = new ArrayList<>(); + + private Block keyBlockTemplate; + private int readOffset; + private int nextBatchSize; + + private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); + @Nullable + private BooleanInputStream presentStream; + + private boolean rowGroupOpen; + + public MapFlatStreamReader(StreamDescriptor streamDescriptor, DateTimeZone hiveStorageTimeZone, AggregatedMemoryContext systemMemoryContext) + { + this.streamDescriptor = requireNonNull(streamDescriptor, "stream is null"); + this.hiveStorageTimeZone = requireNonNull(hiveStorageTimeZone, "hiveStorageTimeZone is null"); + this.systemMemoryContext = requireNonNull(systemMemoryContext, "systemMemoryContext is null"); + this.keyOrcType = streamDescriptor.getNestedStreams().get(0).getStreamType(); + this.baseValueStreamDescriptor = streamDescriptor.getNestedStreams().get(1); + } + + @Override + public void prepareNextRead(int batchSize) + { + readOffset += nextBatchSize; + nextBatchSize = batchSize; + } + + @Override + public Block readBlock(Type type) + throws IOException + { + if (!rowGroupOpen) { + openRowGroup(); + } + + if (readOffset > 0) { + if (presentStream != null) { + // skip ahead the present bit reader, but count the set bits + // and use this as the skip size for the data reader + readOffset = presentStream.countBitsSet(readOffset); + } + if (readOffset > 0) { + for (int i = 0; i < valueStreamReaders.size(); i++) { + int valueReadOffset = inMapStreams.get(i).countBitsSet(readOffset); + valueStreamReaders.get(i).prepareNextRead(valueReadOffset); + } + } + } + + boolean[][] inMapVectors = new boolean[inMapStreamSources.size()][]; + boolean[] nullVector = null; + int totalMapEntries = 0; + + if (presentStream == null) { + for (int keyIndex = 0; keyIndex < inMapStreams.size(); keyIndex++) { + inMapVectors[keyIndex] = new boolean[nextBatchSize]; + totalMapEntries += inMapStreams.get(keyIndex).getSetBits(nextBatchSize, inMapVectors[keyIndex]); + } + } + else { + nullVector = new boolean[nextBatchSize]; + int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector); + if (nullValues == nextBatchSize) { + for (int i = 0; i < inMapStreams.size(); i++) { + inMapVectors[i] = null; + totalMapEntries += nextBatchSize; + } + } + else { + for (int i = 0; i < inMapStreams.size(); i++) { + inMapVectors[i] = new boolean[nextBatchSize]; + totalMapEntries += inMapStreams.get(i).getSetBits(nextBatchSize, inMapVectors[i], nullVector); + } + } + } + + MapType mapType = (MapType) type; + Type valueType = mapType.getValueType(); + + Block[] valueBlocks = new Block[valueStreamReaders.size()]; + + for (int keyIndex = 0; keyIndex < valueStreamReaders.size(); keyIndex++) { + int mapsContainingKey = 0; + + for (int mapIndex = 0; mapIndex < nextBatchSize; mapIndex++) { + if (inMapVectors[keyIndex] == null || inMapVectors[keyIndex][mapIndex]) { + mapsContainingKey++; + } + } + + if (mapsContainingKey > 0) { + StreamReader streamReader = valueStreamReaders.get(keyIndex); + streamReader.prepareNextRead(mapsContainingKey); + valueBlocks[keyIndex] = streamReader.readBlock(valueType); + } + else { + valueBlocks[keyIndex] = valueType.createBlockBuilder(null, 0).build(); + } + } + + int[] valueBlockPositions = new int[inMapVectors.length]; + BlockBuilder valueBlockBuilder = valueType.createBlockBuilder(null, totalMapEntries); + int[] keyIds = new int[totalMapEntries]; + int keyIdsIndex = 0; + int[] mapOffsets = new int[nextBatchSize + 1]; + mapOffsets[0] = 0; + + for (int mapIndex = 0; mapIndex < nextBatchSize; mapIndex++) { + int mapLength = 0; + for (int keyIndex = 0; keyIndex < inMapVectors.length; keyIndex++) { + if (inMapVectors[keyIndex] == null || inMapVectors[keyIndex][mapIndex]) { + mapLength++; + valueType.appendTo(valueBlocks[keyIndex], valueBlockPositions[keyIndex], valueBlockBuilder); + keyIds[keyIdsIndex++] = keyIndex; + valueBlockPositions[keyIndex]++; + } + } + + mapOffsets[mapIndex + 1] = mapOffsets[mapIndex] + mapLength; + } + + readOffset = 0; + nextBatchSize = 0; + + return mapType.createBlockFromKeyValue(Optional.ofNullable(nullVector), mapOffsets, new DictionaryBlock(keyBlockTemplate, keyIds), valueBlockBuilder); + } + + private void openRowGroup() + throws IOException + { + presentStream = presentStreamSource.openStream(); + + for (int i = 0; i < inMapStreamSources.size(); i++) { + BooleanInputStream inMapStream = requireNonNull(inMapStreamSources.get(i).openStream(), "missing inMapStream at position " + i); + inMapStreams.add(inMapStream); + } + + rowGroupOpen = true; + } + + @Override + public void startStripe(InputStreamSources dictionaryStreamSources, List encodings) + throws IOException + { + presentStreamSource = missingStreamSource(BooleanInputStream.class); + + inMapStreamSources.clear(); + valueStreamDescriptors.clear(); + valueStreamReaders.clear(); + + ColumnEncoding encoding = encodings.get(baseValueStreamDescriptor.getStreamId()); + // encoding.getAdditionalSequenceEncodings() may not be present when every map is empty or null + List additionalSequenceEncodings = encoding.getAdditionalSequenceEncodings().orElse(Collections.emptyList()); + // The ColumnEncoding with sequence ID 0 doesn't have any data associated with it + for (int sequence = 1; sequence <= additionalSequenceEncodings.size(); sequence++) { + inMapStreamSources.add(missingStreamSource(BooleanInputStream.class)); + + StreamDescriptor valueStreamDescriptor = copyStreamDescriptorWithSequence(baseValueStreamDescriptor, sequence); + valueStreamDescriptors.add(valueStreamDescriptor); + + StreamReader valueStreamReader = StreamReaders.createStreamReader(valueStreamDescriptor, hiveStorageTimeZone, systemMemoryContext); + valueStreamReader.startStripe(dictionaryStreamSources, encodings); + valueStreamReaders.add(valueStreamReader); + } + + keyBlockTemplate = getKeyBlockTemplate(additionalSequenceEncodings); + readOffset = 0; + nextBatchSize = 0; + + presentStream = null; + + rowGroupOpen = false; + } + + /** + * Creates StreamDescriptor which is a copy of this one with the value of sequence changed to + * the value passed in. Recursively calls itself on the nested streams. + */ + private static StreamDescriptor copyStreamDescriptorWithSequence(StreamDescriptor streamDescriptor, int sequence) + { + List streamDescriptors = streamDescriptor.getNestedStreams().stream() + .map(stream -> copyStreamDescriptorWithSequence(stream, sequence)) + .collect(toImmutableList()); + + return new StreamDescriptor( + streamDescriptor.getStreamName(), + streamDescriptor.getStreamId(), + streamDescriptor.getFieldName(), + streamDescriptor.getStreamType(), + streamDescriptor.getOrcDataSource(), + streamDescriptors, + sequence); + } + + private Block getKeyBlockTemplate(List sequenceEncodings) + { + switch (keyOrcType) { + case BYTE: + case SHORT: + case INT: + case LONG: + return getIntegerKeyBlockTemplate(sequenceEncodings); + case STRING: + case BINARY: + return getSliceKeysBlockTemplate(sequenceEncodings); + default: + throw new IllegalArgumentException("Unsupported flat map key type: " + keyOrcType); + } + } + + private Block getIntegerKeyBlockTemplate(List sequenceEncodings) + { + Type keyType; + + switch (keyOrcType) { + case BYTE: + keyType = TinyintType.TINYINT; + break; + case SHORT: + keyType = SmallintType.SMALLINT; + break; + case INT: + keyType = IntegerType.INTEGER; + break; + case LONG: + keyType = BigintType.BIGINT; + break; + default: + throw new IllegalArgumentException("Unsupported flat map key type: " + keyOrcType); + } + + BlockBuilder blockBuilder = keyType.createBlockBuilder(null, sequenceEncodings.size()); + + for (DwrfSequenceEncoding sequenceEncoding : sequenceEncodings) { + keyType.writeLong(blockBuilder, sequenceEncoding.getKey().getIntKey()); + } + + return blockBuilder.build(); + } + + private Block getSliceKeysBlockTemplate(List sequenceEncodings) + { + int bytes = 0; + + for (DwrfSequenceEncoding sequenceEncoding : sequenceEncodings) { + bytes += sequenceEncoding.getKey().getBytesKey().size(); + } + + VariableWidthBlockBuilder builder = new VariableWidthBlockBuilder(null, sequenceEncodings.size(), bytes); + + for (DwrfSequenceEncoding sequenceEncoding : sequenceEncodings) { + Slice key = Slices.wrappedBuffer(sequenceEncoding.getKey().getBytesKey().toByteArray()); + builder.writeBytes(key, 0, key.length()); + builder.closeEntry(); + } + + return builder.build(); + } + + @Override + public void startRowGroup(InputStreamSources dataStreamSources) + throws IOException + { + presentStreamSource = dataStreamSources.getInputStreamSource(streamDescriptor, PRESENT, BooleanInputStream.class); + + for (int i = 0; i < valueStreamDescriptors.size(); i++) { + InputStreamSource inMapStreamSource = dataStreamSources.getInputStreamSource(valueStreamDescriptors.get(i), IN_MAP, BooleanInputStream.class); + inMapStreamSources.set(i, inMapStreamSource); + } + + readOffset = 0; + nextBatchSize = 0; + + presentStream = null; + inMapStreams.clear(); + + rowGroupOpen = false; + + for (StreamReader valueStreamReader : valueStreamReaders) { + valueStreamReader.startRowGroup(dataStreamSources); + } + } + + @Override + public String toString() + { + return toStringHelper(this) + .addValue(streamDescriptor) + .toString(); + } + + @Override + public void close() + { + try (Closer closer = Closer.create()) { + for (StreamReader valueStreamReader : valueStreamReaders) { + closer.register(valueStreamReader::close); + } + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public long getRetainedSizeInBytes() + { + long retainedSize = INSTANCE_SIZE; + + for (StreamReader valueStreamReader : valueStreamReaders) { + retainedSize += valueStreamReader.getRetainedSizeInBytes(); + } + + return retainedSize; + } +} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapStreamReader.java index b7f262eef0e5d..5c67fbecbd04a 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/MapStreamReader.java @@ -14,34 +14,25 @@ package com.facebook.presto.orc.reader; import com.facebook.presto.memory.context.AggregatedMemoryContext; -import com.facebook.presto.orc.OrcCorruptionException; import com.facebook.presto.orc.StreamDescriptor; import com.facebook.presto.orc.metadata.ColumnEncoding; -import com.facebook.presto.orc.stream.BooleanInputStream; -import com.facebook.presto.orc.stream.InputStreamSource; +import com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind; import com.facebook.presto.orc.stream.InputStreamSources; -import com.facebook.presto.orc.stream.LongInputStream; import com.facebook.presto.spi.block.Block; -import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.Type; import com.google.common.io.Closer; -import it.unimi.dsi.fastutil.ints.IntArrayList; import org.joda.time.DateTimeZone; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import java.io.IOException; import java.io.UncheckedIOException; import java.util.List; -import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; -import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; -import static com.facebook.presto.orc.reader.StreamReaders.createStreamReader; -import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; +import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DIRECT; +import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DIRECT_V2; +import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DWRF_DIRECT; +import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DWRF_MAP_FLAT; import static com.google.common.base.MoreObjects.toStringHelper; -import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; public class MapStreamReader @@ -50,211 +41,55 @@ public class MapStreamReader private static final int INSTANCE_SIZE = ClassLayout.parseClass(MapStreamReader.class).instanceSize(); private final StreamDescriptor streamDescriptor; - - private final StreamReader keyStreamReader; - private final StreamReader valueStreamReader; - - private int readOffset; - private int nextBatchSize; - - @Nonnull - private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); - @Nullable - private BooleanInputStream presentStream; - - @Nonnull - private InputStreamSource lengthStreamSource = missingStreamSource(LongInputStream.class); - @Nullable - private LongInputStream lengthStream; - - private boolean rowGroupOpen; + private final MapDirectStreamReader directReader; + private final MapFlatStreamReader flatReader; + private StreamReader currentReader; public MapStreamReader(StreamDescriptor streamDescriptor, DateTimeZone hiveStorageTimeZone, AggregatedMemoryContext systemMemoryContext) { this.streamDescriptor = requireNonNull(streamDescriptor, "stream is null"); - this.keyStreamReader = createStreamReader(streamDescriptor.getNestedStreams().get(0), hiveStorageTimeZone, systemMemoryContext); - this.valueStreamReader = createStreamReader(streamDescriptor.getNestedStreams().get(1), hiveStorageTimeZone, systemMemoryContext); + directReader = new MapDirectStreamReader(streamDescriptor, hiveStorageTimeZone, systemMemoryContext); + flatReader = new MapFlatStreamReader(streamDescriptor, hiveStorageTimeZone, systemMemoryContext); } @Override public void prepareNextRead(int batchSize) { - readOffset += nextBatchSize; - nextBatchSize = batchSize; + currentReader.prepareNextRead(batchSize); } @Override public Block readBlock(Type type) throws IOException { - if (!rowGroupOpen) { - openRowGroup(); - } - - if (readOffset > 0) { - if (presentStream != null) { - // skip ahead the present bit reader, but count the set bits - // and use this as the skip size for the data reader - readOffset = presentStream.countBitsSet(readOffset); - } - if (readOffset > 0) { - if (lengthStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - long entrySkipSize = lengthStream.sum(readOffset); - keyStreamReader.prepareNextRead(toIntExact(entrySkipSize)); - valueStreamReader.prepareNextRead(toIntExact(entrySkipSize)); - } - } - - // We will use the offsetVector as the buffer to read the length values from lengthStream, - // and the length values will be converted in-place to an offset vector. - int[] offsetVector = new int[nextBatchSize + 1]; - boolean[] nullVector = new boolean[nextBatchSize]; - - if (presentStream == null) { - if (lengthStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - lengthStream.nextIntVector(nextBatchSize, offsetVector, 0); - } - else { - int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector); - if (nullValues != nextBatchSize) { - if (lengthStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - lengthStream.nextIntVector(nextBatchSize, offsetVector, 0, nullVector); - } - } - - MapType mapType = (MapType) type; - Type keyType = mapType.getKeyType(); - Type valueType = mapType.getValueType(); - - // Calculate the entryCount. Note that the values in the offsetVector are still length values now. - int entryCount = 0; - for (int i = 0; i < offsetVector.length - 1; i++) { - entryCount += offsetVector[i]; - } - - Block keys; - Block values; - if (entryCount > 0) { - keyStreamReader.prepareNextRead(entryCount); - valueStreamReader.prepareNextRead(entryCount); - keys = keyStreamReader.readBlock(keyType); - values = valueStreamReader.readBlock(valueType); - } - else { - keys = keyType.createBlockBuilder(null, 0).build(); - values = valueType.createBlockBuilder(null, 1).build(); - } - - Block[] keyValueBlock = createKeyValueBlock(nextBatchSize, keys, values, offsetVector); - - // Convert the length values in the offsetVector to offset values in-place - int currentLength = offsetVector[0]; - offsetVector[0] = 0; - for (int i = 1; i < offsetVector.length; i++) { - int lastLength = offsetVector[i]; - offsetVector[i] = offsetVector[i - 1] + currentLength; - currentLength = lastLength; - } - - readOffset = 0; - nextBatchSize = 0; - - return mapType.createBlockFromKeyValue(nullVector, offsetVector, keyValueBlock[0], keyValueBlock[1]); - } - - private static Block[] createKeyValueBlock(int positionCount, Block keys, Block values, int[] lengths) - { - if (!hasNull(keys)) { - return new Block[] {keys, values}; - } - - // - // Map entries with a null key are skipped in the Hive ORC reader, so skip them here also - // - - IntArrayList nonNullPositions = new IntArrayList(keys.getPositionCount()); - - int position = 0; - for (int mapIndex = 0; mapIndex < positionCount; mapIndex++) { - int length = lengths[mapIndex]; - for (int entryIndex = 0; entryIndex < length; entryIndex++) { - if (keys.isNull(position)) { - // key is null, so remove this entry from the map - lengths[mapIndex]--; - } - else { - nonNullPositions.add(position); - } - position++; - } - } - - Block newKeys = keys.copyPositions(nonNullPositions.elements(), 0, nonNullPositions.size()); - Block newValues = values.copyPositions(nonNullPositions.elements(), 0, nonNullPositions.size()); - return new Block[] {newKeys, newValues}; - } - - private static boolean hasNull(Block keys) - { - for (int position = 0; position < keys.getPositionCount(); position++) { - if (keys.isNull(position)) { - return true; - } - } - return false; - } - - private void openRowGroup() - throws IOException - { - presentStream = presentStreamSource.openStream(); - lengthStream = lengthStreamSource.openStream(); - - rowGroupOpen = true; + return currentReader.readBlock(type); } @Override public void startStripe(InputStreamSources dictionaryStreamSources, List encoding) throws IOException { - presentStreamSource = missingStreamSource(BooleanInputStream.class); - lengthStreamSource = missingStreamSource(LongInputStream.class); - - readOffset = 0; - nextBatchSize = 0; - - presentStream = null; - lengthStream = null; - - rowGroupOpen = false; + ColumnEncodingKind kind = encoding.get(streamDescriptor.getStreamId()) + .getColumnEncoding(streamDescriptor.getSequence()) + .getColumnEncodingKind(); + if (kind == DIRECT || kind == DIRECT_V2 || kind == DWRF_DIRECT) { + currentReader = directReader; + } + else if (kind == DWRF_MAP_FLAT) { + currentReader = flatReader; + } + else { + throw new IllegalArgumentException("Unsupported encoding " + kind); + } - keyStreamReader.startStripe(dictionaryStreamSources, encoding); - valueStreamReader.startStripe(dictionaryStreamSources, encoding); + currentReader.startStripe(dictionaryStreamSources, encoding); } @Override public void startRowGroup(InputStreamSources dataStreamSources) throws IOException { - presentStreamSource = dataStreamSources.getInputStreamSource(streamDescriptor, PRESENT, BooleanInputStream.class); - lengthStreamSource = dataStreamSources.getInputStreamSource(streamDescriptor, LENGTH, LongInputStream.class); - - readOffset = 0; - nextBatchSize = 0; - - presentStream = null; - lengthStream = null; - - rowGroupOpen = false; - - keyStreamReader.startRowGroup(dataStreamSources); - valueStreamReader.startRowGroup(dataStreamSources); + currentReader.startRowGroup(dataStreamSources); } @Override @@ -269,8 +104,8 @@ public String toString() public void close() { try (Closer closer = Closer.create()) { - closer.register(() -> keyStreamReader.close()); - closer.register(() -> valueStreamReader.close()); + closer.register(directReader::close); + closer.register(flatReader::close); } catch (IOException e) { throw new UncheckedIOException(e); @@ -280,6 +115,6 @@ public void close() @Override public long getRetainedSizeInBytes() { - return INSTANCE_SIZE + keyStreamReader.getRetainedSizeInBytes() + valueStreamReader.getRetainedSizeInBytes(); + return INSTANCE_SIZE + directReader.getRetainedSizeInBytes() + flatReader.getRetainedSizeInBytes(); } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceDictionaryStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceDictionaryStreamReader.java index 782aea0684a26..e180a1aaf6357 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceDictionaryStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceDictionaryStreamReader.java @@ -28,10 +28,8 @@ import com.facebook.presto.spi.block.VariableWidthBlock; import com.facebook.presto.spi.type.Type; import io.airlift.slice.Slice; -import io.airlift.slice.Slices; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; @@ -39,7 +37,6 @@ import java.util.List; import java.util.Optional; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DICTIONARY_DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_DICTIONARY; @@ -48,11 +45,13 @@ import static com.facebook.presto.orc.metadata.Stream.StreamKind.ROW_GROUP_DICTIONARY; import static com.facebook.presto.orc.metadata.Stream.StreamKind.ROW_GROUP_DICTIONARY_LENGTH; import static com.facebook.presto.orc.reader.SliceStreamReader.computeTruncatedLength; +import static com.facebook.presto.orc.reader.SliceStreamReader.getMaxCodePointCount; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; +import static com.facebook.presto.spi.type.Chars.isCharType; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; +import static io.airlift.slice.Slices.wrappedBuffer; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; @@ -70,44 +69,33 @@ public class SliceDictionaryStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] isNullVector = new boolean[0]; - @Nonnull private InputStreamSource stripeDictionaryDataStreamSource = missingStreamSource(ByteArrayInputStream.class); private boolean stripeDictionaryOpen; private int stripeDictionarySize; - @Nonnull private int[] stripeDictionaryLength = new int[0]; - @Nonnull private byte[] stripeDictionaryData = EMPTY_DICTIONARY_DATA; - @Nonnull private int[] stripeDictionaryOffsetVector = EMPTY_DICTIONARY_OFFSETS; - private VariableWidthBlock dictionaryBlock = new VariableWidthBlock(1, Slices.wrappedBuffer(EMPTY_DICTIONARY_DATA), EMPTY_DICTIONARY_OFFSETS, Optional.of(new boolean[]{true})); + private VariableWidthBlock dictionaryBlock = new VariableWidthBlock(1, wrappedBuffer(EMPTY_DICTIONARY_DATA), EMPTY_DICTIONARY_OFFSETS, Optional.of(new boolean[] {true})); private byte[] currentDictionaryData = EMPTY_DICTIONARY_DATA; - @Nonnull private InputStreamSource stripeDictionaryLengthStreamSource = missingStreamSource(LongInputStream.class); - @Nonnull private InputStreamSource inDictionaryStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream inDictionaryStream; private boolean[] inDictionaryVector = new boolean[0]; - @Nonnull private InputStreamSource rowGroupDictionaryDataStreamSource = missingStreamSource(ByteArrayInputStream.class); - @Nonnull private InputStreamSource rowGroupDictionaryLengthStreamSource = missingStreamSource(RowGroupDictionaryLengthInputStream.class); - @Nonnull private int[] rowGroupDictionaryLength = new int[0]; - @Nonnull private InputStreamSource dataStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream dataStream; @@ -154,55 +142,52 @@ public Block readBlock(Type type) } } - assureVectorSize(); - - // idsVector will be filled sub-batch by sub-batch. int[] idsVector = new int[nextBatchSize]; - int idsVectorOffset = 0; - while (nextBatchSize > idsVectorOffset) { - int subBatchSize = min(nextBatchSize - idsVectorOffset, MAX_BATCH_SIZE); - if (presentStream == null) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); - } - Arrays.fill(isNullVector, false); - dataStream.nextIntVector(subBatchSize, idsVector, idsVectorOffset); + if (presentStream == null) { + // Data doesn't have nulls + if (dataStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } + if (inDictionaryStream == null) { + dataStream.nextIntVector(nextBatchSize, idsVector, 0); } else { - int nullValues = presentStream.getUnsetBits(subBatchSize, isNullVector); - if (nullValues != subBatchSize) { - if (dataStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + for (int i = 0; i < nextBatchSize; i++) { + idsVector[i] = toIntExact(dataStream.next()); + if (!inDictionaryStream.nextBit()) { + // row group dictionary elements are after the main dictionary + idsVector[i] += stripeDictionarySize; } - dataStream.nextIntVector(subBatchSize, idsVector, idsVectorOffset, isNullVector); } } - - if (inDictionaryStream == null) { - Arrays.fill(inDictionaryVector, true); + } + else { + // Data has nulls + if (dataStream == null) { + // The only valid case for dataStream is null when data has nulls is that all values are nulls. + // In that case the only element in the dictionaryBlock is null and the ids in idsVector should + // be all 0's, so we don't need to update idVector again. + int nullValues = presentStream.getUnsetBits(nextBatchSize); + if (nullValues != nextBatchSize) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but data stream is not present"); + } } else { - inDictionaryStream.getSetBits(subBatchSize, inDictionaryVector, isNullVector); - } - - // create the dictionary ids - for (int i = 0; i < subBatchSize; i++) { - int dataVectorIndex = i + idsVectorOffset; - if (isNullVector[i]) { - // null is the last entry in the slice dictionary - idsVector[dataVectorIndex] = dictionaryBlock.getPositionCount() - 1; - } - else if (inDictionaryVector[i]) { - // stripe dictionary elements have the same dictionary id - } - else { - // row group dictionary elements are after the main dictionary - idsVector[dataVectorIndex] += stripeDictionarySize; + for (int i = 0; i < nextBatchSize; i++) { + if (!presentStream.nextBit()) { + // null is the last entry in the slice dictionary + idsVector[i] = dictionaryBlock.getPositionCount() - 1; + } + else { + idsVector[i] = toIntExact(dataStream.next()); + if (inDictionaryStream != null && !inDictionaryStream.nextBit()) { + // row group dictionary elements are after the main dictionary + idsVector[i] += stripeDictionarySize; + } + } } } - idsVectorOffset += subBatchSize; } - Block block = new DictionaryBlock(nextBatchSize, dictionaryBlock, idsVector); readOffset = 0; @@ -210,16 +195,6 @@ else if (inDictionaryVector[i]) { return block; } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (isNullVector.length < requiredVectorLength) { - isNullVector = new boolean[requiredVectorLength]; - inDictionaryVector = new boolean[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void setDictionaryBlockData(byte[] dictionaryData, int[] dictionaryOffsets, int positionCount) { verify(positionCount > 0); @@ -229,7 +204,7 @@ private void setDictionaryBlockData(byte[] dictionaryData, int[] dictionaryOffse boolean[] isNullVector = new boolean[positionCount]; isNullVector[positionCount - 1] = true; dictionaryOffsets[positionCount] = dictionaryOffsets[positionCount - 1]; - dictionaryBlock = new VariableWidthBlock(positionCount, Slices.wrappedBuffer(dictionaryData), dictionaryOffsets, Optional.of(isNullVector)); + dictionaryBlock = new VariableWidthBlock(positionCount, wrappedBuffer(dictionaryData), dictionaryOffsets, Optional.of(isNullVector)); currentDictionaryData = dictionaryData; } } @@ -323,7 +298,7 @@ private static void readDictionary( Type type) throws IOException { - Slice slice = Slices.wrappedBuffer(data); + Slice slice = wrappedBuffer(data); // initialize the offset if necessary; // otherwise, use the previous offset @@ -338,12 +313,14 @@ private static void readDictionary( int length = dictionaryLengthVector[i]; int truncatedLength; + int maxCodePointCount = getMaxCodePointCount(type); + boolean isCharType = isCharType(type); if (length > 0) { // read data without truncation dictionaryDataStream.next(data, offset, offset + length); // adjust offsets with truncated length - truncatedLength = computeTruncatedLength(slice, offset, length, type); + truncatedLength = computeTruncatedLength(slice, offset, length, maxCodePointCount, isCharType); verify(truncatedLength >= 0); } else { @@ -358,7 +335,9 @@ public void startStripe(InputStreamSources dictionaryStreamSources, List presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; - @Nonnull private InputStreamSource lengthStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream lengthStream; - @Nonnull private InputStreamSource dataByteSource = missingStreamSource(ByteArrayInputStream.class); @Nullable private ByteArrayInputStream dataStream; @@ -182,6 +180,8 @@ public Block readBlock(Type type) // * convert original length values in offsetVector into truncated offset values int currentLength = offsetVector[0]; offsetVector[0] = 0; + int maxCodePointCount = getMaxCodePointCount(type); + boolean isCharType = isCharType(type); for (int i = 1; i <= currentBatchSize; i++) { int nextLength = offsetVector[i]; if (isNullVector != null && isNullVector[i - 1]) { @@ -196,7 +196,7 @@ public Block readBlock(Type type) dataStream.next(data, offset, offset + currentLength); // adjust offsetVector with truncated length - int truncatedLength = computeTruncatedLength(slice, offset, currentLength, type); + int truncatedLength = computeTruncatedLength(slice, offset, currentLength, maxCodePointCount, isCharType); verify(truncatedLength >= 0); offsetVector[i] = offset + truncatedLength; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceStreamReader.java index 92eb148e3bc66..28fb5c5cee845 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/SliceStreamReader.java @@ -37,6 +37,7 @@ import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DWRF_DIRECT; import static com.facebook.presto.spi.type.Chars.byteCountWithoutTrailingSpace; import static com.facebook.presto.spi.type.Chars.isCharType; +import static com.facebook.presto.spi.type.VarbinaryType.isVarbinaryType; import static com.facebook.presto.spi.type.Varchars.byteCount; import static com.facebook.presto.spi.type.Varchars.isVarcharType; import static com.google.common.base.MoreObjects.toStringHelper; @@ -76,7 +77,9 @@ public void prepareNextRead(int batchSize) public void startStripe(InputStreamSources dictionaryStreamSources, List encoding) throws IOException { - ColumnEncodingKind columnEncodingKind = encoding.get(streamDescriptor.getStreamId()).getColumnEncodingKind(); + ColumnEncodingKind columnEncodingKind = encoding.get(streamDescriptor.getStreamId()) + .getColumnEncoding(streamDescriptor.getSequence()) + .getColumnEncodingKind(); if (columnEncodingKind == DIRECT || columnEncodingKind == DIRECT_V2 || columnEncodingKind == DWRF_DIRECT) { currentReader = directReader; } @@ -105,20 +108,31 @@ public String toString() .toString(); } - public static int computeTruncatedLength(Slice slice, int offset, int length, Type type) + public static int getMaxCodePointCount(Type type) { - // calculate truncated length - int truncatedLength = length; if (isVarcharType(type)) { VarcharType varcharType = (VarcharType) type; - int codePointCount = varcharType.isUnbounded() ? length : varcharType.getLengthSafe(); - truncatedLength = byteCount(slice, offset, length, codePointCount); + return varcharType.isUnbounded() ? -1 : varcharType.getLengthSafe(); } - else if (isCharType(type)) { + if (isCharType(type)) { + return ((CharType) type).getLength(); + } + if (isVarbinaryType(type)) { + return -1; + } + throw new IllegalArgumentException("Unsupported encoding " + type.getDisplayName()); + } + + public static int computeTruncatedLength(Slice slice, int offset, int length, int maxCodePointCount, boolean isCharType) + { + if (isCharType) { // truncate the characters and then remove the trailing white spaces - truncatedLength = byteCountWithoutTrailingSpace(slice, offset, length, ((CharType) type).getLength()); + return byteCountWithoutTrailingSpace(slice, offset, length, maxCodePointCount); + } + if (maxCodePointCount >= 0 && length > maxCodePointCount) { + return byteCount(slice, offset, length, maxCodePointCount); } - return truncatedLength; + return length; } @Override diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/StructStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/StructStreamReader.java index 26f17e25f483a..fc3538f041d41 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/StructStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/StructStreamReader.java @@ -28,7 +28,6 @@ import org.joda.time.DateTimeZone; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; @@ -59,7 +58,6 @@ public class StructStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; @@ -99,13 +97,14 @@ public Block readBlock(Type type) } } - boolean[] nullVector = new boolean[nextBatchSize]; + boolean[] nullVector = null; Block[] blocks; if (presentStream == null) { blocks = getBlocksForType(type, nextBatchSize); } else { + nullVector = new boolean[nextBatchSize]; int nullValues = presentStream.getUnsetBits(nextBatchSize, nullVector); if (nullValues != nextBatchSize) { blocks = getBlocksForType(type, nextBatchSize - nullValues); @@ -125,7 +124,7 @@ public Block readBlock(Type type) .count() == 1); // Struct is represented as a row block - Block rowBlock = RowBlock.fromFieldBlocks(nullVector, blocks); + Block rowBlock = RowBlock.fromFieldBlocks(nextBatchSize, Optional.ofNullable(nullVector), blocks); readOffset = 0; nextBatchSize = 0; @@ -185,7 +184,8 @@ public String toString() .toString(); } - private Block[] getBlocksForType(Type type, int positionCount) throws IOException + private Block[] getBlocksForType(Type type, int positionCount) + throws IOException { RowType rowType = (RowType) type; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/reader/TimestampStreamReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/reader/TimestampStreamReader.java index 1acb2418cce66..c4ad8bfbaee2a 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/reader/TimestampStreamReader.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/reader/TimestampStreamReader.java @@ -28,20 +28,18 @@ import org.joda.time.DateTimeZone; import org.openjdk.jol.info.ClassLayout; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.List; -import static com.facebook.presto.orc.OrcReader.MAX_BATCH_SIZE; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.metadata.Stream.StreamKind.SECONDARY; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Verify.verify; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.Math.min; import static java.util.Objects.requireNonNull; public class TimestampStreamReader @@ -57,18 +55,15 @@ public class TimestampStreamReader private int readOffset; private int nextBatchSize; - @Nonnull private InputStreamSource presentStreamSource = missingStreamSource(BooleanInputStream.class); @Nullable private BooleanInputStream presentStream; private boolean[] nullVector = new boolean[0]; - @Nonnull private InputStreamSource secondsStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream secondsStream; - @Nonnull private InputStreamSource nanosStreamSource = missingStreamSource(LongInputStream.class); @Nullable private LongInputStream nanosStream; @@ -121,60 +116,31 @@ public Block readBlock(Type type) } } - assureVectorSize(); - BlockBuilder builder = type.createBlockBuilder(null, nextBatchSize); - while (nextBatchSize > 0) { - int subBatchSize = min(nextBatchSize, MAX_BATCH_SIZE); - if (presentStream == null) { - if (secondsStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but seconds stream is not present"); - } - if (nanosStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but nanos stream is not present"); - } - secondsStream.nextLongVector(subBatchSize, secondsVector); - nanosStream.nextLongVector(subBatchSize, nanosVector); + if (presentStream == null) { + if (secondsStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but seconds stream is not present"); + } + if (nanosStream == null) { + throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but nanos stream is not present"); + } - // merge seconds and nanos together - for (int i = 0; i < subBatchSize; i++) { - type.writeLong(builder, decodeTimestamp(secondsVector[i], nanosVector[i], baseTimestampInSeconds)); - } + for (int i = 0; i < nextBatchSize; i++) { + type.writeLong(builder, decodeTimestamp(secondsStream.next(), nanosStream.next(), baseTimestampInSeconds)); } - else { - if (nullVector.length < subBatchSize) { - nullVector = new boolean[subBatchSize]; - } - int nullValues = presentStream.getUnsetBits(subBatchSize, nullVector); - if (nullValues != subBatchSize) { - if (secondsStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but seconds stream is not present"); - } - if (nanosStream == null) { - throw new OrcCorruptionException(streamDescriptor.getOrcDataSourceId(), "Value is not null but nanos stream is not present"); - } - - secondsStream.nextLongVector(subBatchSize, secondsVector, nullVector); - nanosStream.nextLongVector(subBatchSize, nanosVector, nullVector); - - // merge seconds and nanos together - for (int i = 0; i < subBatchSize; i++) { - if (nullVector[i]) { - builder.appendNull(); - } - else { - type.writeLong(builder, decodeTimestamp(secondsVector[i], nanosVector[i], baseTimestampInSeconds)); - } - } + } + else { + for (int i = 0; i < nextBatchSize; i++) { + if (presentStream.nextBit()) { + verify(secondsStream != null, "Value is not null but seconds stream is not present"); + verify(nanosStream != null, "Value is not null but nanos stream is not present"); + type.writeLong(builder, decodeTimestamp(secondsStream.next(), nanosStream.next(), baseTimestampInSeconds)); } else { - for (int i = 0; i < subBatchSize; i++) { - builder.appendNull(); - } + builder.appendNull(); } } - nextBatchSize -= subBatchSize; } readOffset = 0; @@ -182,16 +148,6 @@ public Block readBlock(Type type) return builder.build(); } - private void assureVectorSize() - { - int requiredVectorLength = min(nextBatchSize, MAX_BATCH_SIZE); - if (secondsVector.length < requiredVectorLength) { - secondsVector = new long[requiredVectorLength]; - nanosVector = new long[requiredVectorLength]; - systemMemoryContext.setBytes(getRetainedSizeInBytes()); - } - } - private void openRowGroup() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/BooleanInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/BooleanInputStream.java index 748878e0228c5..4665f1bbe6d40 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/BooleanInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/BooleanInputStream.java @@ -128,25 +128,31 @@ public int countBitsSet(int items) /** * Sets the vector element to true if the bit is set. */ - public void getSetBits(int batchSize, boolean[] vector) + public int getSetBits(int batchSize, boolean[] vector) throws IOException { + int count = 0; for (int i = 0; i < batchSize; i++) { vector[i] = nextBit(); + count += vector[i] ? 1 : 0; } + return count; } /** * Sets the vector element to true if the bit is set, skipping the null values. */ - public void getSetBits(int batchSize, boolean[] vector, boolean[] isNull) + public int getSetBits(int batchSize, boolean[] vector, boolean[] isNull) throws IOException { + int count = 0; for (int i = 0; i < batchSize; i++) { if (!isNull[i]) { vector[i] = nextBit(); + count += vector[i] ? 1 : 0; } } + return count; } /** @@ -160,32 +166,6 @@ public void getSetBits(Type type, int batchSize, BlockBuilder builder) } } - /** - * Sets the vector element to true if the bit is set, skipping the null values. - */ - public void getSetBits(Type type, int batchSize, BlockBuilder builder, boolean[] isNull) - throws IOException - { - getSetBits(type, batchSize, builder, isNull, 0); - } - - /** - * Sets the vector element to true for the batchSize number of elements starting at offset - * if the bit is set, skipping the null values. - */ - public void getSetBits(Type type, int batchSize, BlockBuilder builder, boolean[] isNull, int offset) - throws IOException - { - for (int i = offset; i < batchSize + offset; i++) { - if (isNull[i]) { - builder.appendNull(); - } - else { - type.writeBoolean(builder, nextBit()); - } - } - } - /** * Sets the vector element to true if the bit is not set. */ @@ -210,6 +190,19 @@ public int getUnsetBits(int batchSize, boolean[] vector, int offset) return count; } + /** + * Return the number of unset bits + */ + public int getUnsetBits(int batchSize) + throws IOException + { + int count = 0; + for (int i = 0; i < batchSize; i++) { + count += nextBit() ? 0 : 1; + } + return count; + } + private static int bitCount(byte data) { return Integer.bitCount(data & 0xFF); diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/ByteInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/ByteInputStream.java index 34a8931116a5e..21edc123ebedb 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/ByteInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/ByteInputStream.java @@ -126,17 +126,4 @@ public void nextVector(Type type, long items, BlockBuilder builder) type.writeLong(builder, next()); } } - - public void nextVector(Type type, long items, BlockBuilder builder, boolean[] isNull) - throws IOException - { - for (int i = 0; i < items; i++) { - if (isNull[i]) { - builder.appendNull(); - } - else { - type.writeLong(builder, next()); - } - } - } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/DecimalInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/DecimalInputStream.java index 121b1589cde96..a52a73030bc4d 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/DecimalInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/DecimalInputStream.java @@ -23,7 +23,6 @@ import java.io.IOException; -import static com.facebook.presto.spi.type.UnscaledDecimal128Arithmetic.rescale; import static java.lang.Long.MAX_VALUE; public class DecimalInputStream @@ -97,35 +96,6 @@ else if (offset == 63) { UnscaledDecimal128Arithmetic.pack(low, high, negative, result); } - public void nextLongDecimalVector(int items, BlockBuilder builder, DecimalType targetType, long[] sourceScale) - throws IOException - { - Slice decimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); - Slice rescaledDecimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); - for (int i = 0; i < items; i++) { - nextLongDecimal(decimal); - rescale(decimal, (int) (targetType.getScale() - sourceScale[i]), rescaledDecimal); - targetType.writeSlice(builder, rescaledDecimal); - } - } - - public void nextLongDecimalVector(int items, BlockBuilder builder, DecimalType targetType, long[] sourceScale, boolean[] isNull) - throws IOException - { - Slice decimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); - Slice rescaledDecimal = UnscaledDecimal128Arithmetic.unscaledDecimal(); - for (int i = 0; i < items; i++) { - if (!isNull[i]) { - nextLongDecimal(decimal); - rescale(decimal, (int) (targetType.getScale() - sourceScale[i]), rescaledDecimal); - targetType.writeSlice(builder, rescaledDecimal); - } - else { - builder.appendNull(); - } - } - } - public long nextLong() throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/DoubleInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/DoubleInputStream.java index b218be26613e2..fcc14b79f4020 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/DoubleInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/DoubleInputStream.java @@ -70,17 +70,4 @@ public void nextVector(Type type, int items, BlockBuilder builder) type.writeDouble(builder, next()); } } - - public void nextVector(Type type, long items, BlockBuilder builder, boolean[] isNull) - throws IOException - { - for (int i = 0; i < items; i++) { - if (isNull[i]) { - builder.appendNull(); - } - else { - type.writeDouble(builder, next()); - } - } - } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/FloatInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/FloatInputStream.java index f62af8d4d5383..34525389d2eb2 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/FloatInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/FloatInputStream.java @@ -71,17 +71,4 @@ public void nextVector(Type type, int items, BlockBuilder builder) type.writeLong(builder, floatToRawIntBits(next())); } } - - public void nextVector(Type type, long items, BlockBuilder builder, boolean[] isNull) - throws IOException - { - for (int i = 0; i < items; i++) { - if (isNull[i]) { - builder.appendNull(); - } - else { - type.writeLong(builder, floatToRawIntBits(next())); - } - } - } } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/InputStreamSources.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/InputStreamSources.java index faba09e312b8c..da4cf6764a940 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/InputStreamSources.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/InputStreamSources.java @@ -18,8 +18,6 @@ import com.facebook.presto.orc.metadata.Stream.StreamKind; import com.google.common.collect.ImmutableMap; -import javax.annotation.Nonnull; - import java.util.Map; import static com.facebook.presto.orc.stream.MissingInputStreamSource.missingStreamSource; @@ -35,13 +33,12 @@ public InputStreamSources(Map> streamSources) this.streamSources = ImmutableMap.copyOf(requireNonNull(streamSources, "streamSources is null")); } - @Nonnull public > InputStreamSource getInputStreamSource(StreamDescriptor streamDescriptor, StreamKind streamKind, Class streamType) { requireNonNull(streamDescriptor, "streamDescriptor is null"); requireNonNull(streamType, "streamType is null"); - InputStreamSource streamSource = streamSources.get(new StreamId(streamDescriptor.getStreamId(), streamKind)); + InputStreamSource streamSource = streamSources.get(new StreamId(streamDescriptor.getStreamId(), streamDescriptor.getSequence(), streamKind)); if (streamSource == null) { streamSource = missingStreamSource(streamType); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongInputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongInputStream.java index cf25d9601e4ef..6a6c4ac0c14e7 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongInputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongInputStream.java @@ -61,19 +61,6 @@ default void nextLongVector(int items, long[] vector) } } - default void nextLongVector(int items, long[] vector, boolean[] isNull) - throws IOException - { - checkPositionIndex(items, vector.length); - checkPositionIndex(items, isNull.length); - - for (int i = 0; i < items; i++) { - if (!isNull[i]) { - vector[i] = next(); - } - } - } - default void nextLongVector(Type type, int items, BlockBuilder builder) throws IOException { @@ -82,19 +69,6 @@ default void nextLongVector(Type type, int items, BlockBuilder builder) } } - default void nextLongVector(Type type, int items, BlockBuilder builder, boolean[] isNull) - throws IOException - { - for (int i = 0; i < items; i++) { - if (isNull[i]) { - builder.appendNull(); - } - else { - type.writeLong(builder, next()); - } - } - } - default long sum(int items) throws IOException { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStream.java index d130af8b531c0..99ddf0410be63 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStream.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStream.java @@ -34,9 +34,4 @@ static LongOutputStream createLengthOutputStream(CompressionKind compression, in } void writeLong(long value); - - /** - * Used for rewriting dictionary output ids after sorting in {@link com.facebook.presto.orc.writer.SliceDictionaryColumnWriter} - */ - LongInputStream getLongInputStream(); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamDwrf.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamDwrf.java index 02e2e3804a245..d867b015dc6b7 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamDwrf.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamDwrf.java @@ -13,27 +13,18 @@ */ package com.facebook.presto.orc.stream; -import com.facebook.presto.orc.ChainedSliceLoader; -import com.facebook.presto.orc.OrcCorruptionException; -import com.facebook.presto.orc.OrcDataSourceId; import com.facebook.presto.orc.OrcOutputBuffer; import com.facebook.presto.orc.checkpoint.LongStreamCheckpoint; import com.facebook.presto.orc.checkpoint.LongStreamDwrfCheckpoint; import com.facebook.presto.orc.metadata.CompressionKind; -import com.facebook.presto.orc.metadata.OrcType.OrcTypeKind; import com.facebook.presto.orc.metadata.Stream; import com.facebook.presto.orc.metadata.Stream.StreamKind; import com.google.common.collect.ImmutableList; -import io.airlift.slice.ChunkedSliceInput; -import io.airlift.slice.FixedLengthSliceInput; import org.openjdk.jol.info.ClassLayout; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; -import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; -import static com.facebook.presto.orc.OrcDecompressor.createOrcDecompressor; import static com.facebook.presto.orc.stream.LongDecode.writeVLong; import static com.google.common.base.Preconditions.checkState; import static java.lang.Math.toIntExact; @@ -44,8 +35,6 @@ public class LongOutputStreamDwrf { private static final int INSTANCE_SIZE = ClassLayout.parseClass(LongOutputStreamDwrf.class).instanceSize(); private final StreamKind streamKind; - private final CompressionKind compression; - private final int bufferSize; private final OrcOutputBuffer buffer; private final boolean signed; private final List checkpoints = new ArrayList<>(); @@ -55,8 +44,6 @@ public class LongOutputStreamDwrf public LongOutputStreamDwrf(CompressionKind compression, int bufferSize, boolean signed, StreamKind streamKind) { this.streamKind = requireNonNull(streamKind, "streamKind is null"); - this.compression = requireNonNull(compression, "compression is null"); - this.bufferSize = bufferSize; this.buffer = new OrcOutputBuffer(compression, bufferSize); this.signed = signed; } @@ -90,30 +77,6 @@ public List getCheckpoints() return ImmutableList.copyOf(checkpoints); } - @Override - public LongInputStream getLongInputStream() - { - checkState(closed); - - FixedLengthSliceInput sliceInput = new ChunkedSliceInput(new ChainedSliceLoader(buffer.getCompressedSlices()), 32 * 1024); - OrcDataSourceId orcDataSourceId = new OrcDataSourceId("LongOutputStream"); - try { - return new LongInputStreamDwrf( - new OrcInputStream( - orcDataSourceId, - sliceInput, - createOrcDecompressor(orcDataSourceId, compression, bufferSize), - newSimpleAggregatedMemoryContext(), - sliceInput.getRetainedSize()), - OrcTypeKind.LONG, - signed, - true); - } - catch (OrcCorruptionException e) { - throw new UncheckedIOException("Unable to create LongInputStream from LongOutputStream", e); - } - } - @Override public StreamDataOutput getStreamDataOutput(int column) { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV1.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV1.java index 1daf47576a5f3..1a3a555accd46 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV1.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV1.java @@ -13,9 +13,6 @@ */ package com.facebook.presto.orc.stream; -import com.facebook.presto.orc.ChainedSliceLoader; -import com.facebook.presto.orc.OrcCorruptionException; -import com.facebook.presto.orc.OrcDataSourceId; import com.facebook.presto.orc.OrcOutputBuffer; import com.facebook.presto.orc.checkpoint.LongStreamCheckpoint; import com.facebook.presto.orc.checkpoint.LongStreamV1Checkpoint; @@ -23,17 +20,12 @@ import com.facebook.presto.orc.metadata.Stream; import com.facebook.presto.orc.metadata.Stream.StreamKind; import com.google.common.collect.ImmutableList; -import io.airlift.slice.ChunkedSliceInput; -import io.airlift.slice.FixedLengthSliceInput; import io.airlift.slice.SizeOf; import org.openjdk.jol.info.ClassLayout; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; -import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; -import static com.facebook.presto.orc.OrcDecompressor.createOrcDecompressor; import static com.facebook.presto.orc.stream.LongDecode.writeVLong; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; @@ -50,8 +42,6 @@ public class LongOutputStreamV1 private static final int MIN_DELTA = -128; private final StreamKind streamKind; - private final CompressionKind compression; - private final int bufferSize; private final OrcOutputBuffer buffer; private final List checkpoints = new ArrayList<>(); @@ -68,8 +58,6 @@ public class LongOutputStreamV1 public LongOutputStreamV1(CompressionKind compression, int bufferSize, boolean signed, StreamKind streamKind) { this.streamKind = requireNonNull(streamKind, "streamKind is null"); - this.compression = requireNonNull(compression, "compression is null"); - this.bufferSize = bufferSize; this.buffer = new OrcOutputBuffer(compression, bufferSize); this.signed = signed; } @@ -199,28 +187,6 @@ public List getCheckpoints() return ImmutableList.copyOf(checkpoints); } - @Override - public LongInputStream getLongInputStream() - { - checkState(closed); - - FixedLengthSliceInput sliceInput = new ChunkedSliceInput(new ChainedSliceLoader(buffer.getCompressedSlices()), 32 * 1024); - OrcDataSourceId orcDataSourceId = new OrcDataSourceId("LongOutputStream"); - try { - return new LongInputStreamV1( - new OrcInputStream( - orcDataSourceId, - sliceInput, - createOrcDecompressor(orcDataSourceId, compression, bufferSize), - newSimpleAggregatedMemoryContext(), - sliceInput.getRetainedSize()), - signed); - } - catch (OrcCorruptionException e) { - throw new UncheckedIOException("Unable to create LongInputStream from LongOutputStream", e); - } - } - @Override public StreamDataOutput getStreamDataOutput(int column) { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV2.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV2.java index ff7f60f9d0f46..0d7ec0c95a097 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV2.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/LongOutputStreamV2.java @@ -13,9 +13,6 @@ */ package com.facebook.presto.orc.stream; -import com.facebook.presto.orc.ChainedSliceLoader; -import com.facebook.presto.orc.OrcCorruptionException; -import com.facebook.presto.orc.OrcDataSourceId; import com.facebook.presto.orc.OrcOutputBuffer; import com.facebook.presto.orc.checkpoint.LongStreamCheckpoint; import com.facebook.presto.orc.checkpoint.LongStreamV2Checkpoint; @@ -23,18 +20,13 @@ import com.facebook.presto.orc.metadata.Stream; import com.facebook.presto.orc.metadata.Stream.StreamKind; import com.google.common.collect.ImmutableList; -import io.airlift.slice.ChunkedSliceInput; -import io.airlift.slice.FixedLengthSliceInput; import io.airlift.slice.SizeOf; import io.airlift.slice.SliceOutput; import org.openjdk.jol.info.ClassLayout; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; -import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; -import static com.facebook.presto.orc.OrcDecompressor.createOrcDecompressor; import static com.facebook.presto.orc.stream.LongOutputStreamV2.SerializationUtils.encodeBitWidth; import static com.facebook.presto.orc.stream.LongOutputStreamV2.SerializationUtils.findClosestNumBits; import static com.facebook.presto.orc.stream.LongOutputStreamV2.SerializationUtils.getClosestAlignedFixedBits; @@ -68,8 +60,6 @@ private int getOpCode() private static final int MAX_SHORT_REPEAT_LENGTH = 10; private final StreamKind streamKind; - private final CompressionKind compression; - private final int bufferSize; private final OrcOutputBuffer buffer; private final List checkpoints = new ArrayList<>(); @@ -101,8 +91,6 @@ private int getOpCode() public LongOutputStreamV2(CompressionKind compression, int bufferSize, boolean signed, StreamKind streamKind) { this.streamKind = requireNonNull(streamKind, "streamKind is null"); - this.compression = requireNonNull(compression, "compression is null"); - this.bufferSize = bufferSize; this.buffer = new OrcOutputBuffer(compression, bufferSize); this.signed = signed; } @@ -764,29 +752,6 @@ public StreamDataOutput getStreamDataOutput(int column) return new StreamDataOutput(buffer::writeDataTo, new Stream(column, streamKind, toIntExact(buffer.getOutputDataSize()), true)); } - @Override - public LongInputStream getLongInputStream() - { - checkState(closed); - - FixedLengthSliceInput sliceInput = new ChunkedSliceInput(new ChainedSliceLoader(buffer.getCompressedSlices()), 32 * 1024); - OrcDataSourceId orcDataSourceId = new OrcDataSourceId("LongOutputStream"); - try { - return new LongInputStreamV2( - new OrcInputStream( - orcDataSourceId, - sliceInput, - createOrcDecompressor(orcDataSourceId, compression, bufferSize), - newSimpleAggregatedMemoryContext(), - sliceInput.getRetainedSize()), - signed, - false); - } - catch (OrcCorruptionException e) { - throw new UncheckedIOException("Unable to create LongInputStream from LongOutputStream", e); - } - } - @Override public long getBufferedBytes() { diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/StreamDataOutput.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/StreamDataOutput.java index 284242db2ed01..2c72180214055 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/StreamDataOutput.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/StreamDataOutput.java @@ -17,8 +17,6 @@ import io.airlift.slice.Slice; import io.airlift.slice.SliceOutput; -import javax.annotation.Nonnull; - import java.util.function.ToLongFunction; import static com.google.common.base.Verify.verify; @@ -47,7 +45,7 @@ public StreamDataOutput(ToLongFunction writer, Stream stream) } @Override - public int compareTo(@Nonnull StreamDataOutput otherStream) + public int compareTo(StreamDataOutput otherStream) { return Long.compare(size(), otherStream.size()); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/stream/ValueStreams.java b/presto-orc/src/main/java/com/facebook/presto/orc/stream/ValueStreams.java index a3854948751a9..4e862eddc8f32 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/stream/ValueStreams.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/stream/ValueStreams.java @@ -28,6 +28,7 @@ import static com.facebook.presto.orc.metadata.Stream.StreamKind.DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.DICTIONARY_DATA; import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_DICTIONARY; +import static com.facebook.presto.orc.metadata.Stream.StreamKind.IN_MAP; import static com.facebook.presto.orc.metadata.Stream.StreamKind.LENGTH; import static com.facebook.presto.orc.metadata.Stream.StreamKind.PRESENT; import static com.facebook.presto.orc.metadata.Stream.StreamKind.ROW_GROUP_DICTIONARY; @@ -48,7 +49,7 @@ public static ValueInputStream createValueStreams( ColumnEncodingKind encoding, boolean usesVInt) { - if (streamId.getStreamKind() == PRESENT) { + if (streamId.getStreamKind() == PRESENT || streamId.getStreamKind() == IN_MAP) { return new BooleanInputStream(inputStream); } diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/writer/SliceDictionaryColumnWriter.java b/presto-orc/src/main/java/com/facebook/presto/orc/writer/SliceDictionaryColumnWriter.java index 5ef16aed60bd5..570acc8ae7ed4 100644 --- a/presto-orc/src/main/java/com/facebook/presto/orc/writer/SliceDictionaryColumnWriter.java +++ b/presto-orc/src/main/java/com/facebook/presto/orc/writer/SliceDictionaryColumnWriter.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.orc.writer; +import com.facebook.presto.array.IntBigArray; import com.facebook.presto.orc.DictionaryCompressionOptimizer.DictionaryColumn; import com.facebook.presto.orc.OrcEncoding; import com.facebook.presto.orc.checkpoint.BooleanStreamCheckpoint; @@ -26,7 +27,6 @@ import com.facebook.presto.orc.metadata.statistics.ColumnStatistics; import com.facebook.presto.orc.metadata.statistics.StringStatisticsBuilder; import com.facebook.presto.orc.stream.ByteArrayOutputStream; -import com.facebook.presto.orc.stream.LongInputStream; import com.facebook.presto.orc.stream.LongOutputStream; import com.facebook.presto.orc.stream.LongOutputStreamV1; import com.facebook.presto.orc.stream.LongOutputStreamV2; @@ -44,12 +44,13 @@ import org.openjdk.jol.info.ClassLayout; import java.io.IOException; -import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.OptionalInt; +import static com.facebook.presto.orc.DictionaryCompressionOptimizer.estimateIndexBytesPerValue; import static com.facebook.presto.orc.OrcEncoding.DWRF; import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DICTIONARY; import static com.facebook.presto.orc.metadata.ColumnEncoding.ColumnEncodingKind.DICTIONARY_V2; @@ -58,6 +59,7 @@ import static com.facebook.presto.orc.stream.LongOutputStream.createLengthOutputStream; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; @@ -66,6 +68,7 @@ public class SliceDictionaryColumnWriter implements ColumnWriter, DictionaryColumn { private static final int INSTANCE_SIZE = ClassLayout.parseClass(SliceDictionaryColumnWriter.class).instanceSize(); + private static final int DIRECT_CONVERSION_CHUNK_MAX_LOGICAL_BYTES = toIntExact(new DataSize(32, MEGABYTE).toBytes()); private final int column; private final Type type; @@ -74,15 +77,6 @@ public class SliceDictionaryColumnWriter private final OrcEncoding orcEncoding; private final int stringStatisticsLimitInBytes; - /** - * To get a good estimate of dictionary id data stream in the stripe, we write - * temporary dictionary ids to this stream and use its buffer size as an estimation. - *

    - * Dictionary will be sorted and dictionary ids has to be re-indexed and written to - * dataStream when flushing data. - */ - private final LongOutputStream tempDictionaryIdDataStream; - private final LongOutputStream dataStream; private final PresentOutputStream presentStream; private final ByteArrayOutputStream dictionaryDataStream; @@ -92,12 +86,13 @@ public class SliceDictionaryColumnWriter private final List rowGroups = new ArrayList<>(); + private IntBigArray values; private int rowGroupValueCount; private StringStatisticsBuilder statisticsBuilder; private long rawBytes; - private int totalValueCount; - private int totalNonNullValueCount; + private long totalValueCount; + private long totalNonNullValueCount; private boolean closed; private boolean inRowGroup; @@ -111,22 +106,22 @@ public SliceDictionaryColumnWriter(int column, Type type, CompressionKind compre checkArgument(column >= 0, "column is negative"); this.column = column; this.type = requireNonNull(type, "type is null"); - this.compression = requireNonNull(compression, "compression is null"); this.bufferSize = bufferSize; this.orcEncoding = requireNonNull(orcEncoding, "orcEncoding is null"); this.stringStatisticsLimitInBytes = toIntExact(requireNonNull(stringStatisticsLimit, "stringStatisticsLimit is null").toBytes()); + LongOutputStream result; if (orcEncoding == DWRF) { - this.dataStream = new LongOutputStreamV1(compression, bufferSize, false, DATA); - this.tempDictionaryIdDataStream = new LongOutputStreamV1(compression, bufferSize, false, DATA); + result = new LongOutputStreamV1(compression, bufferSize, false, DATA); } else { - this.dataStream = new LongOutputStreamV2(compression, bufferSize, false, DATA); - this.tempDictionaryIdDataStream = new LongOutputStreamV2(compression, bufferSize, false, DATA); + result = new LongOutputStreamV2(compression, bufferSize, false, DATA); } + this.dataStream = result; this.presentStream = new PresentOutputStream(compression, bufferSize); this.dictionaryDataStream = new ByteArrayOutputStream(compression, bufferSize, StreamKind.DICTIONARY_DATA); this.dictionaryLengthStream = createLengthOutputStream(compression, bufferSize, orcEncoding); + values = new IntBigArray(); this.statisticsBuilder = newStringStatisticsBuilder(); } @@ -145,14 +140,21 @@ public int getDictionaryBytes() } @Override - public int getValueCount() + public int getIndexBytes() + { + checkState(!directEncoded); + return toIntExact(estimateIndexBytesPerValue(dictionary.getEntryCount()) * getNonNullValueCount()); + } + + @Override + public long getValueCount() { checkState(!directEncoded); return totalValueCount; } @Override - public int getNonNullValueCount() + public long getNonNullValueCount() { checkState(!directEncoded); return totalNonNullValueCount; @@ -166,26 +168,36 @@ public int getDictionaryEntries() } @Override - public long convertToDirect() + public OptionalInt tryConvertToDirect(int maxDirectBytes) { checkState(!closed); checkState(!directEncoded); if (directColumnWriter == null) { directColumnWriter = new SliceDirectColumnWriter(column, type, compression, bufferSize, orcEncoding, this::newStringStatisticsBuilder); } + checkState(directColumnWriter.getBufferedBytes() == 0); Block dictionaryValues = dictionary.getElementBlock(); - tempDictionaryIdDataStream.close(); - LongInputStream tempDictionaryIdInputStream = tempDictionaryIdDataStream.getLongInputStream(); for (DictionaryRowGroup rowGroup : rowGroups) { directColumnWriter.beginRowGroup(); // todo we should be able to pass the stats down to avoid recalculating min and max - writeDictionaryRowGroup(dictionaryValues, rowGroup.getValueCount(), tempDictionaryIdInputStream); + boolean success = writeDictionaryRowGroup(dictionaryValues, rowGroup.getValueCount(), rowGroup.getDictionaryIndexes(), maxDirectBytes); directColumnWriter.finishRowGroup(); + + if (!success) { + directColumnWriter.close(); + directColumnWriter.reset(); + return OptionalInt.empty(); + } } + if (inRowGroup) { directColumnWriter.beginRowGroup(); - writeDictionaryRowGroup(dictionaryValues, rowGroupValueCount, tempDictionaryIdInputStream); + if (!writeDictionaryRowGroup(dictionaryValues, rowGroupValueCount, values, maxDirectBytes)) { + directColumnWriter.close(); + directColumnWriter.reset(); + return OptionalInt.empty(); + } } else { checkState(rowGroupValueCount == 0); @@ -193,36 +205,57 @@ public long convertToDirect() rowGroups.clear(); + // free the dictionary + dictionary.clear(); + rawBytes = 0; totalValueCount = 0; totalNonNullValueCount = 0; - tempDictionaryIdDataStream.close(); rowGroupValueCount = 0; statisticsBuilder = newStringStatisticsBuilder(); directEncoded = true; - return directColumnWriter.getBufferedBytes(); + return OptionalInt.of(toIntExact(directColumnWriter.getBufferedBytes())); } - private void writeDictionaryRowGroup(Block dictionary, int valueCount, LongInputStream dictionaryIdInputStream) + private boolean writeDictionaryRowGroup(Block dictionary, int valueCount, IntBigArray dictionaryIndexes, int maxDirectBytes) { - while (valueCount > 0) { - int batchSize = Math.min(valueCount, 1024); - int[] dictionaryIdBuffer = new int[batchSize]; - try { - dictionaryIdInputStream.nextIntVector(batchSize, dictionaryIdBuffer, 0); - } - catch (IOException e) { - throw new UncheckedIOException("Unable to decode dictionary index stream", e); + int[][] segments = dictionaryIndexes.getSegments(); + for (int i = 0; valueCount > 0 && i < segments.length; i++) { + int[] segment = segments[i]; + int positionCount = Math.min(valueCount, segment.length); + Block block = new DictionaryBlock(positionCount, dictionary, segment); + + while (block != null) { + int chunkPositionCount = block.getPositionCount(); + Block chunk = block.getRegion(0, chunkPositionCount); + + // avoid chunk with huge logical size + while (chunkPositionCount > 1 && chunk.getLogicalSizeInBytes() > DIRECT_CONVERSION_CHUNK_MAX_LOGICAL_BYTES) { + chunkPositionCount /= 2; + chunk = chunk.getRegion(0, chunkPositionCount); + } + + directColumnWriter.writeBlock(chunk); + if (directColumnWriter.getBufferedBytes() > maxDirectBytes) { + return false; + } + + // slice block to only unconverted rows + if (chunkPositionCount < block.getPositionCount()) { + block = block.getRegion(chunkPositionCount, block.getPositionCount() - chunkPositionCount); + } + else { + block = null; + } } - DictionaryBlock dictionaryBlock = new DictionaryBlock(batchSize, dictionary, dictionaryIdBuffer); - directColumnWriter.writeBlock(dictionaryBlock); - valueCount -= batchSize; + valueCount -= positionCount; } checkState(valueCount == 0); + return true; } @Override @@ -258,9 +291,10 @@ public void writeBlock(Block block) } // record values + values.ensureCapacity(rowGroupValueCount + block.getPositionCount()); for (int position = 0; position < block.getPositionCount(); position++) { int index = dictionary.putIfAbsent(block, position); - tempDictionaryIdDataStream.writeLong(index); + values.set(rowGroupValueCount, index); rowGroupValueCount++; totalValueCount++; @@ -286,9 +320,10 @@ public Map finishRowGroup() } ColumnStatistics statistics = statisticsBuilder.buildColumnStatistics(); - rowGroups.add(new DictionaryRowGroup(rowGroupValueCount, statistics)); + rowGroups.add(new DictionaryRowGroup(values, rowGroupValueCount, statistics)); rowGroupValueCount = 0; statisticsBuilder = newStringStatisticsBuilder(); + values = new IntBigArray(); return ImmutableMap.of(column, statistics); } @@ -349,20 +384,13 @@ private void bufferOutputData() presentStream.recordCheckpoint(); dataStream.recordCheckpoint(); } - - tempDictionaryIdDataStream.close(); - LongInputStream tempDictionaryIdInputStream = tempDictionaryIdDataStream.getLongInputStream(); for (DictionaryRowGroup rowGroup : rowGroups) { + IntBigArray dictionaryIndexes = rowGroup.getDictionaryIndexes(); for (int position = 0; position < rowGroup.getValueCount(); position++) { - int originalDictionaryIndex; - try { - originalDictionaryIndex = toIntExact(tempDictionaryIdInputStream.next()); - } - catch (IOException e) { - throw new RuntimeException(e); - } - - presentStream.writeBoolean(originalDictionaryIndex != 0); + presentStream.writeBoolean(dictionaryIndexes.get(position) != 0); + } + for (int position = 0; position < rowGroup.getValueCount(); position++) { + int originalDictionaryIndex = dictionaryIndexes.get(position); // index zero in original dictionary is reserved for null if (originalDictionaryIndex != 0) { int sortedIndex = originalDictionaryToSortedIndex[originalDictionaryIndex]; @@ -378,6 +406,7 @@ private void bufferOutputData() // free the dictionary memory dictionary.clear(); + dictionaryDataStream.close(); dictionaryLengthStream.close(); @@ -392,7 +421,8 @@ private static int[] getSortedDictionaryNullsLast(Block elementBlock) sortedPositions[i] = i; } - IntArrays.quickSort(sortedPositions, 0, sortedPositions.length, new AbstractIntComparator() { + IntArrays.quickSort(sortedPositions, 0, sortedPositions.length, new AbstractIntComparator() + { @Override public int compare(int left, int right) { @@ -487,14 +517,14 @@ public long getBufferedBytes() return directColumnWriter.getBufferedBytes(); } // for dictionary columns we report the data we expect to write to the output stream - return tempDictionaryIdDataStream.getBufferedBytes() + getDictionaryBytes(); + return getIndexBytes() + getDictionaryBytes(); } @Override public long getRetainedBytes() { long retainedBytes = INSTANCE_SIZE + - tempDictionaryIdDataStream.getRetainedBytes() + + values.sizeOf() + dataStream.getRetainedBytes() + presentStream.getRetainedBytes() + dictionaryDataStream.getRetainedBytes() + @@ -513,7 +543,6 @@ public void reset() { checkState(closed); closed = false; - tempDictionaryIdDataStream.reset(); dataStream.reset(); presentStream.reset(); dictionaryDataStream.reset(); @@ -541,18 +570,26 @@ private StringStatisticsBuilder newStringStatisticsBuilder() private static class DictionaryRowGroup { + private final IntBigArray dictionaryIndexes; private final int valueCount; private final ColumnStatistics columnStatistics; - public DictionaryRowGroup(int valueCount, ColumnStatistics columnStatistics) + public DictionaryRowGroup(IntBigArray dictionaryIndexes, int valueCount, ColumnStatistics columnStatistics) { + requireNonNull(dictionaryIndexes, "dictionaryIndexes is null"); checkArgument(valueCount >= 0, "valueCount is negative"); requireNonNull(columnStatistics, "columnStatistics is null"); + this.dictionaryIndexes = dictionaryIndexes; this.valueCount = valueCount; this.columnStatistics = columnStatistics; } + public IntBigArray getDictionaryIndexes() + { + return dictionaryIndexes; + } + public int getValueCount() { return valueCount; diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/BitStream.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/BitStream.java deleted file mode 100644 index 1805caa6c1b50..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/BitStream.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import static com.facebook.presto.orc.zstd.UnsafeUtil.UNSAFE; -import static com.facebook.presto.orc.zstd.Util.highestBit; -import static com.facebook.presto.orc.zstd.Util.verify; -import static io.airlift.slice.SizeOf.SIZE_OF_LONG; - -/** - * Bit streams are encoded as a byte-aligned little-endian stream. Thus, bits are laid out - * in the following manner, and the stream is read from right to left. - *

    - *

    - * ... [16 17 18 19 20 21 22 23] [8 9 10 11 12 13 14 15] [0 1 2 3 4 5 6 7] - */ -class BitStream -{ - private BitStream() - { - } - - public static boolean isEndOfStream(long startAddress, long currentAddress, int bitsConsumed) - { - return startAddress == currentAddress && bitsConsumed == Long.SIZE; - } - - static long readTail(Object inputBase, long inputAddress, int inputSize) - { - long bits = UNSAFE.getByte(inputBase, inputAddress) & 0xFF; - - switch (inputSize) { - case 7: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 6) & 0xFFL) << 48; - case 6: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 5) & 0xFFL) << 40; - case 5: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 4) & 0xFFL) << 32; - case 4: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 3) & 0xFFL) << 24; - case 3: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 2) & 0xFFL) << 16; - case 2: - bits |= (UNSAFE.getByte(inputBase, inputAddress + 1) & 0xFFL) << 8; - } - - return bits; - } - - /** - * @return numberOfBits in the low order bits of a long - */ - public static long peekBits(int bitsConsumed, long bitContainer, int numberOfBits) - { - return (((bitContainer << bitsConsumed) >>> 1) >>> (63 - numberOfBits)); - } - - /** - * numberOfBits must be > 0 - * - * @return numberOfBits in the low order bits of a long - */ - public static long peekBitsFast(int bitsConsumed, long bitContainer, int numberOfBits) - { - return ((bitContainer << bitsConsumed) >>> (64 - numberOfBits)); - } - - static class Initializer - { - private final Object inputBase; - private final long startAddress; - private final long endAddress; - private long bits; - private long currentAddress; - private int bitsConsumed; - - public Initializer(Object inputBase, long startAddress, long endAddress) - { - this.inputBase = inputBase; - this.startAddress = startAddress; - this.endAddress = endAddress; - } - - public long getBits() - { - return bits; - } - - public long getCurrentAddress() - { - return currentAddress; - } - - public int getBitsConsumed() - { - return bitsConsumed; - } - - public void initialize() - { - verify(endAddress - startAddress >= 1, startAddress, "Bitstream is empty"); - - int lastByte = UNSAFE.getByte(inputBase, endAddress - 1) & 0xFF; - verify(lastByte != 0, endAddress, "Bitstream end mark not present"); - - bitsConsumed = SIZE_OF_LONG - highestBit(lastByte); - - int inputSize = (int) (endAddress - startAddress); - if (inputSize >= SIZE_OF_LONG) { /* normal case */ - currentAddress = endAddress - SIZE_OF_LONG; - bits = UNSAFE.getLong(inputBase, currentAddress); - } - else { - currentAddress = startAddress; - bits = readTail(inputBase, startAddress, inputSize); - - bitsConsumed += (SIZE_OF_LONG - inputSize) * 8; - } - } - } - - static final class Loader - { - private final Object inputBase; - private final long startAddress; - private long bits; - private long currentAddress; - private int bitsConsumed; - private boolean overflow; - - public Loader(Object inputBase, long startAddress, long currentAddress, long bits, int bitsConsumed) - { - this.inputBase = inputBase; - this.startAddress = startAddress; - this.bits = bits; - this.currentAddress = currentAddress; - this.bitsConsumed = bitsConsumed; - } - - public long getBits() - { - return bits; - } - - public long getCurrentAddress() - { - return currentAddress; - } - - public int getBitsConsumed() - { - return bitsConsumed; - } - - public boolean isOverflow() - { - return overflow; - } - - public boolean load() - { - if (bitsConsumed > 64) { - overflow = true; - return true; - } - - else if (currentAddress == startAddress) { - return true; - } - - int bytes = bitsConsumed >>> 3; // divide by 8 - if (currentAddress >= startAddress + SIZE_OF_LONG) { - if (bytes > 0) { - currentAddress -= bytes; - bits = UNSAFE.getLong(inputBase, currentAddress); - } - bitsConsumed &= 0b111; - } - else if (currentAddress - bytes < startAddress) { - bytes = (int) (currentAddress - startAddress); - currentAddress = startAddress; - bitsConsumed -= bytes * SIZE_OF_LONG; - bits = UNSAFE.getLong(inputBase, startAddress); - return true; - } - else { - currentAddress -= bytes; - bitsConsumed -= bytes * SIZE_OF_LONG; - bits = UNSAFE.getLong(inputBase, currentAddress); - } - - return false; - } - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FiniteStateEntropy.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FiniteStateEntropy.java deleted file mode 100644 index 24b1deb24463d..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FiniteStateEntropy.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import static com.facebook.presto.orc.zstd.BitStream.peekBits; -import static com.facebook.presto.orc.zstd.FseTableReader.FSE_MAX_SYMBOL_VALUE; -import static com.facebook.presto.orc.zstd.UnsafeUtil.UNSAFE; -import static io.airlift.slice.SizeOf.SIZE_OF_INT; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - -class FiniteStateEntropy -{ - private static final int MAX_TABLE_LOG = 12; - - private final FiniteStateEntropy.Table table; - private final FseTableReader reader = new FseTableReader(); - - public FiniteStateEntropy(int maxLog) - { - table = new FiniteStateEntropy.Table(maxLog); - } - - public int decompress(final Object inputBase, final long inputAddress, final long inputLimit, byte[] weights) - { - long input = inputAddress; - input += reader.readFseTable(table, inputBase, input, inputLimit, FSE_MAX_SYMBOL_VALUE, MAX_TABLE_LOG); - - final Object outputBase = weights; - final long outputAddress = ARRAY_BYTE_BASE_OFFSET; - final long outputLimit = outputAddress + weights.length; - - long output = outputAddress; - - // initialize bit stream - BitStream.Initializer initializer = new BitStream.Initializer(inputBase, input, inputLimit); - initializer.initialize(); - int bitsConsumed = initializer.getBitsConsumed(); - long currentAddress = initializer.getCurrentAddress(); - long bits = initializer.getBits(); - - // initialize first FSE stream - int state1 = (int) peekBits(bitsConsumed, bits, table.log2Size); - bitsConsumed += table.log2Size; - - BitStream.Loader loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader.load(); - bits = loader.getBits(); - bitsConsumed = loader.getBitsConsumed(); - currentAddress = loader.getCurrentAddress(); - - // initialize second FSE stream - int state2 = (int) peekBits(bitsConsumed, bits, table.log2Size); - bitsConsumed += table.log2Size; - - loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader.load(); - bits = loader.getBits(); - bitsConsumed = loader.getBitsConsumed(); - currentAddress = loader.getCurrentAddress(); - - byte[] symbols = table.symbol; - byte[] numbersOfBits = table.numberOfBits; - int[] newStates = table.newState; - - // decode 4 symbols per loop - while (output < outputLimit) { - int numberOfBits; - - UNSAFE.putByte(outputBase, output, symbols[state1]); - numberOfBits = numbersOfBits[state1]; - state1 = (int) (newStates[state1] + peekBits(bitsConsumed, bits, numberOfBits)); - bitsConsumed += numberOfBits; - - UNSAFE.putByte(outputBase, output + 1, symbols[state2]); - numberOfBits = numbersOfBits[state2]; - state2 = (int) (newStates[state2] + peekBits(bitsConsumed, bits, numberOfBits)); - bitsConsumed += numberOfBits; - - UNSAFE.putByte(outputBase, output + 2, symbols[state1]); - numberOfBits = numbersOfBits[state1]; - state1 = (int) (newStates[state1] + peekBits(bitsConsumed, bits, numberOfBits)); - bitsConsumed += numberOfBits; - - UNSAFE.putByte(outputBase, output + 3, symbols[state2]); - numberOfBits = numbersOfBits[state2]; - state2 = (int) (newStates[state2] + peekBits(bitsConsumed, bits, numberOfBits)); - bitsConsumed += numberOfBits; - - output += SIZE_OF_INT; - - loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - boolean done = loader.load(); - bitsConsumed = loader.getBitsConsumed(); - bits = loader.getBits(); - currentAddress = loader.getCurrentAddress(); - if (done) { - break; - } - } - - while (true) { - UNSAFE.putByte(outputBase, output++, symbols[state1]); - int numberOfBits = numbersOfBits[state1]; - state1 = (int) (newStates[state1] + peekBits(bitsConsumed, bits, numberOfBits)); - bitsConsumed += numberOfBits; - - loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader.load(); - bitsConsumed = loader.getBitsConsumed(); - bits = loader.getBits(); - currentAddress = loader.getCurrentAddress(); - - if (loader.isOverflow()) { - UNSAFE.putByte(outputBase, output++, symbols[state2]); - break; - } - - UNSAFE.putByte(outputBase, output++, symbols[state2]); - int numberOfBits1 = numbersOfBits[state2]; - state2 = (int) (newStates[state2] + peekBits(bitsConsumed, bits, numberOfBits1)); - bitsConsumed += numberOfBits1; - - loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader.load(); - bitsConsumed = loader.getBitsConsumed(); - bits = loader.getBits(); - currentAddress = loader.getCurrentAddress(); - - if (loader.isOverflow()) { - UNSAFE.putByte(outputBase, output++, symbols[state1]); - break; - } - } - - return (int) (output - outputAddress); - } - - public static final class Table - { - int log2Size; - final int[] newState; - final byte[] symbol; - final byte[] numberOfBits; - - public Table(int log2Size) - { - int size = 1 << log2Size; - newState = new int[size]; - symbol = new byte[size]; - numberOfBits = new byte[size]; - } - - public Table(int log2Size, int[] newState, byte[] symbol, byte[] numberOfBits) - { - int size = 1 << log2Size; - if (newState.length != size || symbol.length != size || numberOfBits.length != size) { - throw new IllegalArgumentException("Expected arrays to match provided size"); - } - - this.log2Size = log2Size; - this.newState = newState; - this.symbol = symbol; - this.numberOfBits = numberOfBits; - } - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FseTableReader.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FseTableReader.java deleted file mode 100644 index 11977c4bdf603..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/FseTableReader.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import static com.facebook.presto.orc.zstd.UnsafeUtil.UNSAFE; -import static com.facebook.presto.orc.zstd.Util.highestBit; -import static com.facebook.presto.orc.zstd.Util.verify; - -class FseTableReader -{ - private static final int FSE_MIN_TABLE_LOG = 5; - - public static final int FSE_MAX_SYMBOL_VALUE = 255; - private final short[] nextSymbol = new short[FSE_MAX_SYMBOL_VALUE + 1]; - private final short[] normalizedCounters = new short[FSE_MAX_SYMBOL_VALUE + 1]; - - public int readFseTable(FiniteStateEntropy.Table table, Object inputBase, long inputAddress, long inputLimit, int maxSymbol, int maxTableLog) - { - // read table headers - long input = inputAddress; - verify(inputLimit - inputAddress >= 4, input, "Not enough input bytes"); - - int threshold; - int symbolNumber = 0; - boolean previousIsZero = false; - - int bitStream = UNSAFE.getInt(inputBase, input); - - int tableLog = (bitStream & 0xF) + FSE_MIN_TABLE_LOG; - - int numberOfBits = tableLog + 1; - bitStream >>>= 4; - int bitCount = 4; - - verify(tableLog <= maxTableLog, input, "FSE table size exceeds maximum allowed size"); - - int remaining = (1 << tableLog) + 1; - threshold = 1 << tableLog; - - while (remaining > 1 && symbolNumber <= maxSymbol) { - if (previousIsZero) { - int n0 = symbolNumber; - while ((bitStream & 0xFFFF) == 0xFFFF) { - n0 += 24; - if (input < inputLimit - 5) { - input += 2; - bitStream = (UNSAFE.getInt(inputBase, input) >>> bitCount); - } - else { - // end of bit stream - bitStream >>>= 16; - bitCount += 16; - } - } - while ((bitStream & 3) == 3) { - n0 += 3; - bitStream >>>= 2; - bitCount += 2; - } - n0 += bitStream & 3; - bitCount += 2; - - verify(n0 <= maxSymbol, input, "Symbol larger than max value"); - - while (symbolNumber < n0) { - normalizedCounters[symbolNumber++] = 0; - } - if ((input <= inputLimit - 7) || (input + (bitCount >>> 3) <= inputLimit - 4)) { - input += bitCount >>> 3; - bitCount &= 7; - bitStream = UNSAFE.getInt(inputBase, input) >>> bitCount; - } - else { - bitStream >>>= 2; - } - } - - short max = (short) ((2 * threshold - 1) - remaining); - short count; - - if ((bitStream & (threshold - 1)) < max) { - count = (short) (bitStream & (threshold - 1)); - bitCount += numberOfBits - 1; - } - else { - count = (short) (bitStream & (2 * threshold - 1)); - if (count >= threshold) { - count -= max; - } - bitCount += numberOfBits; - } - count--; // extra accuracy - - remaining -= Math.abs(count); - normalizedCounters[symbolNumber++] = count; - previousIsZero = count == 0; - while (remaining < threshold) { - numberOfBits--; - threshold >>>= 1; - } - - if ((input <= inputLimit - 7) || (input + (bitCount >> 3) <= inputLimit - 4)) { - input += bitCount >>> 3; - bitCount &= 7; - } - else { - bitCount -= (int) (8 * (inputLimit - 4 - input)); - input = inputLimit - 4; - } - bitStream = UNSAFE.getInt(inputBase, input) >>> (bitCount & 31); - } - - verify(remaining == 1 && bitCount <= 32, input, "Input is corrupted"); - - maxSymbol = symbolNumber - 1; - verify(maxSymbol <= FSE_MAX_SYMBOL_VALUE, input, "Max symbol value too large (too many symbols for FSE)"); - - input += (bitCount + 7) >> 3; - - // populate decoding table - int symbolCount = maxSymbol + 1; - int tableSize = 1 << tableLog; - int highThreshold = tableSize - 1; - - table.log2Size = tableLog; - - for (byte symbol = 0; symbol < symbolCount; symbol++) { - if (normalizedCounters[symbol] == -1) { - table.symbol[highThreshold--] = symbol; - nextSymbol[symbol] = 1; - } - else { - nextSymbol[symbol] = normalizedCounters[symbol]; - } - } - - // spread symbols - int tableMask = tableSize - 1; - int step = (tableSize >>> 1) + (tableSize >>> 3) + 3; - int position = 0; - for (byte symbol = 0; symbol < symbolCount; symbol++) { - for (int i = 0; i < normalizedCounters[symbol]; i++) { - table.symbol[position] = symbol; - do { - position = (position + step) & tableMask; - } - while (position > highThreshold); - } - } - - // position must reach all cells once, otherwise normalizedCounter is incorrect - verify(position == 0, input, "Input is corrupted"); - - for (int i = 0; i < tableSize; i++) { - byte symbol = table.symbol[i]; - short nextState = nextSymbol[symbol]++; - table.numberOfBits[i] = (byte) (tableLog - highestBit(nextState)); - table.newState[i] = (short) ((nextState << table.numberOfBits[i]) - tableSize); - } - - return (int) (input - inputAddress); - } - - public static void buildRleTable(FiniteStateEntropy.Table table, byte value) - { - table.log2Size = 0; - table.symbol[0] = value; - table.newState[0] = 0; - table.numberOfBits[0] = 0; - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Huffman.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Huffman.java deleted file mode 100644 index 9decf22292a12..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Huffman.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import java.util.Arrays; - -import static com.facebook.presto.orc.zstd.BitStream.isEndOfStream; -import static com.facebook.presto.orc.zstd.BitStream.peekBitsFast; -import static com.facebook.presto.orc.zstd.UnsafeUtil.UNSAFE; -import static com.facebook.presto.orc.zstd.Util.isPowerOf2; -import static com.facebook.presto.orc.zstd.Util.verify; -import static io.airlift.slice.SizeOf.SIZE_OF_INT; -import static io.airlift.slice.SizeOf.SIZE_OF_SHORT; - -class Huffman -{ - private static final int MAX_SYMBOL = 255; - private static final int MAX_TABLE_LOG = 12; - - // stats - private final byte[] weights = new byte[MAX_SYMBOL + 1]; - private final int[] ranks = new int[MAX_TABLE_LOG + 1]; - - // table - private int tableLog = -1; - private final byte[] symbols = new byte[1 << MAX_TABLE_LOG]; - private final byte[] numbersOfBits = new byte[1 << MAX_TABLE_LOG]; - - private final FiniteStateEntropy finiteStateEntropy = new FiniteStateEntropy(6); - - public boolean isLoaded() - { - return tableLog != -1; - } - - public int readTable(final Object inputBase, final long inputAddress, final int size) - { - Arrays.fill(ranks, 0); - long input = inputAddress; - - // read table header - verify(size > 0, input, "Not enough input bytes"); - int inputSize = UNSAFE.getByte(inputBase, input++) & 0xFF; - - int outputSize; - if (inputSize >= 128) { - outputSize = inputSize - 127; - inputSize = ((outputSize + 1) / 2); - - verify(inputSize + 1 <= size, input, "Not enough input bytes"); - verify(outputSize <= MAX_SYMBOL + 1, input, "Input is corrupted"); - - for (int i = 0; i < outputSize; i += 2) { - int value = UNSAFE.getByte(inputBase, input + i / 2) & 0xFF; - weights[i] = (byte) (value >>> 4); - weights[i + 1] = (byte) (value & 0b1111); - } - } - else { - verify(inputSize + 1 <= size, input, "Not enough input bytes"); - - outputSize = finiteStateEntropy.decompress(inputBase, input, input + inputSize, weights); - } - - int totalWeight = 0; - for (int i = 0; i < outputSize; i++) { - ranks[weights[i]]++; - totalWeight += (1 << weights[i]) >> 1; // TODO same as 1 << (weights[n] - 1)? - } - verify(totalWeight != 0, input, "Input is corrupted"); - - tableLog = Util.highestBit(totalWeight) + 1; - verify(tableLog <= MAX_TABLE_LOG, input, "Input is corrupted"); - - int total = 1 << tableLog; - int rest = total - totalWeight; - verify(isPowerOf2(rest), input, "Input is corrupted"); - - int lastWeight = Util.highestBit(rest) + 1; - - weights[outputSize] = (byte) lastWeight; - ranks[lastWeight]++; - - int numberOfSymbols = outputSize + 1; - - // populate table - int nextRankStart = 0; - for (int i = 1; i < tableLog + 1; ++i) { - int current = nextRankStart; - nextRankStart += ranks[i] << (i - 1); - ranks[i] = current; - } - - for (int n = 0; n < numberOfSymbols; n++) { - int weight = weights[n]; - int length = (1 << weight) >> 1; // TODO: 1 << (weight - 1) ?? - - byte symbol = (byte) n; - byte numberOfBits = (byte) (tableLog + 1 - weight); - for (int i = ranks[weight]; i < ranks[weight] + length; i++) { - symbols[i] = symbol; - numbersOfBits[i] = numberOfBits; - } - ranks[weight] += length; - } - - verify(ranks[1] >= 2 && (ranks[1] & 1) == 0, input, "Input is corrupted"); - - return inputSize + 1; - } - - public void decodeSingleStream(final Object inputBase, final long inputAddress, final long inputLimit, final Object outputBase, final long outputAddress, final long outputLimit) - { - BitStream.Initializer initializer = new BitStream.Initializer(inputBase, inputAddress, inputLimit); - initializer.initialize(); - - long bits = initializer.getBits(); - int bitsConsumed = initializer.getBitsConsumed(); - long currentAddress = initializer.getCurrentAddress(); - - int tableLog = this.tableLog; - byte[] numbersOfBits = this.numbersOfBits; - byte[] symbols = this.symbols; - - // 4 symbols at a time - long output = outputAddress; - long fastOutputLimit = outputLimit - 4; - while (output < fastOutputLimit) { - BitStream.Loader loader = new BitStream.Loader(inputBase, inputAddress, currentAddress, bits, bitsConsumed); - boolean done = loader.load(); - bits = loader.getBits(); - bitsConsumed = loader.getBitsConsumed(); - currentAddress = loader.getCurrentAddress(); - if (done) { - break; - } - - bitsConsumed = decodeSymbol(outputBase, output, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - bitsConsumed = decodeSymbol(outputBase, output + 1, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - bitsConsumed = decodeSymbol(outputBase, output + 2, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - bitsConsumed = decodeSymbol(outputBase, output + 3, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - output += SIZE_OF_INT; - } - - decodeTail(inputBase, inputAddress, currentAddress, bitsConsumed, bits, outputBase, output, outputLimit); - } - - public void decode4Streams(final Object inputBase, final long inputAddress, final long inputLimit, final Object outputBase, final long outputAddress, final long outputLimit) - { - verify(inputLimit - inputAddress >= 10, inputAddress, "Input is corrupted"); // jump table + 1 byte per stream - - long start1 = inputAddress + 3 * SIZE_OF_SHORT; // for the shorts we read below - long start2 = start1 + (UNSAFE.getShort(inputBase, inputAddress) & 0xFFFF); - long start3 = start2 + (UNSAFE.getShort(inputBase, inputAddress + 2) & 0xFFFF); - long start4 = start3 + (UNSAFE.getShort(inputBase, inputAddress + 4) & 0xFFFF); - - BitStream.Initializer initializer = new BitStream.Initializer(inputBase, start1, start2); - initializer.initialize(); - int stream1bitsConsumed = initializer.getBitsConsumed(); - long stream1currentAddress = initializer.getCurrentAddress(); - long stream1bits = initializer.getBits(); - - initializer = new BitStream.Initializer(inputBase, start2, start3); - initializer.initialize(); - int stream2bitsConsumed = initializer.getBitsConsumed(); - long stream2currentAddress = initializer.getCurrentAddress(); - long stream2bits = initializer.getBits(); - - initializer = new BitStream.Initializer(inputBase, start3, start4); - initializer.initialize(); - int stream3bitsConsumed = initializer.getBitsConsumed(); - long stream3currentAddress = initializer.getCurrentAddress(); - long stream3bits = initializer.getBits(); - - initializer = new BitStream.Initializer(inputBase, start4, inputLimit); - initializer.initialize(); - int stream4bitsConsumed = initializer.getBitsConsumed(); - long stream4currentAddress = initializer.getCurrentAddress(); - long stream4bits = initializer.getBits(); - - int segmentSize = (int) ((outputLimit - outputAddress + 3) / 4); - - long outputStart2 = outputAddress + segmentSize; - long outputStart3 = outputStart2 + segmentSize; - long outputStart4 = outputStart3 + segmentSize; - - long output1 = outputAddress; - long output2 = outputStart2; - long output3 = outputStart3; - long output4 = outputStart4; - - long fastOutputLimit = outputLimit - 7; - int tableLog = this.tableLog; - byte[] numbersOfBits = this.numbersOfBits; - byte[] symbols = this.symbols; - - while (output4 < fastOutputLimit) { - stream1bitsConsumed = decodeSymbol(outputBase, output1, stream1bits, stream1bitsConsumed, tableLog, numbersOfBits, symbols); - stream2bitsConsumed = decodeSymbol(outputBase, output2, stream2bits, stream2bitsConsumed, tableLog, numbersOfBits, symbols); - stream3bitsConsumed = decodeSymbol(outputBase, output3, stream3bits, stream3bitsConsumed, tableLog, numbersOfBits, symbols); - stream4bitsConsumed = decodeSymbol(outputBase, output4, stream4bits, stream4bitsConsumed, tableLog, numbersOfBits, symbols); - - stream1bitsConsumed = decodeSymbol(outputBase, output1 + 1, stream1bits, stream1bitsConsumed, tableLog, numbersOfBits, symbols); - stream2bitsConsumed = decodeSymbol(outputBase, output2 + 1, stream2bits, stream2bitsConsumed, tableLog, numbersOfBits, symbols); - stream3bitsConsumed = decodeSymbol(outputBase, output3 + 1, stream3bits, stream3bitsConsumed, tableLog, numbersOfBits, symbols); - stream4bitsConsumed = decodeSymbol(outputBase, output4 + 1, stream4bits, stream4bitsConsumed, tableLog, numbersOfBits, symbols); - - stream1bitsConsumed = decodeSymbol(outputBase, output1 + 2, stream1bits, stream1bitsConsumed, tableLog, numbersOfBits, symbols); - stream2bitsConsumed = decodeSymbol(outputBase, output2 + 2, stream2bits, stream2bitsConsumed, tableLog, numbersOfBits, symbols); - stream3bitsConsumed = decodeSymbol(outputBase, output3 + 2, stream3bits, stream3bitsConsumed, tableLog, numbersOfBits, symbols); - stream4bitsConsumed = decodeSymbol(outputBase, output4 + 2, stream4bits, stream4bitsConsumed, tableLog, numbersOfBits, symbols); - - stream1bitsConsumed = decodeSymbol(outputBase, output1 + 3, stream1bits, stream1bitsConsumed, tableLog, numbersOfBits, symbols); - stream2bitsConsumed = decodeSymbol(outputBase, output2 + 3, stream2bits, stream2bitsConsumed, tableLog, numbersOfBits, symbols); - stream3bitsConsumed = decodeSymbol(outputBase, output3 + 3, stream3bits, stream3bitsConsumed, tableLog, numbersOfBits, symbols); - stream4bitsConsumed = decodeSymbol(outputBase, output4 + 3, stream4bits, stream4bitsConsumed, tableLog, numbersOfBits, symbols); - - output1 += SIZE_OF_INT; - output2 += SIZE_OF_INT; - output3 += SIZE_OF_INT; - output4 += SIZE_OF_INT; - - BitStream.Loader loader = new BitStream.Loader(inputBase, start1, stream1currentAddress, stream1bits, stream1bitsConsumed); - boolean done = loader.load(); - stream1bitsConsumed = loader.getBitsConsumed(); - stream1bits = loader.getBits(); - stream1currentAddress = loader.getCurrentAddress(); - - if (done) { - break; - } - - loader = new BitStream.Loader(inputBase, start2, stream2currentAddress, stream2bits, stream2bitsConsumed); - done = loader.load(); - stream2bitsConsumed = loader.getBitsConsumed(); - stream2bits = loader.getBits(); - stream2currentAddress = loader.getCurrentAddress(); - - if (done) { - break; - } - - loader = new BitStream.Loader(inputBase, start3, stream3currentAddress, stream3bits, stream3bitsConsumed); - done = loader.load(); - stream3bitsConsumed = loader.getBitsConsumed(); - stream3bits = loader.getBits(); - stream3currentAddress = loader.getCurrentAddress(); - if (done) { - break; - } - - loader = new BitStream.Loader(inputBase, start4, stream4currentAddress, stream4bits, stream4bitsConsumed); - done = loader.load(); - stream4bitsConsumed = loader.getBitsConsumed(); - stream4bits = loader.getBits(); - stream4currentAddress = loader.getCurrentAddress(); - if (done) { - break; - } - } - - verify(output1 <= outputStart2 && output2 <= outputStart3 && output3 <= outputStart4, inputAddress, "Input is corrupted"); - - /// finish streams one by one - decodeTail(inputBase, start1, stream1currentAddress, stream1bitsConsumed, stream1bits, outputBase, output1, outputStart2); - decodeTail(inputBase, start2, stream2currentAddress, stream2bitsConsumed, stream2bits, outputBase, output2, outputStart3); - decodeTail(inputBase, start3, stream3currentAddress, stream3bitsConsumed, stream3bits, outputBase, output3, outputStart4); - decodeTail(inputBase, start4, stream4currentAddress, stream4bitsConsumed, stream4bits, outputBase, output4, outputLimit); - } - - private void decodeTail(final Object inputBase, final long startAddress, long currentAddress, int bitsConsumed, long bits, final Object outputBase, long outputAddress, final long outputLimit) - { - int tableLog = this.tableLog; - byte[] numbersOfBits = this.numbersOfBits; - byte[] symbols = this.symbols; - - // closer to the end - while (outputAddress < outputLimit) { - BitStream.Loader loader = new BitStream.Loader(inputBase, startAddress, currentAddress, bits, bitsConsumed); - boolean done = loader.load(); - bitsConsumed = loader.getBitsConsumed(); - bits = loader.getBits(); - currentAddress = loader.getCurrentAddress(); - if (done) { - break; - } - - bitsConsumed = decodeSymbol(outputBase, outputAddress++, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - } - - // not more data in bit stream, so no need to reload - while (outputAddress < outputLimit) { - bitsConsumed = decodeSymbol(outputBase, outputAddress++, bits, bitsConsumed, tableLog, numbersOfBits, symbols); - } - - verify(isEndOfStream(startAddress, currentAddress, bitsConsumed), startAddress, "Bit stream is not fully consumed"); - } - - private static int decodeSymbol(Object outputBase, long outputAddress, long bitContainer, int bitsConsumed, int tableLog, byte[] numbersOfBits, byte[] symbols) - { - int value = (int) peekBitsFast(bitsConsumed, bitContainer, tableLog); - UNSAFE.putByte(outputBase, outputAddress, symbols[value]); - return bitsConsumed + numbersOfBits[value]; - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/UnsafeUtil.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/UnsafeUtil.java deleted file mode 100644 index fb91cb2ab780f..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/UnsafeUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.nio.Buffer; - -final class UnsafeUtil -{ - public static final Unsafe UNSAFE; - private static final Field ADDRESS_ACCESSOR; - - private UnsafeUtil() {} - - static { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - UNSAFE = (Unsafe) theUnsafe.get(null); - } - catch (Exception e) { - throw new RuntimeException(e); - } - - try { - Field field = Buffer.class.getDeclaredField("address"); - field.setAccessible(true); - ADDRESS_ACCESSOR = field; - } - catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static long getAddress(Buffer buffer) - { - try { - return (long) ADDRESS_ACCESSOR.get(buffer); - } - catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Util.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Util.java deleted file mode 100644 index aeb24366dfb6d..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/Util.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import io.airlift.compress.MalformedInputException; - -class Util -{ - private Util() - { - } - - public static int highestBit(int value) - { - return 31 - Integer.numberOfLeadingZeros(value); - } - - public static boolean isPowerOf2(int value) - { - return (value & (value - 1)) == 0; - } - - public static int mask(int bits) - { - return (1 << bits) - 1; - } - - public static void verify(boolean condition, long offset, String reason) - { - if (!condition) { - throw new MalformedInputException(offset, reason); - } - } - - public static MalformedInputException fail(long offset, String reason) - { - throw new MalformedInputException(offset, reason); - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdDecompressor.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdDecompressor.java deleted file mode 100644 index 9284364c3f29e..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdDecompressor.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import io.airlift.compress.Decompressor; -import io.airlift.compress.MalformedInputException; - -import java.nio.ByteBuffer; - -import static com.facebook.presto.orc.zstd.UnsafeUtil.getAddress; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - -public class ZstdDecompressor - implements Decompressor -{ - private final ZstdFrameDecompressor decompressor = new ZstdFrameDecompressor(); - - @Override - public int decompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength) - throws MalformedInputException - { - long inputAddress = ARRAY_BYTE_BASE_OFFSET + inputOffset; - long inputLimit = inputAddress + inputLength; - long outputAddress = ARRAY_BYTE_BASE_OFFSET + outputOffset; - long outputLimit = outputAddress + maxOutputLength; - - return decompressor.decompress(input, inputAddress, inputLimit, output, outputAddress, outputLimit); - } - - @Override - public void decompress(ByteBuffer input, ByteBuffer output) - throws MalformedInputException - { - Object inputBase; - long inputAddress; - long inputLimit; - if (input.isDirect()) { - inputBase = null; - long address = getAddress(input); - inputAddress = address + input.position(); - inputLimit = address + input.limit(); - } - else if (input.hasArray()) { - inputBase = input.array(); - inputAddress = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.position(); - inputLimit = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.limit(); - } - else { - throw new IllegalArgumentException("Unsupported input ByteBuffer implementation " + input.getClass().getName()); - } - - Object outputBase; - long outputAddress; - long outputLimit; - if (output.isDirect()) { - outputBase = null; - long address = getAddress(output); - outputAddress = address + output.position(); - outputLimit = address + output.limit(); - } - else if (output.hasArray()) { - outputBase = output.array(); - outputAddress = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.position(); - outputLimit = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.limit(); - } - else { - throw new IllegalArgumentException("Unsupported output ByteBuffer implementation " + output.getClass().getName()); - } - - // HACK: Assure JVM does not collect Slice wrappers while decompressing, since the - // collection may trigger freeing of the underlying memory resulting in a segfault - // There is no other known way to signal to the JVM that an object should not be - // collected in a block, and technically, the JVM is allowed to eliminate these locks. - synchronized (input) { - synchronized (output) { - int written = new ZstdFrameDecompressor().decompress(inputBase, inputAddress, inputLimit, outputBase, outputAddress, outputLimit); - output.position(output.position() + written); - } - } - } - - public static long getDecompressedSize(byte[] input, int offset, int length) - { - int baseAddress = ARRAY_BYTE_BASE_OFFSET + offset; - return ZstdFrameDecompressor.getDecompressedSize(input, baseAddress, baseAddress + length); - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdFrameDecompressor.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdFrameDecompressor.java deleted file mode 100644 index 5a7e6a5345598..0000000000000 --- a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdFrameDecompressor.java +++ /dev/null @@ -1,968 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.orc.zstd; - -import io.airlift.compress.MalformedInputException; -import io.airlift.slice.Slice; -import io.airlift.slice.UnsafeSliceFactory; -import io.airlift.slice.XxHash64; - -import java.util.Arrays; - -import static com.facebook.presto.orc.zstd.BitStream.peekBits; -import static com.facebook.presto.orc.zstd.UnsafeUtil.UNSAFE; -import static com.facebook.presto.orc.zstd.Util.fail; -import static com.facebook.presto.orc.zstd.Util.mask; -import static com.facebook.presto.orc.zstd.Util.verify; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; - -class ZstdFrameDecompressor -{ - private static final int MIN_MATCH = 3; - - private static final int[] DEC_32_TABLE = {4, 1, 2, 1, 4, 4, 4, 4}; - private static final int[] DEC_64_TABLE = {0, 0, 0, -1, 0, 1, 2, 3}; - - private static final int MAGIC_NUMBER = 0xFD2FB528; // v0.5 - - private static final int MIN_SEQUENCES_SIZE = 1; - private static final int MIN_BLOCK_SIZE = 1 // block type tag - + 1 // min size of raw or rle length header - + MIN_SEQUENCES_SIZE; - - private static final int MAX_BLOCK_SIZE = 128 * 1024; - - private static final int MIN_WINDOW_LOG = 10; - private static final int MAX_WINDOW_SIZE = 1 << 23; - - public static final int SIZE_OF_BYTE = 1; - public static final int SIZE_OF_SHORT = 2; - public static final int SIZE_OF_INT = 4; - public static final int SIZE_OF_LONG = 8; - - private static final long SIZE_OF_BLOCK_HEADER = 3; - - // block types - private static final int RAW_BLOCK = 0; - private static final int RLE_BLOCK = 1; - private static final int COMPRESSED_BLOCK = 2; - - // literal block types - private static final int RAW_LITERALS_BLOCK = 0; - private static final int RLE_LITERALS_BLOCK = 1; - private static final int COMPRESSED_LITERALS_BLOCK = 2; - private static final int REPEAT_STATS_LITERALS_BLOCK = 3; - - private static final int LONG_NUMBER_OF_SEQUENCES = 0x7F00; - - private static final int MAX_LITERALS_LENGTH_SYMBOL = 35; - private static final int MAX_MATCH_LENGTH_SYMBOL = 52; - private static final int MAX_OFFSET_CODE_SYMBOL = 28; - - private static final int LITERALS_LENGTH_FSE_LOG = 9; - private static final int MATCH_LENGTH_FSE_LOG = 9; - private static final int OFFSET_CODES_FSE_LOG = 8; - - private static final int SET_BASIC = 0; - private static final int SET_RLE = 1; - private static final int SET_COMPRESSED = 2; - private static final int SET_REPEAT = 3; - - private static final int[] LITERALS_LENGTH_BASE = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 18, 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, - 0x2000, 0x4000, 0x8000, 0x10000}; - - private static final int[] MATCH_LENGTH_BASE = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, - 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; - - private static final int[] OFFSET_CODES_BASE = { - 0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, - 0xFD, 0x1FD, 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, - 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, - 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; - - private static final int[] LITERALS_LENGTH_BITS = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16}; - - private static final int[] MATCH_LENGTH_BITS = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16}; - - private static final FiniteStateEntropy.Table DEFAULT_LITERALS_LENGTH_TABLE = new FiniteStateEntropy.Table( - 6, - new int[] { - 0, 16, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 32, 0, 0, 32, 0, 32, 0, 32, 0, 0, 32, 0, 32, 0, 32, 0, 0, 16, 32, 0, 0, 48, 16, 32, 32, 32, - 32, 32, 32, 32, 32, 0, 32, 32, 32, 32, 32, 32, 0, 0, 0, 0}, - new byte[] { - 0, 0, 1, 3, 4, 6, 7, 9, 10, 12, 14, 16, 18, 19, 21, 22, 24, 25, 26, 27, 29, 31, 0, 1, 2, 4, 5, 7, 8, 10, 11, 13, 16, 17, 19, 20, 22, 23, 25, 25, 26, 28, 30, 0, - 1, 2, 3, 5, 6, 8, 9, 11, 12, 15, 17, 18, 20, 21, 23, 24, 35, 34, 33, 32}, - new byte[] { - 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 4, 4, 5, 6, 6, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6}); - - private static final FiniteStateEntropy.Table DEFAULT_OFFSET_CODES_TABLE = new FiniteStateEntropy.Table( - 5, - new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0}, - new byte[] {0, 6, 9, 15, 21, 3, 7, 12, 18, 23, 5, 8, 14, 20, 2, 7, 11, 17, 22, 4, 8, 13, 19, 1, 6, 10, 16, 28, 27, 26, 25, 24}, - new byte[] {5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5}); - - private static final FiniteStateEntropy.Table DEFAULT_MATCH_LENGTH_TABLE = new FiniteStateEntropy.Table( - 6, - new int[] { - 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 48, 16, 32, 32, 32, 32, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - new byte[] { - 0, 1, 2, 3, 5, 6, 8, 10, 13, 16, 19, 22, 25, 28, 31, 33, 35, 37, 39, 41, 43, 45, 1, 2, 3, 4, 6, 7, 9, 12, 15, 18, 21, 24, 27, 30, 32, 34, 36, 38, 40, 42, 44, 1, - 1, 2, 4, 5, 7, 8, 11, 14, 17, 20, 23, 26, 29, 52, 51, 50, 49, 48, 47, 46}, - new byte[] { - 6, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}); - - private final byte[] literals = new byte[MAX_BLOCK_SIZE + SIZE_OF_LONG]; // extra space to allow for long-at-a-time copy - - // current buffer containing literals - private Object literalsBase; - private long literalsAddress; - private long literalsLimit; - - private final int[] previousOffsets = new int[3]; - - private final FiniteStateEntropy.Table literalsLengthTable = new FiniteStateEntropy.Table(LITERALS_LENGTH_FSE_LOG); - private final FiniteStateEntropy.Table offsetCodesTable = new FiniteStateEntropy.Table(OFFSET_CODES_FSE_LOG); - private final FiniteStateEntropy.Table matchLengthTable = new FiniteStateEntropy.Table(MATCH_LENGTH_FSE_LOG); - - private FiniteStateEntropy.Table currentLiteralsLengthTable; - private FiniteStateEntropy.Table currentOffsetCodesTable; - private FiniteStateEntropy.Table currentMatchLengthTable; - - private final Huffman huffman = new Huffman(); - private final FseTableReader fse = new FseTableReader(); - - public int decompress( - final Object inputBase, - final long inputAddress, - final long inputLimit, - final Object outputBase, - final long outputAddress, - final long outputLimit) - { - if (outputAddress == outputLimit) { - return 0; - } - - reset(); - - long input = inputAddress; - long output = outputAddress; - - input += verifyMagic(inputBase, inputAddress, inputLimit); - - FrameHeader frameHeader = readFrameHeader(inputBase, input, inputLimit); - input += frameHeader.headerSize; - - boolean lastBlock; - do { - verify(input + SIZE_OF_BLOCK_HEADER <= inputLimit, input, "Not enough input bytes"); - - // read block header - int header = UNSAFE.getInt(inputBase, input) & 0xFF_FFFF; - input += SIZE_OF_BLOCK_HEADER; - - lastBlock = (header & 1) != 0; - int blockType = (header >>> 1) & 0b11; - int blockSize = (header >>> 3) & 0x1F_FFFF; // 21 bits - - int decodedSize; - switch (blockType) { - case RAW_BLOCK: - verify(inputAddress + blockSize <= inputLimit, input, "Not enough input bytes"); - decodedSize = decodeRawBlock(inputBase, input, blockSize, outputBase, output, outputLimit); - input += blockSize; - break; - case RLE_BLOCK: - verify(inputAddress + 1 <= inputLimit, input, "Not enough input bytes"); - decodedSize = decodeRleBlock(blockSize, inputBase, input, outputBase, output, outputLimit); - input += 1; - break; - case COMPRESSED_BLOCK: - verify(inputAddress + blockSize <= inputLimit, input, "Not enough input bytes"); - decodedSize = decodeCompressedBlock(inputBase, input, blockSize, outputBase, output, outputLimit, frameHeader.windowSize); - input += blockSize; - break; - default: - throw fail(input, "Invalid block type"); - } - - output += decodedSize; - } - while (!lastBlock); - - if (frameHeader.hasChecksum) { - Slice outputSlice = UnsafeSliceFactory.getInstance().newSlice(outputBase, outputAddress, (int) (outputLimit - outputAddress)); - long hash = XxHash64.hash(0, outputSlice); - - int checksum = UNSAFE.getInt(inputBase, input); - if (checksum != (int) hash) { - throw new MalformedInputException(input, String.format("Bad checksum. Expected: %s, actual: %s", Integer.toHexString(checksum), Integer.toHexString((int) hash))); - } - } - - return (int) (output - outputAddress); - } - - private void reset() - { - previousOffsets[0] = 1; - previousOffsets[1] = 4; - previousOffsets[2] = 8; - - currentLiteralsLengthTable = null; - currentOffsetCodesTable = null; - currentMatchLengthTable = null; - } - - private static int decodeRawBlock(Object inputBase, long inputAddress, int blockSize, Object outputBase, long outputAddress, long outputLimit) - { - verify(outputAddress + blockSize <= outputLimit, inputAddress, "Output buffer too small"); - - UNSAFE.copyMemory(inputBase, inputAddress, outputBase, outputAddress, blockSize); - return blockSize; - } - - private static int decodeRleBlock(int size, Object inputBase, long inputAddress, Object outputBase, long outputAddress, long outputLimit) - { - verify(outputAddress + size <= outputLimit, inputAddress, "Output buffer too small"); - - long output = outputAddress; - long value = UNSAFE.getByte(inputBase, inputAddress) & 0xFFL; - - int remaining = size; - if (remaining >= SIZE_OF_LONG) { - long packed = value - | (value << 8) - | (value << 16) - | (value << 24) - | (value << 32) - | (value << 40) - | (value << 48) - | (value << 56); - - do { - UNSAFE.putLong(outputBase, output, packed); - output += SIZE_OF_LONG; - remaining -= SIZE_OF_LONG; - } - while (remaining >= SIZE_OF_LONG); - } - - for (int i = 0; i < remaining; i++) { - UNSAFE.putByte(outputBase, output, (byte) value); - output++; - } - - return size; - } - - private int decodeCompressedBlock(Object inputBase, final long inputAddress, int blockSize, Object outputBase, long outputAddress, long outputLimit, int windowSize) - { - long inputLimit = inputAddress + blockSize; - long input = inputAddress; - - verify(blockSize <= MAX_BLOCK_SIZE, input, "Expected match length table to be present"); - verify(blockSize >= MIN_BLOCK_SIZE, input, "Compressed block size too small"); - - // decode literals - int literalsBlockType = UNSAFE.getByte(inputBase, input) & 0b11; - - switch (literalsBlockType) { - case RAW_LITERALS_BLOCK: { - input += decodeRawLiterals(inputBase, input, inputLimit); - break; - } - case RLE_LITERALS_BLOCK: { - input += decodeRleLiterals(inputBase, input, blockSize); - break; - } - case REPEAT_STATS_LITERALS_BLOCK: - verify(huffman.isLoaded(), input, "Dictionary is corrupted"); - case COMPRESSED_LITERALS_BLOCK: { - input += decodeCompressedLiterals(inputBase, input, blockSize, literalsBlockType); - break; - } - default: - throw fail(input, "Invalid literals block encoding type"); - } - - verify(windowSize <= MAX_WINDOW_SIZE, input, "Window size too large (not yet supported)"); - - return decompressSequences( - inputBase, input, inputAddress + blockSize, - outputBase, outputAddress, outputLimit, - literalsBase, literalsAddress, literalsLimit); - } - - private int decompressSequences( - final Object inputBase, final long inputAddress, final long inputLimit, - final Object outputBase, final long outputAddress, final long outputLimit, - final Object literalsBase, final long literalsAddress, final long literalsLimit) - { - final long fastOutputLimit = outputLimit - SIZE_OF_LONG; - - long input = inputAddress; - long output = outputAddress; - - long literalsInput = literalsAddress; - - int size = (int) (inputLimit - inputAddress); - verify(size >= MIN_SEQUENCES_SIZE, input, "Not enough input bytes"); - - // decode header - int sequenceCount = UNSAFE.getByte(inputBase, input++) & 0xFF; - if (sequenceCount != 0) { - if (sequenceCount == 255) { - verify(input + SIZE_OF_SHORT <= inputLimit, input, "Not enough input bytes"); - sequenceCount = (UNSAFE.getShort(inputBase, input) & 0xFFFF) + LONG_NUMBER_OF_SEQUENCES; - input += SIZE_OF_SHORT; - } - else if (sequenceCount > 127) { - verify(input < inputLimit, input, "Not enough input bytes"); - sequenceCount = ((sequenceCount - 128) << 8) + (UNSAFE.getByte(inputBase, input++) & 0xFF); - } - - verify(input + SIZE_OF_INT <= inputLimit, input, "Not enough input bytes"); - - byte type = UNSAFE.getByte(inputBase, input++); - - int literalsLengthType = (type & 0xFF) >>> 6; - int offsetCodesType = (type >>> 4) & 0b11; - int matchLengthType = (type >>> 2) & 0b11; - - input = computeLiteralsTable(literalsLengthType, inputBase, input, inputLimit); - input = computeOffsetsTable(offsetCodesType, inputBase, input, inputLimit); - input = computeMatchLengthTable(matchLengthType, inputBase, input, inputLimit); - - // decompress sequences - BitStream.Initializer initializer = new BitStream.Initializer(inputBase, input, inputLimit); - initializer.initialize(); - int bitsConsumed = initializer.getBitsConsumed(); - long bits = initializer.getBits(); - long currentAddress = initializer.getCurrentAddress(); - - FiniteStateEntropy.Table currentLiteralsLengthTable = this.currentLiteralsLengthTable; - FiniteStateEntropy.Table currentOffsetCodesTable = this.currentOffsetCodesTable; - FiniteStateEntropy.Table currentMatchLengthTable = this.currentMatchLengthTable; - - int literalsLengthState = (int) peekBits(bitsConsumed, bits, currentLiteralsLengthTable.log2Size); - bitsConsumed += currentLiteralsLengthTable.log2Size; - - int offsetCodesState = (int) peekBits(bitsConsumed, bits, currentOffsetCodesTable.log2Size); - bitsConsumed += currentOffsetCodesTable.log2Size; - - int matchLengthState = (int) peekBits(bitsConsumed, bits, currentMatchLengthTable.log2Size); - bitsConsumed += currentMatchLengthTable.log2Size; - - int[] previousOffsets = this.previousOffsets; - - byte[] literalsLengthNumbersOfBits = currentLiteralsLengthTable.numberOfBits; - int[] literalsLengthNewStates = currentLiteralsLengthTable.newState; - byte[] literalsLengthSymbols = currentLiteralsLengthTable.symbol; - - byte[] matchLengthNumbersOfBits = currentMatchLengthTable.numberOfBits; - int[] matchLengthNewStates = currentMatchLengthTable.newState; - byte[] matchLengthSymbols = currentMatchLengthTable.symbol; - - byte[] offsetCodesNumbersOfBits = currentOffsetCodesTable.numberOfBits; - int[] offsetCodesNewStates = currentOffsetCodesTable.newState; - byte[] offsetCodesSymbols = currentOffsetCodesTable.symbol; - - while (sequenceCount > 0) { - sequenceCount--; - - BitStream.Loader loader = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader.load(); - bitsConsumed = loader.getBitsConsumed(); - bits = loader.getBits(); - currentAddress = loader.getCurrentAddress(); - if (loader.isOverflow()) { - verify(sequenceCount == 0, input, "Not all sequences were consumed"); - break; - } - - // decode sequence - int literalsLengthCode = literalsLengthSymbols[literalsLengthState]; - int matchLengthCode = matchLengthSymbols[matchLengthState]; - int offsetCode = offsetCodesSymbols[offsetCodesState]; - - int literalsLengthBits = LITERALS_LENGTH_BITS[literalsLengthCode]; - int matchLengthBits = MATCH_LENGTH_BITS[matchLengthCode]; - int offsetBits = offsetCode; - - int offset = OFFSET_CODES_BASE[offsetCode]; - if (offsetCode > 0) { - offset += peekBits(bitsConsumed, bits, offsetBits); - bitsConsumed += offsetBits; - } - - if (offsetCode <= 1) { - if (literalsLengthCode == 0) { - offset++; - } - - if (offset != 0) { - int temp; - if (offset == 3) { - temp = previousOffsets[0] - 1; - } - else { - temp = previousOffsets[offset]; - } - - if (temp == 0) { - temp = 1; - } - - if (offset != 1) { - previousOffsets[2] = previousOffsets[1]; - } - previousOffsets[1] = previousOffsets[0]; - previousOffsets[0] = temp; - - offset = temp; - } - else { - offset = previousOffsets[0]; - } - } - else { - previousOffsets[2] = previousOffsets[1]; - previousOffsets[1] = previousOffsets[0]; - previousOffsets[0] = offset; - } - - int matchLength = MATCH_LENGTH_BASE[matchLengthCode]; - if (matchLengthCode > 31) { - matchLength += peekBits(bitsConsumed, bits, matchLengthBits); - bitsConsumed += matchLengthBits; - } - - int literalsLength = LITERALS_LENGTH_BASE[literalsLengthCode]; - if (literalsLengthCode > 15) { - literalsLength += peekBits(bitsConsumed, bits, literalsLengthBits); - bitsConsumed += literalsLengthBits; - } - - int totalBits = literalsLengthBits + matchLengthBits + offsetBits; - if (totalBits > 64 - 7 - (LITERALS_LENGTH_FSE_LOG + MATCH_LENGTH_FSE_LOG + OFFSET_CODES_FSE_LOG)) { - BitStream.Loader loader1 = new BitStream.Loader(inputBase, input, currentAddress, bits, bitsConsumed); - loader1.load(); - - bitsConsumed = loader1.getBitsConsumed(); - bits = loader1.getBits(); - currentAddress = loader1.getCurrentAddress(); - } - - int numberOfBits; - - numberOfBits = literalsLengthNumbersOfBits[literalsLengthState]; - literalsLengthState = (int) (literalsLengthNewStates[literalsLengthState] + peekBits(bitsConsumed, bits, numberOfBits)); // <= 9 bits - bitsConsumed += numberOfBits; - - numberOfBits = matchLengthNumbersOfBits[matchLengthState]; - matchLengthState = (int) (matchLengthNewStates[matchLengthState] + peekBits(bitsConsumed, bits, numberOfBits)); // <= 9 bits - bitsConsumed += numberOfBits; - - numberOfBits = offsetCodesNumbersOfBits[offsetCodesState]; - offsetCodesState = (int) (offsetCodesNewStates[offsetCodesState] + peekBits(bitsConsumed, bits, numberOfBits)); // <= 8 bits - bitsConsumed += numberOfBits; - - final long literalOutputLimit = output + literalsLength; - final long matchOutputLimit = literalOutputLimit + matchLength; - - verify(matchOutputLimit <= outputLimit, input, "Output buffer too small"); - verify(literalsInput + literalsLength <= literalsLimit, input, "Input is corrupted"); - - long matchAddress = literalOutputLimit - offset; - - if (literalOutputLimit > fastOutputLimit) { - executeLastSequence(outputBase, output, literalOutputLimit, matchOutputLimit, fastOutputLimit, literalsInput, matchAddress); - } - else { - // copy literals. literalOutputLimit <= fastOutputLimit, so we can copy - // long at a time with over-copy - output = copyLiterals(outputBase, literalsBase, output, literalsInput, literalOutputLimit); - copyMatch(outputBase, fastOutputLimit, output, offset, matchOutputLimit, matchAddress); - } - output = matchOutputLimit; - literalsInput += literalsLength; - } - } - - // last literal segment - output = copyLastLiteral(outputBase, literalsBase, literalsLimit, output, literalsInput); - - return (int) (output - outputAddress); - } - - private long copyLastLiteral(Object outputBase, Object literalsBase, long literalsLimit, long output, long literalsInput) - { - long lastLiteralsSize = literalsLimit - literalsInput; - UNSAFE.copyMemory(literalsBase, literalsInput, outputBase, output, lastLiteralsSize); - output += lastLiteralsSize; - return output; - } - - private void copyMatch(Object outputBase, long fastOutputLimit, long output, int offset, long matchOutputLimit, long matchAddress) - { - matchAddress = copyMatchHead(outputBase, output, offset, matchAddress); - output += SIZE_OF_LONG; - - copyMatchTail(outputBase, fastOutputLimit, output, matchOutputLimit, matchAddress); - } - - private void copyMatchTail(Object outputBase, long fastOutputLimit, long output, long matchOutputLimit, long matchAddress) - { - if (matchOutputLimit > fastOutputLimit) { - while (output < fastOutputLimit) { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - - while (output < matchOutputLimit) { - UNSAFE.putByte(outputBase, output++, UNSAFE.getByte(outputBase, matchAddress++)); - } - } - else { - while (output < matchOutputLimit) { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - output += SIZE_OF_LONG; - } - } - } - - private long copyMatchHead(Object outputBase, long output, int offset, long matchAddress) - { - // copy match - if (offset < 8) { - // 8 bytes apart so that we can copy long-at-a-time below - int increment32 = DEC_32_TABLE[offset]; - int decrement64 = DEC_64_TABLE[offset]; - - UNSAFE.putByte(outputBase, output, UNSAFE.getByte(outputBase, matchAddress)); - UNSAFE.putByte(outputBase, output + 1, UNSAFE.getByte(outputBase, matchAddress + 1)); - UNSAFE.putByte(outputBase, output + 2, UNSAFE.getByte(outputBase, matchAddress + 2)); - UNSAFE.putByte(outputBase, output + 3, UNSAFE.getByte(outputBase, matchAddress + 3)); - matchAddress += increment32; - - UNSAFE.putInt(outputBase, output + 4, UNSAFE.getInt(outputBase, matchAddress)); - matchAddress -= decrement64; - } - else { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(outputBase, matchAddress)); - matchAddress += SIZE_OF_LONG; - } - return matchAddress; - } - - private long copyLiterals(Object outputBase, Object literalsBase, long output, long literalsInput, long literalOutputLimit) - { - long literalInput = literalsInput; - do { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(literalsBase, literalInput)); - output += SIZE_OF_LONG; - literalInput += SIZE_OF_LONG; - } - while (output < literalOutputLimit); - output = literalOutputLimit; // correction in case we over-copied - return output; - } - - private long computeMatchLengthTable(int matchLengthType, Object inputBase, long input, long inputLimit) - { - switch (matchLengthType) { - case SET_RLE: - verify(input < inputLimit, input, "Not enough input bytes"); - - byte value = UNSAFE.getByte(inputBase, input++); - verify(value <= MAX_MATCH_LENGTH_SYMBOL, input, "Value exceeds expected maximum value"); - - FseTableReader.buildRleTable(matchLengthTable, value); - currentMatchLengthTable = matchLengthTable; - break; - case SET_BASIC: - currentMatchLengthTable = DEFAULT_MATCH_LENGTH_TABLE; - break; - case SET_REPEAT: - verify(currentMatchLengthTable != null, input, "Expected match length table to be present"); - break; - case SET_COMPRESSED: - input += fse.readFseTable(matchLengthTable, inputBase, input, inputLimit, MAX_MATCH_LENGTH_SYMBOL, MATCH_LENGTH_FSE_LOG); - currentMatchLengthTable = matchLengthTable; - break; - default: - throw fail(input, "Invalid match length encoding type"); - } - return input; - } - - private long computeOffsetsTable(int offsetCodesType, Object inputBase, long input, long inputLimit) - { - switch (offsetCodesType) { - case SET_RLE: - verify(input < inputLimit, input, "Not enough input bytes"); - - byte value = UNSAFE.getByte(inputBase, input++); - verify(value <= MAX_OFFSET_CODE_SYMBOL, input, "Value exceeds expected maximum value"); - - FseTableReader.buildRleTable(offsetCodesTable, value); - currentOffsetCodesTable = offsetCodesTable; - break; - case SET_BASIC: - currentOffsetCodesTable = DEFAULT_OFFSET_CODES_TABLE; - break; - case SET_REPEAT: - verify(currentOffsetCodesTable != null, input, "Expected match length table to be present"); - break; - case SET_COMPRESSED: - input += fse.readFseTable(offsetCodesTable, inputBase, input, inputLimit, MAX_OFFSET_CODE_SYMBOL, OFFSET_CODES_FSE_LOG); - currentOffsetCodesTable = offsetCodesTable; - break; - default: - throw fail(input, "Invalid offset code encoding type"); - } - return input; - } - - private long computeLiteralsTable(int literalsLengthType, Object inputBase, long input, long inputLimit) - { - switch (literalsLengthType) { - case SET_RLE: - verify(input < inputLimit, input, "Not enough input bytes"); - - byte value = UNSAFE.getByte(inputBase, input++); - verify(value <= MAX_LITERALS_LENGTH_SYMBOL, input, "Value exceeds expected maximum value"); - - FseTableReader.buildRleTable(literalsLengthTable, value); - currentLiteralsLengthTable = literalsLengthTable; - break; - case SET_BASIC: - currentLiteralsLengthTable = DEFAULT_LITERALS_LENGTH_TABLE; - break; - case SET_REPEAT: - verify(currentLiteralsLengthTable != null, input, "Expected match length table to be present"); - break; - case SET_COMPRESSED: - input += fse.readFseTable(literalsLengthTable, inputBase, input, inputLimit, MAX_LITERALS_LENGTH_SYMBOL, LITERALS_LENGTH_FSE_LOG); - currentLiteralsLengthTable = literalsLengthTable; - break; - default: - throw fail(input, "Invalid literals length encoding type"); - } - return input; - } - - private void executeLastSequence(Object outputBase, long output, long literalOutputLimit, long matchOutputLimit, long fastOutputLimit, long literalInput, long matchAddress) - { - // copy literals - if (output < fastOutputLimit) { - // wild copy - do { - UNSAFE.putLong(outputBase, output, UNSAFE.getLong(literalsBase, literalInput)); - output += SIZE_OF_LONG; - literalInput += SIZE_OF_LONG; - } - while (output < fastOutputLimit); - - literalInput -= output - fastOutputLimit; - output = fastOutputLimit; - } - - while (output < literalOutputLimit) { - UNSAFE.putByte(outputBase, output, UNSAFE.getByte(literalsBase, literalInput)); - output++; - literalInput++; - } - - // copy match - while (output < matchOutputLimit) { - UNSAFE.putByte(outputBase, output, UNSAFE.getByte(outputBase, matchAddress)); - output++; - matchAddress++; - } - } - - private int decodeCompressedLiterals(Object inputBase, final long inputAddress, int blockSize, int literalsBlockType) - { - long input = inputAddress; - verify(blockSize >= 5, input, "Not enough input bytes"); - - // compressed - int compressedSize; - int uncompressedSize; - boolean singleStream = false; - int headerSize; - int type = (UNSAFE.getByte(inputBase, input) >> 2) & 0b11; - switch (type) { - case 0: - singleStream = true; - case 1: { - int header = UNSAFE.getInt(inputBase, input); - - headerSize = 3; - uncompressedSize = (header >>> 4) & mask(10); - compressedSize = (header >>> 14) & mask(10); - break; - } - case 2: { - int header = UNSAFE.getInt(inputBase, input); - - headerSize = 4; - uncompressedSize = (header >>> 4) & mask(14); - compressedSize = (header >>> 18) & mask(14); - break; - } - case 3: { - // read 5 little-endian bytes - long header = UNSAFE.getByte(inputBase, input) & 0xFF | - (UNSAFE.getInt(inputBase, input + 1) & 0xFFFF_FFFFL) << 8; - - headerSize = 5; - uncompressedSize = (int) ((header >>> 4) & mask(18)); - compressedSize = (int) ((header >>> 22) & mask(18)); - break; - } - default: - throw fail(input, "Invalid literals header size type"); - } - - verify(uncompressedSize <= MAX_BLOCK_SIZE, input, "Block exceeds maximum size"); - verify(headerSize + compressedSize <= blockSize, input, "Input is corrupted"); - - input += headerSize; - - long inputLimit = input + compressedSize; - if (literalsBlockType != REPEAT_STATS_LITERALS_BLOCK) { - input += huffman.readTable(inputBase, input, compressedSize); - } - - literalsBase = literals; - literalsAddress = ARRAY_BYTE_BASE_OFFSET; - literalsLimit = ARRAY_BYTE_BASE_OFFSET + uncompressedSize; - - if (singleStream) { - huffman.decodeSingleStream(inputBase, input, inputLimit, literals, literalsAddress, literalsLimit); - } - else { - huffman.decode4Streams(inputBase, input, inputLimit, literals, literalsAddress, literalsLimit); - } - - return headerSize + compressedSize; - } - - private int decodeRleLiterals(Object inputBase, final long inputAddress, int blockSize) - { - long input = inputAddress; - int outputSize; - - int type = (UNSAFE.getByte(inputBase, input) >> 2) & 0b11; - switch (type) { - case 0: - case 2: - outputSize = (UNSAFE.getByte(inputBase, input) & 0xFF) >>> 3; - input++; - break; - case 1: - outputSize = (UNSAFE.getShort(inputBase, input) & 0xFFFF) >>> 4; - input += 2; - break; - case 3: - // we need at least 4 bytes (3 for the header, 1 for the payload) - verify(blockSize >= SIZE_OF_INT, input, "Not enough input bytes"); - outputSize = (UNSAFE.getInt(inputBase, input) & 0xFF_FFFF) >>> 4; - input += 3; - break; - default: - throw fail(input, "Invalid RLE literals header encoding type"); - } - - verify(outputSize <= MAX_BLOCK_SIZE, input, "Output exceeds maximum block size"); - - byte value = UNSAFE.getByte(inputBase, input++); - Arrays.fill(literals, 0, outputSize + SIZE_OF_LONG, value); - - literalsBase = literals; - literalsAddress = ARRAY_BYTE_BASE_OFFSET; - literalsLimit = ARRAY_BYTE_BASE_OFFSET + outputSize; - - return (int) (input - inputAddress); - } - - private int decodeRawLiterals(Object inputBase, final long inputAddress, long inputLimit) - { - long input = inputAddress; - int type = (UNSAFE.getByte(inputBase, input) >> 2) & 0b11; - - int literalSize; - switch (type) { - case 0: - case 2: - literalSize = (UNSAFE.getByte(inputBase, input) & 0xFF) >>> 3; - input++; - break; - case 1: - literalSize = (UNSAFE.getShort(inputBase, input) & 0xFFFF) >>> 4; - input += 2; - break; - case 3: - // read 3 little-endian bytes - int header = ((UNSAFE.getByte(inputBase, input) & 0xFF) | - ((UNSAFE.getShort(inputBase, input + 1) & 0xFFFF) << 8)); - - literalSize = header >>> 4; - input += 3; - break; - default: - throw fail(input, "Invalid raw literals header encoding type"); - } - - verify(input + literalSize <= inputLimit, input, "Not enough input bytes"); - - // Set literals pointer to [input, literalSize], but only if we can copy 8 bytes at a time during sequence decoding - // Otherwise, copy literals into buffer that's big enough to guarantee that - if (literalSize > (inputLimit - input) - SIZE_OF_LONG) { - literalsBase = literals; - literalsAddress = ARRAY_BYTE_BASE_OFFSET; - literalsLimit = ARRAY_BYTE_BASE_OFFSET + literalSize; - - UNSAFE.copyMemory(inputBase, input, literals, literalsAddress, literalSize); - Arrays.fill(literals, literalSize, literalSize + SIZE_OF_LONG, (byte) 0); - } - else { - literalsBase = inputBase; - literalsAddress = input; - literalsLimit = literalsAddress + literalSize; - } - input += literalSize; - - return (int) (input - inputAddress); - } - - private static FrameHeader readFrameHeader(final Object inputBase, final long inputAddress, final long inputLimit) - { - long input = inputAddress; - verify(input < inputLimit, input, "Not enough input bytes"); - - int frameHeaderDescriptor = UNSAFE.getByte(inputBase, input++) & 0xFF; - boolean singleSegment = (frameHeaderDescriptor & 0b100000) != 0; - int dictionaryDescriptor = frameHeaderDescriptor & 0b11; - int contentSizeDescriptor = frameHeaderDescriptor >>> 6; - - int headerSize = 1 + - (singleSegment ? 0 : 1) + - (dictionaryDescriptor == 0 ? 0 : (1 << (dictionaryDescriptor - 1))) + - (contentSizeDescriptor == 0 ? (singleSegment ? 1 : 0) : (1 << contentSizeDescriptor)); - - verify(headerSize <= inputLimit - inputAddress, input, "Not enough input bytes"); - - // decode window size - int windowSize = -1; - if (!singleSegment) { - int windowDescriptor = UNSAFE.getByte(inputBase, input++) & 0xFF; - int exponent = windowDescriptor >>> 3; - int mantissa = windowDescriptor & 0b111; - - int base = 1 << (MIN_WINDOW_LOG + exponent); - windowSize = base + (base / 8) * mantissa; - } - - // decode dictionary id - long dictionaryId = -1; - switch (dictionaryDescriptor) { - case 1: - dictionaryId = UNSAFE.getByte(inputBase, input) & 0xFF; - input += SIZE_OF_BYTE; - break; - case 2: - dictionaryId = UNSAFE.getShort(inputBase, input) & 0xFFFF; - input += SIZE_OF_SHORT; - break; - case 3: - dictionaryId = UNSAFE.getInt(inputBase, input) & 0xFFFF_FFFFL; - input += SIZE_OF_INT; - break; - } - verify(dictionaryId == -1, input, "Custom dictionaries not supported"); - - // decode content size - long contentSize = -1; - switch (contentSizeDescriptor) { - case 0: - if (singleSegment) { - contentSize = UNSAFE.getByte(inputBase, input) & 0xFF; - input += SIZE_OF_BYTE; - } - break; - case 1: - contentSize = UNSAFE.getShort(inputBase, input) & 0xFFFF; - contentSize += 256; - input += SIZE_OF_SHORT; - break; - case 2: - contentSize = UNSAFE.getInt(inputBase, input) & 0xFFFF_FFFFL; - input += SIZE_OF_INT; - break; - case 3: - contentSize = UNSAFE.getLong(inputBase, input); - input += SIZE_OF_LONG; - break; - } - - boolean hasChecksum = (frameHeaderDescriptor & 0b100) != 0; - - return new FrameHeader( - input - inputAddress, - windowSize, - contentSize, - dictionaryId, - hasChecksum); - } - - public static long getDecompressedSize(final Object inputBase, final long inputAddress, final long inputLimit) - { - long input = inputAddress; - input += verifyMagic(inputBase, input, inputLimit); - return readFrameHeader(inputBase, input, inputLimit).contentSize; - } - - private static int verifyMagic(Object inputBase, long inputAddress, long inputLimit) - { - verify(inputLimit - inputAddress >= 4, inputAddress, "Not enough input bytes"); - - int magic = UNSAFE.getInt(inputBase, inputAddress); - if (magic != MAGIC_NUMBER) { - throw new MalformedInputException(inputAddress, "Invalid magic prefix: " + Integer.toHexString(magic)); - } - - return SIZE_OF_INT; - } -} diff --git a/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdJniCompressor.java b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdJniCompressor.java new file mode 100644 index 0000000000000..e891eeeecb300 --- /dev/null +++ b/presto-orc/src/main/java/com/facebook/presto/orc/zstd/ZstdJniCompressor.java @@ -0,0 +1,49 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc.zstd; + +import com.github.luben.zstd.Zstd; +import io.airlift.compress.Compressor; + +import java.nio.ByteBuffer; + +import static java.lang.Math.toIntExact; + +public class ZstdJniCompressor + implements Compressor +{ + private static final int COMPRESSION_LEVEL = 3; // default level + + @Override + public int maxCompressedLength(int uncompressedSize) + { + return toIntExact(Zstd.compressBound(uncompressedSize)); + } + + @Override + public int compress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength) + { + long size = Zstd.compressByteArray(output, outputOffset, maxOutputLength, input, inputOffset, inputLength, COMPRESSION_LEVEL); + if (Zstd.isError(size)) { + throw new RuntimeException(Zstd.getErrorName(size)); + } + return toIntExact(size); + } + + @Override + public void compress(ByteBuffer input, ByteBuffer output) + { + throw new UnsupportedOperationException(); + } +} diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/BenchmarkStreamReaders.java b/presto-orc/src/test/java/com/facebook/presto/orc/BenchmarkStreamReaders.java new file mode 100644 index 0000000000000..2706e05e05718 --- /dev/null +++ b/presto-orc/src/test/java/com/facebook/presto/orc/BenchmarkStreamReaders.java @@ -0,0 +1,1109 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc; + +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.DecimalType; +import com.facebook.presto.spi.type.SqlDecimal; +import com.facebook.presto.spi.type.SqlTimestamp; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import io.airlift.units.DataSize; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.io.File; +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.orc.OrcEncoding.ORC; +import static com.facebook.presto.orc.OrcReader.INITIAL_BATCH_SIZE; +import static com.facebook.presto.orc.OrcTester.Format.ORC_12; +import static com.facebook.presto.orc.OrcTester.writeOrcColumnHive; +import static com.facebook.presto.orc.metadata.CompressionKind.NONE; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DecimalType.createDecimalType; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static com.google.common.io.Files.createTempDir; +import static com.google.common.io.MoreFiles.deleteRecursively; +import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE; +import static io.airlift.units.DataSize.Unit.MEGABYTE; +import static java.util.UUID.randomUUID; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.joda.time.DateTimeZone.UTC; + +@SuppressWarnings("MethodMayBeStatic") +@State(Scope.Thread) +@OutputTimeUnit(TimeUnit.SECONDS) +@Fork(3) +@Warmup(iterations = 20, time = 500, timeUnit = MILLISECONDS) +@Measurement(iterations = 20, time = 500, timeUnit = MILLISECONDS) +@BenchmarkMode(Mode.AverageTime) +public class BenchmarkStreamReaders +{ + public static final DecimalType DECIMAL_TYPE = createDecimalType(10, 5); + public static final int ROWS = 10_000_000; + + @Benchmark + public Object readBooleanNoNull(BooleanNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(BOOLEAN, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readBooleanWithNull(BooleanWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(BOOLEAN, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readByteNoNull(TinyIntNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(TINYINT, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readByteWithNull(TinyIntWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(TINYINT, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readDecimalNoNull(DecimalNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(DECIMAL_TYPE, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readDecimalWithNull(DecimalWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(DECIMAL_TYPE, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readDoubleNoNull(DoubleNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(DOUBLE, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readDoubleWithNull(DoubleWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(DOUBLE, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readFloatNoNull(FloatNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(REAL, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readFloatWithNull(FloatWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(REAL, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readLongDirectNoNull(BigintNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(BIGINT, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readLongDirectWithNull(BigintWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(BIGINT, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readSliceDictionaryNoNull(VarcharNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(VARCHAR, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readSliceDictionaryWithNull(VarcharWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(VARCHAR, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readTimestampNoNull(TimestampNoNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(TIMESTAMP, 0); + blocks.add(block); + } + return blocks; + } + + @Benchmark + public Object readTimestampWithNull(TimestampWithNullBenchmarkData data) + throws Throwable + { + OrcRecordReader recordReader = data.createRecordReader(); + List blocks = new ArrayList<>(); + while (recordReader.nextBatch() > 0) { + Block block = recordReader.readBlock(TIMESTAMP, 0); + blocks.add(block); + } + return blocks; + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BooleanNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File booleanNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + booleanNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(booleanNoNullFile, ORC_12, NONE, BOOLEAN, createBooleanValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(booleanNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, BOOLEAN), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createBooleanValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(random.nextBoolean()); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BooleanWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File booleanWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + booleanWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(booleanWithNullFile, ORC_12, NONE, BOOLEAN, createBooleanValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(booleanWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, BOOLEAN), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createBooleanValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(random.nextBoolean() ? random.nextBoolean() : null); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class TinyIntNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File tinyIntNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + tinyIntNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(tinyIntNoNullFile, ORC_12, NONE, TINYINT, createTinyIntValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(tinyIntNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, TINYINT), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createTinyIntValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(Long.valueOf(random.nextLong()).byteValue()); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class TinyIntWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File tinyIntWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + tinyIntWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(tinyIntWithNullFile, ORC_12, NONE, TINYINT, createTinyIntValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(tinyIntWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, TINYINT), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createTinyIntValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(Long.valueOf(random.nextLong()).byteValue()); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class DecimalNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File decimalNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + decimalNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(decimalNoNullFile, ORC_12, NONE, DECIMAL_TYPE, createDecimalValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(decimalNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, DECIMAL_TYPE), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createDecimalValuesNoNull() + { + Random random = new Random(); + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(new SqlDecimal(BigInteger.valueOf(random.nextLong() % 10000000000L), 10, 5)); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class DecimalWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File decimalWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + decimalWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(decimalWithNullFile, ORC_12, NONE, DECIMAL_TYPE, createDecimalValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(decimalWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, DECIMAL_TYPE), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createDecimalValuesWithNull() + { + Random random = new Random(); + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(new SqlDecimal(BigInteger.valueOf(random.nextLong() % 10000000000L), 10, 5)); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class DoubleNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File doubleNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + doubleNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(doubleNoNullFile, ORC_12, NONE, DOUBLE, createDoubleValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(doubleNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, DOUBLE), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createDoubleValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(Double.valueOf(random.nextDouble())); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class DoubleWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File doubleWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + doubleWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(doubleWithNullFile, ORC_12, NONE, DOUBLE, createDoubleValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(doubleWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, DOUBLE), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createDoubleValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(Double.valueOf(random.nextDouble())); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class FloatNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File floatNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + floatNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(floatNoNullFile, ORC_12, NONE, REAL, createFloatValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(floatNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, REAL), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createFloatValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(Float.valueOf(random.nextFloat())); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class FloatWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File floatWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + floatWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(floatWithNullFile, ORC_12, NONE, REAL, createFloatValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(floatWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, REAL), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createFloatValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(Float.valueOf(random.nextFloat())); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BigintNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File bigintNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + + bigintNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(bigintNoNullFile, ORC_12, NONE, BIGINT, createBigintValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(bigintNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, BIGINT), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createBigintValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(Long.valueOf(random.nextLong())); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class BigintWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File bigintWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + bigintWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(bigintWithNullFile, ORC_12, NONE, BIGINT, createBigintValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(bigintWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, BIGINT), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createBigintValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(Long.valueOf(random.nextLong())); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class VarcharNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File varcharNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + varcharNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(varcharNoNullFile, ORC_12, NONE, VARCHAR, createVarcharValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(varcharNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, VARCHAR), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createVarcharValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(Strings.repeat("0", 4)); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class VarcharWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File varcharWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + varcharWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(varcharWithNullFile, ORC_12, NONE, VARCHAR, createVarcharValuesWithNulls().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(varcharWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, VARCHAR), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createVarcharValuesWithNulls() + { + Random random = new Random(); + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(Strings.repeat("0", 4)); + } + else { + values.add(null); + } + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class TimestampNoNullBenchmarkData + { + protected File temporaryDirectory; + protected File timestampNoNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + + timestampNoNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(timestampNoNullFile, ORC_12, NONE, TIMESTAMP, createSqlTimeStampValuesNoNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(timestampNoNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, TIMESTAMP), + OrcPredicate.TRUE, + UTC, // arbitrary + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createSqlTimeStampValuesNoNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + values.add(new SqlTimestamp((random.nextLong()), UTC_KEY)); + } + return values; + } + } + + @SuppressWarnings("FieldMayBeFinal") + @State(Scope.Thread) + public static class TimestampWithNullBenchmarkData + { + protected File temporaryDirectory; + protected File timestampWithNullFile; + private Random random; + + @Setup + public void setup() + throws Exception + { + random = new Random(0); + temporaryDirectory = createTempDir(); + + timestampWithNullFile = new File(temporaryDirectory, randomUUID().toString()); + writeOrcColumnHive(timestampWithNullFile, ORC_12, NONE, TIMESTAMP, createSqlTimestampValuesWithNull().iterator()); + } + + @TearDown + public void tearDown() + throws IOException + { + deleteRecursively(temporaryDirectory.toPath(), ALLOW_INSECURE); + } + + private OrcRecordReader createRecordReader() + throws IOException + { + OrcDataSource dataSource = new FileOrcDataSource(timestampWithNullFile, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), true); + OrcReader orcReader = new OrcReader(dataSource, ORC, new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE), new DataSize(1, MEGABYTE)); + return orcReader.createRecordReader( + ImmutableMap.of(0, TIMESTAMP), + OrcPredicate.TRUE, + UTC, + newSimpleAggregatedMemoryContext(), + INITIAL_BATCH_SIZE); + } + + private List createSqlTimestampValuesWithNull() + { + List values = new ArrayList<>(); + for (int i = 0; i < ROWS; ++i) { + if (random.nextBoolean()) { + values.add(new SqlTimestamp(random.nextLong(), UTC_KEY)); + } + else { + values.add(null); + } + } + return values; + } + } + + public static void main(String[] args) + throws Throwable + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkStreamReaders.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/OrcTester.java b/presto-orc/src/test/java/com/facebook/presto/orc/OrcTester.java index 830437990ab38..75987fcc90518 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/OrcTester.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/OrcTester.java @@ -118,6 +118,7 @@ import static com.facebook.presto.orc.metadata.CompressionKind.NONE; import static com.facebook.presto.orc.metadata.CompressionKind.SNAPPY; import static com.facebook.presto.orc.metadata.CompressionKind.ZLIB; +import static com.facebook.presto.orc.metadata.CompressionKind.ZSTD; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; import static com.facebook.presto.spi.type.Chars.truncateToLengthAndTrimSpaces; @@ -169,7 +170,7 @@ public class OrcTester { public static final DataSize MAX_BLOCK_SIZE = new DataSize(1, Unit.MEGABYTE); - public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("Asia/Katmandu"); + public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("America/Bahia_Banderas"); private static final TypeManager TYPE_MANAGER = new TypeRegistry(); @@ -273,7 +274,7 @@ public static OrcTester fullOrcTester() orcTester.skipBatchTestsEnabled = true; orcTester.skipStripeTestsEnabled = true; orcTester.formats = ImmutableSet.copyOf(Format.values()); - orcTester.compressions = ImmutableSet.of(NONE, SNAPPY, ZLIB, LZ4); + orcTester.compressions = ImmutableSet.of(NONE, SNAPPY, ZLIB, LZ4, ZSTD); return orcTester; } @@ -457,7 +458,8 @@ public void assertRoundTrip(Type type, List readValues) assertRoundTrip(type, type, readValues, readValues, true); } - public void assertRoundTrip(Type type, List readValues, boolean verifyWithHiveReader) throws Exception + public void assertRoundTrip(Type type, List readValues, boolean verifyWithHiveReader) + throws Exception { assertRoundTrip(type, type, readValues, readValues, verifyWithHiveReader); } @@ -473,7 +475,7 @@ private void assertRoundTrip(Type writeType, Type readType, List writeValues, OrcEncoding orcEncoding = format.getOrcEncoding(); for (CompressionKind compression : compressions) { - boolean hiveSupported = (compression != LZ4); + boolean hiveSupported = (compression != LZ4) && (compression != ZSTD); // write Hive, read Presto if (hiveSupported) { diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryBuilder.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryBuilder.java index 4ef0f429e994f..2556a4a49b9b6 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryBuilder.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryBuilder.java @@ -34,8 +34,8 @@ public void testSkipReservedSlots() Set positions = new HashSet<>(); DictionaryBuilder dictionaryBuilder = new DictionaryBuilder(64); for (int i = 0; i < 64; i++) { - positions.add(dictionaryBuilder.putIfAbsent(new TestHashCollisionBlock(1, wrappedBuffer(new byte[]{1}), new int[]{0, 1}, new boolean[]{false}), 0)); - positions.add(dictionaryBuilder.putIfAbsent(new TestHashCollisionBlock(1, wrappedBuffer(new byte[]{2}), new int[]{0, 1}, new boolean[]{false}), 0)); + positions.add(dictionaryBuilder.putIfAbsent(new TestHashCollisionBlock(1, wrappedBuffer(new byte[] {1}), new int[] {0, 1}, new boolean[] {false}), 0)); + positions.add(dictionaryBuilder.putIfAbsent(new TestHashCollisionBlock(1, wrappedBuffer(new byte[] {2}), new int[] {0, 1}, new boolean[] {false}), 0)); } assertEquals(positions, ImmutableSet.of(1, 2)); } diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryCompressionOptimizer.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryCompressionOptimizer.java index fcfa174166e7c..9ef2aca2ff38c 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryCompressionOptimizer.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestDictionaryCompressionOptimizer.java @@ -55,13 +55,13 @@ public void testNoDictionariesBytesLimit() // since there are no dictionary columns, the simulator should advance until the strip is full assertFalse(simulator.isDictionaryMemoryFull()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.finalOptimize(); assertFalse(simulator.isDictionaryMemoryFull()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.reset(); @@ -90,14 +90,14 @@ public void testSingleDictionaryColumnRowLimit() // since there dictionary columns is only 1 MB, the simulator should advance until the strip is full assertFalse(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.finalOptimize(); assertFalse(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.reset(); @@ -128,14 +128,14 @@ public void testSingleDictionaryColumnByteLimit() // since there dictionary columns is only 1 MB, the simulator should advance until the strip is full assertFalse(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertLessThan(simulator.getRowCount(), expectedMaxRowCount); simulator.finalOptimize(); assertFalse(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertLessThan(simulator.getRowCount(), expectedMaxRowCount); simulator.reset(); @@ -167,14 +167,14 @@ public void testSingleDictionaryColumnMemoryLimit() // the simulator should advance until memory is full assertTrue(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.finalOptimize(); assertTrue(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.reset(); @@ -182,17 +182,65 @@ public void testSingleDictionaryColumnMemoryLimit() } @Test - public void testSingleDictionaryColumnMemoryLimitHighlyCompressed() + public void testDirectConversionOnDictionaryFull() { - int bytesPerEntry = 1024 * 1024; - int dictionaryMaxMemoryBytes = megabytes(8) + 100; + int bytesPerEntry = 1024; + int dictionaryMaxMemoryBytes = megabytes(8); double uniquePercentage = 0.2; - TestDictionaryColumn column = dictionaryColumn(bytesPerEntry, 16, uniquePercentage); + TestDictionaryColumn column = directColumn(bytesPerEntry, uniquePercentage); + + // construct a simulator that will flip the column to direct and then hit the bytes limit + int stripeMaxBytes = megabytes(100); + int expectedRowCountAtFlip = (int) (dictionaryMaxMemoryBytes / bytesPerEntry / uniquePercentage); + int expectedMaxRowCountAtFull = stripeMaxBytes / bytesPerEntry; + DataSimulator simulator = new DataSimulator(stripeMaxBytes / 2, stripeMaxBytes, expectedMaxRowCountAtFull * 2, dictionaryMaxMemoryBytes, 0, column); + + for (int loop = 0; loop < 3; loop++) { + assertFalse(simulator.isDictionaryMemoryFull()); + assertFalse(column.isDirect()); + assertEquals(simulator.getRowCount(), 0); + assertEquals(simulator.getBufferedBytes(), 0); + + simulator.advanceToNextStateChange(); + + // the simulator should advance until the dictionary column is converted to direct + assertFalse(simulator.isDictionaryMemoryFull()); + assertTrue(column.isDirect()); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getRowCount(), expectedRowCountAtFlip); + + simulator.advanceToNextStateChange(); + + // the simulator should advance until the stripe is full + assertFalse(simulator.isDictionaryMemoryFull()); + assertTrue(column.isDirect()); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); + + simulator.finalOptimize(); + + assertFalse(simulator.isDictionaryMemoryFull()); + assertTrue(column.isDirect()); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); + + simulator.reset(); + } + } - // construct a simulator that will hit the dictionary memory before hitting the minimum row limit + @Test + public void testNotDirectConversionOnDictionaryFull() + { + int bytesPerEntry = 1024; + int dictionaryMaxMemoryBytes = megabytes(8); + double uniquePercentage = 0.01; + TestDictionaryColumn column = directColumn(bytesPerEntry, uniquePercentage); + + // construct a simulator that will be full because of dictionary memory limit; + // the column cannot not be converted to direct encoding because of stripe size limit int stripeMaxBytes = megabytes(100); int expectedMaxRowCount = (int) (dictionaryMaxMemoryBytes / bytesPerEntry / uniquePercentage); - DataSimulator simulator = new DataSimulator(0, stripeMaxBytes, Integer.MAX_VALUE, dictionaryMaxMemoryBytes, 0, column); + DataSimulator simulator = new DataSimulator(stripeMaxBytes / 2, stripeMaxBytes, expectedMaxRowCount * 2, dictionaryMaxMemoryBytes, 0, column); for (int loop = 0; loop < 3; loop++) { assertFalse(simulator.isDictionaryMemoryFull()); @@ -205,14 +253,14 @@ public void testSingleDictionaryColumnMemoryLimitHighlyCompressed() // the simulator should advance until memory is full assertTrue(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.finalOptimize(); assertTrue(simulator.isDictionaryMemoryFull()); assertFalse(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCount); simulator.reset(); @@ -243,7 +291,7 @@ public void testSingleDirectBytesLimit() // the simulator should advance until the dictionary column is flipped assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(column.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedRowCountAtFlip); simulator.advanceToNextStateChange(); @@ -251,14 +299,14 @@ public void testSingleDirectBytesLimit() // the simulator should advance until the stripe is full assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(column.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); simulator.finalOptimize(); assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(column.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); simulator.reset(); @@ -293,7 +341,7 @@ public void testDictionaryAndDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedRowCountAtFlip); simulator.advanceToNextStateChange(); @@ -302,7 +350,7 @@ public void testDictionaryAndDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); simulator.finalOptimize(); @@ -310,7 +358,7 @@ public void testDictionaryAndDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertGreaterThanOrEqual(simulator.getBufferedBytes(), stripeMaxBytes); + assertGreaterThanOrEqual(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedMaxRowCountAtFull); simulator.reset(); @@ -349,7 +397,7 @@ public void testWideDictionaryAndNarrowDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), expectedRowCountAtFlip); simulator.advanceToNextStateChange(); @@ -358,7 +406,7 @@ public void testWideDictionaryAndNarrowDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), maxRowCount); simulator.finalOptimize(); @@ -366,7 +414,7 @@ public void testWideDictionaryAndNarrowDirectBytesLimit() assertFalse(simulator.isDictionaryMemoryFull()); assertTrue(directColumn.isDirect()); assertFalse(dictionaryColumn.isDirect()); - assertLessThan(simulator.getBufferedBytes(), stripeMaxBytes); + assertLessThan(simulator.getBufferedBytes(), (long) stripeMaxBytes); assertGreaterThanOrEqual(simulator.getRowCount(), maxRowCount); simulator.reset(); @@ -414,7 +462,7 @@ public void advanceToNextStateChange() for (TestDictionaryColumn dictionaryColumn : dictionaryColumns) { dictionaryColumn.advanceTo(rowCount); } - optimizer.optimize(getBufferedBytes(), getRowCount()); + optimizer.optimize(toIntExact(getBufferedBytes()), getRowCount()); } } @@ -425,7 +473,7 @@ public boolean isDictionaryMemoryFull() public void finalOptimize() { - optimizer.finalOptimize(); + optimizer.finalOptimize(toIntExact(getBufferedBytes())); } private List getDirectColumnFlags() @@ -444,11 +492,11 @@ public void reset() } } - public int getBufferedBytes() + public long getBufferedBytes() { - return (rowCount * otherColumnsBytesPerRow) + + return (long) rowCount * otherColumnsBytesPerRow + dictionaryColumns.stream() - .mapToInt(TestDictionaryColumn::getBufferedBytes) + .mapToLong(TestDictionaryColumn::getBufferedBytes) .sum(); } @@ -517,10 +565,10 @@ public void advanceTo(int rowCount) this.rowCount = rowCount; } - public int getBufferedBytes() + public long getBufferedBytes() { if (direct) { - return (int) (rowCount * valuesPerRow * bytesPerEntry); + return (long) (rowCount * valuesPerRow * bytesPerEntry); } int dictionaryEntries = getDictionaryEntries(); int bytesPerValue = estimateIndexBytesPerValue(dictionaryEntries); @@ -528,15 +576,15 @@ public int getBufferedBytes() } @Override - public int getValueCount() + public long getValueCount() { - return (int) (rowCount * valuesPerRow); + return (long) (rowCount * valuesPerRow); } @Override - public int getNonNullValueCount() + public long getNonNullValueCount() { - return (int) (getValueCount() * (1 - nullRate)); + return (long) (getValueCount() * (1 - nullRate)); } @Override @@ -559,11 +607,23 @@ public int getDictionaryBytes() } @Override - public long convertToDirect() + public int getIndexBytes() + { + return toIntExact(estimateIndexBytesPerValue(getDictionaryEntries()) * getNonNullValueCount()); + } + + @Override + public OptionalInt tryConvertToDirect(int maxDirectBytes) { assertFalse(direct); - direct = true; - return getBufferedBytes(); + long directBytes = (long) (rowCount * valuesPerRow * bytesPerEntry); + if (directBytes <= maxDirectBytes) { + direct = true; + return OptionalInt.of(toIntExact(directBytes)); + } + else { + return OptionalInt.empty(); + } } public boolean isDirect() diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestFlatMap.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestFlatMap.java new file mode 100644 index 0000000000000..39bf0621e2a03 --- /dev/null +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestFlatMap.java @@ -0,0 +1,524 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc; + +import com.facebook.presto.block.BlockEncodingManager; +import com.facebook.presto.metadata.FunctionRegistry; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.type.BigintType; +import com.facebook.presto.spi.type.BooleanType; +import com.facebook.presto.spi.type.DoubleType; +import com.facebook.presto.spi.type.IntegerType; +import com.facebook.presto.spi.type.NamedTypeSignature; +import com.facebook.presto.spi.type.RealType; +import com.facebook.presto.spi.type.RowFieldName; +import com.facebook.presto.spi.type.SmallintType; +import com.facebook.presto.spi.type.SqlTimestamp; +import com.facebook.presto.spi.type.SqlVarbinary; +import com.facebook.presto.spi.type.StandardTypes; +import com.facebook.presto.spi.type.TimeZoneKey; +import com.facebook.presto.spi.type.TinyintType; +import com.facebook.presto.spi.type.Type; +import com.facebook.presto.spi.type.TypeManager; +import com.facebook.presto.spi.type.TypeSignatureParameter; +import com.facebook.presto.spi.type.VarbinaryType; +import com.facebook.presto.spi.type.VarcharType; +import com.facebook.presto.sql.analyzer.FeaturesConfig; +import com.facebook.presto.type.TypeRegistry; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.airlift.units.DataSize; +import org.testng.annotations.Test; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +import static com.facebook.presto.memory.context.AggregatedMemoryContext.newSimpleAggregatedMemoryContext; +import static com.facebook.presto.orc.OrcTester.HIVE_STORAGE_TIME_ZONE; +import static com.facebook.presto.orc.TestFlatMap.ExpectedValuesBuilder.Frequency.ALL; +import static com.facebook.presto.orc.TestFlatMap.ExpectedValuesBuilder.Frequency.NONE; +import static com.facebook.presto.orc.TestFlatMap.ExpectedValuesBuilder.Frequency.SOME; +import static com.facebook.presto.orc.TestingOrcPredicate.createOrcPredicate; +import static com.facebook.presto.testing.TestingConnectorSession.SESSION; +import static com.google.common.collect.Iterators.advance; +import static com.google.common.io.Resources.getResource; +import static io.airlift.units.DataSize.Unit.MEGABYTE; +import static java.lang.Math.toIntExact; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +public class TestFlatMap +{ + // TODO: Add tests for timestamp as value type + + private static final TypeManager TYPE_MANAGER = new TypeRegistry(); + private static final int NUM_ROWS = 31_234; + + static { + // associate TYPE_MANAGER with a function registry + new FunctionRegistry(TYPE_MANAGER, new BlockEncodingManager(TYPE_MANAGER), new FeaturesConfig()); + } + + private static final Type LIST_TYPE = TYPE_MANAGER.getParameterizedType( + StandardTypes.ARRAY, + ImmutableList.of(TypeSignatureParameter.of(IntegerType.INTEGER.getTypeSignature()))); + private static final Type MAP_TYPE = TYPE_MANAGER.getParameterizedType( + StandardTypes.MAP, + ImmutableList.of(TypeSignatureParameter.of(VarcharType.VARCHAR.getTypeSignature()), TypeSignatureParameter.of(RealType.REAL.getTypeSignature()))); + private static final Type STRUCT_TYPE = TYPE_MANAGER.getParameterizedType( + StandardTypes.ROW, + ImmutableList.of( + TypeSignatureParameter.of(new NamedTypeSignature(Optional.of(new RowFieldName("value1", false)), IntegerType.INTEGER.getTypeSignature())), + TypeSignatureParameter.of(new NamedTypeSignature(Optional.of(new RowFieldName("value2", false)), IntegerType.INTEGER.getTypeSignature())), + TypeSignatureParameter.of(new NamedTypeSignature(Optional.of(new RowFieldName("value3", false)), IntegerType.INTEGER.getTypeSignature())))); + + @Test + public void testByte() + throws Exception + { + runTest("test_flat_map/flat_map_byte.dwrf", + TinyintType.TINYINT, + ExpectedValuesBuilder.get(Integer::byteValue)); + } + + @Test + public void testByteWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_byte_with_null.dwrf", + TinyintType.TINYINT, + ExpectedValuesBuilder.get(Integer::byteValue).setNullValuesFrequency(SOME)); + } + + @Test + public void testShort() + throws Exception + { + runTest("test_flat_map/flat_map_short.dwrf", + SmallintType.SMALLINT, + ExpectedValuesBuilder.get(Integer::shortValue)); + } + + @Test + public void testInteger() + throws Exception + { + runTest("test_flat_map/flat_map_int.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity())); + } + + @Test + public void testIntegerWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_int_with_null.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity()).setNullValuesFrequency(SOME)); + } + + @Test + public void testLong() + throws Exception + { + runTest("test_flat_map/flat_map_long.dwrf", + BigintType.BIGINT, + ExpectedValuesBuilder.get(Integer::longValue)); + } + + @Test + public void testString() + throws Exception + { + runTest("test_flat_map/flat_map_string.dwrf", + VarcharType.VARCHAR, + ExpectedValuesBuilder.get(i -> Integer.toString(i))); + } + + @Test + public void testStringWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_string_with_null.dwrf", + VarcharType.VARCHAR, + ExpectedValuesBuilder.get(i -> Integer.toString(i)).setNullValuesFrequency(SOME)); + } + + @Test + public void testBinary() + throws Exception + { + runTest("test_flat_map/flat_map_binary.dwrf", + VarbinaryType.VARBINARY, + ExpectedValuesBuilder.get(i -> new SqlVarbinary(Integer.toString(i).getBytes(StandardCharsets.UTF_8)))); + } + + @Test + public void testBoolean() + throws Exception + { + runTest("test_flat_map/flat_map_boolean.dwrf", + IntegerType.INTEGER, + BooleanType.BOOLEAN, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToBoolean)); + } + + @Test + public void testBooleanWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_boolean_with_null.dwrf", + IntegerType.INTEGER, + BooleanType.BOOLEAN, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToBoolean).setNullValuesFrequency(SOME)); + } + + @Test + public void testFloat() + throws Exception + { + runTest("test_flat_map/flat_map_float.dwrf", + IntegerType.INTEGER, + RealType.REAL, + ExpectedValuesBuilder.get(Function.identity(), Float::valueOf)); + } + + @Test + public void testFloatWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_float_with_null.dwrf", + IntegerType.INTEGER, + RealType.REAL, + ExpectedValuesBuilder.get(Function.identity(), Float::valueOf).setNullValuesFrequency(SOME)); + } + + @Test + public void testDouble() + throws Exception + { + runTest("test_flat_map/flat_map_double.dwrf", + IntegerType.INTEGER, + DoubleType.DOUBLE, + ExpectedValuesBuilder.get(Function.identity(), Double::valueOf)); + } + + @Test + public void testDoubleWithNull() + throws Exception + { + runTest("test_flat_map/flat_map_double_with_null.dwrf", + IntegerType.INTEGER, + DoubleType.DOUBLE, + ExpectedValuesBuilder.get(Function.identity(), Double::valueOf).setNullValuesFrequency(SOME)); + } + + @Test + public void testList() + throws Exception + { + runTest( + "test_flat_map/flat_map_list.dwrf", + IntegerType.INTEGER, + LIST_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToList)); + } + + @Test + public void testListWithNull() + throws Exception + { + runTest( + "test_flat_map/flat_map_list_with_null.dwrf", + IntegerType.INTEGER, + LIST_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToList).setNullValuesFrequency(SOME)); + } + + @Test + public void testMap() + throws Exception + { + runTest( + "test_flat_map/flat_map_map.dwrf", + IntegerType.INTEGER, + MAP_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToMap)); + } + + @Test + public void testMapWithNull() + throws Exception + { + runTest( + "test_flat_map/flat_map_map_with_null.dwrf", + IntegerType.INTEGER, + MAP_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToMap).setNullValuesFrequency(SOME)); + } + + @Test + public void testStruct() + throws Exception + { + runTest( + "test_flat_map/flat_map_struct.dwrf", + IntegerType.INTEGER, + STRUCT_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToList)); + } + + @Test + public void testStructWithNull() + throws Exception + { + runTest( + "test_flat_map/flat_map_struct_with_null.dwrf", + IntegerType.INTEGER, + STRUCT_TYPE, + ExpectedValuesBuilder.get(Function.identity(), TestFlatMap::intToList).setNullValuesFrequency(SOME)); + } + + @Test + public void testWithNulls() + throws Exception + { + // A test case where some of the flat maps are null + runTest( + "test_flat_map/flat_map_some_null_maps.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity()).setNullRowsFrequency(SOME)); + } + + @Test + public void testWithAllNulls() + throws Exception + { + // A test case where every flat map is null + runTest( + "test_flat_map/flat_map_all_null_maps.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity()).setNullRowsFrequency(ALL)); + } + + @Test + public void testWithEmptyMaps() + throws Exception + { + // A test case where some of the flat maps are empty + runTest( + "test_flat_map/flat_map_some_empty_maps.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity()).setEmptyMapsFrequency(SOME)); + } + + @Test + public void testWithAllMaps() + throws Exception + { + // A test case where all of the flat maps are empty + runTest( + "test_flat_map/flat_map_all_empty_maps.dwrf", + IntegerType.INTEGER, + ExpectedValuesBuilder.get(Function.identity()).setEmptyMapsFrequency(ALL)); + } + + private void runTest(String testOrcFileName, Type type, ExpectedValuesBuilder expectedValuesBuilder) + throws Exception + { + runTest(testOrcFileName, type, type, expectedValuesBuilder); + } + + private void runTest(String testOrcFileName, Type keyType, Type valueType, ExpectedValuesBuilder expectedValuesBuilder) + throws Exception + { + List> expectedValues = expectedValuesBuilder.build(); + + runTest(testOrcFileName, keyType, valueType, expectedValues, false, false); + runTest(testOrcFileName, keyType, valueType, expectedValues, true, false); + runTest(testOrcFileName, keyType, valueType, expectedValues, false, true); + } + + private void runTest(String testOrcFileName, Type keyType, Type valueType, List> expectedValues, boolean skipFirstBatch, boolean skipFirstStripe) + throws Exception + { + OrcDataSource orcDataSource = new FileOrcDataSource( + new File(getResource(testOrcFileName).getFile()), + new DataSize(1, MEGABYTE), + new DataSize(1, MEGABYTE), + new DataSize(1, MEGABYTE), + true); + OrcReader orcReader = new OrcReader( + orcDataSource, + OrcEncoding.DWRF, + new DataSize(1, MEGABYTE), + new DataSize(1, MEGABYTE), + new DataSize(1, MEGABYTE), + new DataSize(1, DataSize.Unit.MEGABYTE)); + Type mapType = TYPE_MANAGER.getParameterizedType( + StandardTypes.MAP, + ImmutableList.of( + TypeSignatureParameter.of(keyType.getTypeSignature()), + TypeSignatureParameter.of(valueType.getTypeSignature()))); + + try (OrcRecordReader recordReader = orcReader.createRecordReader(ImmutableMap.of(0, mapType), createOrcPredicate(mapType, expectedValues, OrcTester.Format.DWRF, true), HIVE_STORAGE_TIME_ZONE, newSimpleAggregatedMemoryContext(), 1024)) { + Iterator expectedValuesIterator = expectedValues.iterator(); + + boolean isFirst = true; + int rowsProcessed = 0; + for (int batchSize = toIntExact(recordReader.nextBatch()); batchSize >= 0; batchSize = toIntExact(recordReader.nextBatch())) { + if (skipFirstStripe && rowsProcessed < 10_000) { + assertEquals(advance(expectedValuesIterator, batchSize), batchSize); + } + else if (skipFirstBatch && isFirst) { + assertEquals(advance(expectedValuesIterator, batchSize), batchSize); + isFirst = false; + } + else { + Block block = recordReader.readBlock(mapType, 0); + + for (int position = 0; position < block.getPositionCount(); position++) { + assertEquals(mapType.getObjectValue(SESSION, block, position), expectedValuesIterator.next()); + } + } + + assertEquals(recordReader.getReaderPosition(), rowsProcessed); + assertEquals(recordReader.getFilePosition(), rowsProcessed); + rowsProcessed += batchSize; + } + + assertFalse(expectedValuesIterator.hasNext()); + assertEquals(recordReader.getReaderPosition(), rowsProcessed); + assertEquals(recordReader.getFilePosition(), rowsProcessed); + } + } + + private static boolean intToBoolean(int i) + { + return i % 2 == 0; + } + + private static SqlTimestamp intToTimestamp(int i) + { + return new SqlTimestamp(i, TimeZoneKey.UTC_KEY); + } + + private static List intToList(int i) + { + return ImmutableList.of(i * 3, i * 3 + 1, i * 3 + 2); + } + + private static Map intToMap(int i) + { + return ImmutableMap.of(Integer.toString(i * 3), (float) (i * 3), Integer.toString(i * 3 + 1), (float) (i * 3 + 1), Integer.toString(i * 3 + 2), (float) (i * 3 + 2)); + } + + static class ExpectedValuesBuilder + { + enum Frequency + { + NONE, + SOME, + ALL + } + + private final Function keyConverter; + private final Function valueConverter; + private Frequency nullValuesFrequency = NONE; + private Frequency nullRowsFrequency = NONE; + private Frequency emptyMapsFrequency = NONE; + + private ExpectedValuesBuilder(Function keyConverter, Function valueConverter) + { + this.keyConverter = keyConverter; + this.valueConverter = valueConverter; + } + + public static ExpectedValuesBuilder get(Function converter) + { + return new ExpectedValuesBuilder<>(converter, converter); + } + + public static ExpectedValuesBuilder get(Function keyConverter, Function valueConverter) + { + return new ExpectedValuesBuilder<>(keyConverter, valueConverter); + } + + public ExpectedValuesBuilder setNullValuesFrequency(Frequency frequency) + { + this.nullValuesFrequency = frequency; + + return this; + } + + public ExpectedValuesBuilder setNullRowsFrequency(Frequency frequency) + { + this.nullRowsFrequency = frequency; + + return this; + } + + public ExpectedValuesBuilder setEmptyMapsFrequency(Frequency frequency) + { + this.emptyMapsFrequency = frequency; + + return this; + } + + public List> build() + { + List> result = new ArrayList<>(NUM_ROWS); + + for (int i = 0; i < NUM_ROWS; ++i) { + if (passesFrequencyCheck(nullRowsFrequency, i)) { + result.add((Map) null); + } + else if (passesFrequencyCheck(emptyMapsFrequency, i)) { + result.add(Collections.emptyMap()); + } + else { + Map row = new HashMap<>(); + row.put(keyConverter.apply(i * 3 % 32), passesFrequencyCheck(nullValuesFrequency, i) ? null : valueConverter.apply(i * 3 % 32)); + row.put(keyConverter.apply((i * 3 + 1) % 32), valueConverter.apply((i * 3 + 1) % 32)); + row.put(keyConverter.apply((i * 3 + 2) % 32), valueConverter.apply((i * 3 + 2) % 32)); + result.add(row); + } + } + + return result; + } + + private boolean passesFrequencyCheck(Frequency frequency, int i) + { + switch (frequency) { + case NONE: + return false; + case ALL: + return true; + case SOME: + return i % 5 == 0; + default: + throw new IllegalArgumentException("Got unexpected Frequency: " + frequency); + } + } + } +} diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcReaderMemoryUsage.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcReaderMemoryUsage.java index 2636479ca4485..ff2390c6c05f7 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcReaderMemoryUsage.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcReaderMemoryUsage.java @@ -50,7 +50,6 @@ import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static io.airlift.testing.Assertions.assertGreaterThan; -import static io.airlift.testing.Assertions.assertGreaterThanOrEqual; import static org.testng.Assert.assertEquals; public class TestOrcReaderMemoryUsage @@ -84,7 +83,7 @@ public void testVarcharTypeWithoutNulls() break; } - Block block = reader.readBlock(BIGINT, 0); + Block block = reader.readBlock(VARCHAR, 0); assertEquals(block.getPositionCount(), batchSize); // We only verify the memory usage when the batchSize reaches MAX_BATCH_SIZE as batchSize may be @@ -95,12 +94,11 @@ public void testVarcharTypeWithoutNulls() // StripeReader memory should increase after reading a block. assertGreaterThan(reader.getCurrentStripeRetainedSizeInBytes(), stripeReaderRetainedSize); - // We also account for the StreamReader local buffers. For SliceDictionaryStreamReader, there are two - // buffers: isNullVector and inDictionaryVector, and each buffer has 1024 boolean values. - assertGreaterThanOrEqual(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 2048L); - // The total retained size and system memory usage should be strictly larger than 2048L because of the instance sizes. - assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 2048L); - assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 2048L); + // There are no local buffers needed. + assertEquals(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 0L); + // The total retained size and system memory usage should be greater than 0 byte because of the instance sizes. + assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 0L); + assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 0L); } } finally { @@ -143,12 +141,11 @@ public void testBigIntTypeWithNulls() // StripeReader memory should increase after reading a block. assertGreaterThan(reader.getCurrentStripeRetainedSizeInBytes(), stripeReaderRetainedSize); - // We also account for the StreamReader local buffers. For LongDirectStreamReader, there is one - // buffer isNullVector, and it has 1024 boolean values. - assertGreaterThanOrEqual(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 1024L); - // The total retained size and system memory usage should be strictly larger than 2048L because of the instance sizes. - assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 1024L); - assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 1024L); + // There are no local buffers needed. + assertEquals(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 0L); + // The total retained size and system memory usage should be strictly larger than 0L because of the instance sizes. + assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 0L); + assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 0L); } } finally { @@ -208,13 +205,11 @@ public void testMapTypeWithNulls() // StripeReader memory should increase after reading a block. assertGreaterThan(reader.getCurrentStripeRetainedSizeInBytes(), stripeReaderRetainedSize); - // We also account the StreamReader local buffers. For MapStreamReader, there is a - // keyStreamReader(LongStreamReader) and a valueStreamReader(LongStreamReader), and each of them is - // holding a isNullVector buffer which has 1024 boolean values. - assertGreaterThanOrEqual(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 2048L); - // The total retained size and system memory usage should be strictly larger than 2048L because of the instance sizes. - assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 2048L); - assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 2048L); + // There are no local buffers needed. + assertEquals(reader.getStreamReaderRetainedSizeInBytes() - streamReaderRetainedSize, 0L); + // The total retained size and system memory usage should be strictly larger than 0L because of the instance sizes. + assertGreaterThan(reader.getRetainedSizeInBytes() - readerRetainedSize, 0L); + assertGreaterThan(reader.getSystemMemoryUsage() - readerSystemMemoryUsage, 0L); } } finally { diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcWriter.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcWriter.java index 7dd84fd8baa6c..70c8d19c3273e 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcWriter.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestOrcWriter.java @@ -73,7 +73,7 @@ public void testWriteOutputStreamsInOrder() new OrcWriterStats()); // write down some data with unsorted streams - String[] data = new String[]{"a", "bbbbb", "ccc", "dd", "eeee"}; + String[] data = new String[] {"a", "bbbbb", "ccc", "dd", "eeee"}; Block[] blocks = new Block[data.length]; int entries = 65536; BlockBuilder blockBuilder = VARCHAR.createBlockBuilder(null, entries); diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestSliceDictionaryColumnWriter.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestSliceDictionaryColumnWriter.java new file mode 100644 index 0000000000000..fe6762b66f093 --- /dev/null +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestSliceDictionaryColumnWriter.java @@ -0,0 +1,61 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.orc; + +import com.facebook.presto.orc.metadata.CompressionKind; +import com.facebook.presto.orc.writer.SliceDictionaryColumnWriter; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.RunLengthEncodedBlock; +import io.airlift.slice.Slices; +import io.airlift.units.DataSize; +import org.testng.annotations.Test; + +import java.util.concurrent.ThreadLocalRandom; + +import static com.facebook.presto.orc.OrcWriterOptions.DEFAULT_MAX_COMPRESSION_BUFFER_SIZE; +import static com.facebook.presto.orc.OrcWriterOptions.DEFAULT_MAX_STRING_STATISTICS_LIMIT; +import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static io.airlift.units.DataSize.Unit.MEGABYTE; +import static java.lang.Math.toIntExact; +import static org.testng.Assert.assertFalse; + +public class TestSliceDictionaryColumnWriter +{ + @Test + public void testDirectConversion() + { + SliceDictionaryColumnWriter writer = new SliceDictionaryColumnWriter( + 0, + VARCHAR, + CompressionKind.NONE, + toIntExact(DEFAULT_MAX_COMPRESSION_BUFFER_SIZE.toBytes()), + OrcEncoding.ORC, + DEFAULT_MAX_STRING_STATISTICS_LIMIT); + + // a single row group exceeds 2G after direct conversion + byte[] value = new byte[megabytes(1)]; + ThreadLocalRandom.current().nextBytes(value); + Block data = RunLengthEncodedBlock.create(VARCHAR, Slices.wrappedBuffer(value), 3000); + writer.beginRowGroup(); + writer.writeBlock(data); + writer.finishRowGroup(); + + assertFalse(writer.tryConvertToDirect(megabytes(64)).isPresent()); + } + + private static int megabytes(int size) + { + return toIntExact(new DataSize(size, MEGABYTE).toBytes()); + } +} diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/TestStructStreamReader.java b/presto-orc/src/test/java/com/facebook/presto/orc/TestStructStreamReader.java index de84f7f65a90d..d313ba9b09787 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/TestStructStreamReader.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/TestStructStreamReader.java @@ -77,7 +77,8 @@ public void setUp() } @AfterMethod - public void tearDown() throws IOException + public void tearDown() + throws IOException { tempFile.close(); } @@ -86,7 +87,8 @@ public void tearDown() throws IOException * Reader and writer have the same fields. Checks that fields are read in correctly */ @Test - public void testValuesAreReadInCorrectly() throws IOException + public void testValuesAreReadInCorrectly() + throws IOException { List readerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); List writerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); @@ -108,7 +110,8 @@ public void testValuesAreReadInCorrectly() throws IOException * The writer has fields with upper case characters, reader has same names downcased. */ @Test - public void testReaderLowerCasesFieldNamesFromStream() throws IOException + public void testReaderLowerCasesFieldNamesFromStream() + throws IOException { List readerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); List writerFields = new ArrayList<>(Arrays.asList("field_A", "field_B", "field_C")); @@ -130,7 +133,8 @@ public void testReaderLowerCasesFieldNamesFromStream() throws IOException * Reader has fields with upper case characters, writer has same names downcased. */ @Test - public void testReaderLowerCasesFieldNamesFromType() throws IOException + public void testReaderLowerCasesFieldNamesFromType() + throws IOException { List readerFields = new ArrayList<>(Arrays.asList("field_A", "field_B", "field_C")); List writerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); @@ -150,7 +154,8 @@ public void testReaderLowerCasesFieldNamesFromType() throws IOException @Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Missing struct field name in type row\\(varchar,varchar,varchar\\)") - public void testThrowsExceptionWhenFieldNameMissing() throws IOException + public void testThrowsExceptionWhenFieldNameMissing() + throws IOException { List readerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); List writerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); @@ -166,7 +171,8 @@ public void testThrowsExceptionWhenFieldNameMissing() throws IOException * The reader has a field that is missing from the ORC file */ @Test - public void testExtraFieldsInReader() throws IOException + public void testExtraFieldsInReader() + throws IOException { List readerFields = new ArrayList<>(Arrays.asList("field_a", "field_b", "field_c")); @@ -190,7 +196,8 @@ public void testExtraFieldsInReader() throws IOException * The ORC file has a field that is missing from the reader */ @Test - public void testExtraFieldsInWriter() throws IOException + public void testExtraFieldsInWriter() + throws IOException { // field_b is missing List readerFields = new ArrayList<>(Arrays.asList("field_a", "field_c")); @@ -208,14 +215,26 @@ public void testExtraFieldsInWriter() throws IOException assertEquals(actual.get(1), "field_c_value"); } - private void write(TempFile tempFile, Type writerType, List data) throws IOException + private void write(TempFile tempFile, Type writerType, List data) + throws IOException { OrcWriter writer = new OrcWriter( - new OutputStreamOrcDataSink( - new FileOutputStream(tempFile.getFile())), ImmutableList.of(STRUCT_COL_NAME), ImmutableList.of(writerType), ORC, NONE, - new OrcWriterOptions().withStripeMinSize(new DataSize(0, MEGABYTE)).withStripeMaxSize(new DataSize(32, MEGABYTE)).withStripeMaxRowCount(ORC_STRIPE_SIZE) - .withRowGroupMaxRowCount(ORC_ROW_GROUP_SIZE).withDictionaryMaxMemory(new DataSize(32, MEGABYTE)), - ImmutableMap.of(), HIVE_STORAGE_TIME_ZONE, true, BOTH, new OrcWriterStats()); + new OutputStreamOrcDataSink(new FileOutputStream(tempFile.getFile())), + ImmutableList.of(STRUCT_COL_NAME), + ImmutableList.of(writerType), + ORC, + NONE, + new OrcWriterOptions() + .withStripeMinSize(new DataSize(0, MEGABYTE)) + .withStripeMaxSize(new DataSize(32, MEGABYTE)) + .withStripeMaxRowCount(ORC_STRIPE_SIZE) + .withRowGroupMaxRowCount(ORC_ROW_GROUP_SIZE) + .withDictionaryMaxMemory(new DataSize(32, MEGABYTE)), + ImmutableMap.of(), + HIVE_STORAGE_TIME_ZONE, + true, + BOTH, + new OrcWriterStats()); // write down some data with unsorted streams Block[] fieldBlocks = new Block[data.size()]; @@ -234,12 +253,13 @@ private void write(TempFile tempFile, Type writerType, List data) throws fieldBlocks[i] = blockBuilder.build(); blockBuilder = blockBuilder.newBlockBuilderLike(null); } - Block rowBlock = RowBlock.fromFieldBlocks(rowIsNull, fieldBlocks); + Block rowBlock = RowBlock.fromFieldBlocks(rowIsNull.length, Optional.of(rowIsNull), fieldBlocks); writer.write(new Page(rowBlock)); writer.close(); } - private RowBlock read(TempFile tempFile, Type readerType) throws IOException + private RowBlock read(TempFile tempFile, Type readerType) + throws IOException { DataSize dataSize = new DataSize(1, MEGABYTE); OrcDataSource orcDataSource = new FileOrcDataSource(tempFile.getFile(), dataSize, dataSize, dataSize, true); diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/metadata/TestDwrfMetadataReader.java b/presto-orc/src/test/java/com/facebook/presto/orc/metadata/TestDwrfMetadataReader.java index 59f6a81af176d..e95376f35191b 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/metadata/TestDwrfMetadataReader.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/metadata/TestDwrfMetadataReader.java @@ -50,21 +50,21 @@ public void testToStringStatistics() for (boolean isRowGroup : ImmutableList.of(true, false)) { assertEquals( DwrfMetadataReader.toStringStatistics( - HiveWriterVersion.ORC_HIVE_8732, - DwrfProto.StringStatistics.newBuilder() - .setSum(45) - .build(), - isRowGroup), + HiveWriterVersion.ORC_HIVE_8732, + DwrfProto.StringStatistics.newBuilder() + .setSum(45) + .build(), + isRowGroup), new StringStatistics(null, null, 45)); } // and the ORIGINAL version row group stats (but not rolled up stats) assertEquals( DwrfMetadataReader.toStringStatistics( - HiveWriterVersion.ORIGINAL, - DwrfProto.StringStatistics.newBuilder() - .setSum(45) - .build(), - true), + HiveWriterVersion.ORIGINAL, + DwrfProto.StringStatistics.newBuilder() + .setSum(45) + .build(), + true), new StringStatistics(null, null, 45)); // having only a min or max should work diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/metadata/statistics/TestStringStatisticsBuilder.java b/presto-orc/src/test/java/com/facebook/presto/orc/metadata/statistics/TestStringStatisticsBuilder.java index 8b951df0791d1..81dda1a9d6387 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/metadata/statistics/TestStringStatisticsBuilder.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/metadata/statistics/TestStringStatisticsBuilder.java @@ -146,6 +146,16 @@ public void testMergeWithLimit() assertMinMax(mergeColumnStatistics(statisticsList).getStringStatistics(), null, null); } + @Test + public void testMergeWithNullMinMaxValues() + { + List statisticsList = new ArrayList<>(); + statisticsList.add(stringColumnStatistics(MEDIUM_BOTTOM_VALUE, MEDIUM_BOTTOM_VALUE)); + assertMinMax(mergeColumnStatistics(statisticsList).getStringStatistics(), MEDIUM_BOTTOM_VALUE, MEDIUM_BOTTOM_VALUE); + statisticsList.add(stringColumnStatistics(null, null)); + assertMinMax(mergeColumnStatistics(statisticsList).getStringStatistics(), null, null); + } + @Test public void testMixingAddValueAndMergeWithLimit() { diff --git a/presto-orc/src/test/java/com/facebook/presto/orc/stream/TestLongDecode.java b/presto-orc/src/test/java/com/facebook/presto/orc/stream/TestLongDecode.java index cca399bdac9bf..3021a2b8e6725 100644 --- a/presto-orc/src/test/java/com/facebook/presto/orc/stream/TestLongDecode.java +++ b/presto-orc/src/test/java/com/facebook/presto/orc/stream/TestLongDecode.java @@ -137,7 +137,8 @@ private static long readVulong(InputStream in) } result |= (0x7f & b) << offset; offset += 7; - } while (b >= 0x80); + } + while (b >= 0x80); return result; } diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_all_empty_maps.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_all_empty_maps.dwrf new file mode 100755 index 0000000000000..68dca46a1db7d Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_all_empty_maps.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_all_null_maps.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_all_null_maps.dwrf new file mode 100755 index 0000000000000..6b27f13f7d418 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_all_null_maps.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_binary.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_binary.dwrf new file mode 100755 index 0000000000000..0efa10fc39bcd Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_binary.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_boolean.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_boolean.dwrf new file mode 100755 index 0000000000000..e9ddbf5c3cb76 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_boolean.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_boolean_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_boolean_with_null.dwrf new file mode 100755 index 0000000000000..aa9e2ce1bc1fe Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_boolean_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_byte.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_byte.dwrf new file mode 100755 index 0000000000000..aae7f616061aa Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_byte.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_byte_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_byte_with_null.dwrf new file mode 100755 index 0000000000000..ab0cd8c17556f Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_byte_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_double.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_double.dwrf new file mode 100755 index 0000000000000..0381edd170402 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_double.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_double_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_double_with_null.dwrf new file mode 100755 index 0000000000000..65e199aa3d779 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_double_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_float.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_float.dwrf new file mode 100755 index 0000000000000..3d50c6db1ac69 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_float.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_float_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_float_with_null.dwrf new file mode 100755 index 0000000000000..8986e2dbc1eaa Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_float_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_int.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_int.dwrf new file mode 100755 index 0000000000000..5395f2f338043 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_int.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_int_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_int_with_null.dwrf new file mode 100755 index 0000000000000..c95b96ff75483 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_int_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_list.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_list.dwrf new file mode 100755 index 0000000000000..ecb33e38e4996 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_list.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_list_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_list_with_null.dwrf new file mode 100755 index 0000000000000..c77e5e3b7ab97 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_list_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_long.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_long.dwrf new file mode 100755 index 0000000000000..b1b24a6076ecd Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_long.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_map.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_map.dwrf new file mode 100755 index 0000000000000..77be39ff4bac4 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_map.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_map_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_map_with_null.dwrf new file mode 100755 index 0000000000000..0469c3d904133 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_map_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_short.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_short.dwrf new file mode 100755 index 0000000000000..7692e61e73903 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_short.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_some_empty_maps.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_some_empty_maps.dwrf new file mode 100755 index 0000000000000..95d99cb91cbb1 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_some_empty_maps.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_some_null_maps.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_some_null_maps.dwrf new file mode 100755 index 0000000000000..2f97c1d4ddde5 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_some_null_maps.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_string.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_string.dwrf new file mode 100755 index 0000000000000..974d03c4f6382 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_string.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_string_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_string_with_null.dwrf new file mode 100755 index 0000000000000..6cc0fbc39dda1 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_string_with_null.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_struct.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_struct.dwrf new file mode 100755 index 0000000000000..be1292bfb4261 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_struct.dwrf differ diff --git a/presto-orc/src/test/resources/test_flat_map/flat_map_struct_with_null.dwrf b/presto-orc/src/test/resources/test_flat_map/flat_map_struct_with_null.dwrf new file mode 100755 index 0000000000000..69b34be2fe377 Binary files /dev/null and b/presto-orc/src/test/resources/test_flat_map/flat_map_struct_with_null.dwrf differ diff --git a/presto-parquet/pom.xml b/presto-parquet/pom.xml new file mode 100644 index 0000000000000..68368c60c4ee5 --- /dev/null +++ b/presto-parquet/pom.xml @@ -0,0 +1,180 @@ + + + 4.0.0 + + + com.facebook.presto + presto-root + 0.216-SNAPSHOT + + + presto-parquet + presto-parquet + + + ${project.parent.basedir} + + + + + com.facebook.presto + presto-memory-context + + + + com.facebook.presto.hadoop + hadoop-apache2 + provided + + + + com.facebook.presto.hive + hive-apache + + + + io.airlift + aircompressor + + + + com.google.guava + guava + + + + it.unimi.dsi + fastutil + + + + org.xerial.snappy + snappy-java + runtime + + + + + io.airlift + log-manager + runtime + + + + + com.facebook.presto + presto-spi + + + + io.airlift + slice + + + + + com.facebook.presto + presto-main + test + + + + com.facebook.presto + presto-tests + test + + + + com.facebook.presto + presto-tpch + test + + + + io.airlift.tpch + tpch + test + + + + org.jetbrains + annotations + provided + + + + org.testng + testng + test + + + + io.airlift + testing + test + + + + org.assertj + assertj-core + test + + + + org.anarres.lzo + lzo-hadoop + test + + + + + com.facebook.presto + presto-benchmark + test + + + + org.openjdk.jmh + jmh-core + test + + + + org.openjdk.jmh + jmh-generator-annprocess + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + **/TestFullParquetReader.java + + + + + + + + + ci + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + + + + + + diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPage.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPage.java similarity index 80% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPage.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/DataPage.java index b8d8122a32eac..065684e6f91fb 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPage.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPage.java @@ -11,14 +11,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; -public abstract class ParquetDataPage - extends ParquetPage +public abstract class DataPage + extends Page { protected final int valueCount; - public ParquetDataPage(int compressedSize, int uncompressedSize, int valueCount) + public DataPage(int compressedSize, int uncompressedSize, int valueCount) { super(compressedSize, uncompressedSize); this.valueCount = valueCount; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV1.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV1.java similarity index 95% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV1.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV1.java index 5e7a41eebd10d..f278fe1f2ee17 100755 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV1.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV1.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import io.airlift.slice.Slice; import parquet.column.statistics.Statistics; @@ -19,8 +19,8 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; -public class ParquetDataPageV1 - extends ParquetDataPage +public class DataPageV1 + extends DataPage { private final Slice slice; private final Statistics statistics; @@ -28,7 +28,7 @@ public class ParquetDataPageV1 private final ParquetEncoding definitionLevelEncoding; private final ParquetEncoding valuesEncoding; - public ParquetDataPageV1( + public DataPageV1( Slice slice, int valueCount, int uncompressedSize, diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV2.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV2.java similarity index 96% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV2.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV2.java index 39fb49db8cb6e..9034c0a66a004 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataPageV2.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/DataPageV2.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import io.airlift.slice.Slice; import parquet.column.statistics.Statistics; @@ -19,8 +19,8 @@ import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; -public class ParquetDataPageV2 - extends ParquetDataPage +public class DataPageV2 + extends DataPage { private final int rowCount; private final int nullCount; @@ -31,7 +31,7 @@ public class ParquetDataPageV2 private final Statistics statistics; private final boolean isCompressed; - public ParquetDataPageV2( + public DataPageV2( int rowCount, int nullCount, int valueCount, diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDictionaryPage.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/DictionaryPage.java similarity index 79% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDictionaryPage.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/DictionaryPage.java index c1b91d5bc43bb..dc48a898105f8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDictionaryPage.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/DictionaryPage.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import io.airlift.slice.Slice; @@ -21,14 +21,14 @@ import static io.airlift.slice.Slices.wrappedBuffer; import static java.util.Objects.requireNonNull; -public class ParquetDictionaryPage - extends ParquetPage +public class DictionaryPage + extends Page { private final Slice slice; private final int dictionarySize; private final ParquetEncoding encoding; - public ParquetDictionaryPage(Slice slice, int dictionarySize, ParquetEncoding encoding) + public DictionaryPage(Slice slice, int dictionarySize, ParquetEncoding encoding) { this(requireNonNull(slice, "slice is null"), slice.length(), @@ -36,7 +36,7 @@ public ParquetDictionaryPage(Slice slice, int dictionarySize, ParquetEncoding en requireNonNull(encoding, "encoding is null")); } - public ParquetDictionaryPage(Slice slice, int uncompressedSize, int dictionarySize, ParquetEncoding encoding) + public DictionaryPage(Slice slice, int uncompressedSize, int dictionarySize, ParquetEncoding encoding) { super(requireNonNull(slice, "slice is null").length(), uncompressedSize); this.slice = slice; @@ -59,9 +59,9 @@ public ParquetEncoding getEncoding() return encoding; } - public ParquetDictionaryPage copy() + public DictionaryPage copy() { - return new ParquetDictionaryPage(wrappedBuffer(Arrays.copyOf(slice.getBytes(), slice.length())), getUncompressedSize(), dictionarySize, encoding); + return new DictionaryPage(wrappedBuffer(Arrays.copyOf(slice.getBytes(), slice.length())), getUncompressedSize(), dictionarySize, encoding); } @Override diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/Field.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/Field.java similarity index 97% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/Field.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/Field.java index 063e426239694..c3ceea2db14f7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/Field.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/Field.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import com.facebook.presto.spi.type.Type; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/GroupField.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/GroupField.java similarity index 96% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/GroupField.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/GroupField.java index 934a1c2c43348..8618bb02940f7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/GroupField.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/GroupField.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPage.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/Page.java similarity index 86% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPage.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/Page.java index 50fee0831ad6a..b5b917af2aa57 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetPage.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/Page.java @@ -11,14 +11,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; -public abstract class ParquetPage +public abstract class Page { protected final int compressedSize; protected final int uncompressedSize; - public ParquetPage(int compressedSize, int uncompressedSize) + public Page(int compressedSize, int uncompressedSize) { this.compressedSize = compressedSize; this.uncompressedSize = uncompressedSize; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCompressionUtils.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCompressionUtils.java similarity index 99% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCompressionUtils.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCompressionUtils.java index ef9a300a4acdf..65579e6d1ddb8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCompressionUtils.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCompressionUtils.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import io.airlift.compress.Decompressor; import io.airlift.compress.lzo.LzoDecompressor; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCorruptionException.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCorruptionException.java similarity index 96% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCorruptionException.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCorruptionException.java index 7065bbe6142e2..d99e72c5f6596 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetCorruptionException.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetCorruptionException.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import java.io.IOException; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataSource.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSource.java similarity index 90% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataSource.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSource.java index 75cbdab480b7c..e28e982953257 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetDataSource.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSource.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import java.io.Closeable; import java.io.IOException; @@ -19,8 +19,12 @@ public interface ParquetDataSource extends Closeable { + ParquetDataSourceId getId(); + long getReadBytes(); + long getReadTimeNanos(); + long getSize(); void readFully(long position, byte[] buffer); diff --git a/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSourceId.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSourceId.java new file mode 100644 index 0000000000000..59bba0a598767 --- /dev/null +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetDataSourceId.java @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.parquet; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public final class ParquetDataSourceId +{ + private final String id; + + public ParquetDataSourceId(String id) + { + this.id = requireNonNull(id, "id is null"); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ParquetDataSourceId that = (ParquetDataSourceId) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() + { + return Objects.hash(id); + } + + @Override + public String toString() + { + return id; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetEncoding.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetEncoding.java similarity index 73% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetEncoding.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetEncoding.java index 77879cee2e769..fc4c93670c8d3 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetEncoding.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetEncoding.java @@ -11,15 +11,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; - -import com.facebook.presto.hive.parquet.dictionary.ParquetBinaryDictionary; -import com.facebook.presto.hive.parquet.dictionary.ParquetDictionary; -import com.facebook.presto.hive.parquet.dictionary.ParquetDictionaryReader; -import com.facebook.presto.hive.parquet.dictionary.ParquetDoubleDictionary; -import com.facebook.presto.hive.parquet.dictionary.ParquetFloatDictionary; -import com.facebook.presto.hive.parquet.dictionary.ParquetIntegerDictionary; -import com.facebook.presto.hive.parquet.dictionary.ParquetLongDictionary; +package com.facebook.presto.parquet; + +import com.facebook.presto.parquet.dictionary.BinaryDictionary; +import com.facebook.presto.parquet.dictionary.Dictionary; +import com.facebook.presto.parquet.dictionary.DictionaryReader; +import com.facebook.presto.parquet.dictionary.DoubleDictionary; +import com.facebook.presto.parquet.dictionary.FloatDictionary; +import com.facebook.presto.parquet.dictionary.IntegerDictionary; +import com.facebook.presto.parquet.dictionary.LongDictionary; import parquet.bytes.BytesUtils; import parquet.column.ColumnDescriptor; import parquet.column.values.ValuesReader; @@ -51,7 +51,7 @@ public enum ParquetEncoding { PLAIN { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { switch (descriptor.getType()) { case BOOLEAN: @@ -76,24 +76,24 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy } @Override - public ParquetDictionary initDictionary(ColumnDescriptor descriptor, ParquetDictionaryPage dictionaryPage) + public Dictionary initDictionary(ColumnDescriptor descriptor, DictionaryPage dictionaryPage) throws IOException { switch (descriptor.getType()) { case BINARY: - return new ParquetBinaryDictionary(dictionaryPage); + return new BinaryDictionary(dictionaryPage); case FIXED_LEN_BYTE_ARRAY: - return new ParquetBinaryDictionary(dictionaryPage, descriptor.getTypeLength()); + return new BinaryDictionary(dictionaryPage, descriptor.getTypeLength()); case INT96: - return new ParquetBinaryDictionary(dictionaryPage, INT96_TYPE_LENGTH); + return new BinaryDictionary(dictionaryPage, INT96_TYPE_LENGTH); case INT64: - return new ParquetLongDictionary(dictionaryPage); + return new LongDictionary(dictionaryPage); case DOUBLE: - return new ParquetDoubleDictionary(dictionaryPage); + return new DoubleDictionary(dictionaryPage); case INT32: - return new ParquetIntegerDictionary(dictionaryPage); + return new IntegerDictionary(dictionaryPage); case FLOAT: - return new ParquetFloatDictionary(dictionaryPage); + return new FloatDictionary(dictionaryPage); default: throw new ParquetDecodingException("Dictionary encoding does not support: " + descriptor.getType()); } @@ -102,7 +102,7 @@ public ParquetDictionary initDictionary(ColumnDescriptor descriptor, ParquetDict RLE { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { int bitWidth = BytesUtils.getWidthFromMaxInt(getMaxLevel(descriptor, valuesType)); if (bitWidth == 0) { @@ -114,7 +114,7 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy BIT_PACKED { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { return new ByteBitPackingValuesReader(getMaxLevel(descriptor, valuesType), BIG_ENDIAN); } @@ -122,13 +122,13 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy PLAIN_DICTIONARY { @Override - public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType, ParquetDictionary dictionary) + public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ValuesType valuesType, Dictionary dictionary) { return RLE_DICTIONARY.getDictionaryBasedValuesReader(descriptor, valuesType, dictionary); } @Override - public ParquetDictionary initDictionary(ColumnDescriptor descriptor, ParquetDictionaryPage dictionaryPage) + public Dictionary initDictionary(ColumnDescriptor descriptor, DictionaryPage dictionaryPage) throws IOException { return PLAIN.initDictionary(descriptor, dictionaryPage); @@ -143,7 +143,7 @@ public boolean usesDictionary() DELTA_BINARY_PACKED { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { checkArgument(descriptor.getType() == INT32, "Encoding DELTA_BINARY_PACKED is only supported for type INT32"); return new DeltaBinaryPackingValuesReader(); @@ -152,7 +152,7 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy DELTA_LENGTH_BYTE_ARRAY { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { checkArgument(descriptor.getType() == BINARY, "Encoding DELTA_LENGTH_BYTE_ARRAY is only supported for type BINARY"); return new DeltaLengthByteArrayValuesReader(); @@ -161,7 +161,7 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy DELTA_BYTE_ARRAY { @Override - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { checkArgument( descriptor.getType() == BINARY || descriptor.getType() == FIXED_LEN_BYTE_ARRAY, @@ -172,13 +172,13 @@ public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesTy RLE_DICTIONARY { @Override - public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType, ParquetDictionary dictionary) + public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ValuesType valuesType, Dictionary dictionary) { - return new ParquetDictionaryReader(dictionary); + return new DictionaryReader(dictionary); } @Override - public ParquetDictionary initDictionary(ColumnDescriptor descriptor, ParquetDictionaryPage dictionaryPage) + public Dictionary initDictionary(ColumnDescriptor descriptor, DictionaryPage dictionaryPage) throws IOException { return PLAIN.initDictionary(descriptor, dictionaryPage); @@ -193,7 +193,7 @@ public boolean usesDictionary() static final int INT96_TYPE_LENGTH = 12; - static int getMaxLevel(ColumnDescriptor descriptor, ParquetValuesType valuesType) + static int getMaxLevel(ColumnDescriptor descriptor, ValuesType valuesType) { switch (valuesType) { case REPETITION_LEVEL: @@ -205,7 +205,7 @@ static int getMaxLevel(ColumnDescriptor descriptor, ParquetValuesType valuesType return 1; } default: - throw new ParquetDecodingException("Unsupported Parquet values type: " + valuesType); + throw new ParquetDecodingException("Unsupported values type: " + valuesType); } } @@ -214,19 +214,19 @@ public boolean usesDictionary() return false; } - public ParquetDictionary initDictionary(ColumnDescriptor descriptor, ParquetDictionaryPage dictionaryPage) + public Dictionary initDictionary(ColumnDescriptor descriptor, DictionaryPage dictionaryPage) throws IOException { - throw new UnsupportedOperationException("Parquet Dictionary encoding is not supported for: " + name()); + throw new UnsupportedOperationException(" Dictionary encoding is not supported for: " + name()); } - public ValuesReader getValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType) + public ValuesReader getValuesReader(ColumnDescriptor descriptor, ValuesType valuesType) { - throw new UnsupportedOperationException("Error decoding Parquet values in encoding: " + this.name()); + throw new UnsupportedOperationException("Error decoding values in encoding: " + this.name()); } - public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ParquetValuesType valuesType, ParquetDictionary dictionary) + public ValuesReader getDictionaryBasedValuesReader(ColumnDescriptor descriptor, ValuesType valuesType, Dictionary dictionary) { - throw new UnsupportedOperationException("Parquet Dictionary encoding is not supported for: " + name()); + throw new UnsupportedOperationException(" Dictionary encoding is not supported for: " + name()); } } diff --git a/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTimestampUtils.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTimestampUtils.java new file mode 100644 index 0000000000000..7a1e856633a6e --- /dev/null +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTimestampUtils.java @@ -0,0 +1,61 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.parquet; + +import com.facebook.presto.spi.PrestoException; +import com.google.common.primitives.Ints; +import com.google.common.primitives.Longs; +import parquet.io.api.Binary; + +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; + +/** + * Utility class for decoding INT96 encoded parquet timestamp to timestamp millis in GMT. + *

    + */ +public final class ParquetTimestampUtils +{ + private static final int JULIAN_EPOCH_OFFSET_DAYS = 2_440_588; + private static final long MILLIS_IN_DAY = TimeUnit.DAYS.toMillis(1); + private static final long NANOS_PER_MILLISECOND = TimeUnit.MILLISECONDS.toNanos(1); + + private ParquetTimestampUtils() {} + + /** + * Returns GMT timestamp from binary encoded parquet timestamp (12 bytes - julian date + time of day nanos). + * + * @param timestampBinary INT96 parquet timestamp + * @return timestamp in millis, GMT timezone + */ + public static long getTimestampMillis(Binary timestampBinary) + { + if (timestampBinary.length() != 12) { + throw new PrestoException(NOT_SUPPORTED, "Parquet timestamp must be 12 bytes, actual " + timestampBinary.length()); + } + byte[] bytes = timestampBinary.getBytes(); + + // little endian encoding - need to invert byte order + long timeOfDayNanos = Longs.fromBytes(bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]); + int julianDay = Ints.fromBytes(bytes[11], bytes[10], bytes[9], bytes[8]); + + return julianDayToMillis(julianDay) + (timeOfDayNanos / NANOS_PER_MILLISECOND); + } + + private static long julianDayToMillis(int julianDay) + { + return (julianDay - JULIAN_EPOCH_OFFSET_DAYS) * MILLIS_IN_DAY; + } +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetTypeUtils.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTypeUtils.java similarity index 88% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetTypeUtils.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTypeUtils.java index 88c8bb895dc27..29644e2282b62 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetTypeUtils.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetTypeUtils.java @@ -11,17 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; -import com.facebook.presto.hive.HiveColumnHandle; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.BigintType; import com.facebook.presto.spi.type.BooleanType; +import com.facebook.presto.spi.type.DateType; import com.facebook.presto.spi.type.DecimalType; import com.facebook.presto.spi.type.DoubleType; -import com.facebook.presto.spi.type.IntegerType; import com.facebook.presto.spi.type.RealType; import com.facebook.presto.spi.type.TimestampType; import com.facebook.presto.spi.type.Type; @@ -37,6 +36,7 @@ import parquet.io.PrimitiveColumnIO; import parquet.schema.DecimalMetadata; import parquet.schema.MessageType; +import parquet.schema.OriginalType; import java.util.Arrays; import java.util.HashMap; @@ -46,8 +46,8 @@ import java.util.Optional; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Optional.empty; import static parquet.schema.OriginalType.DECIMAL; import static parquet.schema.Type.Repetition.REPEATED; @@ -128,7 +128,7 @@ public static Optional getDescriptor(List= 1, "Parquet nested path should have at least one component"); int index = getPathIndex(columns, path); if (index == -1) { - return empty(); + return Optional.empty(); } PrimitiveColumnIO columnIO = columns.get(index); return Optional.of(new RichColumnDescriptor(columnIO.getColumnDescriptor(), columnIO.getType().asPrimitiveType())); @@ -171,7 +171,7 @@ public static Type getPrestoType(TupleDomain effectivePredicat case DOUBLE: return DoubleType.DOUBLE; case INT32: - return createDecimalType(descriptor).orElse(IntegerType.INTEGER); + return getInt32Type(descriptor); case INT64: return createDecimalType(descriptor).orElse(BigintType.BIGINT); case INT96: @@ -214,18 +214,6 @@ public static int getFieldIndex(MessageType fileSchema, String name) } } - public static parquet.schema.Type getParquetType(HiveColumnHandle column, MessageType messageType, boolean useParquetColumnNames) - { - if (useParquetColumnNames) { - return getParquetTypeByName(column.getName(), messageType); - } - - if (column.getHiveColumnIndex() < messageType.getFieldCount()) { - return messageType.getType(column.getHiveColumnIndex()); - } - return null; - } - public static ParquetEncoding getParquetEncoding(Encoding encoding) { switch (encoding) { @@ -250,7 +238,7 @@ public static ParquetEncoding getParquetEncoding(Encoding encoding) } } - private static parquet.schema.Type getParquetTypeByName(String columnName, MessageType messageType) + public static parquet.schema.Type getParquetTypeByName(String columnName, MessageType messageType) { if (messageType.containsField(columnName)) { return messageType.getType(columnName); @@ -292,8 +280,12 @@ public static Optional createDecimalType(RichColumnDescriptor descriptor) if (descriptor.getPrimitiveType().getOriginalType() != DECIMAL) { return Optional.empty(); } - DecimalMetadata decimalMetadata = descriptor.getPrimitiveType().getDecimalMetadata(); - return Optional.of(DecimalType.createDecimalType(decimalMetadata.getPrecision(), decimalMetadata.getScale())); + return Optional.of(createDecimalType(descriptor.getPrimitiveType().getDecimalMetadata())); + } + + private static Type createDecimalType(DecimalMetadata decimalMetadata) + { + return DecimalType.createDecimalType(decimalMetadata.getPrecision(), decimalMetadata.getScale()); } /** @@ -306,4 +298,38 @@ public static boolean isValueNull(boolean required, int definitionLevel, int max { return !required && (definitionLevel == maxDefinitionLevel - 1); } + + // copied from presto-hive DecimalUtils + public static long getShortDecimalValue(byte[] bytes) + { + long value = 0; + if ((bytes[0] & 0x80) != 0) { + for (int i = 0; i < 8 - bytes.length; ++i) { + value |= 0xFFL << (8 * (7 - i)); + } + } + + for (int i = 0; i < bytes.length; i++) { + value |= ((long) bytes[bytes.length - i - 1] & 0xFFL) << (8 * i); + } + + return value; + } + + private static Type getInt32Type(RichColumnDescriptor descriptor) + { + OriginalType originalType = descriptor.getPrimitiveType().getOriginalType(); + if (originalType == null) { + return INTEGER; + } + + switch (originalType) { + case DECIMAL: + return createDecimalType(descriptor.getPrimitiveType().getDecimalMetadata()); + case DATE: + return DateType.DATE; + default: + return INTEGER; + } + } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetValidationUtils.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetValidationUtils.java similarity index 95% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetValidationUtils.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetValidationUtils.java index aae8432aa9a74..1e4ca48c9c764 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/ParquetValidationUtils.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ParquetValidationUtils.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import static java.lang.String.format; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/PrimitiveField.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/PrimitiveField.java similarity index 96% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/PrimitiveField.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/PrimitiveField.java index 4eca7ef9af7ed..71915444bba0a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/PrimitiveField.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/PrimitiveField.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import com.facebook.presto.spi.type.Type; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/RichColumnDescriptor.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/RichColumnDescriptor.java similarity index 97% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/RichColumnDescriptor.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/RichColumnDescriptor.java index 4f69e3a46dc2d..364018e3a489a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/RichColumnDescriptor.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/RichColumnDescriptor.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import parquet.column.ColumnDescriptor; import parquet.schema.PrimitiveType; diff --git a/presto-parquet/src/main/java/com/facebook/presto/parquet/ValuesType.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/ValuesType.java new file mode 100644 index 0000000000000..66558498dfa3f --- /dev/null +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/ValuesType.java @@ -0,0 +1,21 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.parquet; + +public enum ValuesType +{ + REPETITION_LEVEL, + DEFINITION_LEVEL, + VALUES +} diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetBinaryDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/BinaryDictionary.java similarity index 85% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetBinaryDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/BinaryDictionary.java index 529e95c218367..98de7778662fd 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetBinaryDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/BinaryDictionary.java @@ -11,9 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.io.api.Binary; import java.io.IOException; @@ -22,18 +22,18 @@ import static com.google.common.base.Preconditions.checkArgument; import static parquet.bytes.BytesUtils.readIntLittleEndian; -public class ParquetBinaryDictionary - extends ParquetDictionary +public class BinaryDictionary + extends Dictionary { private final Binary[] content; - public ParquetBinaryDictionary(ParquetDictionaryPage dictionaryPage) + public BinaryDictionary(DictionaryPage dictionaryPage) throws IOException { this(dictionaryPage, null); } - public ParquetBinaryDictionary(ParquetDictionaryPage dictionaryPage, Integer length) + public BinaryDictionary(DictionaryPage dictionaryPage, Integer length) throws IOException { super(dictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/Dictionary.java similarity index 83% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/Dictionary.java index 328992de7e80b..389ae428f03b9 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/Dictionary.java @@ -11,22 +11,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetEncoding; +import com.facebook.presto.parquet.ParquetEncoding; import parquet.io.api.Binary; import static com.google.common.base.Preconditions.checkArgument; -public abstract class ParquetDictionary +public abstract class Dictionary { private final ParquetEncoding encoding; - public ParquetDictionary(ParquetEncoding encoding) + public Dictionary(ParquetEncoding encoding) { checkArgument( encoding == ParquetEncoding.PLAIN_DICTIONARY || encoding == ParquetEncoding.PLAIN, - "Parquet dictionary does not support encoding: %s", encoding); + " dictionary does not support encoding: %s", encoding); this.encoding = encoding; } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionaryReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DictionaryReader.java similarity index 91% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionaryReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DictionaryReader.java index fca427e3e221c..714c1213e9cc1 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDictionaryReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DictionaryReader.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; import parquet.bytes.BytesUtils; import parquet.column.values.ValuesReader; @@ -24,13 +24,13 @@ import static com.google.common.base.Preconditions.checkArgument; -public class ParquetDictionaryReader +public class DictionaryReader extends ValuesReader { - private final ParquetDictionary dictionary; + private final Dictionary dictionary; private RunLengthBitPackingHybridDecoder decoder; - public ParquetDictionaryReader(ParquetDictionary dictionary) + public DictionaryReader(Dictionary dictionary) { this.dictionary = dictionary; } @@ -39,7 +39,7 @@ public ParquetDictionaryReader(ParquetDictionary dictionary) public void initFromPage(int valueCount, byte[] page, int offset) throws IOException { - checkArgument(page.length > offset, "Attempt to read offset not in the Parquet page"); + checkArgument(page.length > offset, "Attempt to read offset not in the page"); ByteArrayInputStream in = new ByteArrayInputStream(page, offset, page.length - offset); int bitWidth = BytesUtils.readIntLittleEndianOnOneByte(in); decoder = new RunLengthBitPackingHybridDecoder(bitWidth, in); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDoubleDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DoubleDictionary.java similarity index 85% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDoubleDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DoubleDictionary.java index 825b043a708d2..9ba33b629e480 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetDoubleDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/DoubleDictionary.java @@ -11,21 +11,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.column.values.plain.PlainValuesReader.DoublePlainValuesReader; import java.io.IOException; import static com.google.common.base.MoreObjects.toStringHelper; -public class ParquetDoubleDictionary - extends ParquetDictionary +public class DoubleDictionary + extends Dictionary { private final double[] content; - public ParquetDoubleDictionary(ParquetDictionaryPage dictionaryPage) + public DoubleDictionary(DictionaryPage dictionaryPage) throws IOException { super(dictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetFloatDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/FloatDictionary.java similarity index 85% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetFloatDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/FloatDictionary.java index 2db9b64b81eb3..2ae885f53f6c3 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetFloatDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/FloatDictionary.java @@ -11,21 +11,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.column.values.plain.PlainValuesReader.FloatPlainValuesReader; import java.io.IOException; import static com.google.common.base.MoreObjects.toStringHelper; -public class ParquetFloatDictionary - extends ParquetDictionary +public class FloatDictionary + extends Dictionary { private final float[] content; - public ParquetFloatDictionary(ParquetDictionaryPage dictionaryPage) + public FloatDictionary(DictionaryPage dictionaryPage) throws IOException { super(dictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetIntegerDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/IntegerDictionary.java similarity index 84% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetIntegerDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/IntegerDictionary.java index c4660797876be..0d8fb9974fa5f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetIntegerDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/IntegerDictionary.java @@ -11,21 +11,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.column.values.plain.PlainValuesReader.IntegerPlainValuesReader; import java.io.IOException; import static com.google.common.base.MoreObjects.toStringHelper; -public class ParquetIntegerDictionary - extends ParquetDictionary +public class IntegerDictionary + extends Dictionary { private final int[] content; - public ParquetIntegerDictionary(ParquetDictionaryPage dictionaryPage) + public IntegerDictionary(DictionaryPage dictionaryPage) throws IOException { super(dictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetLongDictionary.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/LongDictionary.java similarity index 85% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetLongDictionary.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/LongDictionary.java index adf2aeb60296d..a1b227d7f1134 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/dictionary/ParquetLongDictionary.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/dictionary/LongDictionary.java @@ -11,21 +11,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.dictionary; +package com.facebook.presto.parquet.dictionary; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.column.values.plain.PlainValuesReader.LongPlainValuesReader; import java.io.IOException; import static com.google.common.base.MoreObjects.toStringHelper; -public class ParquetLongDictionary - extends ParquetDictionary +public class LongDictionary + extends Dictionary { private final long[] content; - public ParquetLongDictionary(ParquetDictionaryPage dictionaryPage) + public LongDictionary(DictionaryPage dictionaryPage) throws IOException { super(dictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDictionaryDescriptor.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/DictionaryDescriptor.java similarity index 69% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDictionaryDescriptor.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/DictionaryDescriptor.java index e2ba85cbe29e0..f789e391f809a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDictionaryDescriptor.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/DictionaryDescriptor.java @@ -11,19 +11,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DictionaryPage; import parquet.column.ColumnDescriptor; import java.util.Optional; -public class ParquetDictionaryDescriptor +public class DictionaryDescriptor { private final ColumnDescriptor columnDescriptor; - private final Optional dictionaryPage; + private final Optional dictionaryPage; - public ParquetDictionaryDescriptor(ColumnDescriptor columnDescriptor, Optional dictionaryPage) + public DictionaryDescriptor(ColumnDescriptor columnDescriptor, Optional dictionaryPage) { this.columnDescriptor = columnDescriptor; this.dictionaryPage = dictionaryPage; @@ -34,7 +34,7 @@ public ColumnDescriptor getColumnDescriptor() return columnDescriptor; } - public Optional getDictionaryPage() + public Optional getDictionaryPage() { return dictionaryPage; } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDoubleStatistics.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetDoubleStatistics.java similarity index 95% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDoubleStatistics.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetDoubleStatistics.java index 8c70e0f93368f..1879663736f2a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetDoubleStatistics.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetDoubleStatistics.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; public class ParquetDoubleStatistics implements ParquetRangeStatistics diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetIntegerStatistics.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetIntegerStatistics.java similarity index 95% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetIntegerStatistics.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetIntegerStatistics.java index b5f8223a06269..46f0abde71bd2 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetIntegerStatistics.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetIntegerStatistics.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; public class ParquetIntegerStatistics implements ParquetRangeStatistics diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetRangeStatistics.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetRangeStatistics.java similarity index 92% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetRangeStatistics.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetRangeStatistics.java index 6d0ff77f0c278..43f54e55b53f7 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetRangeStatistics.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetRangeStatistics.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; public interface ParquetRangeStatistics { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetStringStatistics.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetStringStatistics.java similarity index 95% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetStringStatistics.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetStringStatistics.java index 7722e66fdeb09..89fc21d8e25db 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetStringStatistics.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/ParquetStringStatistics.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; import io.airlift.slice.Slice; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicate.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/Predicate.java similarity index 62% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicate.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/Predicate.java index f0b97b668c93b..e92a87c7a9933 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicate.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/Predicate.java @@ -11,25 +11,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.ParquetDataSourceId; import parquet.column.ColumnDescriptor; import parquet.column.statistics.Statistics; import java.util.Map; -public interface ParquetPredicate +public interface Predicate { - ParquetPredicate TRUE = new ParquetPredicate() + Predicate TRUE = new Predicate() { @Override - public boolean matches(long numberOfRows, Map> statistics) + public boolean matches(long numberOfRows, Map> statistics, ParquetDataSourceId id, boolean failOnCorruptedParquetStatistics) + throws ParquetCorruptionException { return true; } @Override - public boolean matches(Map dictionaries) + public boolean matches(Map dictionaries) { return true; } @@ -41,13 +44,16 @@ public boolean matches(Map dictio * @param numberOfRows the number of rows in the segment; this can be used with * Statistics to determine if a column is only null * @param statistics column statistics + * @param id Parquet file name + * @param failOnCorruptedParquetStatistics whether to fail query when scanning a Parquet file with corrupted statistics */ - boolean matches(long numberOfRows, Map> statistics); + boolean matches(long numberOfRows, Map> statistics, ParquetDataSourceId id, boolean failOnCorruptedParquetStatistics) + throws ParquetCorruptionException; /** * Should the Parquet Reader process a file section with the specified dictionary. * * @param dictionaries dictionaries per column */ - boolean matches(Map dictionaries); + boolean matches(Map dictionaries); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicateUtils.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/PredicateUtils.java similarity index 66% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicateUtils.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/PredicateUtils.java index 1de48312d1580..b4b85c2079de1 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/ParquetPredicateUtils.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/PredicateUtils.java @@ -11,14 +11,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; - -import com.facebook.presto.hive.HiveColumnHandle; -import com.facebook.presto.hive.parquet.ParquetDataSource; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; -import com.facebook.presto.hive.parquet.ParquetEncoding; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; -import com.facebook.presto.spi.predicate.Domain; +package com.facebook.presto.parquet.predicate; + +import com.facebook.presto.parquet.DictionaryPage; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.ParquetDataSource; +import com.facebook.presto.parquet.ParquetEncoding; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.type.Type; import com.google.common.annotations.VisibleForTesting; @@ -47,23 +46,21 @@ import java.util.Optional; import java.util.Set; -import static com.facebook.presto.hive.parquet.ParquetCompressionUtils.decompress; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getParquetEncoding; +import static com.facebook.presto.parquet.ParquetCompressionUtils.decompress; +import static com.facebook.presto.parquet.ParquetTypeUtils.getParquetEncoding; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.SmallintType.SMALLINT; import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.google.common.base.Verify.verify; import static io.airlift.slice.Slices.wrappedBuffer; import static java.lang.Math.toIntExact; -import static java.util.Map.Entry; -import static org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector.Category.PRIMITIVE; import static parquet.column.Encoding.BIT_PACKED; import static parquet.column.Encoding.PLAIN_DICTIONARY; import static parquet.column.Encoding.RLE; -public final class ParquetPredicateUtils +public final class PredicateUtils { - private ParquetPredicateUtils() + private PredicateUtils() { } @@ -76,29 +73,7 @@ public static boolean isStatisticsOverflow(Type type, ParquetIntegerStatistics p (type.equals(INTEGER) && (min < Integer.MIN_VALUE || max > Integer.MAX_VALUE)); } - public static TupleDomain getParquetTupleDomain(Map, RichColumnDescriptor> descriptorsByPath, TupleDomain effectivePredicate) - { - if (effectivePredicate.isNone()) { - return TupleDomain.none(); - } - - ImmutableMap.Builder predicate = ImmutableMap.builder(); - for (Entry entry : effectivePredicate.getDomains().get().entrySet()) { - HiveColumnHandle columnHandle = entry.getKey(); - // skip looking up predicates for complex types as Parquet only stores stats for primitives - if (!columnHandle.getHiveType().getCategory().equals(PRIMITIVE)) { - continue; - } - - RichColumnDescriptor descriptor = descriptorsByPath.get(ImmutableList.of(columnHandle.getName())); - if (descriptor != null) { - predicate.put(descriptor, entry.getValue()); - } - } - return TupleDomain.withColumnDomains(predicate.build()); - } - - public static ParquetPredicate buildParquetPredicate(MessageType requestedSchema, TupleDomain parquetTupleDomain, Map, RichColumnDescriptor> descriptorsByPath) + public static Predicate buildPredicate(MessageType requestedSchema, TupleDomain parquetTupleDomain, Map, RichColumnDescriptor> descriptorsByPath) { ImmutableList.Builder columnReferences = ImmutableList.builder(); for (String[] paths : requestedSchema.getPaths()) { @@ -110,14 +85,15 @@ public static ParquetPredicate buildParquetPredicate(MessageType requestedSchema return new TupleDomainParquetPredicate(parquetTupleDomain, columnReferences.build()); } - public static boolean predicateMatches(ParquetPredicate parquetPredicate, BlockMetaData block, ParquetDataSource dataSource, Map, RichColumnDescriptor> descriptorsByPath, TupleDomain parquetTupleDomain) + public static boolean predicateMatches(Predicate parquetPredicate, BlockMetaData block, ParquetDataSource dataSource, Map, RichColumnDescriptor> descriptorsByPath, TupleDomain parquetTupleDomain, boolean failOnCorruptedParquetStatistics) + throws ParquetCorruptionException { Map> columnStatistics = getStatistics(block, descriptorsByPath); - if (!parquetPredicate.matches(block.getRowCount(), columnStatistics)) { + if (!parquetPredicate.matches(block.getRowCount(), columnStatistics, dataSource.getId(), failOnCorruptedParquetStatistics)) { return false; } - Map dictionaries = getDictionaries(block, dataSource, descriptorsByPath, parquetTupleDomain); + Map dictionaries = getDictionaries(block, dataSource, descriptorsByPath, parquetTupleDomain); return parquetPredicate.matches(dictionaries); } @@ -136,9 +112,9 @@ private static Map> getStatistics(BlockMetaData return statistics.build(); } - private static Map getDictionaries(BlockMetaData blockMetadata, ParquetDataSource dataSource, Map, RichColumnDescriptor> descriptorsByPath, TupleDomain parquetTupleDomain) + private static Map getDictionaries(BlockMetaData blockMetadata, ParquetDataSource dataSource, Map, RichColumnDescriptor> descriptorsByPath, TupleDomain parquetTupleDomain) { - ImmutableMap.Builder dictionaries = ImmutableMap.builder(); + ImmutableMap.Builder dictionaries = ImmutableMap.builder(); for (ColumnChunkMetaData columnMetaData : blockMetadata.getColumns()) { RichColumnDescriptor descriptor = descriptorsByPath.get(Arrays.asList(columnMetaData.getPath().toArray())); if (descriptor != null) { @@ -146,8 +122,8 @@ private static Map getDictionarie int totalSize = toIntExact(columnMetaData.getTotalSize()); byte[] buffer = new byte[totalSize]; dataSource.readFully(columnMetaData.getStartingPos(), buffer); - Optional dictionaryPage = readDictionaryPage(buffer, columnMetaData.getCodec()); - dictionaries.put(descriptor, new ParquetDictionaryDescriptor(descriptor, dictionaryPage)); + Optional dictionaryPage = readDictionaryPage(buffer, columnMetaData.getCodec()); + dictionaries.put(descriptor, new DictionaryDescriptor(descriptor, dictionaryPage)); break; } } @@ -155,7 +131,7 @@ private static Map getDictionarie return dictionaries.build(); } - private static Optional readDictionaryPage(byte[] data, CompressionCodecName codecName) + private static Optional readDictionaryPage(byte[] data, CompressionCodecName codecName) { try { ByteArrayInputStream inputStream = new ByteArrayInputStream(data); @@ -170,7 +146,7 @@ private static Optional readDictionaryPage(byte[] data, C ParquetEncoding encoding = getParquetEncoding(Encoding.valueOf(dicHeader.getEncoding().name())); int dictionarySize = dicHeader.getNum_values(); - return Optional.of(new ParquetDictionaryPage(decompress(codecName, compressedData, pageHeader.getUncompressed_page_size()), dictionarySize, encoding)); + return Optional.of(new DictionaryPage(decompress(codecName, compressedData, pageHeader.getUncompressed_page_size()), dictionarySize, encoding)); } catch (IOException ignored) { return Optional.empty(); @@ -185,7 +161,7 @@ private static boolean isColumnPredicate(ColumnDescriptor columnDescriptor, Tupl @VisibleForTesting @SuppressWarnings("deprecation") - static boolean isOnlyDictionaryEncodingPages(Set encodings) + public static boolean isOnlyDictionaryEncodingPages(Set encodings) { // TODO: update to use EncodingStats in ColumnChunkMetaData when available if (encodings.contains(PLAIN_DICTIONARY)) { diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/TupleDomainParquetPredicate.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/TupleDomainParquetPredicate.java similarity index 80% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/TupleDomainParquetPredicate.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/TupleDomainParquetPredicate.java index 598a7d7a83a16..b1d24247eab6d 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/predicate/TupleDomainParquetPredicate.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/predicate/TupleDomainParquetPredicate.java @@ -11,11 +11,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.predicate; +package com.facebook.presto.parquet.predicate; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; -import com.facebook.presto.hive.parquet.dictionary.ParquetDictionary; +import com.facebook.presto.parquet.DictionaryPage; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.ParquetDataSourceId; +import com.facebook.presto.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.dictionary.Dictionary; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.Range; import com.facebook.presto.spi.predicate.TupleDomain; @@ -42,10 +44,11 @@ import java.util.Optional; import java.util.function.Function; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getPrestoType; -import static com.facebook.presto.hive.parquet.predicate.ParquetPredicateUtils.isStatisticsOverflow; +import static com.facebook.presto.parquet.ParquetTypeUtils.getPrestoType; +import static com.facebook.presto.parquet.predicate.PredicateUtils.isStatisticsOverflow; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DateType.DATE; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; import static com.facebook.presto.spi.type.RealType.REAL; @@ -53,10 +56,11 @@ import static com.facebook.presto.spi.type.TinyintType.TINYINT; import static com.facebook.presto.spi.type.Varchars.isVarcharType; import static java.lang.Float.floatToRawIntBits; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; public class TupleDomainParquetPredicate - implements ParquetPredicate + implements Predicate { private final TupleDomain effectivePredicate; private final List columns; @@ -68,7 +72,8 @@ public TupleDomainParquetPredicate(TupleDomain effectivePredic } @Override - public boolean matches(long numberOfRows, Map> statistics) + public boolean matches(long numberOfRows, Map> statistics, ParquetDataSourceId id, boolean failOnCorruptedParquetStatistics) + throws ParquetCorruptionException { if (numberOfRows == 0) { return false; @@ -85,7 +90,7 @@ public boolean matches(long numberOfRows, Map> s domain = Domain.all(type); } else { - domain = getDomain(type, numberOfRows, columnStatistics); + domain = getDomain(type, numberOfRows, columnStatistics, id, column.toString(), failOnCorruptedParquetStatistics); } domains.put(column, domain); } @@ -95,12 +100,12 @@ public boolean matches(long numberOfRows, Map> s } @Override - public boolean matches(Map dictionaries) + public boolean matches(Map dictionaries) { ImmutableMap.Builder domains = ImmutableMap.builder(); for (RichColumnDescriptor column : columns) { - ParquetDictionaryDescriptor dictionaryDescriptor = dictionaries.get(column); + DictionaryDescriptor dictionaryDescriptor = dictionaries.get(column); Domain domain = getDomain(getPrestoType(effectivePredicate, column), dictionaryDescriptor); if (domain != null) { domains.put(column, domain); @@ -112,7 +117,8 @@ public boolean matches(Map dictio } @VisibleForTesting - public static Domain getDomain(Type type, long rowCount, Statistics statistics) + public static Domain getDomain(Type type, long rowCount, Statistics statistics, ParquetDataSourceId id, String column, boolean failOnCorruptedParquetStatistics) + throws ParquetCorruptionException { if (statistics == null || statistics.isEmpty()) { return Domain.all(type); @@ -124,7 +130,6 @@ public static Domain getDomain(Type type, long rowCount, Statistics statistic boolean hasNullValue = statistics.getNumNulls() != 0L; - // ignore corrupted statistics if (statistics.genericGetMin() == null || statistics.genericGetMax() == null) { return Domain.create(ValueSet.all(type), hasNullValue); } @@ -148,16 +153,16 @@ else if ((type.equals(BIGINT) || type.equals(TINYINT) || type.equals(SMALLINT) | ParquetIntegerStatistics parquetIntegerStatistics; if (statistics instanceof LongStatistics) { LongStatistics longStatistics = (LongStatistics) statistics; - // ignore corrupted statistics if (longStatistics.genericGetMin() > longStatistics.genericGetMax()) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, longStatistics); return Domain.create(ValueSet.all(type), hasNullValue); } parquetIntegerStatistics = new ParquetIntegerStatistics(longStatistics.genericGetMin(), longStatistics.genericGetMax()); } else { IntStatistics intStatistics = (IntStatistics) statistics; - // ignore corrupted statistics if (intStatistics.genericGetMin() > intStatistics.genericGetMax()) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, intStatistics); return Domain.create(ValueSet.all(type), hasNullValue); } parquetIntegerStatistics = new ParquetIntegerStatistics((long) intStatistics.getMin(), (long) intStatistics.getMax()); @@ -169,8 +174,8 @@ else if ((type.equals(BIGINT) || type.equals(TINYINT) || type.equals(SMALLINT) | } else if (type.equals(REAL) && statistics instanceof FloatStatistics) { FloatStatistics floatStatistics = (FloatStatistics) statistics; - // ignore corrupted statistics if (floatStatistics.genericGetMin() > floatStatistics.genericGetMax()) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, floatStatistics); return Domain.create(ValueSet.all(type), hasNullValue); } @@ -182,8 +187,8 @@ else if (type.equals(REAL) && statistics instanceof FloatStatistics) { } else if (type.equals(DOUBLE) && statistics instanceof DoubleStatistics) { DoubleStatistics doubleStatistics = (DoubleStatistics) statistics; - // ignore corrupted statistics if (doubleStatistics.genericGetMin() > doubleStatistics.genericGetMax()) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, doubleStatistics); return Domain.create(ValueSet.all(type), hasNullValue); } ParquetDoubleStatistics parquetDoubleStatistics = new ParquetDoubleStatistics(doubleStatistics.genericGetMin(), doubleStatistics.genericGetMax()); @@ -193,30 +198,39 @@ else if (isVarcharType(type) && statistics instanceof BinaryStatistics) { BinaryStatistics binaryStatistics = (BinaryStatistics) statistics; Slice minSlice = Slices.wrappedBuffer(binaryStatistics.getMin().getBytes()); Slice maxSlice = Slices.wrappedBuffer(binaryStatistics.getMax().getBytes()); - // ignore corrupted statistics if (minSlice.compareTo(maxSlice) > 0) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, binaryStatistics); return Domain.create(ValueSet.all(type), hasNullValue); } ParquetStringStatistics parquetStringStatistics = new ParquetStringStatistics(minSlice, maxSlice); return createDomain(type, hasNullValue, parquetStringStatistics); } + else if (type.equals(DATE) && statistics instanceof IntStatistics) { + IntStatistics intStatistics = (IntStatistics) statistics; + if (intStatistics.genericGetMin() > intStatistics.genericGetMax()) { + failWithCorruptionException(failOnCorruptedParquetStatistics, column, id, intStatistics); + return Domain.create(ValueSet.all(type), hasNullValue); + } + ParquetIntegerStatistics parquetIntegerStatistics = new ParquetIntegerStatistics((long) intStatistics.getMin(), (long) intStatistics.getMax()); + return createDomain(type, hasNullValue, parquetIntegerStatistics); + } return Domain.create(ValueSet.all(type), hasNullValue); } @VisibleForTesting - public static Domain getDomain(Type type, ParquetDictionaryDescriptor dictionaryDescriptor) + public static Domain getDomain(Type type, DictionaryDescriptor dictionaryDescriptor) { if (dictionaryDescriptor == null) { return null; } ColumnDescriptor columnDescriptor = dictionaryDescriptor.getColumnDescriptor(); - Optional dictionaryPage = dictionaryDescriptor.getDictionaryPage(); + Optional dictionaryPage = dictionaryDescriptor.getDictionaryPage(); if (!dictionaryPage.isPresent()) { return null; } - ParquetDictionary dictionary; + Dictionary dictionary; try { dictionary = dictionaryPage.get().getEncoding().initDictionary(columnDescriptor, dictionaryPage.get()); } @@ -235,7 +249,7 @@ public static Domain getDomain(Type type, ParquetDictionaryDescriptor dictionary domains.add(Domain.onlyNull(type)); return Domain.union(domains); } - else if (type.equals(BIGINT) && columnDescriptor.getType() == PrimitiveTypeName.INT32) { + else if ((type.equals(BIGINT) || type.equals(DATE)) && columnDescriptor.getType() == PrimitiveTypeName.INT32) { List domains = new ArrayList<>(); for (int i = 0; i < dictionarySize; i++) { domains.add(Domain.singleValue(type, (long) dictionary.decodeToInt(i))); @@ -270,6 +284,14 @@ else if (isVarcharType(type) && columnDescriptor.getType() == PrimitiveTypeName. return null; } + private static void failWithCorruptionException(boolean failOnCorruptedParquetStatistics, String column, ParquetDataSourceId id, Statistics statistics) + throws ParquetCorruptionException + { + if (failOnCorruptedParquetStatistics) { + throw new ParquetCorruptionException(format("Corrupted statistics for column \"%s\" in Parquet file \"%s\": [%s]", column, id, statistics)); + } + } + private static > Domain createDomain(Type type, boolean hasNullValue, ParquetRangeStatistics rangeStatistics) { return createDomain(type, hasNullValue, rangeStatistics, value -> value); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBinaryColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BinaryColumnReader.java similarity index 88% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBinaryColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BinaryColumnReader.java index d63b4b60ed9dd..2722e028604ff 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBinaryColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BinaryColumnReader.java @@ -11,9 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; import io.airlift.slice.Slice; @@ -26,10 +26,10 @@ import static io.airlift.slice.Slices.EMPTY_SLICE; import static io.airlift.slice.Slices.wrappedBuffer; -public class ParquetBinaryColumnReader - extends ParquetPrimitiveColumnReader +public class BinaryColumnReader + extends PrimitiveColumnReader { - public ParquetBinaryColumnReader(RichColumnDescriptor descriptor) + public BinaryColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBooleanColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BooleanColumnReader.java similarity index 82% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBooleanColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BooleanColumnReader.java index 3d1f0225f4dc4..24bc03236a299 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetBooleanColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/BooleanColumnReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; -public class ParquetBooleanColumnReader - extends ParquetPrimitiveColumnReader +public class BooleanColumnReader + extends PrimitiveColumnReader { - public ParquetBooleanColumnReader(RichColumnDescriptor descriptor) + public BooleanColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ColumnChunk.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunk.java similarity index 96% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ColumnChunk.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunk.java index 1cfcabe844418..f4a1a8dcf3dcc 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ColumnChunk.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunk.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; import com.facebook.presto.spi.block.Block; diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunkDescriptor.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunkDescriptor.java similarity index 90% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunkDescriptor.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunkDescriptor.java index 65a148a29241e..388ca74b0f041 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunkDescriptor.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ColumnChunkDescriptor.java @@ -11,18 +11,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; import parquet.column.ColumnDescriptor; import parquet.hadoop.metadata.ColumnChunkMetaData; -public class ParquetColumnChunkDescriptor +public class ColumnChunkDescriptor { private final ColumnDescriptor columnDescriptor; private final ColumnChunkMetaData columnChunkMetaData; private final int size; - public ParquetColumnChunkDescriptor( + public ColumnChunkDescriptor( ColumnDescriptor columnDescriptor, ColumnChunkMetaData columnChunkMetaData, int size) diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDecimalColumnReaderFactory.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DecimalColumnReaderFactory.java similarity index 62% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDecimalColumnReaderFactory.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DecimalColumnReaderFactory.java index 4903f3811fc6b..4616f0563bc56 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDecimalColumnReaderFactory.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DecimalColumnReaderFactory.java @@ -11,23 +11,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.type.DecimalType; -public final class ParquetDecimalColumnReaderFactory +public final class DecimalColumnReaderFactory { - private ParquetDecimalColumnReaderFactory() {} + private DecimalColumnReaderFactory() {} - public static ParquetPrimitiveColumnReader createReader(RichColumnDescriptor descriptor, int precision, int scale) + public static PrimitiveColumnReader createReader(RichColumnDescriptor descriptor, int precision, int scale) { DecimalType decimalType = DecimalType.createDecimalType(precision, scale); if (decimalType.isShort()) { - return new ParquetShortDecimalColumnReader(descriptor); + return new ShortDecimalColumnReader(descriptor); } else { - return new ParquetLongDecimalColumnReader(descriptor); + return new LongDecimalColumnReader(descriptor); } } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDoubleColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DoubleColumnReader.java similarity index 82% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDoubleColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DoubleColumnReader.java index 416ae35f10dc9..5adb4ee8fbd3e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetDoubleColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/DoubleColumnReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; -public class ParquetDoubleColumnReader - extends ParquetPrimitiveColumnReader +public class DoubleColumnReader + extends PrimitiveColumnReader { - public ParquetDoubleColumnReader(RichColumnDescriptor descriptor) + public DoubleColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetFloatColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/FloatColumnReader.java similarity index 82% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetFloatColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/FloatColumnReader.java index 559b3e6d2f10f..d8e8253d65cf4 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetFloatColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/FloatColumnReader.java @@ -11,18 +11,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; import static java.lang.Float.floatToRawIntBits; -public class ParquetFloatColumnReader - extends ParquetPrimitiveColumnReader +public class FloatColumnReader + extends PrimitiveColumnReader { - public ParquetFloatColumnReader(RichColumnDescriptor descriptor) + public FloatColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetIntColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/IntColumnReader.java similarity index 82% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetIntColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/IntColumnReader.java index 8564ef56cfdee..33e5d3d477388 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetIntColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/IntColumnReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; -public class ParquetIntColumnReader - extends ParquetPrimitiveColumnReader +public class IntColumnReader + extends PrimitiveColumnReader { - public ParquetIntColumnReader(RichColumnDescriptor descriptor) + public IntColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelNullReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelNullReader.java similarity index 83% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelNullReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelNullReader.java index 444f6b31e051e..04004504d39f8 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelNullReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelNullReader.java @@ -11,10 +11,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -public class ParquetLevelNullReader - implements ParquetLevelReader +public class LevelNullReader + implements LevelReader { @Override public int readLevel() diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelRLEReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelRLEReader.java similarity index 83% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelRLEReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelRLEReader.java index 60d04f7e7766f..f64ef584d9808 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelRLEReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelRLEReader.java @@ -11,19 +11,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; import parquet.column.values.rle.RunLengthBitPackingHybridDecoder; import parquet.io.ParquetDecodingException; import java.io.IOException; -public class ParquetLevelRLEReader - implements ParquetLevelReader +public class LevelRLEReader + implements LevelReader { private final RunLengthBitPackingHybridDecoder delegate; - public ParquetLevelRLEReader(RunLengthBitPackingHybridDecoder delegate) + public LevelRLEReader(RunLengthBitPackingHybridDecoder delegate) { this.delegate = delegate; } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelReader.java similarity index 87% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelReader.java index 2bfe049128ace..4028afdd99c0f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelReader.java @@ -11,9 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -public interface ParquetLevelReader +public interface LevelReader { int readLevel(); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelValuesReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelValuesReader.java similarity index 81% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelValuesReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelValuesReader.java index 6e36cc9e3ee57..622ccdb4501bd 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLevelValuesReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LevelValuesReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; import parquet.column.values.ValuesReader; -public class ParquetLevelValuesReader - implements ParquetLevelReader +public class LevelValuesReader + implements LevelReader { private final ValuesReader delegate; - public ParquetLevelValuesReader(ValuesReader delegate) + public LevelValuesReader(ValuesReader delegate) { this.delegate = delegate; } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetListColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ListColumnReader.java similarity index 94% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetListColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ListColumnReader.java index d24fc55d86c94..71773540cd998 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetListColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ListColumnReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.Field; -import com.facebook.presto.hive.parquet.ParquetTypeUtils; +import com.facebook.presto.parquet.Field; +import com.facebook.presto.parquet.ParquetTypeUtils; import it.unimi.dsi.fastutil.booleans.BooleanList; import it.unimi.dsi.fastutil.ints.IntList; -public class ParquetListColumnReader +public class ListColumnReader { - private ParquetListColumnReader() + private ListColumnReader() { } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongColumnReader.java similarity index 82% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongColumnReader.java index 7c1da0904c502..a4284b5fb8964 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongColumnReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; -public class ParquetLongColumnReader - extends ParquetPrimitiveColumnReader +public class LongColumnReader + extends PrimitiveColumnReader { - public ParquetLongColumnReader(RichColumnDescriptor descriptor) + public LongColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongDecimalColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongDecimalColumnReader.java similarity index 84% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongDecimalColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongDecimalColumnReader.java index b2a3ad711283b..f090080c2214c 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetLongDecimalColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/LongDecimalColumnReader.java @@ -11,9 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.Type; @@ -21,10 +21,10 @@ import java.math.BigInteger; -public class ParquetLongDecimalColumnReader - extends ParquetPrimitiveColumnReader +public class LongDecimalColumnReader + extends PrimitiveColumnReader { - ParquetLongDecimalColumnReader(RichColumnDescriptor descriptor) + LongDecimalColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetMetadataReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/MetadataReader.java similarity index 98% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetMetadataReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/MetadataReader.java index f45ef6373cb0c..bfe31878c9f2f 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetMetadataReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/MetadataReader.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileSystem; @@ -51,16 +51,16 @@ import java.util.Map; import java.util.Set; -import static com.facebook.presto.hive.parquet.ParquetValidationUtils.validateParquet; +import static com.facebook.presto.parquet.ParquetValidationUtils.validateParquet; import static java.nio.charset.StandardCharsets.US_ASCII; import static parquet.format.Util.readFileMetaData; -public final class ParquetMetadataReader +public final class MetadataReader { private static final int PARQUET_METADATA_LENGTH = 4; private static final byte[] MAGIC = "PAR1".getBytes(US_ASCII); - private ParquetMetadataReader() {} + private MetadataReader() {} public static ParquetMetadata readFooter(FileSystem fileSystem, Path file, long fileSize) throws IOException diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPageReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PageReader.java similarity index 72% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPageReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PageReader.java index 4302c3be22fea..6e036e2be9b9e 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPageReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PageReader.java @@ -11,37 +11,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.ParquetDataPage; -import com.facebook.presto.hive.parquet.ParquetDataPageV1; -import com.facebook.presto.hive.parquet.ParquetDataPageV2; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DataPage; +import com.facebook.presto.parquet.DataPageV1; +import com.facebook.presto.parquet.DataPageV2; +import com.facebook.presto.parquet.DictionaryPage; import parquet.hadoop.metadata.CompressionCodecName; import java.io.IOException; import java.util.LinkedList; import java.util.List; -import static com.facebook.presto.hive.parquet.ParquetCompressionUtils.decompress; +import static com.facebook.presto.parquet.ParquetCompressionUtils.decompress; import static java.lang.Math.toIntExact; -class ParquetPageReader +class PageReader { private final CompressionCodecName codec; private final long valueCount; - private final List compressedPages; - private final ParquetDictionaryPage compressedDictionaryPage; + private final List compressedPages; + private final DictionaryPage compressedDictionaryPage; - public ParquetPageReader(CompressionCodecName codec, - List compressedPages, - ParquetDictionaryPage compressedDictionaryPage) + public PageReader(CompressionCodecName codec, + List compressedPages, + DictionaryPage compressedDictionaryPage) { this.codec = codec; this.compressedPages = new LinkedList<>(compressedPages); this.compressedDictionaryPage = compressedDictionaryPage; int count = 0; - for (ParquetDataPage page : compressedPages) { + for (DataPage page : compressedPages) { count += page.getValueCount(); } this.valueCount = count; @@ -52,16 +52,16 @@ public long getTotalValueCount() return valueCount; } - public ParquetDataPage readPage() + public DataPage readPage() { if (compressedPages.isEmpty()) { return null; } - ParquetDataPage compressedPage = compressedPages.remove(0); + DataPage compressedPage = compressedPages.remove(0); try { - if (compressedPage instanceof ParquetDataPageV1) { - ParquetDataPageV1 dataPageV1 = (ParquetDataPageV1) compressedPage; - return new ParquetDataPageV1( + if (compressedPage instanceof DataPageV1) { + DataPageV1 dataPageV1 = (DataPageV1) compressedPage; + return new DataPageV1( decompress(codec, dataPageV1.getSlice(), dataPageV1.getUncompressedSize()), dataPageV1.getValueCount(), dataPageV1.getUncompressedSize(), @@ -71,14 +71,14 @@ public ParquetDataPage readPage() dataPageV1.getValueEncoding()); } else { - ParquetDataPageV2 dataPageV2 = (ParquetDataPageV2) compressedPage; + DataPageV2 dataPageV2 = (DataPageV2) compressedPage; if (!dataPageV2.isCompressed()) { return dataPageV2; } int uncompressedSize = toIntExact(dataPageV2.getUncompressedSize() - dataPageV2.getDefinitionLevels().length() - dataPageV2.getRepetitionLevels().length()); - return new ParquetDataPageV2( + return new DataPageV2( dataPageV2.getRowCount(), dataPageV2.getNullCount(), dataPageV2.getValueCount(), @@ -96,13 +96,13 @@ public ParquetDataPage readPage() } } - public ParquetDictionaryPage readDictionaryPage() + public DictionaryPage readDictionaryPage() { if (compressedDictionaryPage == null) { return null; } try { - return new ParquetDictionaryPage( + return new DictionaryPage( decompress(codec, compressedDictionaryPage.getSlice(), compressedDictionaryPage.getUncompressedSize()), compressedDictionaryPage.getDictionarySize(), compressedDictionaryPage.getEncoding()); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunk.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetColumnChunk.java similarity index 79% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunk.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetColumnChunk.java index 188ae23d5259f..c54c1628b0462 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetColumnChunk.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetColumnChunk.java @@ -11,13 +11,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.ParquetCorruptionException; -import com.facebook.presto.hive.parquet.ParquetDataPage; -import com.facebook.presto.hive.parquet.ParquetDataPageV1; -import com.facebook.presto.hive.parquet.ParquetDataPageV2; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; +import com.facebook.presto.parquet.DataPage; +import com.facebook.presto.parquet.DataPageV1; +import com.facebook.presto.parquet.DataPageV2; +import com.facebook.presto.parquet.DictionaryPage; +import com.facebook.presto.parquet.ParquetCorruptionException; import io.airlift.slice.Slice; import parquet.column.Encoding; import parquet.format.DataPageHeader; @@ -31,16 +31,16 @@ import java.util.ArrayList; import java.util.List; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.getParquetEncoding; +import static com.facebook.presto.parquet.ParquetTypeUtils.getParquetEncoding; import static io.airlift.slice.Slices.wrappedBuffer; public class ParquetColumnChunk extends ByteArrayInputStream { - private final ParquetColumnChunkDescriptor descriptor; + private final ColumnChunkDescriptor descriptor; public ParquetColumnChunk( - ParquetColumnChunkDescriptor descriptor, + ColumnChunkDescriptor descriptor, byte[] data, int offset) { @@ -49,7 +49,7 @@ public ParquetColumnChunk( this.pos = offset; } - public ParquetColumnChunkDescriptor getDescriptor() + public ColumnChunkDescriptor getDescriptor() { return descriptor; } @@ -60,11 +60,11 @@ protected PageHeader readPageHeader() return Util.readPageHeader(this); } - public ParquetPageReader readAllPages() + public PageReader readAllPages() throws IOException { - List pages = new ArrayList<>(); - ParquetDictionaryPage dictionaryPage = null; + List pages = new ArrayList<>(); + DictionaryPage dictionaryPage = null; long valueCount = 0; while (valueCount < descriptor.getColumnChunkMetaData().getValueCount()) { PageHeader pageHeader = readPageHeader(); @@ -88,7 +88,7 @@ public ParquetPageReader readAllPages() break; } } - return new ParquetPageReader(descriptor.getColumnChunkMetaData().getCodec(), pages, dictionaryPage); + return new PageReader(descriptor.getColumnChunkMetaData().getCodec(), pages, dictionaryPage); } public int getPosition() @@ -103,10 +103,10 @@ private Slice getSlice(int size) return slice; } - private ParquetDictionaryPage readDictionaryPage(PageHeader pageHeader, int uncompressedPageSize, int compressedPageSize) + private DictionaryPage readDictionaryPage(PageHeader pageHeader, int uncompressedPageSize, int compressedPageSize) { DictionaryPageHeader dicHeader = pageHeader.getDictionary_page_header(); - return new ParquetDictionaryPage( + return new DictionaryPage( getSlice(compressedPageSize), uncompressedPageSize, dicHeader.getNum_values(), @@ -116,14 +116,14 @@ private ParquetDictionaryPage readDictionaryPage(PageHeader pageHeader, int unco private long readDataPageV1(PageHeader pageHeader, int uncompressedPageSize, int compressedPageSize, - List pages) + List pages) { DataPageHeader dataHeaderV1 = pageHeader.getData_page_header(); - pages.add(new ParquetDataPageV1( + pages.add(new DataPageV1( getSlice(compressedPageSize), dataHeaderV1.getNum_values(), uncompressedPageSize, - ParquetMetadataReader.readStats( + MetadataReader.readStats( dataHeaderV1.getStatistics(), descriptor.getColumnDescriptor().getType()), getParquetEncoding(Encoding.valueOf(dataHeaderV1.getRepetition_level_encoding().name())), @@ -135,11 +135,11 @@ private long readDataPageV1(PageHeader pageHeader, private long readDataPageV2(PageHeader pageHeader, int uncompressedPageSize, int compressedPageSize, - List pages) + List pages) { DataPageHeaderV2 dataHeaderV2 = pageHeader.getData_page_header_v2(); int dataSize = compressedPageSize - dataHeaderV2.getRepetition_levels_byte_length() - dataHeaderV2.getDefinition_levels_byte_length(); - pages.add(new ParquetDataPageV2( + pages.add(new DataPageV2( dataHeaderV2.getNum_rows(), dataHeaderV2.getNum_nulls(), dataHeaderV2.getNum_values(), @@ -148,7 +148,7 @@ private long readDataPageV2(PageHeader pageHeader, getParquetEncoding(Encoding.valueOf(dataHeaderV2.getEncoding().name())), getSlice(dataSize), uncompressedPageSize, - ParquetMetadataReader.readStats( + MetadataReader.readStats( dataHeaderV2.getStatistics(), descriptor.getColumnDescriptor().getType()), dataHeaderV2.isIs_compressed())); diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetReader.java similarity index 87% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetReader.java index cf6b544fadf25..570bd37bd2a98 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ParquetReader.java @@ -11,16 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.Field; -import com.facebook.presto.hive.parquet.GroupField; -import com.facebook.presto.hive.parquet.ParquetCorruptionException; -import com.facebook.presto.hive.parquet.ParquetDataSource; -import com.facebook.presto.hive.parquet.PrimitiveField; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; import com.facebook.presto.memory.context.AggregatedMemoryContext; import com.facebook.presto.memory.context.LocalMemoryContext; +import com.facebook.presto.parquet.Field; +import com.facebook.presto.parquet.GroupField; +import com.facebook.presto.parquet.ParquetCorruptionException; +import com.facebook.presto.parquet.ParquetDataSource; +import com.facebook.presto.parquet.PrimitiveField; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.ArrayBlock; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.block.RowBlock; @@ -45,8 +45,8 @@ import java.util.List; import java.util.Optional; -import static com.facebook.presto.hive.parquet.ParquetValidationUtils.validateParquet; -import static com.facebook.presto.hive.parquet.reader.ParquetListColumnReader.calculateCollectionOffsets; +import static com.facebook.presto.parquet.ParquetValidationUtils.validateParquet; +import static com.facebook.presto.parquet.reader.ListColumnReader.calculateCollectionOffsets; import static com.facebook.presto.spi.type.StandardTypes.ARRAY; import static com.facebook.presto.spi.type.StandardTypes.MAP; import static com.facebook.presto.spi.type.StandardTypes.ROW; @@ -71,7 +71,7 @@ public class ParquetReader private long currentGroupRowCount; private long nextRowInGroup; private int batchSize; - private final ParquetPrimitiveColumnReader[] columnReaders; + private final PrimitiveColumnReader[] columnReaders; private AggregatedMemoryContext currentRowGroupMemoryContext; @@ -85,7 +85,7 @@ public ParquetReader(MessageColumnIO messageColumnIO, this.systemMemoryContext = requireNonNull(systemMemoryContext, "systemMemoryContext is null"); this.currentRowGroupMemoryContext = systemMemoryContext.newAggregatedMemoryContext(); columns = messageColumnIO.getLeaves(); - columnReaders = new ParquetPrimitiveColumnReader[columns.size()]; + columnReaders = new PrimitiveColumnReader[columns.size()]; } @Override @@ -144,7 +144,7 @@ private ColumnChunk readArray(GroupField field) BooleanList valueIsNull = new BooleanArrayList(); calculateCollectionOffsets(field, offsets, valueIsNull, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); - Block arrayBlock = ArrayBlock.fromElementBlock(valueIsNull.size(), valueIsNull.toBooleanArray(), offsets.toIntArray(), columnChunk.getBlock()); + Block arrayBlock = ArrayBlock.fromElementBlock(valueIsNull.size(), Optional.of(valueIsNull.toBooleanArray()), offsets.toIntArray(), columnChunk.getBlock()); return new ColumnChunk(arrayBlock, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); } @@ -161,7 +161,7 @@ private ColumnChunk readMap(GroupField field) IntList offsets = new IntArrayList(); BooleanList valueIsNull = new BooleanArrayList(); calculateCollectionOffsets(field, offsets, valueIsNull, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); - Block mapBlock = ((MapType) field.getType()).createBlockFromKeyValue(valueIsNull.toBooleanArray(), offsets.toIntArray(), blocks[0], blocks[1]); + Block mapBlock = ((MapType) field.getType()).createBlockFromKeyValue(Optional.of(valueIsNull.toBooleanArray()), offsets.toIntArray(), blocks[0], blocks[1]); return new ColumnChunk(mapBlock, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); } @@ -184,8 +184,9 @@ private ColumnChunk readStruct(GroupField field) blocks[i] = RunLengthEncodedBlock.create(field.getType(), null, columnChunk.getBlock().getPositionCount()); } } - BooleanList structIsNull = ParquetStructColumnReader.calculateStructOffsets(field, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); - Block rowBlock = RowBlock.fromFieldBlocks(structIsNull.toBooleanArray(), blocks); + BooleanList structIsNull = StructColumnReader.calculateStructOffsets(field, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); + boolean[] structIsNullVector = structIsNull.toBooleanArray(); + Block rowBlock = RowBlock.fromFieldBlocks(structIsNullVector.length, Optional.of(structIsNullVector), blocks); return new ColumnChunk(rowBlock, columnChunk.getDefinitionLevels(), columnChunk.getRepetitionLevels()); } @@ -193,7 +194,7 @@ private ColumnChunk readPrimitive(PrimitiveField field) throws IOException { ColumnDescriptor columnDescriptor = field.getDescriptor(); - ParquetPrimitiveColumnReader columnReader = columnReaders[field.getId()]; + PrimitiveColumnReader columnReader = columnReaders[field.getId()]; if (columnReader.getPageReader() == null) { validateParquet(currentBlockMetadata.getRowCount() > 0, "Row group has 0 rows"); ColumnChunkMetaData metadata = getColumnChunkMetaData(columnDescriptor); @@ -201,7 +202,7 @@ private ColumnChunk readPrimitive(PrimitiveField field) int totalSize = toIntExact(metadata.getTotalSize()); byte[] buffer = allocateBlock(totalSize); dataSource.readFully(startingPosition, buffer); - ParquetColumnChunkDescriptor descriptor = new ParquetColumnChunkDescriptor(columnDescriptor, metadata, totalSize); + ColumnChunkDescriptor descriptor = new ColumnChunkDescriptor(columnDescriptor, metadata, totalSize); ParquetColumnChunk columnChunk = new ParquetColumnChunk(descriptor, buffer, 0); columnReader.setPageReader(columnChunk.readAllPages()); } @@ -231,7 +232,7 @@ private void initializeColumnReaders() { for (PrimitiveColumnIO columnIO : columns) { RichColumnDescriptor column = new RichColumnDescriptor(columnIO.getColumnDescriptor(), columnIO.getType().asPrimitiveType()); - columnReaders[columnIO.getId()] = ParquetPrimitiveColumnReader.createReader(column); + columnReaders[columnIO.getId()] = PrimitiveColumnReader.createReader(column); } } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPrimitiveColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PrimitiveColumnReader.java similarity index 76% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPrimitiveColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PrimitiveColumnReader.java index db60d55af3633..e2ad69008617a 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetPrimitiveColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/PrimitiveColumnReader.java @@ -11,17 +11,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.Field; -import com.facebook.presto.hive.parquet.ParquetDataPage; -import com.facebook.presto.hive.parquet.ParquetDataPageV1; -import com.facebook.presto.hive.parquet.ParquetDataPageV2; -import com.facebook.presto.hive.parquet.ParquetDictionaryPage; -import com.facebook.presto.hive.parquet.ParquetEncoding; -import com.facebook.presto.hive.parquet.ParquetTypeUtils; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; -import com.facebook.presto.hive.parquet.dictionary.ParquetDictionary; +import com.facebook.presto.parquet.DataPage; +import com.facebook.presto.parquet.DataPageV1; +import com.facebook.presto.parquet.DataPageV2; +import com.facebook.presto.parquet.DictionaryPage; +import com.facebook.presto.parquet.Field; +import com.facebook.presto.parquet.ParquetEncoding; +import com.facebook.presto.parquet.ParquetTypeUtils; +import com.facebook.presto.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.dictionary.Dictionary; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.DecimalType; @@ -40,16 +40,16 @@ import java.util.Optional; import java.util.function.Consumer; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.createDecimalType; -import static com.facebook.presto.hive.parquet.ParquetValuesType.DEFINITION_LEVEL; -import static com.facebook.presto.hive.parquet.ParquetValuesType.REPETITION_LEVEL; -import static com.facebook.presto.hive.parquet.ParquetValuesType.VALUES; +import static com.facebook.presto.parquet.ParquetTypeUtils.createDecimalType; +import static com.facebook.presto.parquet.ValuesType.DEFINITION_LEVEL; +import static com.facebook.presto.parquet.ValuesType.REPETITION_LEVEL; +import static com.facebook.presto.parquet.ValuesType.VALUES; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verify; import static java.util.Objects.requireNonNull; -public abstract class ParquetPrimitiveColumnReader +public abstract class PrimitiveColumnReader { private static final int EMPTY_LEVEL_VALUE = -1; protected final RichColumnDescriptor columnDescriptor; @@ -59,13 +59,13 @@ public abstract class ParquetPrimitiveColumnReader protected ValuesReader valuesReader; private int nextBatchSize; - private ParquetLevelReader repetitionReader; - private ParquetLevelReader definitionReader; + private LevelReader repetitionReader; + private LevelReader definitionReader; private long totalValueCount; - private ParquetPageReader pageReader; - private ParquetDictionary dictionary; + private PageReader pageReader; + private Dictionary dictionary; private int currentValueCount; - private ParquetDataPage page; + private DataPage page; private int remainingValueCountInPage; private int readOffset; @@ -78,56 +78,56 @@ protected boolean isValueNull() return ParquetTypeUtils.isValueNull(columnDescriptor.isRequired(), definitionLevel, columnDescriptor.getMaxDefinitionLevel()); } - public static ParquetPrimitiveColumnReader createReader(RichColumnDescriptor descriptor) + public static PrimitiveColumnReader createReader(RichColumnDescriptor descriptor) { switch (descriptor.getType()) { case BOOLEAN: - return new ParquetBooleanColumnReader(descriptor); + return new BooleanColumnReader(descriptor); case INT32: - return createDecimalColumnReader(descriptor).orElse(new ParquetIntColumnReader(descriptor)); + return createDecimalColumnReader(descriptor).orElse(new IntColumnReader(descriptor)); case INT64: - return createDecimalColumnReader(descriptor).orElse(new ParquetLongColumnReader(descriptor)); + return createDecimalColumnReader(descriptor).orElse(new LongColumnReader(descriptor)); case INT96: - return new ParquetTimestampColumnReader(descriptor); + return new TimestampColumnReader(descriptor); case FLOAT: - return new ParquetFloatColumnReader(descriptor); + return new FloatColumnReader(descriptor); case DOUBLE: - return new ParquetDoubleColumnReader(descriptor); + return new DoubleColumnReader(descriptor); case BINARY: - return createDecimalColumnReader(descriptor).orElse(new ParquetBinaryColumnReader(descriptor)); + return createDecimalColumnReader(descriptor).orElse(new BinaryColumnReader(descriptor)); case FIXED_LEN_BYTE_ARRAY: return createDecimalColumnReader(descriptor) - .orElseThrow(() -> new PrestoException(NOT_SUPPORTED, "Parquet type FIXED_LEN_BYTE_ARRAY supported as DECIMAL; got " + descriptor.getPrimitiveType().getOriginalType())); + .orElseThrow(() -> new PrestoException(NOT_SUPPORTED, " type FIXED_LEN_BYTE_ARRAY supported as DECIMAL; got " + descriptor.getPrimitiveType().getOriginalType())); default: throw new PrestoException(NOT_SUPPORTED, "Unsupported parquet type: " + descriptor.getType()); } } - private static Optional createDecimalColumnReader(RichColumnDescriptor descriptor) + private static Optional createDecimalColumnReader(RichColumnDescriptor descriptor) { Optional type = createDecimalType(descriptor); if (type.isPresent()) { DecimalType decimalType = (DecimalType) type.get(); - return Optional.of(ParquetDecimalColumnReaderFactory.createReader(descriptor, decimalType.getPrecision(), decimalType.getScale())); + return Optional.of(DecimalColumnReaderFactory.createReader(descriptor, decimalType.getPrecision(), decimalType.getScale())); } return Optional.empty(); } - public ParquetPrimitiveColumnReader(RichColumnDescriptor columnDescriptor) + public PrimitiveColumnReader(RichColumnDescriptor columnDescriptor) { this.columnDescriptor = requireNonNull(columnDescriptor, "columnDescriptor"); pageReader = null; } - public ParquetPageReader getPageReader() + public PageReader getPageReader() { return pageReader; } - public void setPageReader(ParquetPageReader pageReader) + public void setPageReader(PageReader pageReader) { this.pageReader = requireNonNull(pageReader, "pageReader"); - ParquetDictionaryPage dictionaryPage = pageReader.readDictionaryPage(); + DictionaryPage dictionaryPage = pageReader.readDictionaryPage(); if (dictionaryPage != null) { try { @@ -245,11 +245,11 @@ private boolean readNextPage() return false; } remainingValueCountInPage = page.getValueCount(); - if (page instanceof ParquetDataPageV1) { - valuesReader = readPageV1((ParquetDataPageV1) page); + if (page instanceof DataPageV1) { + valuesReader = readPageV1((DataPageV1) page); } else { - valuesReader = readPageV2((ParquetDataPageV2) page); + valuesReader = readPageV2((DataPageV2) page); } return true; } @@ -264,12 +264,12 @@ private void updateValueCounts(int valuesRead) currentValueCount += valuesRead; } - private ValuesReader readPageV1(ParquetDataPageV1 page) + private ValuesReader readPageV1(DataPageV1 page) { ValuesReader rlReader = page.getRepetitionLevelEncoding().getValuesReader(columnDescriptor, REPETITION_LEVEL); ValuesReader dlReader = page.getDefinitionLevelEncoding().getValuesReader(columnDescriptor, DEFINITION_LEVEL); - repetitionReader = new ParquetLevelValuesReader(rlReader); - definitionReader = new ParquetLevelValuesReader(dlReader); + repetitionReader = new LevelValuesReader(rlReader); + definitionReader = new LevelValuesReader(dlReader); try { byte[] bytes = page.getSlice().getBytes(); rlReader.initFromPage(page.getValueCount(), bytes, 0); @@ -283,19 +283,19 @@ private ValuesReader readPageV1(ParquetDataPageV1 page) } } - private ValuesReader readPageV2(ParquetDataPageV2 page) + private ValuesReader readPageV2(DataPageV2 page) { repetitionReader = buildLevelRLEReader(columnDescriptor.getMaxRepetitionLevel(), page.getRepetitionLevels()); definitionReader = buildLevelRLEReader(columnDescriptor.getMaxDefinitionLevel(), page.getDefinitionLevels()); return initDataReader(page.getDataEncoding(), page.getSlice().getBytes(), 0, page.getValueCount()); } - private ParquetLevelReader buildLevelRLEReader(int maxLevel, Slice slice) + private LevelReader buildLevelRLEReader(int maxLevel, Slice slice) { if (maxLevel == 0) { - return new ParquetLevelNullReader(); + return new LevelNullReader(); } - return new ParquetLevelRLEReader(new RunLengthBitPackingHybridDecoder(BytesUtils.getWidthFromMaxInt(maxLevel), new ByteArrayInputStream(slice.getBytes()))); + return new LevelRLEReader(new RunLengthBitPackingHybridDecoder(BytesUtils.getWidthFromMaxInt(maxLevel), new ByteArrayInputStream(slice.getBytes()))); } private ValuesReader initDataReader(ParquetEncoding dataEncoding, byte[] bytes, int offset, int valueCount) diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetShortDecimalColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ShortDecimalColumnReader.java similarity index 85% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetShortDecimalColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ShortDecimalColumnReader.java index e3d72532fab0f..db15d58cf7835 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetShortDecimalColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/ShortDecimalColumnReader.java @@ -11,20 +11,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; -import static com.facebook.presto.hive.util.DecimalUtils.getShortDecimalValue; +import static com.facebook.presto.parquet.ParquetTypeUtils.getShortDecimalValue; import static parquet.schema.PrimitiveType.PrimitiveTypeName.INT32; import static parquet.schema.PrimitiveType.PrimitiveTypeName.INT64; -public class ParquetShortDecimalColumnReader - extends ParquetPrimitiveColumnReader +public class ShortDecimalColumnReader + extends PrimitiveColumnReader { - ParquetShortDecimalColumnReader(RichColumnDescriptor descriptor) + ShortDecimalColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetStructColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/StructColumnReader.java similarity index 88% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetStructColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/StructColumnReader.java index a6cebcda36c11..da35f4fb3f2b2 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetStructColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/StructColumnReader.java @@ -11,17 +11,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.Field; +import com.facebook.presto.parquet.Field; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import it.unimi.dsi.fastutil.booleans.BooleanList; -import static com.facebook.presto.hive.parquet.ParquetTypeUtils.isValueNull; +import static com.facebook.presto.parquet.ParquetTypeUtils.isValueNull; -public class ParquetStructColumnReader +public class StructColumnReader { - private ParquetStructColumnReader() + private StructColumnReader() { } diff --git a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetTimestampColumnReader.java b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/TimestampColumnReader.java similarity index 78% rename from presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetTimestampColumnReader.java rename to presto-parquet/src/main/java/com/facebook/presto/parquet/reader/TimestampColumnReader.java index 7c79859dac908..9a3820df675b1 100644 --- a/presto-hive/src/main/java/com/facebook/presto/hive/parquet/reader/ParquetTimestampColumnReader.java +++ b/presto-parquet/src/main/java/com/facebook/presto/parquet/reader/TimestampColumnReader.java @@ -11,19 +11,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet.reader; +package com.facebook.presto.parquet.reader; -import com.facebook.presto.hive.parquet.RichColumnDescriptor; +import com.facebook.presto.parquet.RichColumnDescriptor; import com.facebook.presto.spi.block.BlockBuilder; import com.facebook.presto.spi.type.Type; import parquet.io.api.Binary; -import static com.facebook.presto.hive.parquet.ParquetTimestampUtils.getTimestampMillis; +import static com.facebook.presto.parquet.ParquetTimestampUtils.getTimestampMillis; -public class ParquetTimestampColumnReader - extends ParquetPrimitiveColumnReader +public class TimestampColumnReader + extends PrimitiveColumnReader { - public ParquetTimestampColumnReader(RichColumnDescriptor descriptor) + public TimestampColumnReader(RichColumnDescriptor descriptor) { super(descriptor); } diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestParquetTimestampUtils.java b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTimestampUtils.java similarity index 87% rename from presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestParquetTimestampUtils.java rename to presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTimestampUtils.java index 3bdfd508cfad9..3205c2e663245 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/parquet/TestParquetTimestampUtils.java +++ b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTimestampUtils.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.hive.parquet; +package com.facebook.presto.parquet; import com.facebook.presto.spi.PrestoException; import org.apache.hadoop.hive.ql.io.parquet.timestamp.NanoTimeUtils; @@ -20,8 +20,8 @@ import java.sql.Timestamp; -import static com.facebook.presto.hive.HiveErrorCode.HIVE_BAD_DATA; -import static com.facebook.presto.hive.parquet.ParquetTimestampUtils.getTimestampMillis; +import static com.facebook.presto.parquet.ParquetTimestampUtils.getTimestampMillis; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static org.testng.Assert.assertEquals; public class TestParquetTimestampUtils @@ -42,7 +42,7 @@ public void testInvalidBinaryLength() getTimestampMillis(Binary.fromByteArray(invalidLengthBinaryTimestamp)); } catch (PrestoException e) { - assertEquals(e.getErrorCode(), HIVE_BAD_DATA.toErrorCode()); + assertEquals(e.getErrorCode(), NOT_SUPPORTED.toErrorCode()); assertEquals(e.getMessage(), "Parquet timestamp must be 12 bytes, actual 8"); } } diff --git a/presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTypeUtils.java b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTypeUtils.java new file mode 100644 index 0000000000000..b1ec40901aeb2 --- /dev/null +++ b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestParquetTypeUtils.java @@ -0,0 +1,59 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.parquet; + +import com.facebook.presto.spi.predicate.TupleDomain; +import org.testng.annotations.Test; +import parquet.column.ColumnDescriptor; +import parquet.schema.OriginalType; +import parquet.schema.PrimitiveType; +import parquet.schema.PrimitiveType.PrimitiveTypeName; + +import static com.facebook.presto.parquet.ParquetTypeUtils.getPrestoType; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static org.testng.Assert.assertEquals; +import static parquet.schema.Type.Repetition.OPTIONAL; + +public class TestParquetTypeUtils +{ + @Test + public void testMapInt32ToPrestoInteger() + { + PrimitiveType intType = new PrimitiveType(OPTIONAL, PrimitiveTypeName.INT32, "int_col", OriginalType.INT_32); + ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[]{"int_col"}, PrimitiveTypeName.INT32, 0, 1); + RichColumnDescriptor intColumn = new RichColumnDescriptor(columnDescriptor, intType); + assertEquals(getPrestoType(TupleDomain.all(), intColumn), INTEGER); + } + + @Test + public void testMapInt32WithoutOriginalTypeToPrestoInteger() + { + // int32 primitive should default to Presto integer if original type metadata isn't available + PrimitiveType intType = new PrimitiveType(OPTIONAL, PrimitiveTypeName.INT32, "int_col"); + ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[]{"int_col"}, PrimitiveTypeName.INT32, 0, 1); + RichColumnDescriptor intColumn = new RichColumnDescriptor(columnDescriptor, intType); + assertEquals(getPrestoType(TupleDomain.all(), intColumn), INTEGER); + } + + @Test + public void testMapInt32ToPrestoDate() + { + // int32 primitive with original type of date should map to a Presto date + PrimitiveType dateType = new PrimitiveType(OPTIONAL, PrimitiveTypeName.INT32, "date_col", OriginalType.DATE); + ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[]{"date_col"}, PrimitiveTypeName.INT32, 0, 1); + RichColumnDescriptor dateColumn = new RichColumnDescriptor(columnDescriptor, dateType); + assertEquals(getPrestoType(TupleDomain.all(), dateColumn), DATE); + } +} diff --git a/presto-parquet/src/test/java/com/facebook/presto/parquet/TestTupleDomainParquetPredicate.java b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestTupleDomainParquetPredicate.java new file mode 100644 index 0000000000000..dfaf6325bcfb1 --- /dev/null +++ b/presto-parquet/src/test/java/com/facebook/presto/parquet/TestTupleDomainParquetPredicate.java @@ -0,0 +1,322 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.parquet; + +import com.facebook.presto.parquet.predicate.DictionaryDescriptor; +import com.facebook.presto.parquet.predicate.TupleDomainParquetPredicate; +import com.facebook.presto.spi.predicate.Domain; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.predicate.ValueSet; +import com.facebook.presto.spi.type.VarcharType; +import io.airlift.slice.Slice; +import io.airlift.slice.Slices; +import org.testng.annotations.Test; +import parquet.column.ColumnDescriptor; +import parquet.column.statistics.BinaryStatistics; +import parquet.column.statistics.BooleanStatistics; +import parquet.column.statistics.DoubleStatistics; +import parquet.column.statistics.FloatStatistics; +import parquet.column.statistics.IntStatistics; +import parquet.column.statistics.LongStatistics; +import parquet.column.statistics.Statistics; +import parquet.io.api.Binary; +import parquet.schema.PrimitiveType; + +import java.util.Map; +import java.util.Optional; + +import static com.facebook.presto.parquet.ParquetEncoding.PLAIN_DICTIONARY; +import static com.facebook.presto.parquet.predicate.TupleDomainParquetPredicate.getDomain; +import static com.facebook.presto.spi.predicate.Domain.all; +import static com.facebook.presto.spi.predicate.Domain.create; +import static com.facebook.presto.spi.predicate.Domain.notNull; +import static com.facebook.presto.spi.predicate.Domain.singleValue; +import static com.facebook.presto.spi.predicate.Range.range; +import static com.facebook.presto.spi.predicate.TupleDomain.withColumnDomains; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.DoubleType.DOUBLE; +import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.RealType.REAL; +import static com.facebook.presto.spi.type.SmallintType.SMALLINT; +import static com.facebook.presto.spi.type.TinyintType.TINYINT; +import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType; +import static com.facebook.presto.spi.type.VarcharType.createVarcharType; +import static io.airlift.slice.Slices.EMPTY_SLICE; +import static io.airlift.slice.Slices.utf8Slice; +import static java.lang.Float.floatToRawIntBits; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static parquet.column.statistics.Statistics.getStatsBasedOnType; +import static parquet.schema.PrimitiveType.PrimitiveTypeName.BINARY; +import static parquet.schema.Type.Repetition.OPTIONAL; + +public class TestTupleDomainParquetPredicate +{ + private static final ParquetDataSourceId ID = new ParquetDataSourceId("testFile"); + + @Test + public void testBoolean() + throws ParquetCorruptionException + { + String column = "BooleanColumn"; + assertEquals(getDomain(BOOLEAN, 0, null, ID, column, true), all(BOOLEAN)); + + assertEquals(getDomain(BOOLEAN, 10, booleanColumnStats(true, true), ID, column, true), singleValue(BOOLEAN, true)); + assertEquals(getDomain(BOOLEAN, 10, booleanColumnStats(false, false), ID, column, true), singleValue(BOOLEAN, false)); + + assertEquals(getDomain(BOOLEAN, 20, booleanColumnStats(false, true), ID, column, true), all(BOOLEAN)); + } + + private static BooleanStatistics booleanColumnStats(boolean minimum, boolean maximum) + { + BooleanStatistics statistics = new BooleanStatistics(); + statistics.setMinMax(minimum, maximum); + return statistics; + } + + @Test + public void testBigint() + throws ParquetCorruptionException + { + String column = "BigintColumn"; + assertEquals(getDomain(BIGINT, 0, null, ID, column, true), all(BIGINT)); + + assertEquals(getDomain(BIGINT, 10, longColumnStats(100L, 100L), ID, column, true), singleValue(BIGINT, 100L)); + + assertEquals(getDomain(BIGINT, 10, longColumnStats(0L, 100L), ID, column, true), create(ValueSet.ofRanges(range(BIGINT, 0L, true, 100L, true)), false)); + // ignore corrupted statistics + assertEquals(getDomain(BIGINT, 10, longColumnStats(100L, 0L), ID, column, false), create(ValueSet.all(BIGINT), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(BIGINT, 10, longColumnStats(100L, 10L), ID, column, true)) + .withMessage("Corrupted statistics for column \"BigintColumn\" in Parquet file \"testFile\": [min: 100, max: 10, num_nulls: 0]"); + } + + private static LongStatistics longColumnStats(long minimum, long maximum) + { + LongStatistics statistics = new LongStatistics(); + statistics.setMinMax(minimum, maximum); + return statistics; + } + + @Test + public void testInteger() + throws ParquetCorruptionException + { + String column = "IntegerColumn"; + assertEquals(getDomain(INTEGER, 0, null, ID, column, true), all(INTEGER)); + + assertEquals(getDomain(INTEGER, 10, longColumnStats(100, 100), ID, column, true), singleValue(INTEGER, 100L)); + + assertEquals(getDomain(INTEGER, 10, longColumnStats(0, 100), ID, column, true), create(ValueSet.ofRanges(range(INTEGER, 0L, true, 100L, true)), false)); + + assertEquals(getDomain(INTEGER, 20, longColumnStats(0, 2147483648L), ID, column, true), notNull(INTEGER)); + // ignore corrupted statistics + assertEquals(getDomain(INTEGER, 10, longColumnStats(2147483648L, 0), ID, column, false), create(ValueSet.all(INTEGER), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(INTEGER, 10, longColumnStats(2147483648L, 10), ID, column, true)) + .withMessage("Corrupted statistics for column \"IntegerColumn\" in Parquet file \"testFile\": [min: 2147483648, max: 10, num_nulls: 0]"); + } + + @Test + public void testSmallint() + throws ParquetCorruptionException + { + String column = "SmallintColumn"; + assertEquals(getDomain(SMALLINT, 0, null, ID, column, true), all(SMALLINT)); + + assertEquals(getDomain(SMALLINT, 10, longColumnStats(100, 100), ID, column, true), singleValue(SMALLINT, 100L)); + + assertEquals(getDomain(SMALLINT, 10, longColumnStats(0, 100), ID, column, true), create(ValueSet.ofRanges(range(SMALLINT, 0L, true, 100L, true)), false)); + + assertEquals(getDomain(SMALLINT, 20, longColumnStats(0, 2147483648L), ID, column, true), notNull(SMALLINT)); + // ignore corrupted statistics + assertEquals(getDomain(SMALLINT, 10, longColumnStats(2147483648L, 0), ID, column, false), create(ValueSet.all(SMALLINT), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(SMALLINT, 10, longColumnStats(2147483648L, 10), ID, column, true)) + .withMessage("Corrupted statistics for column \"SmallintColumn\" in Parquet file \"testFile\": [min: 2147483648, max: 10, num_nulls: 0]"); + } + + @Test + public void testTinyint() + throws ParquetCorruptionException + { + String column = "TinyintColumn"; + assertEquals(getDomain(TINYINT, 0, null, ID, column, true), all(TINYINT)); + + assertEquals(getDomain(TINYINT, 10, longColumnStats(100, 100), ID, column, true), singleValue(TINYINT, 100L)); + + assertEquals(getDomain(TINYINT, 10, longColumnStats(0, 100), ID, column, true), create(ValueSet.ofRanges(range(TINYINT, 0L, true, 100L, true)), false)); + + assertEquals(getDomain(TINYINT, 20, longColumnStats(0, 2147483648L), ID, column, true), notNull(TINYINT)); + + // ignore corrupted statistics + assertEquals(getDomain(TINYINT, 10, longColumnStats(2147483648L, 0), ID, column, false), create(ValueSet.all(TINYINT), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(TINYINT, 10, longColumnStats(2147483648L, 10), ID, column, true)) + .withMessage("Corrupted statistics for column \"TinyintColumn\" in Parquet file \"testFile\": [min: 2147483648, max: 10, num_nulls: 0]"); + } + + @Test + public void testDouble() + throws ParquetCorruptionException + { + String column = "DoubleColumn"; + assertEquals(getDomain(DOUBLE, 0, null, ID, column, true), all(DOUBLE)); + + assertEquals(getDomain(DOUBLE, 10, doubleColumnStats(42.24, 42.24), ID, column, true), singleValue(DOUBLE, 42.24)); + + assertEquals(getDomain(DOUBLE, 10, doubleColumnStats(3.3, 42.24), ID, column, true), create(ValueSet.ofRanges(range(DOUBLE, 3.3, true, 42.24, true)), false)); + + // ignore corrupted statistics + assertEquals(getDomain(DOUBLE, 10, doubleColumnStats(42.24, 3.3), ID, column, false), create(ValueSet.all(DOUBLE), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(DOUBLE, 10, doubleColumnStats(42.24, 3.3), ID, column, true)) + .withMessage("Corrupted statistics for column \"DoubleColumn\" in Parquet file \"testFile\": [min: 42.24000, max: 3.30000, num_nulls: 0]"); + } + + private static DoubleStatistics doubleColumnStats(double minimum, double maximum) + { + DoubleStatistics statistics = new DoubleStatistics(); + statistics.setMinMax(minimum, maximum); + return statistics; + } + + @Test + public void testString() + throws ParquetCorruptionException + { + String column = "StringColumn"; + assertEquals(getDomain(createUnboundedVarcharType(), 0, null, ID, column, true), all(createUnboundedVarcharType())); + + assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("taco", "taco"), ID, column, true), singleValue(createUnboundedVarcharType(), utf8Slice("taco"))); + + assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("apple", "taco"), ID, column, true), create(ValueSet.ofRanges(range(createUnboundedVarcharType(), utf8Slice("apple"), true, utf8Slice("taco"), true)), false)); + + assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("中国", "美利坚"), ID, column, true), create(ValueSet.ofRanges(range(createUnboundedVarcharType(), utf8Slice("中国"), true, utf8Slice("美利坚"), true)), false)); + + // ignore corrupted statistics + assertEquals(getDomain(createUnboundedVarcharType(), 10, stringColumnStats("taco", "apple"), ID, column, false), create(ValueSet.all(createUnboundedVarcharType()), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(createUnboundedVarcharType(), 10, stringColumnStats("taco", "apple"), ID, column, true)) + .withMessage("Corrupted statistics for column \"StringColumn\" in Parquet file \"testFile\": [min: taco, max: apple, num_nulls: 0]"); + } + + private static BinaryStatistics stringColumnStats(String minimum, String maximum) + { + BinaryStatistics statistics = new BinaryStatistics(); + statistics.setMinMax(Binary.fromString(minimum), Binary.fromString(maximum)); + return statistics; + } + + @Test + public void testFloat() + throws ParquetCorruptionException + { + String column = "FloatColumn"; + assertEquals(getDomain(REAL, 0, null, ID, column, true), all(REAL)); + + float minimum = 4.3f; + float maximum = 40.3f; + + assertEquals(getDomain(REAL, 10, floatColumnStats(minimum, minimum), ID, column, true), singleValue(REAL, (long) floatToRawIntBits(minimum))); + + assertEquals( + getDomain(REAL, 10, floatColumnStats(minimum, maximum), ID, column, true), + create(ValueSet.ofRanges(range(REAL, (long) floatToRawIntBits(minimum), true, (long) floatToRawIntBits(maximum), true)), false)); + + // ignore corrupted statistics + assertEquals(getDomain(REAL, 10, floatColumnStats(maximum, minimum), ID, column, false), create(ValueSet.all(REAL), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(REAL, 10, floatColumnStats(maximum, minimum), ID, column, true)) + .withMessage("Corrupted statistics for column \"FloatColumn\" in Parquet file \"testFile\": [min: 40.30000, max: 4.30000, num_nulls: 0]"); + } + + @Test + public void testDate() + throws ParquetCorruptionException + { + String column = "DateColumn"; + assertEquals(getDomain(DATE, 0, null, ID, column, true), all(DATE)); + assertEquals(getDomain(DATE, 10, intColumnStats(100, 100), ID, column, true), singleValue(DATE, 100L)); + assertEquals(getDomain(DATE, 10, intColumnStats(0, 100), ID, column, true), create(ValueSet.ofRanges(range(DATE, 0L, true, 100L, true)), false)); + + // ignore corrupted statistics + assertEquals(getDomain(DATE, 10, intColumnStats(200, 100), ID, column, false), create(ValueSet.all(DATE), false)); + // fail on corrupted statistics + assertThatExceptionOfType(ParquetCorruptionException.class) + .isThrownBy(() -> getDomain(DATE, 10, intColumnStats(200, 100), ID, column, true)) + .withMessage("Corrupted statistics for column \"DateColumn\" in Parquet file \"testFile\": [min: 200, max: 100, num_nulls: 0]"); + } + + @Test + public void testMatchesWithStatistics() + throws ParquetCorruptionException + { + String value = "Test"; + ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[] {"path"}, BINARY, 0, 0); + RichColumnDescriptor column = new RichColumnDescriptor(columnDescriptor, new PrimitiveType(OPTIONAL, BINARY, "Test column")); + TupleDomain effectivePredicate = getEffectivePredicate(column, createVarcharType(255), utf8Slice(value)); + TupleDomainParquetPredicate parquetPredicate = new TupleDomainParquetPredicate(effectivePredicate, singletonList(column)); + Statistics stats = getStatsBasedOnType(column.getType()); + stats.setNumNulls(1L); + stats.setMinMaxFromBytes(value.getBytes(), value.getBytes()); + assertTrue(parquetPredicate.matches(2, singletonMap(column, stats), ID, true)); + } + + @Test + public void testMatchesWithDescriptors() + throws ParquetCorruptionException + { + ColumnDescriptor columnDescriptor = new ColumnDescriptor(new String[] {"path"}, BINARY, 0, 0); + RichColumnDescriptor column = new RichColumnDescriptor(columnDescriptor, new PrimitiveType(OPTIONAL, BINARY, "Test column")); + TupleDomain effectivePredicate = getEffectivePredicate(column, createVarcharType(255), EMPTY_SLICE); + TupleDomainParquetPredicate parquetPredicate = new TupleDomainParquetPredicate(effectivePredicate, singletonList(column)); + DictionaryPage page = new DictionaryPage(Slices.wrappedBuffer(new byte[] {0, 0, 0, 0}), 1, PLAIN_DICTIONARY); + assertTrue(parquetPredicate.matches(singletonMap(column, new DictionaryDescriptor(column, Optional.of(page))))); + } + + private TupleDomain getEffectivePredicate(RichColumnDescriptor column, VarcharType type, Slice value) + { + ColumnDescriptor predicateColumn = new ColumnDescriptor(column.getPath(), column.getType(), 0, 0); + Domain predicateDomain = singleValue(type, value); + Map predicateColumns = singletonMap(predicateColumn, predicateDomain); + return withColumnDomains(predicateColumns); + } + + private static FloatStatistics floatColumnStats(float minimum, float maximum) + { + FloatStatistics statistics = new FloatStatistics(); + statistics.setMinMax(minimum, maximum); + return statistics; + } + + private static IntStatistics intColumnStats(int minimum, int maximum) + { + IntStatistics statistics = new IntStatistics(); + statistics.setMinMax(minimum, maximum); + return statistics; + } +} diff --git a/presto-parser/pom.xml b/presto-parser/pom.xml index 34f0d6a537a48..06c5d58be2b9e 100644 --- a/presto-parser/pom.xml +++ b/presto-parser/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-parser diff --git a/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 b/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 index 184e86c0f2152..62655f5ad36d0 100644 --- a/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 +++ b/presto-parser/src/main/antlr4/com/facebook/presto/sql/parser/SqlBase.g4 @@ -22,10 +22,14 @@ singleStatement : statement EOF ; -singleExpression +standaloneExpression : expression EOF ; +standalonePathSpecification + : pathSpecification EOF + ; + statement : query #statementDefault | USE schema=identifier #use @@ -75,7 +79,7 @@ statement (LIKE pattern=string (ESCAPE escape=string)?)? #showSchemas | SHOW CATALOGS (LIKE pattern=string)? #showCatalogs | SHOW COLUMNS (FROM | IN) qualifiedName #showColumns - | SHOW STATS (FOR | ON) qualifiedName #showStats + | SHOW STATS FOR qualifiedName #showStats | SHOW STATS FOR '(' querySpecification ')' #showStatsForQuery | DESCRIBE qualifiedName #showColumns | DESC qualifiedName #showColumns @@ -86,10 +90,6 @@ statement | START TRANSACTION (transactionMode (',' transactionMode)*)? #startTransaction | COMMIT WORK? #commit | ROLLBACK WORK? #rollback - | SHOW PARTITIONS (FROM | IN) qualifiedName - (WHERE booleanExpression)? - (ORDER BY sortItem (',' sortItem)*)? - (LIMIT limit=(INTEGER_VALUE | ALL))? #showPartitions | PREPARE identifier FROM statement #prepare | DEALLOCATE PREPARE identifier #deallocate | EXECUTE identifier (USING expression (',' expression)*)? #execute @@ -466,7 +466,7 @@ nonReserved | MAP | MINUTE | MONTH | NFC | NFD | NFKC | NFKD | NO | NULLIF | NULLS | ONLY | OPTION | ORDINALITY | OUTPUT | OVER - | PARTITION | PARTITIONS | PATH | POSITION | PRECEDING | PRIVILEGES | PROPERTIES | PUBLIC + | PARTITION | PARTITIONS | PATH | POSITION | PRECEDING | PRIVILEGES | PROPERTIES | RANGE | READ | RENAME | REPEATABLE | REPLACE | RESET | RESTRICT | REVOKE | ROLLBACK | ROW | ROWS | SCHEMA | SCHEMAS | SECOND | SERIALIZABLE | SESSION | SET | SETS | SHOW | SOME | START | STATS | SUBSTRING | SYSTEM @@ -600,7 +600,6 @@ PRECEDING: 'PRECEDING'; PREPARE: 'PREPARE'; PRIVILEGES: 'PRIVILEGES'; PROPERTIES: 'PROPERTIES'; -PUBLIC: 'PUBLIC'; RANGE: 'RANGE'; READ: 'READ'; RECURSIVE: 'RECURSIVE'; diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java b/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java index 5e589384b1598..508a89b1e1783 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/SqlFormatter.java @@ -77,7 +77,6 @@ import com.facebook.presto.sql.tree.ShowCreate; import com.facebook.presto.sql.tree.ShowFunctions; import com.facebook.presto.sql.tree.ShowGrants; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowStats; @@ -106,7 +105,6 @@ import static com.facebook.presto.sql.ExpressionFormatter.formatExpression; import static com.facebook.presto.sql.ExpressionFormatter.formatGroupBy; import static com.facebook.presto.sql.ExpressionFormatter.formatOrderBy; -import static com.facebook.presto.sql.ExpressionFormatter.formatSortItems; import static com.facebook.presto.sql.ExpressionFormatter.formatStringLiteral; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Iterables.getOnlyElement; @@ -674,30 +672,6 @@ protected Void visitShowStats(ShowStats node, Integer context) return null; } - @Override - protected Void visitShowPartitions(ShowPartitions node, Integer context) - { - builder.append("SHOW PARTITIONS FROM ") - .append(formatName(node.getTable())); - - if (node.getWhere().isPresent()) { - builder.append(" WHERE ") - .append(formatExpression(node.getWhere().get(), parameters)); - } - - if (!node.getOrderBy().isEmpty()) { - builder.append(" ORDER BY ") - .append(formatSortItems(node.getOrderBy(), parameters)); - } - - if (node.getLimit().isPresent()) { - builder.append(" LIMIT ") - .append(node.getLimit().get()); - } - - return null; - } - @Override protected Void visitShowFunctions(ShowFunctions node, Integer context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java b/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java index 2cfd6bf5630e1..6b5a4c5e305f9 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java @@ -126,7 +126,6 @@ import com.facebook.presto.sql.tree.ShowCreate; import com.facebook.presto.sql.tree.ShowFunctions; import com.facebook.presto.sql.tree.ShowGrants; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowStats; @@ -193,11 +192,17 @@ public Node visitSingleStatement(SqlBaseParser.SingleStatementContext context) } @Override - public Node visitSingleExpression(SqlBaseParser.SingleExpressionContext context) + public Node visitStandaloneExpression(SqlBaseParser.StandaloneExpressionContext context) { return visit(context.expression()); } + @Override + public Node visitStandalonePathSpecification(SqlBaseParser.StandalonePathSpecificationContext context) + { + return visit(context.pathSpecification()); + } + // ******************* statements ********************** @Override @@ -770,17 +775,6 @@ public Node visitShowStatsForQuery(SqlBaseParser.ShowStatsForQueryContext contex return new ShowStats(Optional.of(getLocation(context)), new TableSubquery(query)); } - @Override - public Node visitShowPartitions(SqlBaseParser.ShowPartitionsContext context) - { - return new ShowPartitions( - getLocation(context), - getQualifiedName(context.qualifiedName()), - visitIfPresent(context.booleanExpression(), Expression.class), - visit(context.sortItem(), SortItem.class), - getTextIfPresent(context.limit)); - } - @Override public Node visitShowCreateView(SqlBaseParser.ShowCreateViewContext context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/parser/ErrorHandler.java b/presto-parser/src/main/java/com/facebook/presto/sql/parser/ErrorHandler.java index c7bd446a1b241..7ea711acc4847 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/parser/ErrorHandler.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/parser/ErrorHandler.java @@ -17,6 +17,7 @@ import com.google.common.collect.Multimap; import io.airlift.log.Logger; import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.NoViableAltException; import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; @@ -34,7 +35,6 @@ import org.antlr.v4.runtime.misc.IntervalSet; import java.util.ArrayDeque; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -69,11 +69,28 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int Parser parser = (Parser) recognizer; ATN atn = parser.getATN(); - ATNState currentState = atn.states.get(parser.getState()); - Multimap candidates = HashMultimap.create(); - Analyzer analyzer = new Analyzer(atn, parser.getVocabulary(), specialRules, specialTokens, ignoredRules, parser.getTokenStream(), candidates); - analyzer.process(currentState, parser.getCurrentToken().getTokenIndex(), parser.getContext()); + ATNState currentState; + Token currentToken; + RuleContext context; + + if (e != null) { + currentState = atn.states.get(e.getOffendingState()); + currentToken = e.getOffendingToken(); + context = e.getCtx(); + + if (e instanceof NoViableAltException) { + currentToken = ((NoViableAltException) e).getStartToken(); + } + } + else { + currentState = atn.states.get(parser.getState()); + currentToken = parser.getCurrentToken(); + context = parser.getContext(); + } + + Analyzer analyzer = new Analyzer(atn, parser.getVocabulary(), specialRules, specialTokens, ignoredRules, parser.getTokenStream()); + Multimap candidates = analyzer.process(currentState, currentToken.getTokenIndex(), context); // pick the candidate tokens associated largest token index processed (i.e., the path that consumed the most input) String expected = candidates.asMap().entrySet().stream() @@ -86,7 +103,7 @@ public void syntaxError(Recognizer recognizer, Object offendingSymbol, int message = String.format("mismatched input '%s'. Expecting: %s", ((Token) offendingSymbol).getText(), expected); } catch (Exception exception) { - LOG.error(e, "Unexpected failure when handling parsing error. This is likely a bug in the implementation"); + LOG.error(exception, "Unexpected failure when handling parsing error. This is likely a bug in the implementation"); } throw new ParsingException(message, e, line, charPositionInLine); @@ -96,11 +113,25 @@ private static class ParsingState { public final ATNState state; public final int tokenIndex; + private final CallerContext caller; - public ParsingState(ATNState state, int tokenIndex) + public ParsingState(ATNState state, int tokenIndex, CallerContext caller) { this.state = state; this.tokenIndex = tokenIndex; + this.caller = caller; + } + } + + private static class CallerContext + { + public final ATNState followState; + public final CallerContext parent; + + public CallerContext(CallerContext parent, ATNState followState) + { + this.parent = parent; + this.followState = followState; } } @@ -112,7 +143,6 @@ private static class Analyzer private final Map specialTokens; private final Set ignoredRules; private final TokenStream stream; - private final Multimap candidates; public Analyzer( ATN atn, @@ -120,8 +150,7 @@ public Analyzer( Map specialRules, Map specialTokens, Set ignoredRules, - TokenStream stream, - Multimap candidates) + TokenStream stream) { this.stream = stream; this.atn = atn; @@ -129,51 +158,61 @@ public Analyzer( this.specialRules = specialRules; this.specialTokens = specialTokens; this.ignoredRules = ignoredRules; - this.candidates = candidates; } - private Set process(ATNState startState, int tokenIndex, RuleContext next) + public Multimap process(ATNState currentState, int tokenIndex, RuleContext context) { - int currentRule = startState.ruleIndex; - - // if startState is the beginning or a rule that's in the list of "special" rules, just bail out and - // add that as the value - if (startState.getStateType() == BLOCK_START || startState.getStateType() == RULE_START) { - if (specialRules.containsKey(currentRule)) { - candidates.put(tokenIndex, specialRules.get(currentRule)); - return Collections.emptySet(); - } - else if (ignoredRules.contains(currentRule)) { - return Collections.emptySet(); - } - } + return process(new ParsingState(currentState, tokenIndex, makeCallStack(context))); + } - Set endTokens = new HashSet<>(); + private Multimap process(ParsingState start) + { + Multimap candidates = HashMultimap.create(); + // Simulates the ATN by consuming input tokens and walking transitions. + // The ATN can be in multiple states (similar to an NFA) Queue activeStates = new ArrayDeque<>(); - - activeStates.add(new ParsingState(startState, tokenIndex)); + activeStates.add(start); while (!activeStates.isEmpty()) { - ParsingState parsingState = activeStates.poll(); + ParsingState current = activeStates.poll(); + + ATNState state = current.state; + int tokenIndex = current.tokenIndex; + CallerContext caller = current.caller; - if (parsingState.state instanceof RuleStopState) { - if (next != null) { - process(((RuleTransition) atn.states.get(next.invokingState).transition(0)).followState, parsingState.tokenIndex, next.parent); + if (state.getStateType() == BLOCK_START || state.getStateType() == RULE_START) { + int rule = state.ruleIndex; + + if (specialRules.containsKey(rule)) { + candidates.put(tokenIndex, specialRules.get(rule)); + continue; + } + else if (ignoredRules.contains(rule)) { + continue; + } + } + + if (state instanceof RuleStopState) { + if (caller != null) { + // continue from the target state of the rule transition in the parent rule + activeStates.add(new ParsingState(caller.followState, tokenIndex, caller.parent)); + } + else { + // we've reached the end of the top-level rule, so the only candidate left is EOF at this point + candidates.putAll(tokenIndex, getTokenNames(IntervalSet.of(Token.EOF))); } continue; } - for (int i = 0; i < parsingState.state.getNumberOfTransitions(); i++) { - Transition transition = parsingState.state.transition(i); + for (int i = 0; i < state.getNumberOfTransitions(); i++) { + Transition transition = state.transition(i); if (transition instanceof RuleTransition) { - for (int token : process(transition.target, parsingState.tokenIndex, null)) { - activeStates.add(new ParsingState(((RuleTransition) transition).followState, token)); - } + activeStates.add(new ParsingState(transition.target, tokenIndex, new CallerContext(caller, ((RuleTransition) transition).followState))); } else if (transition.isEpsilon()) { - activeStates.add(new ParsingState(transition.target, parsingState.tokenIndex)); + activeStates.add(new ParsingState(transition.target, tokenIndex, caller)); } else if (transition instanceof WildcardTransition) { throw new UnsupportedOperationException("not yet implemented: wildcard transition"); @@ -185,18 +224,18 @@ else if (transition instanceof WildcardTransition) { labels = labels.complement(IntervalSet.of(Token.MIN_USER_TOKEN_TYPE, atn.maxTokenType)); } - int currentToken = stream.get(parsingState.tokenIndex).getType(); + int currentToken = stream.get(tokenIndex).getType(); if (labels.contains(currentToken)) { - activeStates.add(new ParsingState(transition.target, parsingState.tokenIndex + 1)); + activeStates.add(new ParsingState(transition.target, tokenIndex + 1, caller)); } else { - candidates.putAll(parsingState.tokenIndex, getTokenNames(labels)); + candidates.putAll(tokenIndex, getTokenNames(labels)); } } } } - return endTokens; + return candidates; } private Set getTokenNames(IntervalSet tokens) @@ -210,6 +249,18 @@ private Set getTokenNames(IntervalSet tokens) }) .collect(Collectors.toSet()); } + + private CallerContext makeCallStack(RuleContext context) + { + if (context == null || context.invokingState == -1) { + return null; + } + + CallerContext parent = makeCallStack(context.parent); + + ATNState followState = ((RuleTransition) atn.states.get(context.invokingState).transition(0)).followState; + return new CallerContext(parent, followState); + } } public static Builder builder() diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/parser/SqlParser.java b/presto-parser/src/main/java/com/facebook/presto/sql/parser/SqlParser.java index 18484ed0a17e0..c760e7b274dfb 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/parser/SqlParser.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/parser/SqlParser.java @@ -17,10 +17,13 @@ import com.facebook.presto.sql.tree.Node; import com.facebook.presto.sql.tree.PathSpecification; import com.facebook.presto.sql.tree.Statement; -import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonToken; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.InputMismatchException; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; @@ -104,21 +107,38 @@ public Expression createExpression(String expression) public Expression createExpression(String expression, ParsingOptions parsingOptions) { - return (Expression) invokeParser("expression", expression, SqlBaseParser::singleExpression, parsingOptions); + return (Expression) invokeParser("expression", expression, SqlBaseParser::standaloneExpression, parsingOptions); } public PathSpecification createPathSpecification(String expression) { - return (PathSpecification) invokeParser("pathSpec", expression, SqlBaseParser::pathSpecification, new ParsingOptions()); + return (PathSpecification) invokeParser("path specification", expression, SqlBaseParser::standalonePathSpecification, new ParsingOptions()); } private Node invokeParser(String name, String sql, Function parseFunction, ParsingOptions parsingOptions) { try { - SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(new ANTLRInputStream(sql))); + SqlBaseLexer lexer = new SqlBaseLexer(new CaseInsensitiveStream(CharStreams.fromString(sql))); CommonTokenStream tokenStream = new CommonTokenStream(lexer); SqlBaseParser parser = new SqlBaseParser(tokenStream); + // Override the default error strategy to not attempt inserting or deleting a token. + // Otherwise, it messes up error reporting + parser.setErrorHandler(new DefaultErrorStrategy() + { + @Override + public Token recoverInline(Parser recognizer) + throws RecognitionException + { + if (nextTokensContext == null) { + throw new InputMismatchException(recognizer); + } + else { + throw new InputMismatchException(recognizer, nextTokensState, nextTokensContext); + } + } + }); + parser.addParseListener(new PostProcessor(Arrays.asList(parser.getRuleNames()))); lexer.removeErrorListeners(); diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java index 9d2269f53058f..ae9ff8e69dd96 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/AstVisitor.java @@ -147,11 +147,6 @@ protected R visitShowStats(ShowStats node, C context) return visitStatement(node, context); } - protected R visitShowPartitions(ShowPartitions node, C context) - { - return visitStatement(node, context); - } - protected R visitShowCreate(ShowCreate node, C context) { return visitStatement(node, context); diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/Cube.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/Cube.java index bb5cdda9a228a..fc89489ae7808 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/Cube.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/Cube.java @@ -14,13 +14,10 @@ package com.facebook.presto.sql.tree; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -52,12 +49,6 @@ public List getExpressions() return columns; } - @Override - public List> enumerateGroupingSets() - { - return ImmutableList.copyOf(Sets.powerSet(ImmutableSet.copyOf(columns))); - } - @Override protected R accept(AstVisitor visitor, C context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentPath.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentPath.java index 03d52ef299377..1d7c03e7b09ad 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentPath.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentPath.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import com.google.common.collect.ImmutableList; import java.util.List; diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentUser.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentUser.java index b1e33233309e3..3e7aa0850416e 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentUser.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/CurrentUser.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import com.google.common.collect.ImmutableList; import java.util.List; diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/DefaultTraversalVisitor.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/DefaultTraversalVisitor.java index 27ee492a60130..9aa5330f09c26 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/DefaultTraversalVisitor.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/DefaultTraversalVisitor.java @@ -581,20 +581,6 @@ protected R visitCreateTable(CreateTable node, C context) return null; } - @Override - protected R visitShowPartitions(ShowPartitions node, C context) - { - if (node.getWhere().isPresent()) { - process(node.getWhere().get(), context); - } - - for (SortItem sortItem : node.getOrderBy()) { - process(sortItem, context); - } - - return null; - } - @Override protected R visitStartTransaction(StartTransaction node, C context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingElement.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingElement.java index 2c965a5ca7d11..dc106bef06483 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingElement.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingElement.java @@ -15,7 +15,6 @@ import java.util.List; import java.util.Optional; -import java.util.Set; public abstract class GroupingElement extends Node @@ -27,8 +26,6 @@ protected GroupingElement(Optional location) public abstract List getExpressions(); - public abstract List> enumerateGroupingSets(); - @Override protected R accept(AstVisitor visitor, C context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingSets.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingSets.java index 47355e73e6f82..7a80d636bf23b 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingSets.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/GroupingSets.java @@ -14,20 +14,16 @@ package com.facebook.presto.sql.tree; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.collectingAndThen; public final class GroupingSets extends GroupingElement @@ -57,14 +53,6 @@ public List> getSets() return sets; } - @Override - public List> enumerateGroupingSets() - { - return sets.stream() - .map(ImmutableSet::copyOf) - .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); - } - @Override public List getExpressions() { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/Rollup.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/Rollup.java index 5985365cf396c..78dfeea65d21f 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/Rollup.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/Rollup.java @@ -14,17 +14,13 @@ package com.facebook.presto.sql.tree; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.IntStream; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; public final class Rollup extends GroupingElement @@ -53,18 +49,6 @@ public List getExpressions() return columns; } - @Override - public List> enumerateGroupingSets() - { - int numColumns = columns.size(); - return ImmutableList.>builder() - .addAll(IntStream.range(0, numColumns) - .mapToObj(i -> ImmutableSet.copyOf(columns.subList(0, numColumns - i))) - .collect(toList())) - .add(ImmutableSet.of()) - .build(); - } - @Override protected R accept(AstVisitor visitor, C context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/ShowPartitions.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/ShowPartitions.java deleted file mode 100644 index 3ac6e392814d4..0000000000000 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/ShowPartitions.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.sql.tree; - -import com.google.common.collect.ImmutableList; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static java.util.Objects.requireNonNull; - -public class ShowPartitions - extends Statement -{ - private final QualifiedName table; - private final Optional where; - private final List orderBy; - private final Optional limit; - - public ShowPartitions(QualifiedName table, Optional where, List orderBy, Optional limit) - { - this(Optional.empty(), table, where, orderBy, limit); - } - - public ShowPartitions(NodeLocation location, QualifiedName table, Optional where, List orderBy, Optional limit) - { - this(Optional.of(location), table, where, orderBy, limit); - } - - private ShowPartitions(Optional location, QualifiedName table, Optional where, List orderBy, Optional limit) - { - super(location); - this.table = requireNonNull(table, "table is null"); - this.where = requireNonNull(where, "where is null"); - this.orderBy = ImmutableList.copyOf(requireNonNull(orderBy, "orderBy is null")); - this.limit = requireNonNull(limit, "limit is null"); - } - - public QualifiedName getTable() - { - return table; - } - - public Optional getWhere() - { - return where; - } - - public List getOrderBy() - { - return orderBy; - } - - public Optional getLimit() - { - return limit; - } - - @Override - public R accept(AstVisitor visitor, C context) - { - return visitor.visitShowPartitions(this, context); - } - - @Override - public List getChildren() - { - ImmutableList.Builder nodes = ImmutableList.builder(); - where.ifPresent(nodes::add); - nodes.addAll(orderBy); - return nodes.build(); - } - - @Override - public int hashCode() - { - return Objects.hash(table, where, orderBy, limit); - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) { - return true; - } - if ((obj == null) || (getClass() != obj.getClass())) { - return false; - } - ShowPartitions o = (ShowPartitions) obj; - return Objects.equals(table, o.table) && - Objects.equals(where, o.where) && - Objects.equals(orderBy, o.orderBy) && - Objects.equals(limit, o.limit); - } - - @Override - public String toString() - { - return toStringHelper(this) - .add("table", table) - .add("where", where) - .add("orderBy", orderBy) - .add("limit", limit) - .toString(); - } -} diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/tree/SimpleGroupBy.java b/presto-parser/src/main/java/com/facebook/presto/sql/tree/SimpleGroupBy.java index d3a0bf1484e63..0309bf08f48ef 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/tree/SimpleGroupBy.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/tree/SimpleGroupBy.java @@ -14,12 +14,10 @@ package com.facebook.presto.sql.tree; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -51,12 +49,6 @@ public List getExpressions() return columns; } - @Override - public List> enumerateGroupingSets() - { - return ImmutableList.of(ImmutableSet.copyOf(columns)); - } - @Override protected R accept(AstVisitor visitor, C context) { diff --git a/presto-parser/src/main/java/com/facebook/presto/sql/util/AstUtils.java b/presto-parser/src/main/java/com/facebook/presto/sql/util/AstUtils.java index b5b63cffa1662..a55096e418fa5 100644 --- a/presto-parser/src/main/java/com/facebook/presto/sql/util/AstUtils.java +++ b/presto-parser/src/main/java/com/facebook/presto/sql/util/AstUtils.java @@ -14,14 +14,15 @@ package com.facebook.presto.sql.util; import com.facebook.presto.sql.tree.Node; -import com.google.common.collect.TreeTraverser; +import com.google.common.graph.SuccessorsFunction; +import com.google.common.graph.Traverser; import java.util.stream.Stream; -import static com.google.common.collect.Iterables.unmodifiableIterable; +import static com.google.common.collect.Streams.stream; import static java.util.Objects.requireNonNull; -public class AstUtils +public final class AstUtils { public static boolean nodeContains(Node node, Node subNode) { @@ -34,9 +35,9 @@ public static boolean nodeContains(Node node, Node subNode) public static Stream preOrder(Node node) { - return TreeTraverser.using((Node n) -> unmodifiableIterable(n.getChildren())) - .preOrderTraversal(requireNonNull(node, "node is null")) - .stream(); + return stream( + Traverser.forTree((SuccessorsFunction) Node::getChildren) + .depthFirstPreOrder(requireNonNull(node, "node is null"))); } private AstUtils() {} diff --git a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java index 2a8270324fd34..a0b91b29174cf 100644 --- a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java +++ b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParser.java @@ -105,7 +105,6 @@ import com.facebook.presto.sql.tree.ShowCatalogs; import com.facebook.presto.sql.tree.ShowColumns; import com.facebook.presto.sql.tree.ShowGrants; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowStats; @@ -695,42 +694,6 @@ public void testShowColumns() assertStatement("SHOW COLUMNS FROM \"awesome schema\".\"awesome table\"", new ShowColumns(QualifiedName.of("awesome schema", "awesome table"))); } - @Test - public void testShowPartitions() - { - assertStatement("SHOW PARTITIONS FROM t", new ShowPartitions(QualifiedName.of("t"), Optional.empty(), ImmutableList.of(), Optional.empty())); - assertStatement("SHOW PARTITIONS FROM \"awesome table\"", - new ShowPartitions(QualifiedName.of("awesome table"), Optional.empty(), ImmutableList.of(), Optional.empty())); - - assertStatement("SHOW PARTITIONS FROM t WHERE x = 1", - new ShowPartitions( - QualifiedName.of("t"), - Optional.of(new ComparisonExpression(ComparisonExpression.Operator.EQUAL, new Identifier("x"), new LongLiteral("1"))), - ImmutableList.of(), - Optional.empty())); - - assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y", - new ShowPartitions( - QualifiedName.of("t"), - Optional.of(new ComparisonExpression(ComparisonExpression.Operator.EQUAL, new Identifier("x"), new LongLiteral("1"))), - ImmutableList.of(new SortItem(new Identifier("y"), ASCENDING, UNDEFINED)), - Optional.empty())); - - assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y LIMIT 10", - new ShowPartitions( - QualifiedName.of("t"), - Optional.of(new ComparisonExpression(ComparisonExpression.Operator.EQUAL, new Identifier("x"), new LongLiteral("1"))), - ImmutableList.of(new SortItem(new Identifier("y"), ASCENDING, UNDEFINED)), - Optional.of("10"))); - - assertStatement("SHOW PARTITIONS FROM t WHERE x = 1 ORDER BY y LIMIT ALL", - new ShowPartitions( - QualifiedName.of("t"), - Optional.of(new ComparisonExpression(ComparisonExpression.Operator.EQUAL, new Identifier("x"), new LongLiteral("1"))), - ImmutableList.of(new SortItem(new Identifier("y"), ASCENDING, UNDEFINED)), - Optional.of("ALL"))); - } - @Test public void testSubstringBuiltInFunction() { @@ -1880,7 +1843,6 @@ public void testShowStats() for (String fullName : tableNames) { QualifiedName qualifiedName = QualifiedName.of(Arrays.asList(fullName.split("\\."))); assertStatement(format("SHOW STATS FOR %s", qualifiedName), new ShowStats(new Table(qualifiedName))); - assertStatement(format("SHOW STATS ON %s", qualifiedName), new ShowStats(new Table(qualifiedName))); } } diff --git a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParserErrorHandling.java b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParserErrorHandling.java index 5764c9d4e4286..ddc3cbc808be7 100644 --- a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParserErrorHandling.java +++ b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestSqlParserErrorHandling.java @@ -49,8 +49,8 @@ public Object[][] getStatements() {"select * from 'oops", "line 1:15: mismatched input '''. Expecting: '(', 'LATERAL', 'UNNEST', "}, {"select *\nfrom x\nfrom", - "line 3:1: mismatched input 'from'. Expecting: ',', '.', 'AS', 'CROSS', 'EXCEPT', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'LEFT', 'LIMIT', 'NATURAL', 'ORDER', " + - "'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', , "}, + "line 3:1: mismatched input 'from'. Expecting: ',', '.', 'AS', 'CROSS', 'EXCEPT', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', 'LIMIT', 'NATURAL', " + + "'ORDER', 'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', , "}, {"select *\nfrom x\nwhere from", "line 3:7: mismatched input 'from'. Expecting: "}, {"select * from", @@ -86,7 +86,7 @@ public Object[][] getStatements() {"CREATE TABLE foo (*) AS (VALUES 1)", "line 1:19: mismatched input '*'. Expecting: 'OR', 'SCHEMA', 'TABLE', 'VIEW'"}, {"SELECT grouping(a+2) FROM (VALUES (1)) AS t (a) GROUP BY a+2", - "line 1:18: mismatched input '+'. Expecting: ')', ',', '.'"}, + "line 1:18: mismatched input '+'. Expecting: ')', ','"}, {"SELECT x() over (ROWS select) FROM t", "line 1:23: mismatched input 'select'. Expecting: 'BETWEEN', 'CURRENT', 'UNBOUNDED', "}, {"SELECT X() OVER (ROWS UNBOUNDED) FROM T", @@ -112,10 +112,10 @@ public Object[][] getStatements() {"SELECT foo(*) filter (", "line 1:23: mismatched input ''. Expecting: 'WHERE'"}, {"SELECT * FROM t t x", - "line 1:19: mismatched input 'x'. Expecting: '(', ',', 'CROSS', 'EXCEPT', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'LEFT', 'LIMIT', 'NATURAL', 'ORDER', 'RIGHT', " + - "'TABLESAMPLE', 'UNION', 'WHERE', "}, + "line 1:19: mismatched input 'x'. Expecting: '(', ',', 'CROSS', 'EXCEPT', 'FULL', 'GROUP', 'HAVING', 'INNER', 'INTERSECT', 'JOIN', 'LEFT', 'LIMIT', 'NATURAL', 'ORDER', " + + "'RIGHT', 'TABLESAMPLE', 'UNION', 'WHERE', "}, {"SELECT * FROM t WHERE EXISTS (", - "line 1:31: mismatched input ''. Expecting: '(', 'SELECT', 'TABLE', 'VALUES'"}}; + "line 1:31: mismatched input ''. Expecting: "}}; } @Test(dataProvider = "statements") diff --git a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestStatementBuilder.java b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestStatementBuilder.java index 0e7519d413c98..0f3e3d70f4bb1 100644 --- a/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestStatementBuilder.java +++ b/presto-parser/src/test/java/com/facebook/presto/sql/parser/TestStatementBuilder.java @@ -115,14 +115,6 @@ public void testStatementBuilder() printStatement("show tables like '%'"); printStatement("show tables from information_schema like '%'"); - printStatement("show partitions from foo"); - printStatement("show partitions from foo where name = 'foo'"); - printStatement("show partitions from foo order by x"); - printStatement("show partitions from foo limit 10"); - printStatement("show partitions from foo limit all"); - printStatement("show partitions from foo order by x desc limit 10"); - printStatement("show partitions from foo order by x desc limit all"); - printStatement("show functions"); printStatement("select cast('123' as bigint), try_cast('foo' as bigint)"); diff --git a/presto-password-authenticators/pom.xml b/presto-password-authenticators/pom.xml index f9cc178bccfe6..7d6724a67fb03 100644 --- a/presto-password-authenticators/pom.xml +++ b/presto-password-authenticators/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-password-authenticators diff --git a/presto-plugin-toolkit/pom.xml b/presto-plugin-toolkit/pom.xml index dbc206ff3a6cc..09fe3e31e455c 100644 --- a/presto-plugin-toolkit/pom.xml +++ b/presto-plugin-toolkit/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-plugin-toolkit @@ -52,6 +52,16 @@ jackson-databind + + io.airlift + log + + + + io.airlift + units + + com.facebook.presto @@ -76,6 +86,13 @@ test + + com.facebook.presto + presto-spi + test-jar + test + + org.assertj assertj-core diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/JsonUtils.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/JsonUtils.java new file mode 100644 index 0000000000000..9ed100ac93bb8 --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/JsonUtils.java @@ -0,0 +1,52 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.airlift.json.ObjectMapperProvider; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; +import static java.nio.file.Files.exists; +import static java.nio.file.Files.isReadable; + +public final class JsonUtils +{ + private JsonUtils() {} + + public static T parseJson(Path path, Class javaType) + { + if (!path.isAbsolute()) { + path = path.toAbsolutePath(); + } + + checkArgument(exists(path), "File does not exist: %s", path); + checkArgument(isReadable(path), "File is not readable: %s", path); + + try { + byte[] json = Files.readAllBytes(path); + ObjectMapper mapper = new ObjectMapperProvider().get() + .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + return mapper.readValue(json, javaType); + } + catch (IOException e) { + throw new IllegalArgumentException(format("Invalid JSON file '%s' for '%s'", path, javaType), e); + } + } +} diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/AllowAllAccessControl.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/AllowAllAccessControl.java index 6f6e574607c79..01629ce6ae633 100644 --- a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/AllowAllAccessControl.java +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/AllowAllAccessControl.java @@ -24,6 +24,21 @@ public class AllowAllAccessControl implements ConnectorAccessControl { + @Override + public void checkCanCreateSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + } + + @Override + public void checkCanDropSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + } + + @Override + public void checkCanRenameSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName, String newSchemaName) + { + } + @Override public void checkCanShowSchemas(ConnectorTransactionHandle transactionHandle, Identity identity) { @@ -107,7 +122,7 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { } diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControl.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControl.java index 91e7d5ff7f178..8ea8dc1d0c51e 100644 --- a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControl.java +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControl.java @@ -20,40 +20,38 @@ import com.facebook.presto.spi.security.AccessDeniedException; import com.facebook.presto.spi.security.Identity; import com.facebook.presto.spi.security.Privilege; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; -import io.airlift.json.ObjectMapperProvider; import javax.inject.Inject; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; import java.util.Optional; import java.util.Set; +import static com.facebook.presto.plugin.base.JsonUtils.parseJson; import static com.facebook.presto.plugin.base.security.TableAccessControlRule.TablePrivilege.DELETE; import static com.facebook.presto.plugin.base.security.TableAccessControlRule.TablePrivilege.GRANT_SELECT; import static com.facebook.presto.plugin.base.security.TableAccessControlRule.TablePrivilege.INSERT; import static com.facebook.presto.plugin.base.security.TableAccessControlRule.TablePrivilege.OWNERSHIP; import static com.facebook.presto.plugin.base.security.TableAccessControlRule.TablePrivilege.SELECT; import static com.facebook.presto.spi.security.AccessDeniedException.denyAddColumn; +import static com.facebook.presto.spi.security.AccessDeniedException.denyCreateSchema; import static com.facebook.presto.spi.security.AccessDeniedException.denyCreateTable; import static com.facebook.presto.spi.security.AccessDeniedException.denyCreateView; import static com.facebook.presto.spi.security.AccessDeniedException.denyCreateViewWithSelect; import static com.facebook.presto.spi.security.AccessDeniedException.denyDeleteTable; import static com.facebook.presto.spi.security.AccessDeniedException.denyDropColumn; +import static com.facebook.presto.spi.security.AccessDeniedException.denyDropSchema; import static com.facebook.presto.spi.security.AccessDeniedException.denyDropTable; import static com.facebook.presto.spi.security.AccessDeniedException.denyDropView; import static com.facebook.presto.spi.security.AccessDeniedException.denyGrantTablePrivilege; import static com.facebook.presto.spi.security.AccessDeniedException.denyInsertTable; import static com.facebook.presto.spi.security.AccessDeniedException.denyRenameColumn; +import static com.facebook.presto.spi.security.AccessDeniedException.denyRenameSchema; import static com.facebook.presto.spi.security.AccessDeniedException.denyRenameTable; import static com.facebook.presto.spi.security.AccessDeniedException.denyRevokeTablePrivilege; import static com.facebook.presto.spi.security.AccessDeniedException.denySelectTable; -import static java.lang.String.format; public class FileBasedAccessControl implements ConnectorAccessControl @@ -66,26 +64,30 @@ public class FileBasedAccessControl @Inject public FileBasedAccessControl(FileBasedAccessControlConfig config) - throws IOException { - AccessControlRules rules = parse(Files.readAllBytes(Paths.get(config.getConfigFile()))); + AccessControlRules rules = parseJson(Paths.get(config.getConfigFile()), AccessControlRules.class); this.schemaRules = rules.getSchemaRules(); this.tableRules = rules.getTableRules(); this.sessionPropertyRules = rules.getSessionPropertyRules(); } - private static AccessControlRules parse(byte[] json) + @Override + public void checkCanCreateSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) { - ObjectMapper mapper = new ObjectMapperProvider().get() - .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - Class javaType = AccessControlRules.class; - try { - return mapper.readValue(json, javaType); - } - catch (IOException e) { - throw new IllegalArgumentException(format("Invalid JSON string for %s", javaType), e); - } + denyCreateSchema(schemaName); + } + + @Override + public void checkCanDropSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + denyDropSchema(schemaName); + } + + @Override + public void checkCanRenameSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName, String newSchemaName) + { + denyRenameSchema(schemaName, newSchemaName); } @Override @@ -212,7 +214,7 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { if (!canSetSessionProperty(identity, propertyName)) { denySetSessionProperty(propertyName); diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlConfig.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlConfig.java index 195f747730fc6..b82923bd7f619 100644 --- a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlConfig.java +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlConfig.java @@ -14,12 +14,18 @@ package com.facebook.presto.plugin.base.security; import io.airlift.configuration.Config; +import io.airlift.units.Duration; +import io.airlift.units.MinDuration; import javax.validation.constraints.NotNull; public class FileBasedAccessControlConfig { + public static final String SECURITY_CONFIG_FILE = "security.config-file"; + public static final String SECURITY_REFRESH_PERIOD = "security.refresh-period"; + private String configFile; + private Duration refreshPeriod; @NotNull public String getConfigFile() @@ -27,10 +33,23 @@ public String getConfigFile() return configFile; } - @Config("security.config-file") + @Config(SECURITY_CONFIG_FILE) public FileBasedAccessControlConfig setConfigFile(String configFile) { this.configFile = configFile; return this; } + + @MinDuration("1ms") + public Duration getRefreshPeriod() + { + return refreshPeriod; + } + + @Config(SECURITY_REFRESH_PERIOD) + public FileBasedAccessControlConfig setRefreshPeriod(Duration refreshPeriod) + { + this.refreshPeriod = refreshPeriod; + return this; + } } diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlModule.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlModule.java index 95b97b5202d29..2c771053de81c 100644 --- a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlModule.java +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/FileBasedAccessControlModule.java @@ -15,18 +15,39 @@ import com.facebook.presto.spi.connector.ConnectorAccessControl; import com.google.inject.Binder; +import com.google.inject.Inject; import com.google.inject.Module; -import com.google.inject.Scopes; +import com.google.inject.Provides; +import io.airlift.log.Logger; +import static com.google.common.base.Suppliers.memoizeWithExpiration; import static io.airlift.configuration.ConfigBinder.configBinder; +import static java.util.concurrent.TimeUnit.MILLISECONDS; public class FileBasedAccessControlModule implements Module { + private static final Logger log = Logger.get(FileBasedAccessControlModule.class); + @Override public void configure(Binder binder) { - binder.bind(ConnectorAccessControl.class).to(FileBasedAccessControl.class).in(Scopes.SINGLETON); configBinder(binder).bindConfig(FileBasedAccessControlConfig.class); } + + @Inject + @Provides + public ConnectorAccessControl getConnectorAccessControl(FileBasedAccessControlConfig config) + { + if (config.getRefreshPeriod() != null) { + return ForwardingConnectorAccessControl.of(memoizeWithExpiration( + () -> { + log.info("Refreshing system access control from %s", config.getConfigFile()); + return new FileBasedAccessControl(config); + }, + config.getRefreshPeriod().toMillis(), + MILLISECONDS)); + } + return new FileBasedAccessControl(config); + } } diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingConnectorAccessControl.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingConnectorAccessControl.java new file mode 100644 index 0000000000000..85ed55474e37b --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingConnectorAccessControl.java @@ -0,0 +1,176 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base.security; + +import com.facebook.presto.spi.SchemaTableName; +import com.facebook.presto.spi.connector.ConnectorAccessControl; +import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.security.Identity; +import com.facebook.presto.spi.security.Privilege; + +import java.util.Set; +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +public abstract class ForwardingConnectorAccessControl + implements ConnectorAccessControl +{ + public static ConnectorAccessControl of(Supplier connectorAccessControlSupplier) + { + requireNonNull(connectorAccessControlSupplier, "connectorAccessControlSupplier is null"); + return new ForwardingConnectorAccessControl() + { + @Override + protected ConnectorAccessControl delegate() + { + return connectorAccessControlSupplier.get(); + } + }; + } + + protected abstract ConnectorAccessControl delegate(); + + @Override + public void checkCanCreateSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + delegate().checkCanCreateSchema(transactionHandle, identity, schemaName); + } + + @Override + public void checkCanDropSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + delegate().checkCanDropSchema(transactionHandle, identity, schemaName); + } + + @Override + public void checkCanRenameSchema(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName, String newSchemaName) + { + delegate().checkCanRenameSchema(transactionHandle, identity, schemaName, newSchemaName); + } + + @Override + public void checkCanShowSchemas(ConnectorTransactionHandle transactionHandle, Identity identity) + { + delegate().checkCanShowSchemas(transactionHandle, identity); + } + + @Override + public Set filterSchemas(ConnectorTransactionHandle transactionHandle, Identity identity, Set schemaNames) + { + return delegate().filterSchemas(transactionHandle, identity, schemaNames); + } + + @Override + public void checkCanCreateTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanCreateTable(transactionHandle, identity, tableName); + } + + @Override + public void checkCanDropTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanDropTable(transactionHandle, identity, tableName); + } + + @Override + public void checkCanRenameTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName, SchemaTableName newTableName) + { + delegate().checkCanRenameTable(transactionHandle, identity, tableName, newTableName); + } + + @Override + public void checkCanShowTablesMetadata(ConnectorTransactionHandle transactionHandle, Identity identity, String schemaName) + { + delegate().checkCanShowTablesMetadata(transactionHandle, identity, schemaName); + } + + @Override + public Set filterTables(ConnectorTransactionHandle transactionHandle, Identity identity, Set tableNames) + { + return delegate().filterTables(transactionHandle, identity, tableNames); + } + + @Override + public void checkCanAddColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanAddColumn(transactionHandle, identity, tableName); + } + + @Override + public void checkCanDropColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanDropColumn(transactionHandle, identity, tableName); + } + + @Override + public void checkCanRenameColumn(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanRenameColumn(transactionHandle, identity, tableName); + } + + @Override + public void checkCanSelectFromColumns(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName, Set columnNames) + { + delegate().checkCanSelectFromColumns(transactionHandle, identity, tableName, columnNames); + } + + @Override + public void checkCanInsertIntoTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanInsertIntoTable(transactionHandle, identity, tableName); + } + + @Override + public void checkCanDeleteFromTable(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName) + { + delegate().checkCanDeleteFromTable(transactionHandle, identity, tableName); + } + + @Override + public void checkCanCreateView(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName viewName) + { + delegate().checkCanCreateView(transactionHandle, identity, viewName); + } + + @Override + public void checkCanDropView(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName viewName) + { + delegate().checkCanDropView(transactionHandle, identity, viewName); + } + + @Override + public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle transactionHandle, Identity identity, SchemaTableName tableName, Set columnNames) + { + delegate().checkCanCreateViewWithSelectFromColumns(transactionHandle, identity, tableName, columnNames); + } + + @Override + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) + { + delegate().checkCanSetCatalogSessionProperty(transactionHandle, identity, propertyName); + } + + @Override + public void checkCanGrantTablePrivilege(ConnectorTransactionHandle transactionHandle, Identity identity, Privilege privilege, SchemaTableName tableName, String grantee, boolean withGrantOption) + { + delegate().checkCanGrantTablePrivilege(transactionHandle, identity, privilege, tableName, grantee, withGrantOption); + } + + @Override + public void checkCanRevokeTablePrivilege(ConnectorTransactionHandle transactionHandle, Identity identity, Privilege privilege, SchemaTableName tableName, String revokee, boolean grantOptionFor) + { + delegate().checkCanGrantTablePrivilege(transactionHandle, identity, privilege, tableName, revokee, grantOptionFor); + } +} diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingSystemAccessControl.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingSystemAccessControl.java new file mode 100644 index 0000000000000..7842536c24d9a --- /dev/null +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ForwardingSystemAccessControl.java @@ -0,0 +1,203 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base.security; + +import com.facebook.presto.spi.CatalogSchemaName; +import com.facebook.presto.spi.CatalogSchemaTableName; +import com.facebook.presto.spi.SchemaTableName; +import com.facebook.presto.spi.security.Identity; +import com.facebook.presto.spi.security.Privilege; +import com.facebook.presto.spi.security.SystemAccessControl; + +import java.security.Principal; +import java.util.Optional; +import java.util.Set; +import java.util.function.Supplier; + +import static java.util.Objects.requireNonNull; + +public abstract class ForwardingSystemAccessControl + implements SystemAccessControl +{ + public static SystemAccessControl of(Supplier systemAccessControlSupplier) + { + requireNonNull(systemAccessControlSupplier, "systemAccessControlSupplier is null"); + return new ForwardingSystemAccessControl() + { + @Override + protected SystemAccessControl delegate() + { + return systemAccessControlSupplier.get(); + } + }; + } + + protected abstract SystemAccessControl delegate(); + + @Override + public void checkCanSetUser(Optional principal, String userName) + { + delegate().checkCanSetUser(principal, userName); + } + + @Override + public void checkCanSetSystemSessionProperty(Identity identity, String propertyName) + { + delegate().checkCanSetSystemSessionProperty(identity, propertyName); + } + + @Override + public void checkCanAccessCatalog(Identity identity, String catalogName) + { + delegate().checkCanAccessCatalog(identity, catalogName); + } + + @Override + public Set filterCatalogs(Identity identity, Set catalogs) + { + return delegate().filterCatalogs(identity, catalogs); + } + + @Override + public void checkCanCreateSchema(Identity identity, CatalogSchemaName schema) + { + delegate().checkCanCreateSchema(identity, schema); + } + + @Override + public void checkCanDropSchema(Identity identity, CatalogSchemaName schema) + { + delegate().checkCanDropSchema(identity, schema); + } + + @Override + public void checkCanRenameSchema(Identity identity, CatalogSchemaName schema, String newSchemaName) + { + delegate().checkCanRenameSchema(identity, schema, newSchemaName); + } + + @Override + public void checkCanShowSchemas(Identity identity, String catalogName) + { + delegate().checkCanShowSchemas(identity, catalogName); + } + + @Override + public Set filterSchemas(Identity identity, String catalogName, Set schemaNames) + { + return delegate().filterSchemas(identity, catalogName, schemaNames); + } + + @Override + public void checkCanCreateTable(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanCreateTable(identity, table); + } + + @Override + public void checkCanDropTable(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanDropTable(identity, table); + } + + @Override + public void checkCanRenameTable(Identity identity, CatalogSchemaTableName table, CatalogSchemaTableName newTable) + { + delegate().checkCanRenameTable(identity, table, newTable); + } + + @Override + public void checkCanShowTablesMetadata(Identity identity, CatalogSchemaName schema) + { + delegate().checkCanShowTablesMetadata(identity, schema); + } + + @Override + public Set filterTables(Identity identity, String catalogName, Set tableNames) + { + return delegate().filterTables(identity, catalogName, tableNames); + } + + @Override + public void checkCanAddColumn(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanAddColumn(identity, table); + } + + @Override + public void checkCanDropColumn(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanDropColumn(identity, table); + } + + @Override + public void checkCanRenameColumn(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanRenameColumn(identity, table); + } + + @Override + public void checkCanSelectFromColumns(Identity identity, CatalogSchemaTableName table, Set columns) + { + delegate().checkCanSelectFromColumns(identity, table, columns); + } + + @Override + public void checkCanInsertIntoTable(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanInsertIntoTable(identity, table); + } + + @Override + public void checkCanDeleteFromTable(Identity identity, CatalogSchemaTableName table) + { + delegate().checkCanDeleteFromTable(identity, table); + } + + @Override + public void checkCanCreateView(Identity identity, CatalogSchemaTableName view) + { + delegate().checkCanCreateView(identity, view); + } + + @Override + public void checkCanDropView(Identity identity, CatalogSchemaTableName view) + { + delegate().checkCanDropView(identity, view); + } + + @Override + public void checkCanCreateViewWithSelectFromColumns(Identity identity, CatalogSchemaTableName table, Set columns) + { + delegate().checkCanCreateViewWithSelectFromColumns(identity, table, columns); + } + + @Override + public void checkCanSetCatalogSessionProperty(Identity identity, String catalogName, String propertyName) + { + delegate().checkCanSetCatalogSessionProperty(identity, catalogName, propertyName); + } + + @Override + public void checkCanGrantTablePrivilege(Identity identity, Privilege privilege, CatalogSchemaTableName table, String grantee, boolean withGrantOption) + { + delegate().checkCanGrantTablePrivilege(identity, privilege, table, grantee, withGrantOption); + } + + @Override + public void checkCanRevokeTablePrivilege(Identity identity, Privilege privilege, CatalogSchemaTableName table, String revokee, boolean grantOptionFor) + { + delegate().checkCanRevokeTablePrivilege(identity, privilege, table, revokee, grantOptionFor); + } +} diff --git a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ReadOnlyAccessControl.java b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ReadOnlyAccessControl.java index 74f4f5c18531b..7fa98aea7b488 100644 --- a/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ReadOnlyAccessControl.java +++ b/presto-plugin-toolkit/src/main/java/com/facebook/presto/plugin/base/security/ReadOnlyAccessControl.java @@ -132,7 +132,7 @@ public void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle t } @Override - public void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + public void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { // allow } diff --git a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestAllowAllAccessControl.java b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestAllowAllAccessControl.java new file mode 100644 index 0000000000000..c29eada0164f1 --- /dev/null +++ b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestAllowAllAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base.security; + +import com.facebook.presto.spi.connector.ConnectorAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestAllowAllAccessControl +{ + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(ConnectorAccessControl.class, AllowAllAccessControl.class); + } +} diff --git a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControl.java b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControl.java index 18a7681ce69bc..9b4d7a57d1513 100644 --- a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControl.java +++ b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControl.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.util.Optional; +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.testng.Assert.assertThrows; @@ -70,13 +71,13 @@ public void testSessionPropertyRules() throws IOException { ConnectorAccessControl accessControl = createAccessControl("session_property.json"); - accessControl.checkCanSetCatalogSessionProperty(user("admin"), "dangerous"); - accessControl.checkCanSetCatalogSessionProperty(user("alice"), "safe"); - accessControl.checkCanSetCatalogSessionProperty(user("alice"), "unsafe"); - accessControl.checkCanSetCatalogSessionProperty(user("bob"), "safe"); - assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(user("bob"), "unsafe")); - assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(user("alice"), "dangerous")); - assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(user("charlie"), "safe")); + accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("admin"), "dangerous"); + accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("alice"), "safe"); + accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("alice"), "unsafe"); + accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("bob"), "safe"); + assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("bob"), "unsafe")); + assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("alice"), "dangerous")); + assertDenied(() -> accessControl.checkCanSetCatalogSessionProperty(TRANSACTION_HANDLE, user("charlie"), "safe")); } @Test @@ -86,6 +87,12 @@ public void testInvalidRules() .hasMessageContaining("Invalid JSON"); } + @Test + public void testEverythingImplemented() + { + assertAllMethodsOverridden(ConnectorAccessControl.class, FileBasedAccessControl.class); + } + private static Identity user(String name) { return new Identity(name, Optional.empty()); diff --git a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControlConfig.java b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControlConfig.java index 528c4a4632613..3ec7b97fcc7f6 100644 --- a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControlConfig.java +++ b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestFileBasedAccessControlConfig.java @@ -14,13 +14,20 @@ package com.facebook.presto.plugin.base.security; import com.google.common.collect.ImmutableMap; +import com.google.inject.ConfigurationException; +import io.airlift.configuration.ConfigurationFactory; import io.airlift.configuration.testing.ConfigAssertions; +import io.airlift.units.Duration; import org.testng.annotations.Test; import java.util.Map; +import java.util.concurrent.TimeUnit; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_CONFIG_FILE; +import static com.facebook.presto.plugin.base.security.FileBasedAccessControlConfig.SECURITY_REFRESH_PERIOD; import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class TestFileBasedAccessControlConfig { @@ -28,19 +35,44 @@ public class TestFileBasedAccessControlConfig public void testDefaults() { assertRecordedDefaults(ConfigAssertions.recordDefaults(FileBasedAccessControlConfig.class) - .setConfigFile(null)); + .setConfigFile(null) + .setRefreshPeriod(null)); } @Test public void testExplicitPropertyMappings() { Map properties = new ImmutableMap.Builder() - .put("security.config-file", "/test.json") + .put(SECURITY_CONFIG_FILE, "/test.json") + .put(SECURITY_REFRESH_PERIOD, "1s") .build(); FileBasedAccessControlConfig expected = new FileBasedAccessControlConfig() - .setConfigFile("/test.json"); + .setConfigFile("/test.json") + .setRefreshPeriod(new Duration(1, TimeUnit.SECONDS)); assertFullMapping(properties, expected); } + + @Test + public void testValidation() + { + assertThatThrownBy(() -> newInstance(ImmutableMap.of(SECURITY_REFRESH_PERIOD, "1ms"))) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("security.config-file: may not be null "); + + assertThatThrownBy(() -> newInstance(ImmutableMap.of( + SECURITY_CONFIG_FILE, "/test.json", + SECURITY_REFRESH_PERIOD, "1us"))) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining("Invalid configuration property security.refresh-period"); + + newInstance(ImmutableMap.of(SECURITY_CONFIG_FILE, "/test.json")); + } + + private static FileBasedAccessControlConfig newInstance(Map properties) + { + ConfigurationFactory configurationFactory = new ConfigurationFactory(properties); + return configurationFactory.build(FileBasedAccessControlConfig.class); + } } diff --git a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingConnectorAccessControl.java b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingConnectorAccessControl.java new file mode 100644 index 0000000000000..834af1cce38fe --- /dev/null +++ b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingConnectorAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base.security; + +import com.facebook.presto.spi.connector.ConnectorAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestForwardingConnectorAccessControl +{ + @Test + public void testEverythingDelegated() + { + assertAllMethodsOverridden(ConnectorAccessControl.class, ForwardingConnectorAccessControl.class); + } +} diff --git a/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingSystemAccessControl.java b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingSystemAccessControl.java new file mode 100644 index 0000000000000..7925bba37835b --- /dev/null +++ b/presto-plugin-toolkit/src/test/java/com/facebook/presto/plugin/base/security/TestForwardingSystemAccessControl.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.plugin.base.security; + +import com.facebook.presto.spi.security.SystemAccessControl; +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; + +public class TestForwardingSystemAccessControl +{ + @Test + public void testEverythingDelegated() + { + assertAllMethodsOverridden(SystemAccessControl.class, ForwardingSystemAccessControl.class); + } +} diff --git a/presto-postgresql/pom.xml b/presto-postgresql/pom.xml index ff5ef9c7b1b4d..74557513d23d6 100644 --- a/presto-postgresql/pom.xml +++ b/presto-postgresql/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-postgresql @@ -14,14 +14,6 @@ ${project.parent.basedir} - - America/Bahia_Banderas diff --git a/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java b/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java index c19456536877f..504dc225b5887 100644 --- a/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java +++ b/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlIntegrationSmokeTest.java @@ -13,7 +13,11 @@ */ package com.facebook.presto.plugin.postgresql; +import com.facebook.presto.testing.MaterializedResult; +import com.facebook.presto.testing.QueryRunner; import com.facebook.presto.tests.AbstractTestIntegrationSmokeTest; +import com.facebook.presto.tests.DistributedQueryRunner; +import com.google.common.collect.ImmutableMap; import io.airlift.testing.postgresql.TestingPostgreSqlServer; import org.testng.annotations.AfterClass; import org.testng.annotations.Test; @@ -23,10 +27,13 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.util.Map; +import java.util.UUID; import static io.airlift.tpch.TpchTable.ORDERS; import static java.lang.String.format; import static org.assertj.core.api.Assertions.assertThat; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; @@ -137,8 +144,10 @@ public void testTableWithNoSupportedColumns() assertQuery("SHOW COLUMNS FROM no_columns", "SELECT 'nothing' WHERE false"); // Other tables should be visible in SHOW TABLES (the no_supported_columns might be included or might be not) and information_schema.tables - assertQuery("SHOW TABLES", "VALUES 'orders', 'no_supported_columns', 'supported_columns', 'no_columns'"); - assertQuery("SELECT table_name FROM information_schema.tables WHERE table_schema = 'tpch'", "VALUES 'orders', 'no_supported_columns', 'supported_columns', 'no_columns'"); + assertThat(computeActual("SHOW TABLES").getOnlyColumn()) + .contains("orders", "no_supported_columns", "supported_columns", "no_columns"); + assertThat(computeActual("SELECT table_name FROM information_schema.tables WHERE table_schema = 'tpch'").getOnlyColumn()) + .contains("orders", "no_supported_columns", "supported_columns", "no_columns"); // Other tables should be introspectable with SHOW COLUMNS and information_schema.columns assertQuery("SHOW COLUMNS FROM supported_columns", "VALUES ('good', 'varchar(5)', '', '')"); @@ -148,6 +157,68 @@ public void testTableWithNoSupportedColumns() } } + @Test + public void testInsertWithFailureDoesntLeaveBehindOrphanedTable() + throws Exception + { + String schemaName = format("tmp_schema_%s", UUID.randomUUID().toString().replaceAll("-", "")); + try (AutoCloseable schema = withSchema(schemaName); + AutoCloseable table = withTable(format("%s.test_cleanup", schemaName), "(x INTEGER)")) { + assertQuery(format("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", schemaName), "VALUES 'test_cleanup'"); + + execute(format("ALTER TABLE %s.test_cleanup ADD CHECK (x > 0)", schemaName)); + + assertQueryFails(format("INSERT INTO %s.test_cleanup (x) VALUES (0)", schemaName), "ERROR: new row .* violates check constraint [\\s\\S]*"); + assertQuery(format("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", schemaName), "VALUES 'test_cleanup'"); + } + } + + @Test + public void testCharTrailingSpace() + throws Exception + { + execute("CREATE TABLE tpch.char_trailing_space (x char(10))"); + assertUpdate("INSERT INTO char_trailing_space VALUES ('test')", 1); + + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test'", "VALUES 'test'"); + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test '", "VALUES 'test'"); + assertQuery("SELECT * FROM char_trailing_space WHERE x = char 'test '", "VALUES 'test'"); + + assertEquals(getQueryRunner().execute("SELECT * FROM char_trailing_space WHERE x = char ' test'").getRowCount(), 0); + + Map properties = ImmutableMap.of("deprecated.legacy-char-to-varchar-coercion", "true"); + Map connectorProperties = ImmutableMap.of("connection-url", postgreSqlServer.getJdbcUrl()); + + try (QueryRunner queryRunner = new DistributedQueryRunner(getSession(), 3, properties);) { + queryRunner.installPlugin(new PostgreSqlPlugin()); + queryRunner.createCatalog("postgresql", "postgresql", connectorProperties); + + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test'").getRowCount(), 0); + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '").getRowCount(), 0); + assertEquals(queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '").getRowCount(), 0); + + MaterializedResult result = queryRunner.execute("SELECT * FROM char_trailing_space WHERE x = char 'test '"); + assertEquals(result.getRowCount(), 1); + assertEquals(result.getMaterializedRows().get(0).getField(0), "test "); + } + + assertUpdate("DROP TABLE char_trailing_space"); + } + + private AutoCloseable withSchema(String schema) + throws Exception + { + execute(format("CREATE SCHEMA %s", schema)); + return () -> { + try { + execute(format("DROP SCHEMA %s", schema)); + } + catch (SQLException e) { + throw new RuntimeException(e); + } + }; + } + private AutoCloseable withTable(String tableName, String tableDefinition) throws Exception { diff --git a/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlTypeMapping.java b/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlTypeMapping.java index 5fe0f83743ae9..081496c8640e2 100644 --- a/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlTypeMapping.java +++ b/presto-postgresql/src/test/java/com/facebook/presto/plugin/postgresql/TestPostgreSqlTypeMapping.java @@ -33,6 +33,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.function.Function; +import java.util.function.IntFunction; import static com.facebook.presto.plugin.postgresql.PostgreSqlQueryRunner.createPostgreSqlQueryRunner; import static com.facebook.presto.spi.type.TimeZoneKey.UTC_KEY; @@ -173,7 +174,7 @@ private DataTypeTest unicodeVarcharDateTypeTest() .addRoundTrip(varcharDataType(), "\u041d\u0443, \u043f\u043e\u0433\u043e\u0434\u0438!"); } - private DataTypeTest unicodeDataTypeTest(Function> dataTypeFactory) + private DataTypeTest unicodeDataTypeTest(IntFunction> dataTypeFactory) { String sampleUnicodeText = "\u653b\u6bbb\u6a5f\u52d5\u968a"; String sampleFourByteUnicodeCharacter = "\uD83D\uDE02"; diff --git a/presto-product-tests/README.md b/presto-product-tests/README.md index 971362deec4cc..51db907ad4bf5 100644 --- a/presto-product-tests/README.md +++ b/presto-product-tests/README.md @@ -35,78 +35,17 @@ broken. pip install docker-compose ``` -### OS X using Docker for Mac (macOS 10.10.3 Yosemite or newer) [PREFERRED WAY] +### OS X using Docker for Mac -* Install Docker for Mac: https://docs.docker.com/docker-for-mac/ +* Install [Docker for Mac](https://docs.docker.com/docker-for-mac/) * Add entries in `/etc/hosts` for all services running in docker containers: `hadoop-master`, `mysql`, `postgres`, `cassandra`, `presto-master`. -They should point to your external IP address (shown by `ifconfig` on your Mac (not inside docker)). +They should point to your external IP address (shown by `ifconfig` on your Mac, not inside Docker). * The default memory setting of 2GB might not be sufficient for some profiles like `singlenode-ldap`. You may need 4-8 GB or even more to run certain tests. You can increase Docker memory by going to -Docker Preferences -> Advanced -> Memory. - -### OS X using Docker Toolbox (macOS 10.8 "Mountain Lion" or newer) [NOT RECOMMENDED] - -* [`VirtualBox >= 5.0`](https://www.virtualbox.org/wiki/Downloads) - -The Docker daemon cannot run natively on OS X because it uses Linux-specific -kernel features. Instead, the Docker daemon runs in a Linux VM created by -the `docker-machine` binary. Docker containers run in the Linux VM and are -controlled by the `docker` client binary that runs natively on OS X. -Both `docker-machine` and `docker` are included in the `docker-toolbox` -package, which must be installed. - -* [`docker-toolbox >= 1.10`](https://www.docker.com/products/docker-toolbox) - -In addition to `docker-machine` and `docker`, the `docker-toolbox` -package also install `docker-compose`, which is a multi-container -orchestration Python utility. To gain access to these utilities, start the -pre-configured shell environment by double-clicking on the "Docker Quickstart -Terminal" icon located in ~/Applications/Docker. Note that all commands listed -in subsequent parts of this tutorial must be run within such a pre-configured -shell. - -#### Setting up a Linux VM for Docker Toolbox - -The `docker-toolbox` installation creates a VirtualBox VM called `default`. -To run product-tests on the `default` VM, it must be re-configured to use -4GB of memory with the following commands: - -``` -docker-machine stop -vboxmanage modifyvm default --memory 4096 -docker-machine start -``` - -Alternatively, if you do not want to use the `default` VM to run the -product tests, you can create a new VM with the commands below. Note that -the `default` VM will always be running when you start a new pre-configured -shell environment. Permanently removing or replacing the `default` VM -is beyond the scope of this tutorial. - -* Create a VM called . This should be done only once and not -every time a pre-configured shell is started: - - ``` - docker-machine create -d virtualbox --virtualbox-memory 4096 - ``` - -* After the new VM is created, the pre-configured shell environment must be -told to use the `` VM instead of the `default` VM to run Docker -containers. These commands must be run every time a new pre-configured -shell is started: - - ``` - docker-machine start - eval $(docker-machine env ) - ``` - -Note that for every new VM, the docker images on the previous -VM will have to be re-downloaded when the product tests are kicked -off. To avoid this unnecessary re-download, do not create new -VMs often. +*Docker Preferences -> Advanced -> Memory*. ## Use the `docker-compose` wrappers @@ -200,7 +139,7 @@ Please keep in mind that if you run tests on Hive of version not greater than 1. First version of Hive capable of running tests from `post_hive_1_0_1` group is Hive 1.1.0. For more information on the various ways in which Presto can be configured to -interact with Kerberized Hive and Hadoop, please refer to the [Hive connector documentation](https://prestodb.io/docs/current/connector/hive.html). +interact with Kerberized Hive and Hadoop, please refer to the [Hive connector documentation](https://prestodb.github.io/docs/current/connector/hive.html). ### Running a single test diff --git a/presto-product-tests/bin/lib.sh b/presto-product-tests/bin/lib.sh index 0d2bc0ebbc530..1f490c4bf5c69 100644 --- a/presto-product-tests/bin/lib.sh +++ b/presto-product-tests/bin/lib.sh @@ -39,8 +39,7 @@ function stop_application_runner_containers() { echo "Container stopped: ${CONTAINER_NAME}" done echo "Removing dead application-runner containers" - local CONTAINERS=`docker ps -aq --no-trunc --filter status=dead --filter status=exited --filter name=common_application-runner` - for CONTAINER in ${CONTAINERS}; + for CONTAINER in $(docker ps -aq --no-trunc --filter status=dead --filter status=exited --filter name=common_application-runner); do docker rm -v "${CONTAINER}" done diff --git a/presto-product-tests/bin/run_on_docker.sh b/presto-product-tests/bin/run_on_docker.sh index 540102ee3563f..3b24924b31fb6 100755 --- a/presto-product-tests/bin/run_on_docker.sh +++ b/presto-product-tests/bin/run_on_docker.sh @@ -33,7 +33,8 @@ function check_hadoop() { } function run_in_application_runner_container() { - local CONTAINER_NAME=$(environment_compose run -d application-runner "$@") + local CONTAINER_NAME + CONTAINER_NAME=$(environment_compose run -p 5007:5007 -d application-runner "$@") echo "Showing logs from $CONTAINER_NAME:" docker logs -f $CONTAINER_NAME return $(docker inspect --format '{{.State.ExitCode}}' $CONTAINER_NAME) @@ -59,9 +60,7 @@ function run_product_tests() { } function prefetch_images_silently() { - local IMAGES=$(docker_images_used) - for IMAGE in $IMAGES - do + for IMAGE in $(docker_images_used); do echo "Pulling docker image [$IMAGE]" docker pull $IMAGE > /dev/null done diff --git a/presto-product-tests/conf/docker/common/compose-commons.sh b/presto-product-tests/conf/docker/common/compose-commons.sh index 8058a1fa52651..70c9c247f89c8 100644 --- a/presto-product-tests/conf/docker/common/compose-commons.sh +++ b/presto-product-tests/conf/docker/common/compose-commons.sh @@ -6,7 +6,8 @@ function export_canonical_path() { local PATH_REFERENCE=$1 # when ref=var; var=value; then ${!ref} returns value # echo the variable to resolve any wildcards in paths - local PATH=$( echo ${!PATH_REFERENCE} ) + local PATH + PATH=$( echo ${!PATH_REFERENCE} ) if [[ ${PATH} != /* ]] ; then PATH=./${PATH} fi @@ -23,7 +24,7 @@ function export_canonical_path() { source "${BASH_SOURCE%/*}/../../../bin/locations.sh" -export DOCKER_IMAGES_VERSION=${DOCKER_IMAGES_VERSION:-6} +export DOCKER_IMAGES_VERSION=${DOCKER_IMAGES_VERSION:-10} export HADOOP_BASE_IMAGE=${HADOOP_BASE_IMAGE:-"prestodb/hdp2.6-hive"} # The following variables are defined to enable running product tests with arbitrary/downloaded jars diff --git a/presto-product-tests/conf/docker/common/standard.yml b/presto-product-tests/conf/docker/common/standard.yml index 4ed5bd6978858..95b9dcdf9fac3 100644 --- a/presto-product-tests/conf/docker/common/standard.yml +++ b/presto-product-tests/conf/docker/common/standard.yml @@ -24,6 +24,8 @@ services: - '19888:19888' - '50070:50070' - '50075:50075' + volumes: + - ../../../src/main/resources/avro:/docker/volumes/presto-product-tests/avro presto-master: extends: @@ -37,6 +39,7 @@ services: - hadoop-master volumes: - ${PRESTO_SERVER_DIR}:/docker/volumes/presto-server + - ../../../src/main/resources/avro:/docker/volumes/presto-product-tests/avro application-runner: extends: diff --git a/presto-product-tests/conf/docker/files/run-tempto.sh b/presto-product-tests/conf/docker/files/run-tempto.sh index 1afa5aa873a4b..7410a52a3fa3a 100755 --- a/presto-product-tests/conf/docker/files/run-tempto.sh +++ b/presto-product-tests/conf/docker/files/run-tempto.sh @@ -15,6 +15,7 @@ if ! test -z ${TEMPTO_EXTRA_CONFIG_FILE:-}; then fi java \ + `#-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5007` \ "-Djava.util.logging.config.file=${DOCKER_TEMPTO_CONF_DIR}/logging.properties" \ -Duser.timezone=Asia/Kathmandu \ -cp "/docker/volumes/jdbc/driver.jar:/docker/volumes/presto-product-tests/presto-product-tests-executable.jar" \ diff --git a/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/compose.sh b/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/compose.sh new file mode 100755 index 0000000000000..ded20a6688252 --- /dev/null +++ b/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/compose.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source "${BASH_SOURCE%/*}/../common/compose-commons.sh" + +docker-compose \ + -f ${BASH_SOURCE%/*}/../common/standard.yml \ + -f ${BASH_SOURCE%/*}/../common/kerberos.yml \ + -f ${BASH_SOURCE%/*}/docker-compose.yml \ + "$@" diff --git a/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/docker-compose.yml b/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/docker-compose.yml new file mode 100644 index 0000000000000..44257bdf7e114 --- /dev/null +++ b/presto-product-tests/conf/docker/singlenode-kerberos-hdfs-impersonation-cross-realm/docker-compose.yml @@ -0,0 +1,6 @@ +version: '2' +services: + + presto-master: + volumes: + - ../../../conf/presto/etc/environment-specific-catalogs/singlenode-kerberos-hdfs-impersonation-cross-realm/hive.properties:/docker/volumes/conf/presto/etc/catalog/hive.properties diff --git a/presto-product-tests/conf/presto/etc/catalog/hive.properties b/presto-product-tests/conf/presto/etc/catalog/hive.properties index c4fc127127f6b..842df94866d0f 100644 --- a/presto-product-tests/conf/presto/etc/catalog/hive.properties +++ b/presto-product-tests/conf/presto/etc/catalog/hive.properties @@ -8,6 +8,7 @@ connector.name=hive-hadoop2 hive.metastore.uri=thrift://hadoop-master:9083 hive.metastore.thrift.client.socks-proxy=hadoop-master:1180 +hive.config.resources=/docker/volumes/conf/presto/etc/hive-default-fs-site.xml hive.allow-add-column=true hive.allow-drop-column=true hive.allow-rename-column=true diff --git a/presto-product-tests/conf/presto/etc/environment-specific-catalogs/singlenode-kerberos-hdfs-impersonation-cross-realm/hive.properties b/presto-product-tests/conf/presto/etc/environment-specific-catalogs/singlenode-kerberos-hdfs-impersonation-cross-realm/hive.properties new file mode 100644 index 0000000000000..b217e8d96fa03 --- /dev/null +++ b/presto-product-tests/conf/presto/etc/environment-specific-catalogs/singlenode-kerberos-hdfs-impersonation-cross-realm/hive.properties @@ -0,0 +1,29 @@ +# +# WARNING +# ^^^^^^^ +# This configuration file is for development only and should NOT be used be +# used in production. For example configuration, see the Presto documentation. +# + +connector.name=hive-hadoop2 +hive.metastore.uri=thrift://hadoop-master:9083 +hive.metastore.thrift.client.socks-proxy=hadoop-master:1180 +hive.allow-drop-table=true +hive.allow-rename-table=true +hive.metastore-cache-ttl=0s +hive.allow-add-column=true +hive.allow-drop-column=true +hive.allow-rename-column=true + +hive.metastore.authentication.type=KERBEROS +hive.metastore.service.principal=hive/hadoop-master@LABS.TERADATA.COM +hive.metastore.client.principal=hive/hadoop-master@OTHERLABS.TERADATA.COM +hive.metastore.client.keytab=/etc/hive/conf/hive-other.keytab +hive.config.resources = /etc/hadoop/conf/core-site.xml + +hive.hdfs.authentication.type=KERBEROS +hive.hdfs.impersonation.enabled=true +hive.hdfs.presto.principal=hdfs/hadoop-master@OTHERLABS.TERADATA.COM +hive.hdfs.presto.keytab=/etc/hadoop/conf/hdfs-other.keytab +hive.fs.cache.max-size=10 +hive.max-partitions-per-scan=100 diff --git a/presto-product-tests/conf/presto/etc/hive-default-fs-site.xml b/presto-product-tests/conf/presto/etc/hive-default-fs-site.xml new file mode 100644 index 0000000000000..d496a936c8cd0 --- /dev/null +++ b/presto-product-tests/conf/presto/etc/hive-default-fs-site.xml @@ -0,0 +1,6 @@ + + + fs.default.name + hdfs://hadoop-master:9000 + + diff --git a/presto-product-tests/conf/presto/etc/log.properties b/presto-product-tests/conf/presto/etc/log.properties index c7c3a2d190a2a..e2d6f6bc084cc 100644 --- a/presto-product-tests/conf/presto/etc/log.properties +++ b/presto-product-tests/conf/presto/etc/log.properties @@ -8,5 +8,5 @@ com.facebook.presto=INFO com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory=WARN com.ning.http.client=DEBUG -com.facebook.presto.server.PluginManager=DEBUG +com.facebook.presto.server.PluginManager=INFO io.airlift.discovery.client=INFO diff --git a/presto-product-tests/pom.xml b/presto-product-tests/pom.xml index f8fa0f50ef574..933a3eafbd07e 100644 --- a/presto-product-tests/pom.xml +++ b/presto-product-tests/pom.xml @@ -5,7 +5,7 @@ presto-root com.facebook.presto - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-product-tests @@ -112,10 +112,6 @@ io.airlift.tpch tpch - - joda-time - joda-time - com.facebook.presto.hive hive-apache-jdbc @@ -171,14 +167,14 @@ shade - + We remove signed information from META-INF + --> *:* diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/SystemConnectorTests.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/SystemConnectorTests.java index 971f626b5c22a..22caf7757495b 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/SystemConnectorTests.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/SystemConnectorTests.java @@ -16,9 +16,13 @@ import io.prestodb.tempto.ProductTest; import org.testng.annotations.Test; +import java.sql.JDBCType; + import static com.facebook.presto.tests.TestGroups.JDBC; import static com.facebook.presto.tests.TestGroups.SYSTEM_CONNECTOR; +import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingTeradataJdbcDriver; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; +import static io.prestodb.tempto.query.QueryExecutor.defaultQueryExecutor; import static io.prestodb.tempto.query.QueryExecutor.query; import static java.sql.JDBCType.ARRAY; import static java.sql.JDBCType.BIGINT; @@ -41,7 +45,6 @@ public void selectRuntimeNodes() public void selectRuntimeQueries() { String sql = "SELECT" + - " node_id," + " query_id," + " state," + " user," + @@ -55,8 +58,9 @@ public void selectRuntimeQueries() " last_heartbeat," + " 'end' " + "FROM system.runtime.queries"; + JDBCType arrayType = usingTeradataJdbcDriver(defaultQueryExecutor().getConnection()) ? VARCHAR : ARRAY; assertThat(query(sql)) - .hasColumns(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, ARRAY, + .hasColumns(VARCHAR, VARCHAR, VARCHAR, VARCHAR, arrayType, BIGINT, BIGINT, BIGINT, TIMESTAMP, TIMESTAMP, TIMESTAMP, VARCHAR) .hasAnyRows(); } @@ -76,7 +80,6 @@ public void selectRuntimeTasks() " completed_splits," + " split_scheduled_time_ms," + " split_cpu_time_ms," + - " split_user_time_ms," + " split_blocked_time_ms," + " raw_input_bytes," + " raw_input_rows," + @@ -92,7 +95,7 @@ public void selectRuntimeTasks() "FROM SYSTEM.runtime.tasks"; assertThat(query(sql)) .hasColumns(VARCHAR, VARCHAR, VARCHAR, VARCHAR, VARCHAR, - BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, + BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, BIGINT, TIMESTAMP, TIMESTAMP, TIMESTAMP, VARCHAR) .hasAnyRows(); } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/TestGroups.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/TestGroups.java index 2126992de2af2..264a755cb5e5e 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/TestGroups.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/TestGroups.java @@ -22,7 +22,6 @@ public final class TestGroups public static final String QUARANTINE = "quarantine"; public static final String FUNCTIONS = "functions"; public static final String CLI = "cli"; - public static final String HIVE_CONNECTOR = "hive_connector"; public static final String SYSTEM_CONNECTOR = "system"; public static final String JMX_CONNECTOR = "jmx"; public static final String BLACKHOLE_CONNECTOR = "blackhole"; @@ -49,7 +48,7 @@ public final class TestGroups public static final String PROFILE_SPECIFIC_TESTS = "profile_specific_tests"; public static final String HDFS_IMPERSONATION = "hdfs_impersonation"; public static final String HDFS_NO_IMPERSONATION = "hdfs_no_impersonation"; - public static final String BASIC_SQL = "basic_sql"; + public static final String HIVE_PARTITIONING = "hive_partitioning"; public static final String AUTHORIZATION = "authorization"; public static final String POST_HIVE_1_0_1 = "post_hive_1_0_1"; public static final String HIVE_COERCION = "hive_coercion"; @@ -63,6 +62,7 @@ public final class TestGroups public static final String BIG_QUERY = "big_query"; public static final String HIVE_TABLE_STATISTICS = "hive_table_statistics"; public static final String KAFKA = "kafka"; + public static final String AVRO = "avro"; private TestGroups() {} } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/cli/PrestoCliLauncher.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/cli/PrestoCliLauncher.java index af742dc4fb3f4..a3c33449adbfa 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/cli/PrestoCliLauncher.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/cli/PrestoCliLauncher.java @@ -78,10 +78,9 @@ protected void launchPrestoCli(List arguments) protected ProcessBuilder getProcessBuilder(List arguments) { - ImmutableList command = ImmutableList.builder() - .add(new String[] {JAVA_BIN, "-cp", CLASSPATH, Presto.class.getCanonicalName()}) + return new ProcessBuilder(ImmutableList.builder() + .add(JAVA_BIN, "-cp", CLASSPATH, Presto.class.getCanonicalName()) .addAll(arguments) - .build(); - return new ProcessBuilder(command); + .build()); } } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/AllSimpleTypesTableDefinitions.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/AllSimpleTypesTableDefinitions.java index e33be839934db..844492ef3eeae 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/AllSimpleTypesTableDefinitions.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/AllSimpleTypesTableDefinitions.java @@ -16,13 +16,11 @@ import io.prestodb.tempto.fulfillment.table.TableDefinitionsRepository; import io.prestodb.tempto.fulfillment.table.hive.HiveDataSource; import io.prestodb.tempto.fulfillment.table.hive.HiveTableDefinition; -import io.prestodb.tempto.query.QueryExecutor; import java.util.Locale; import java.util.Optional; -import java.util.concurrent.ThreadLocalRandom; -import static io.prestodb.tempto.context.ThreadLocalTestContextHolder.testContext; +import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static io.prestodb.tempto.fulfillment.table.hive.InlineDataSource.createResourceDataSource; import static java.lang.String.format; @@ -132,7 +130,7 @@ private static HiveTableDefinition.HiveTableDefinitionBuilder parquetTableDefini private static HiveDataSource getTextFileDataSource() { - return createResourceDataSource(format(tableNameFormat, "textfile"), String.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE)), "com/facebook/presto/tests/hive/data/all_types/data.textfile"); + return createResourceDataSource(format(tableNameFormat, "textfile"), "com/facebook/presto/tests/hive/data/all_types/data.textfile"); } public static void populateDataToHiveTable(String tableName) @@ -141,9 +139,4 @@ public static void populateDataToHiveTable(String tableName) tableName, format(tableNameFormat, "textfile"))); } - - public static QueryExecutor onHive() - { - return testContext().getDependency(QueryExecutor.class, "hive"); - } } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/HiveTableDefinitions.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/HiveTableDefinitions.java index df1f073c471f3..1019682fa3714 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/HiveTableDefinitions.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/HiveTableDefinitions.java @@ -20,8 +20,6 @@ public class HiveTableDefinitions { - private static final String DATA_REVISION = "1"; - private static final String NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME = "nation_partitioned_by_regionkey"; public static final int NATION_PARTITIONED_BY_REGIONKEY_NUMBER_OF_LINES_PER_SPLIT = 5; @@ -39,19 +37,16 @@ public class HiveTableDefinitions "p_regionkey=1", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("bigint", "1"))) .addPartition( "p_regionkey=2", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("bigint", "2"))) .addPartition( "p_regionkey=3", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("bigint", "3"))) .build(); @@ -68,19 +63,16 @@ public class HiveTableDefinitions "p_regionkey='AMERICA'", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("varchar", "america"))) .addPartition( "p_regionkey='ASIA'", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("varchar", "asia"))) .addPartition( "p_regionkey='EUROPE'", createResourceDataSource( NATION_PARTITIONED_BY_REGIONKEY_TABLE_NAME, - DATA_REVISION, partitionDataFileResource("varchar", "europe"))) .build(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAllDatatypesFromHiveConnector.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAllDatatypesFromHiveConnector.java index d11d4a9206fef..f3455abadc366 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAllDatatypesFromHiveConnector.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAllDatatypesFromHiveConnector.java @@ -32,7 +32,7 @@ import java.sql.Timestamp; import java.time.LocalDateTime; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; +import static com.facebook.presto.tests.TestGroups.AVRO; import static com.facebook.presto.tests.TestGroups.JDBC; import static com.facebook.presto.tests.TestGroups.POST_HIVE_1_0_1; import static com.facebook.presto.tests.TestGroups.SKIP_ON_CDH; @@ -42,8 +42,8 @@ import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.ALL_HIVE_SIMPLE_TYPES_PARQUET; import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.ALL_HIVE_SIMPLE_TYPES_RCFILE; import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.ALL_HIVE_SIMPLE_TYPES_TEXTFILE; -import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.onHive; import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.populateDataToHiveTable; +import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; import static io.prestodb.tempto.context.ThreadLocalTestContextHolder.testContext; @@ -126,7 +126,7 @@ public Requirement getRequirements(Configuration configuration) } @Requires(TextRequirements.class) - @Test(groups = {HIVE_CONNECTOR, SMOKE}) + @Test(groups = {SMOKE}) public void testSelectAllDatatypesTextFile() { String tableName = ALL_HIVE_SIMPLE_TYPES_TEXTFILE.getName(); @@ -155,7 +155,7 @@ public void testSelectAllDatatypesTextFile() } @Requires(OrcRequirements.class) - @Test(groups = {HIVE_CONNECTOR, JDBC}) + @Test(groups = {JDBC}) public void testSelectAllDatatypesOrc() { String tableName = mutableTableInstanceOf(ALL_HIVE_SIMPLE_TYPES_ORC).getNameInDatabase(); @@ -186,7 +186,7 @@ public void testSelectAllDatatypesOrc() } @Requires(RcfileRequirements.class) - @Test(groups = {HIVE_CONNECTOR, JDBC}) + @Test(groups = {JDBC}) public void testSelectAllDatatypesRcfile() { String tableName = mutableTableInstanceOf(ALL_HIVE_SIMPLE_TYPES_RCFILE).getNameInDatabase(); @@ -217,7 +217,7 @@ public void testSelectAllDatatypesRcfile() } @Requires(AvroRequirements.class) - @Test(groups = {HIVE_CONNECTOR, JDBC, SKIP_ON_CDH}) + @Test(groups = {JDBC, SKIP_ON_CDH, AVRO}) public void testSelectAllDatatypesAvro() { String tableName = mutableTableInstanceOf(ALL_HIVE_SIMPLE_TYPES_AVRO).getNameInDatabase(); @@ -347,7 +347,7 @@ private void assertColumnTypesParquet(QueryResult queryResult) } @Requires(ParquetRequirements.class) - @Test(groups = {HIVE_CONNECTOR, POST_HIVE_1_0_1}) + @Test(groups = {POST_HIVE_1_0_1}) public void testSelectAllDatatypesParquetFile() { String tableName = mutableTableInstanceOf(ALL_HIVE_SIMPLE_TYPES_PARQUET).getNameInDatabase(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaEvolution.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaEvolution.java new file mode 100644 index 0000000000000..0d2ec69c20434 --- /dev/null +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaEvolution.java @@ -0,0 +1,182 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.tests.hive; + +import io.prestodb.tempto.AfterTestWithContext; +import io.prestodb.tempto.BeforeTestWithContext; +import io.prestodb.tempto.ProductTest; +import io.prestodb.tempto.query.QueryExecutor; +import org.testng.annotations.Test; + +import static com.facebook.presto.tests.TestGroups.AVRO; +import static io.prestodb.tempto.assertions.QueryAssert.Row.row; +import static io.prestodb.tempto.assertions.QueryAssert.assertThat; +import static io.prestodb.tempto.context.ThreadLocalTestContextHolder.testContext; +import static io.prestodb.tempto.query.QueryExecutor.query; +import static java.lang.String.format; + +public class TestAvroSchemaEvolution + extends ProductTest +{ + private static final String TABLE_NAME = "product_tests_avro_table"; + private static final String ORIGINAL_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/original_schema.avsc"; + private static final String CREATE_TABLE = format("" + + "CREATE TABLE %s (dummy_col VARCHAR)" + + "WITH (" + + "format='AVRO', " + + "avro_schema_url='%s'" + + ")", + TABLE_NAME, + ORIGINAL_SCHEMA); + private static final String RENAMED_COLUMN_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/rename_column_schema.avsc"; + private static final String REMOVED_COLUMN_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/remove_column_schema.avsc"; + private static final String ADDED_COLUMN_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/add_column_schema.avsc"; + private static final String CHANGE_COLUMN_TYPE_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/change_column_type_schema.avsc"; + private static final String INCOMPATIBLE_TYPE_SCHEMA = "file:///docker/volumes/presto-product-tests/avro/incompatible_type_schema.avsc"; + private static final String SELECT_STAR = "SELECT * FROM " + TABLE_NAME; + private static final String COLUMNS_IN_TABLE = "SHOW COLUMNS IN " + TABLE_NAME; + + @BeforeTestWithContext + public void createAndLoadTable() + { + query(CREATE_TABLE); + query(format("INSERT INTO %s VALUES ('string0', 0)", TABLE_NAME)); + } + + @AfterTestWithContext + public void dropTestTable() + { + query(format("DROP TABLE IF EXISTS %s", TABLE_NAME)); + } + + @Test(groups = {AVRO}) + public void testSelectTable() + { + assertThat(query(format("SELECT string_col FROM %s", TABLE_NAME))) + .containsExactly(row("string0")); + } + + @Test(groups = {AVRO}) + public void testInsertAfterSchemaEvolution() + { + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0)); + + alterTableSchemaTo(ADDED_COLUMN_SCHEMA); + query(format("INSERT INTO %s VALUES ('string1', 1, 101)", TABLE_NAME)); + assertThat(query(SELECT_STAR)) + .containsOnly( + row("string0", 0, 100), + row("string1", 1, 101)); + } + + @Test(groups = {AVRO}) + public void testSchemaEvolutionWithIncompatibleType() + { + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0)); + + alterTableSchemaTo(INCOMPATIBLE_TYPE_SCHEMA); + assertThat(() -> query(SELECT_STAR)) + .failsWithMessage("Found int, expecting string"); + } + + @Test(groups = {AVRO}) + public void testSchemaEvolution() + { + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0)); + + alterTableSchemaTo(CHANGE_COLUMN_TYPE_SCHEMA); + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col", "bigint", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0)); + + alterTableSchemaTo(ADDED_COLUMN_SCHEMA); + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col", "integer", "", ""), + row("int_col_added", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0, 100)); + + alterTableSchemaTo(REMOVED_COLUMN_SCHEMA); + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly(row("int_col", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row(0)); + + alterTableSchemaTo(RENAMED_COLUMN_SCHEMA); + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col_renamed", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", null)); + } + + @Test(groups = {AVRO}) + public void testSchemaWhenUrlIsUnset() + { + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("string_col", "varchar", "", ""), + row("int_col", "integer", "", "")); + assertThat(query(SELECT_STAR)) + .containsExactly(row("string0", 0)); + + executeHiveQuery(format("ALTER TABLE %s UNSET TBLPROPERTIES('avro.schema.url')", TABLE_NAME)); + assertThat(query(COLUMNS_IN_TABLE)) + .containsExactly( + row("dummy_col", "varchar", "", "")); + } + + @Test(groups = {AVRO}) + public void testCreateTableLike() + { + String createTableLikeName = "test_avro_like"; + query(format( + "CREATE TABLE %s (LIKE %s INCLUDING PROPERTIES)", + createTableLikeName, + TABLE_NAME)); + + query(format("INSERT INTO %s VALUES ('string0', 0)", createTableLikeName)); + + assertThat(query(format("SELECT string_col FROM %s", createTableLikeName))) + .containsExactly(row("string0")); + query("DROP TABLE IF EXISTS " + createTableLikeName); + } + + private void alterTableSchemaTo(String schema) + { + executeHiveQuery(format("ALTER TABLE %s SET TBLPROPERTIES('avro.schema.url'='%s')", TABLE_NAME, schema)); + } + + private static void executeHiveQuery(String query) + { + testContext().getDependency(QueryExecutor.class, "hive").executeQuery(query); + } +} diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaUrl.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaUrl.java new file mode 100644 index 0000000000000..688f12c0e9c8d --- /dev/null +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestAvroSchemaUrl.java @@ -0,0 +1,99 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.tests.hive; + +import com.google.common.io.Resources; +import com.google.inject.Inject; +import io.prestodb.tempto.AfterTestWithContext; +import io.prestodb.tempto.BeforeTestWithContext; +import io.prestodb.tempto.ProductTest; +import io.prestodb.tempto.hadoop.hdfs.HdfsClient; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.InputStream; + +import static com.facebook.presto.tests.TestGroups.AVRO; +import static com.facebook.presto.tests.utils.QueryExecutors.onHive; +import static com.facebook.presto.tests.utils.QueryExecutors.onPresto; +import static io.prestodb.tempto.assertions.QueryAssert.Row.row; +import static io.prestodb.tempto.assertions.QueryAssert.assertThat; +import static java.lang.String.format; + +public class TestAvroSchemaUrl + extends ProductTest +{ + @Inject + private HdfsClient hdfsClient; + + @BeforeTestWithContext + public void setup() + throws Exception + { + hdfsClient.createDirectory("/user/hive/warehouse/TestAvroSchemaUrl/schemas"); + try (InputStream inputStream = Resources.asByteSource(Resources.getResource("avro/original_schema.avsc")).openStream()) { + hdfsClient.saveFile("/user/hive/warehouse/TestAvroSchemaUrl/schemas/original_schema.avsc", inputStream); + } + } + + @AfterTestWithContext + public void cleanup() + { + hdfsClient.delete("/user/hive/warehouse/TestAvroSchemaUrl/schemas"); + } + + @DataProvider + public Object[][] avroSchemaLocations() + { + return new Object[][] { + {"file:///docker/volumes/presto-product-tests/avro/original_schema.avsc"}, // mounted in hadoop and presto containers + {"hdfs://hadoop-master:9000/user/hive/warehouse/TestAvroSchemaUrl/schemas/original_schema.avsc"}, + {"hdfs:///user/hive/warehouse/TestAvroSchemaUrl/schemas/original_schema.avsc"}, + {"/user/hive/warehouse/TestAvroSchemaUrl/schemas/original_schema.avsc"}, // `avro.schema.url` can actually be path on HDFS (not URL) + }; + } + + @Test(dataProvider = "avroSchemaLocations", groups = {AVRO}) + public void testHiveCreatedTable(String schemaLocation) + { + onHive().executeQuery("DROP TABLE IF EXISTS test_avro_schema_url_hive"); + onHive().executeQuery(format("" + + "CREATE TABLE test_avro_schema_url_hive " + + "ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe' " + + "STORED AS " + + "INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat' " + + "OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat' " + + "TBLPROPERTIES ('avro.schema.url'='%s')", + schemaLocation)); + onHive().executeQuery("INSERT INTO test_avro_schema_url_hive VALUES ('some text', 123042)"); + + assertThat(onHive().executeQuery("SELECT * FROM test_avro_schema_url_hive")).containsExactly(row("some text", 123042)); + assertThat(onPresto().executeQuery("SELECT * FROM test_avro_schema_url_hive")).containsExactly(row("some text", 123042)); + + onHive().executeQuery("DROP TABLE test_avro_schema_url_hive"); + } + + @Test(dataProvider = "avroSchemaLocations", groups = {AVRO}) + public void testPrestoCreatedTable(String schemaLocation) + { + onPresto().executeQuery("DROP TABLE IF EXISTS test_avro_schema_url_presto"); + onPresto().executeQuery(format("CREATE TABLE test_avro_schema_url_presto (dummy_col VARCHAR) WITH (format='AVRO', avro_schema_url='%s')", schemaLocation)); + onPresto().executeQuery("INSERT INTO test_avro_schema_url_presto VALUES ('some text', 123042)"); + + assertThat(onHive().executeQuery("SELECT * FROM test_avro_schema_url_presto")).containsExactly(row("some text", 123042)); + assertThat(onPresto().executeQuery("SELECT * FROM test_avro_schema_url_presto")).containsExactly(row("some text", 123042)); + + onPresto().executeQuery("DROP TABLE test_avro_schema_url_presto"); + } +} diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestExternalHiveTable.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestExternalHiveTable.java index af52aa87b68de..19d982270d388 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestExternalHiveTable.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestExternalHiveTable.java @@ -20,7 +20,6 @@ import io.prestodb.tempto.fulfillment.table.TableInstance; import org.testng.annotations.Test; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_BIGINT_REGIONKEY; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_REGIONKEY_NUMBER_OF_LINES_PER_SPLIT; import static com.facebook.presto.tests.utils.QueryExecutors.onHive; @@ -44,7 +43,7 @@ public Requirement getRequirements(Configuration configuration) mutableTable(NATION_PARTITIONED_BY_BIGINT_REGIONKEY)); } - @Test(groups = {HIVE_CONNECTOR}) + @Test public void testInsertIntoExternalTable() { TableInstance nation = mutableTablesState().get(NATION.getName()); @@ -55,7 +54,7 @@ public void testInsertIntoExternalTable() .failsWithMessage("Cannot write to non-managed Hive table"); } - @Test(groups = {HIVE_CONNECTOR}) + @Test public void testDeleteFromExternalTable() { TableInstance nation = mutableTablesState().get(NATION.getName()); @@ -65,7 +64,7 @@ public void testDeleteFromExternalTable() .failsWithMessage("Cannot delete from non-managed Hive table"); } - @Test(groups = {HIVE_CONNECTOR}) + @Test public void testDeleteFromExternalPartitionedTableTable() { TableInstance nation = mutableTablesState().get(NATION_PARTITIONED_BY_BIGINT_REGIONKEY.getName()); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestGrantRevoke.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestGrantRevoke.java index 5a81e27e67185..3065120b8a7a0 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestGrantRevoke.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestGrantRevoke.java @@ -22,14 +22,13 @@ import org.testng.annotations.Test; import static com.facebook.presto.tests.TestGroups.AUTHORIZATION; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.PROFILE_SPECIFIC_TESTS; import static com.facebook.presto.tests.utils.QueryExecutors.connectToPresto; +import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static io.prestodb.tempto.assertions.QueryAssert.Row; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; import static io.prestodb.tempto.context.ContextDsl.executeWith; -import static io.prestodb.tempto.context.ThreadLocalTestContextHolder.testContext; import static io.prestodb.tempto.sql.SqlContexts.createViewAs; import static java.lang.String.format; @@ -49,7 +48,7 @@ public class TestGrantRevoke * - "alice@presto" that has "jdbc_user: alice" * - "bob@presto" that has "jdbc_user: bob" * (all other values of the connection are same as that of the default "presto" connection). - */ + */ @BeforeTestWithContext public void setup() @@ -77,7 +76,7 @@ public void cleanup() } } - @Test(groups = {HIVE_CONNECTOR, AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testGrantRevoke() { // test GRANT @@ -99,7 +98,7 @@ public void testGrantRevoke() .failsWithMessage(format("Access Denied: Cannot select from table default.%s", tableName)); } - @Test(groups = {HIVE_CONNECTOR, AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testShowGrants() { assertThat(aliceExecutor.executeQuery(format("SHOW GRANTS ON %s", tableName))) @@ -118,7 +117,7 @@ public void testShowGrants() row("bob", "hive", "default", tableName, "INSERT", Boolean.FALSE))); } - @Test(groups = {HIVE_CONNECTOR, AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAll() { aliceExecutor.executeQuery(format("GRANT ALL PRIVILEGES ON %s TO bob", tableName)); @@ -133,7 +132,7 @@ public void testAll() assertThat(bobExecutor.executeQuery(format("SHOW GRANTS ON %s", tableName))).hasNoRows(); } - @Test(groups = {HIVE_CONNECTOR, AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testPublic() { aliceExecutor.executeQuery(format("GRANT SELECT ON %s TO PUBLIC", tableName)); @@ -144,7 +143,7 @@ public void testPublic() assertThat(aliceExecutor.executeQuery(format("SELECT * FROM %s", tableName))).hasNoRows(); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testTableOwnerPrivileges() { onHive().executeQuery("set role admin;"); @@ -153,7 +152,7 @@ public void testTableOwnerPrivileges() .containsOnly(ownerGrants()); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testViewOwnerPrivileges() { onHive().executeQuery("set role admin;"); @@ -169,11 +168,6 @@ private ImmutableList ownerGrants() return ImmutableList.of(row("SELECT", Boolean.TRUE), row("INSERT", Boolean.TRUE), row("UPDATE", Boolean.TRUE), row("DELETE", Boolean.TRUE)); } - public static QueryExecutor onHive() - { - return testContext().getDependency(QueryExecutor.class, "hive"); - } - private static void assertAccessDeniedOnAllOperationsOnTable(QueryExecutor queryExecutor, String tableName) { assertThat(() -> queryExecutor.executeQuery(format("SELECT * FROM %s", tableName))) diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBasicTableStatistics.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBasicTableStatistics.java index 8836ea5c9edea..4a5b78b14afc8 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBasicTableStatistics.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBasicTableStatistics.java @@ -25,7 +25,6 @@ import java.util.Optional; import java.util.OptionalLong; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.HIVE_TABLE_STATISTICS; import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static com.facebook.presto.tests.utils.QueryExecutors.onPresto; @@ -39,7 +38,7 @@ public class TestHiveBasicTableStatistics extends ProductTest { - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testCreateUnpartitioned() { String tableName = "test_basic_statistics_unpartitioned_ctas_presto"; @@ -57,7 +56,7 @@ public void testCreateUnpartitioned() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testCreateTableWithNoData() { String tableName = "test_basic_statistics_unpartitioned_ctas_presto_with_no_data"; @@ -74,7 +73,7 @@ public void testCreateTableWithNoData() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testInsertUnpartitioned() { String tableName = "test_basic_statistics_unpartitioned_insert_presto"; @@ -110,7 +109,7 @@ public void testInsertUnpartitioned() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testCreatePartitioned() { String tableName = "test_basic_statistics_partitioned_ctas_presto"; @@ -145,7 +144,7 @@ public void testCreatePartitioned() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testInsertPartitioned() { String tableName = "test_basic_statistics_partitioned_insert_presto"; @@ -183,7 +182,7 @@ public void testInsertPartitioned() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testInsertBucketed() { String tableName = "test_basic_statistics_bucketed_insert_presto"; @@ -218,7 +217,7 @@ public void testInsertBucketed() } } - @Test(groups = {HIVE_CONNECTOR, HIVE_TABLE_STATISTICS}) + @Test(groups = {HIVE_TABLE_STATISTICS}) public void testInsertBucketedPartitioned() { String tableName = "test_basic_statistics_bucketed_partitioned_insert_presto"; diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBucketedTables.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBucketedTables.java index e97397e3e4a56..d6b84081d0a5a 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBucketedTables.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveBucketedTables.java @@ -24,7 +24,6 @@ import org.testng.annotations.Test; import static com.facebook.presto.tests.TestGroups.BIG_QUERY; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; @@ -61,7 +60,7 @@ public Requirement getRequirements(Configuration configuration) immutableTable(NATION)); } - @Test(groups = {HIVE_CONNECTOR, BIG_QUERY}) + @Test(groups = {BIG_QUERY}) public void testIgnorePartitionBucketingIfNotBucketed() { String tableName = mutableTablesState().get(BUCKETED_PARTITIONED_NATION).getNameInDatabase(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveCoercion.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveCoercion.java index 7adc0335ef51e..e595b242b23f4 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveCoercion.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveCoercion.java @@ -40,7 +40,6 @@ import java.util.Optional; import static com.facebook.presto.tests.TestGroups.HIVE_COERCION; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.JDBC; import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingPrestoJdbcDriver; import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingTeradataJdbcDriver; @@ -190,42 +189,42 @@ public Requirement getRequirements(Configuration configuration) } @Requires(TextRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionTextFile() { doTestHiveCoercion(HIVE_COERCION_TEXTFILE); } @Requires(OrcRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionOrc() { doTestHiveCoercion(HIVE_COERCION_ORC); } @Requires(RcTextRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionRcText() { doTestHiveCoercion(HIVE_COERCION_RCTEXT); } @Requires(RcBinaryRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionRcBinary() { doTestHiveCoercion(HIVE_COERCION_RCBINARY); } @Requires(ParquetRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionParquet() { doTestHiveCoercion(HIVE_COERCION_PARQUET); } @Requires(AvroRequirements.class) - @Test(groups = {HIVE_COERCION, HIVE_CONNECTOR, JDBC}) + @Test(groups = {HIVE_COERCION, JDBC}) public void testHiveCoercionAvro() { String tableName = mutableTableInstanceOf(HIVE_COERCION_AVRO).getNameInDatabase(); @@ -283,32 +282,32 @@ private void doTestHiveCoercion(HiveTableDefinition tableDefinition) Connection connection = defaultQueryExecutor().getConnection(); if (usingPrestoJdbcDriver(connection)) { expectedRows = ImmutableList.of( - row( - -1, - 2, - -3L, - 100, - -101L, - 2323L, - "12345", - 0.5, - asMap("keep", "as is", "ti2si", (short) -1, "si2int", 100, "int2bi", 2323L, "bi2vc", "12345"), - ImmutableList.of(asMap("ti2int", 2, "si2bi", -101L, "bi2vc", "12345")), - asMap(2, asMap("ti2bi", -3L, "int2bi", 2323L, "float2double", 0.5, "add", null)), - 1), - row( - 1, - -2, - null, - -100, - 101L, - -2323L, - "-12345", - -1.5, - asMap("keep", null, "ti2si", (short) 1, "si2int", -100, "int2bi", -2323L, "bi2vc", "-12345"), - ImmutableList.of(asMap("ti2int", -2, "si2bi", 101L, "bi2vc", "-12345")), - ImmutableMap.of(-2, asMap("ti2bi", null, "int2bi", -2323L, "float2double", -1.5, "add", null)), - 1)); + row( + -1, + 2, + -3L, + 100, + -101L, + 2323L, + "12345", + 0.5, + asMap("keep", "as is", "ti2si", (short) -1, "si2int", 100, "int2bi", 2323L, "bi2vc", "12345"), + ImmutableList.of(asMap("ti2int", 2, "si2bi", -101L, "bi2vc", "12345")), + asMap(2, asMap("ti2bi", -3L, "int2bi", 2323L, "float2double", 0.5, "add", null)), + 1), + row( + 1, + -2, + null, + -100, + 101L, + -2323L, + "-12345", + -1.5, + asMap("keep", null, "ti2si", (short) 1, "si2int", -100, "int2bi", -2323L, "bi2vc", "-12345"), + ImmutableList.of(asMap("ti2int", -2, "si2bi", 101L, "bi2vc", "-12345")), + ImmutableMap.of(-2, asMap("ti2bi", null, "int2bi", -2323L, "float2double", -1.5, "add", null)), + 1)); } else if (usingTeradataJdbcDriver(connection)) { expectedRows = ImmutableList.of( diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHivePartitionsTable.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHivePartitionsTable.java index 13cacc0db6728..6e6f7085b5075 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHivePartitionsTable.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHivePartitionsTable.java @@ -30,10 +30,9 @@ import java.math.RoundingMode; import java.util.Optional; -import java.util.concurrent.ThreadLocalRandom; import java.util.stream.IntStream; -import static com.facebook.presto.tests.TestGroups.BASIC_SQL; +import static com.facebook.presto.tests.TestGroups.HIVE_PARTITIONING; import static io.prestodb.tempto.Requirements.compose; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; @@ -77,8 +76,8 @@ private static TableDefinition partitionedTableDefinition() "PARTITIONED BY (part_col INT) " + "STORED AS ORC"; - HiveDataSource dataSource = createResourceDataSource(PARTITIONED_TABLE, String.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE)), "com/facebook/presto/tests/hive/data/single_int_column/data.orc"); - HiveDataSource invalidData = createStringDataSource(PARTITIONED_TABLE, String.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE)), "INVALID DATA"); + HiveDataSource dataSource = createResourceDataSource(PARTITIONED_TABLE, "com/facebook/presto/tests/hive/data/single_int_column/data.orc"); + HiveDataSource invalidData = createStringDataSource(PARTITIONED_TABLE, "INVALID DATA"); return HiveTableDefinition.builder(PARTITIONED_TABLE) .setCreateTableDDLTemplate(createTableDdl) .addPartition("part_col = 1", invalidData) @@ -98,7 +97,7 @@ private static TableDefinition partitionedTableWithVariablePartitionsDefinition( .build(); } - @Test(groups = {BASIC_SQL}) + @Test(groups = {HIVE_PARTITIONING}) public void testShowPartitionsFromHiveTable() { String tableNameInDatabase = tablesState.get(PARTITIONED_TABLE).getNameInDatabase(); @@ -120,14 +119,14 @@ public void testShowPartitionsFromHiveTable() .failsWithMessage("Column 'col' cannot be resolved"); } - @Test(groups = {BASIC_SQL}) + @Test(groups = {HIVE_PARTITIONING}) public void testShowPartitionsFromUnpartitionedTable() { assertThat(() -> query("SELECT * FROM \"nation$partitions\"")) .failsWithMessageMatching(".*Table hive.default.nation\\$partitions does not exist"); } - @Test(groups = {BASIC_SQL}) + @Test(groups = {HIVE_PARTITIONING}) public void testShowPartitionsFromHiveTableWithTooManyPartitions() { String tableName = tablesState.get(PARTITIONED_TABLE_WITH_VARIABLE_PARTITIONS).getNameInDatabase(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveStorageFormats.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveStorageFormats.java index 4c1d2c98b353a..a23a2f03967ec 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveStorageFormats.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveStorageFormats.java @@ -47,11 +47,10 @@ public class TestHiveStorageFormats public static Object[][] storageFormats() { return new StorageFormat[][] { - {storageFormat("ORC")}, + {storageFormat("ORC", ImmutableMap.of("hive.orc_optimized_writer_enabled", "false"))}, {storageFormat("ORC", ImmutableMap.of("hive.orc_optimized_writer_enabled", "true", "hive.orc_optimized_writer_validate", "true"))}, {storageFormat("DWRF")}, {storageFormat("PARQUET")}, - {storageFormat("PARQUET", ImmutableMap.of("hive.parquet_optimized_reader_enabled", "true"))}, {storageFormat("RCBINARY", ImmutableMap.of("hive.rcfile_optimized_writer_enabled", "false", "hive.rcfile_optimized_writer_validate", "false"))}, {storageFormat("RCBINARY", ImmutableMap.of("hive.rcfile_optimized_writer_enabled", "true", "hive.rcfile_optimized_writer_validate", "true"))}, {storageFormat("RCTEXT", ImmutableMap.of("hive.rcfile_optimized_writer_enabled", "false", "hive.rcfile_optimized_writer_validate", "false"))}, @@ -197,7 +196,7 @@ public void testSnappyCompressedParquetTableCreatedInHive() { String tableName = "table_created_in_hive_parquet"; - query("DROP TABLE IF EXISTS " + tableName); + onHive().executeQuery("DROP TABLE IF EXISTS " + tableName); onHive().executeQuery(format( "CREATE TABLE %s (" + @@ -211,7 +210,7 @@ public void testSnappyCompressedParquetTableCreatedInHive() assertThat(query("SELECT * FROM " + tableName)).containsExactly(row(1, "test data")); - query("DROP TABLE " + tableName); + onHive().executeQuery("DROP TABLE " + tableName); } private static void assertSelect(String query, String tableName) diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveTableStatistics.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveTableStatistics.java index 856db802dcfb7..17cd7711eff9f 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveTableStatistics.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestHiveTableStatistics.java @@ -24,20 +24,18 @@ import io.prestodb.tempto.fulfillment.table.MutableTableRequirement; import io.prestodb.tempto.fulfillment.table.hive.HiveTableDefinition; import io.prestodb.tempto.fulfillment.table.hive.InlineDataSource; -import io.prestodb.tempto.query.QueryExecutor; import org.testng.annotations.Test; import java.util.List; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.SKIP_ON_CDH; import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.ALL_HIVE_SIMPLE_TYPES_TEXTFILE; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_BIGINT_REGIONKEY; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_VARCHAR_REGIONKEY; +import static com.facebook.presto.tests.utils.QueryExecutors.onHive; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.anyOf; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; -import static io.prestodb.tempto.context.ThreadLocalTestContextHolder.testContext; import static io.prestodb.tempto.fulfillment.table.MutableTablesState.mutableTablesState; import static io.prestodb.tempto.fulfillment.table.TableRequirements.mutableTable; import static io.prestodb.tempto.fulfillment.table.hive.tpch.TpchTableDefinitions.NATION; @@ -93,7 +91,6 @@ public Requirement getRequirements(Configuration configuration) private static final HiveTableDefinition ALL_TYPES_TABLE = HiveTableDefinition.like(ALL_HIVE_SIMPLE_TYPES_TEXTFILE) .setDataSource(InlineDataSource.createStringDataSource( "all_analyzable_types", - "", "121|32761|2147483641|9223372036854775801|123.341|234.561|344.671|345.671|2015-05-10 12:15:31.123456|2015-05-09|ela ma kota|ela ma kot|ela ma |false|cGllcyBiaW5hcm55|\n" + "127|32767|2147483647|9223372036854775807|123.345|235.567|345.678|345.678|2015-05-10 12:15:35.123456|2015-06-10|ala ma kota|ala ma kot|ala ma |true|a290IGJpbmFybnk=|\n")) .build(); @@ -101,7 +98,6 @@ public Requirement getRequirements(Configuration configuration) private static final HiveTableDefinition ALL_TYPES_ALL_NULL_TABLE = HiveTableDefinition.like(ALL_HIVE_SIMPLE_TYPES_TEXTFILE) .setDataSource(InlineDataSource.createStringDataSource( "all_analyzable_types_all_null", - "", "\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\\N|\n")) .build(); @@ -109,12 +105,12 @@ public Requirement getRequirements(Configuration configuration) row("c_tinyint", null, 2.0, 0.0, null, "121", "127"), row("c_smallint", null, 2.0, 0.0, null, "32761", "32767"), row("c_int", null, 2.0, 0.0, null, "2147483641", "2147483647"), - row("c_bigint", null, 2.0, 0.0, null, "9223372036854775801", "9223372036854775807"), + row("c_bigint", null, 2.0, 0.0, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 2.0, 0.0, null, "123.341", "123.345"), row("c_double", null, 2.0, 0.0, null, "234.561", "235.567"), - row("c_decimal", null, 2.0, 0.0, null, "345", "346"), - row("c_decimal_w_params", null, 2.0, 0.0, null, "345.67100", "345.67800"), - row("c_timestamp", null, 2.0, 0.0, null, "2015-05-10 12:15:31.000", "2015-05-10 12:15:35.000"), + row("c_decimal", null, 2.0, 0.0, null, "345.0", "346.0"), + row("c_decimal_w_params", null, 2.0, 0.0, null, "345.671", "345.678"), + row("c_timestamp", null, 2.0, 0.0, null, null, null), row("c_date", null, 2.0, 0.0, null, "2015-05-09", "2015-06-10"), row("c_string", 22.0, 2.0, 0.0, null, null, null), row("c_varchar", 20.0, 2.0, 0.0, null, null, null), @@ -172,7 +168,7 @@ public Requirement getRequirements(Configuration configuration) } } - @Test(groups = {HIVE_CONNECTOR}) + @Test @Requires(UnpartitionedNationTable.class) public void testStatisticsForUnpartitionedTable() { @@ -208,11 +204,11 @@ public void testStatisticsForUnpartitionedTable() row("n_nationkey", null, 19.0, 0.0, null, "0", "24"), row("n_name", 177.0, 24.0, 0.0, null, null, null), row("n_regionkey", null, 5.0, 0.0, null, "0", "4"), - row("n_comment", 1857.0, 31.0, 0.0, null, null, null), + row("n_comment", 1857.0, 25.0, 0.0, null, null, null), row(null, null, null, null, 25.0, null, null)); } - @Test(groups = {HIVE_CONNECTOR}) + @Test @Requires(NationPartitionedByBigintTable.class) public void testStatisticsForTablePartitionedByBigint() { @@ -227,14 +223,14 @@ public void testStatisticsForTablePartitionedByBigint() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 3.0, null, null, "1", "3"), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 1.0, null, null, "1", "1"), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); @@ -259,7 +255,7 @@ public void testStatisticsForTablePartitionedByBigint() assertThat(query(showStatsPartitionTwo)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 1.0, null, null, "2", "2"), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); @@ -294,16 +290,16 @@ public void testStatisticsForTablePartitionedByBigint() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 114.0, 6.0, 0.0, null, null, null), + row("p_name", 114.0, 5.0, 0.0, null, null, null), row("p_regionkey", null, 3.0, 0.0, null, "1", "3"), - row("p_comment", 1497.0, 7.0, 0.0, null, null, null), + row("p_comment", 1497.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 15.0, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 38.0, 6.0, 0.0, null, null, null), + row("p_name", 38.0, 5.0, 0.0, null, null, null), row("p_regionkey", null, 1.0, 0.0, null, "1", "1"), - row("p_comment", 499.0, 7.0, 0.0, null, null, null), + row("p_comment", 499.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); assertThat(query(showStatsPartitionTwo)).containsOnly( @@ -319,27 +315,27 @@ public void testStatisticsForTablePartitionedByBigint() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 109.0, 6.0, 0.0, null, null, null), + row("p_name", 109.0, 5.0, 0.0, null, null, null), row("p_regionkey", null, 3.0, 0.0, null, "1", "3"), - row("p_comment", 1197.0, 7.0, 0.0, null, null, null), + row("p_comment", 1197.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 15.0, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 38.0, 6.0, 0.0, null, null, null), + row("p_name", 38.0, 5.0, 0.0, null, null, null), row("p_regionkey", null, 1.0, 0.0, null, "1", "1"), - row("p_comment", 499.0, 7.0, 0.0, null, null, null), + row("p_comment", 499.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); assertThat(query(showStatsPartitionTwo)).containsOnly( row("p_nationkey", null, 4.0, 0.0, null, "8", "21"), - row("p_name", 31.0, 6.0, 0.0, null, null, null), + row("p_name", 31.0, 5.0, 0.0, null, null, null), row("p_regionkey", null, 1.0, 0.0, null, "2", "2"), row("p_comment", 351.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); } - @Test(groups = {HIVE_CONNECTOR}) + @Test @Requires(NationPartitionedByVarcharTable.class) public void testStatisticsForTablePartitionedByVarchar() { @@ -354,14 +350,14 @@ public void testStatisticsForTablePartitionedByVarchar() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 3.0, null, null, null, null), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 1.0, null, null, null, null), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); @@ -386,7 +382,7 @@ public void testStatisticsForTablePartitionedByVarchar() assertThat(query(showStatsPartitionTwo)).containsOnly( row("p_nationkey", null, null, null, null, null, null), row("p_name", null, null, null, null, null, null), - row("p_regionkey", null, 1.0, null, null, null, null), + row("p_regionkey", null, null, null, null, null, null), row("p_comment", null, null, null, null, null, null), row(null, null, null, null, null, null, null)); @@ -421,16 +417,16 @@ public void testStatisticsForTablePartitionedByVarchar() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 114.0, 6.0, 0.0, null, null, null), + row("p_name", 114.0, 5.0, 0.0, null, null, null), row("p_regionkey", 85.0, 3.0, 0.0, null, null, null), - row("p_comment", 1497.0, 7.0, 0.0, null, null, null), + row("p_comment", 1497.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 15.0, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 38.0, 6.0, 0.0, null, null, null), + row("p_name", 38.0, 5.0, 0.0, null, null, null), row("p_regionkey", 35.0, 1.0, 0.0, null, null, null), - row("p_comment", 499.0, 7.0, 0.0, null, null, null), + row("p_comment", 499.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); assertThat(query(showStatsPartitionTwo)).containsOnly( @@ -446,28 +442,28 @@ public void testStatisticsForTablePartitionedByVarchar() assertThat(query(showStatsWholeTable)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 109.0, 6.0, 0.0, null, null, null), + row("p_name", 109.0, 5.0, 0.0, null, null, null), row("p_regionkey", 85.0, 3.0, 0.0, null, null, null), - row("p_comment", 1197.0, 7.0, 0.0, null, null, null), + row("p_comment", 1197.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 15.0, null, null)); assertThat(query(showStatsPartitionOne)).containsOnly( row("p_nationkey", null, 5.0, 0.0, null, "1", "24"), - row("p_name", 38.0, 6.0, 0.0, null, null, null), + row("p_name", 38.0, 5.0, 0.0, null, null, null), row("p_regionkey", 35.0, 1.0, 0.0, null, null, null), - row("p_comment", 499.0, 7.0, 0.0, null, null, null), + row("p_comment", 499.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); assertThat(query(showStatsPartitionTwo)).containsOnly( row("p_nationkey", null, 4.0, 0.0, null, "8", "21"), - row("p_name", 31.0, 6.0, 0.0, null, null, null), + row("p_name", 31.0, 5.0, 0.0, null, null, null), row("p_regionkey", 20.0, 1.0, 0.0, null, null, null), row("p_comment", 351.0, 5.0, 0.0, null, null, null), row(null, null, null, null, 5.0, null, null)); } // This covers also stats calculation for unpartitioned table - @Test(groups = {HIVE_CONNECTOR, SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats + @Test(groups = {SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats @Requires(AllTypesTable.class) public void testStatisticsForAllDataTypes() { @@ -500,12 +496,12 @@ public void testStatisticsForAllDataTypes() row("c_tinyint", null, 2.0, 0.0, null, "121", "127"), row("c_smallint", null, 2.0, 0.0, null, "32761", "32767"), row("c_int", null, 2.0, 0.0, null, "2147483641", "2147483647"), - row("c_bigint", null, 2.0, 0.0, null, "9223372036854775801", "9223372036854775807"), + row("c_bigint", null, 2.0, 0.0, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 2.0, 0.0, null, "123.341", "123.345"), row("c_double", null, 2.0, 0.0, null, "234.561", "235.567"), - row("c_decimal", null, 2.0, 0.0, null, "345", "346"), - row("c_decimal_w_params", null, 2.0, 0.0, null, "345.67100", "345.67800"), - row("c_timestamp", null, 2.0, 0.0, null, "2015-05-10 06:30:31.000", "2015-05-10 06:30:35.000"), // timestamp is shifted by hive.time-zone on read + row("c_decimal", null, 2.0, 0.0, null, "345.0", "346.0"), + row("c_decimal_w_params", null, 2.0, 0.0, null, "345.671", "345.678"), + row("c_timestamp", null, 2.0, 0.0, null, null, null), // timestamp is shifted by hive.time-zone on read row("c_date", null, 2.0, 0.0, null, "2015-05-09", "2015-06-10"), row("c_string", 22.0, 2.0, 0.0, null, null, null), row("c_varchar", 20.0, 2.0, 0.0, null, null, null), @@ -515,7 +511,7 @@ public void testStatisticsForAllDataTypes() row(null, null, null, null, 2.0, null, null)); } - @Test(groups = {HIVE_CONNECTOR, SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats + @Test(groups = {SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats @Requires(AllTypesTable.class) public void testStatisticsForAllDataTypesNoData() { @@ -524,21 +520,21 @@ public void testStatisticsForAllDataTypesNoData() onHive().executeQuery("ANALYZE TABLE " + tableNameInDatabase + " COMPUTE STATISTICS"); assertThat(query("SHOW STATS FOR " + tableNameInDatabase)).containsOnly( - row("c_tinyint", null, null, 0.0, null, null, null), - row("c_smallint", null, null, 0.0, null, null, null), - row("c_int", null, null, 0.0, null, null, null), - row("c_bigint", null, null, 0.0, null, null, null), - row("c_float", null, null, 0.0, null, null, null), - row("c_double", null, null, 0.0, null, null, null), - row("c_decimal", null, null, 0.0, null, null, null), - row("c_decimal_w_params", null, null, 0.0, null, null, null), - row("c_timestamp", null, null, 0.0, null, null, null), - row("c_date", null, null, 0.0, null, null, null), - row("c_string", null, null, 0.0, null, null, null), - row("c_varchar", null, null, 0.0, null, null, null), - row("c_char", null, null, 0.0, null, null, null), - row("c_boolean", null, null, 0.0, null, null, null), - row("c_binary", null, null, 0.0, null, null, null), + row("c_tinyint", null, null, null, null, null, null), + row("c_smallint", null, null, null, null, null, null), + row("c_int", null, null, null, null, null, null), + row("c_bigint", null, null, null, null, null, null), + row("c_float", null, null, null, null, null, null), + row("c_double", null, null, null, null, null, null), + row("c_decimal", null, null, null, null, null, null), + row("c_decimal_w_params", null, null, null, null, null, null), + row("c_timestamp", null, null, null, null, null, null), + row("c_date", null, null, null, null, null, null), + row("c_string", null, null, null, null, null, null), + row("c_varchar", null, null, null, null, null, null), + row("c_char", null, null, null, null, null, null), + row("c_boolean", null, null, null, null, null, null), + row("c_binary", null, null, null, null, null, null), row(null, null, null, null, 0.0, null, null)); onHive().executeQuery("ANALYZE TABLE " + tableNameInDatabase + " COMPUTE STATISTICS FOR COLUMNS"); @@ -562,7 +558,7 @@ public void testStatisticsForAllDataTypesNoData() row(null, null, null, null, 0.0, null, null)); } - @Test(groups = {HIVE_CONNECTOR, SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats + @Test(groups = {SKIP_ON_CDH}) // skip on cdh due to no support for date column and stats @Requires(AllTypesTable.class) public void testStatisticsForAllDataTypesOnlyNulls() { @@ -646,12 +642,12 @@ public void testComputeTableStatisticsOnInsert() row("c_tinyint", null, 2.0, 0.5, null, "121", "127"), row("c_smallint", null, 2.0, 0.5, null, "32761", "32767"), row("c_int", null, 2.0, 0.5, null, "2147483641", "2147483647"), - row("c_bigint", null, 2.0, 0.5, null, "9223372036854775801", "9223372036854775807"), + row("c_bigint", null, 2.0, 0.5, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 2.0, 0.5, null, "123.341", "123.345"), row("c_double", null, 2.0, 0.5, null, "234.561", "235.567"), - row("c_decimal", null, 2.0, 0.5, null, "345", "346"), - row("c_decimal_w_params", null, 2.0, 0.5, null, "345.67100", "345.67800"), - row("c_timestamp", null, 2.0, 0.5, null, "2015-05-10 12:15:31.000", "2015-05-10 12:15:35.000"), + row("c_decimal", null, 2.0, 0.5, null, "345.0", "346.0"), + row("c_decimal_w_params", null, 2.0, 0.5, null, "345.671", "345.678"), + row("c_timestamp", null, 2.0, 0.5, null, null, null), row("c_date", null, 2.0, 0.5, null, "2015-05-09", "2015-06-10"), row("c_string", 22.0, 2.0, 0.5, null, null, null), row("c_varchar", 20.0, 2.0, 0.5, null, null, null), @@ -681,12 +677,12 @@ public void testComputeTableStatisticsOnInsert() row("c_tinyint", null, 2.0, 0.4, null, "120", "127"), row("c_smallint", null, 2.0, 0.4, null, "32760", "32767"), row("c_int", null, 2.0, 0.4, null, "2147483640", "2147483647"), - row("c_bigint", null, 2.0, 0.4, null, "9223372036854775800", "9223372036854775807"), + row("c_bigint", null, 2.0, 0.4, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 2.0, 0.4, null, "123.34", "123.345"), row("c_double", null, 2.0, 0.4, null, "234.56", "235.567"), - row("c_decimal", null, 2.0, 0.4, null, "343", "346"), - row("c_decimal_w_params", null, 2.0, 0.4, null, "345.67000", "345.67800"), - row("c_timestamp", null, 2.0, 0.4, null, "2015-05-10 12:15:30.000", "2015-05-10 12:15:35.000"), + row("c_decimal", null, 2.0, 0.4, null, "343.0", "346.0"), + row("c_decimal_w_params", null, 2.0, 0.4, null, "345.67", "345.678"), + row("c_timestamp", null, 2.0, 0.4, null, null, null), row("c_date", null, 2.0, 0.4, null, "2015-05-08", "2015-06-10"), row("c_string", 32.0, 2.0, 0.4, null, null, null), row("c_varchar", 29.0, 2.0, 0.4, null, null, null), @@ -757,12 +753,12 @@ public void testComputePartitionStatisticsOnCreateTable() row("c_tinyint", null, 1.0, 0.5, null, "120", "120"), row("c_smallint", null, 1.0, 0.5, null, "32760", "32760"), row("c_int", null, 1.0, 0.5, null, "2147483640", "2147483640"), - row("c_bigint", null, 1.0, 0.5, null, "9223372036854775800", "9223372036854775800"), + row("c_bigint", null, 1.0, 0.5, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 1.0, 0.5, null, "123.34", "123.34"), row("c_double", null, 1.0, 0.5, null, "234.56", "234.56"), - row("c_decimal", null, 1.0, 0.5, null, "343", "343"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "345.67000", "345.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:15:30.000", "2015-05-10 12:15:30.000"), + row("c_decimal", null, 1.0, 0.5, null, "343.0", "343.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "345.67", "345.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-08", "2015-05-08"), row("c_string", 10.0, 1.0, 0.5, null, null, null), row("c_varchar", 10.0, 1.0, 0.5, null, null, null), @@ -780,9 +776,9 @@ public void testComputePartitionStatisticsOnCreateTable() row("c_bigint", null, 1.0, 0.5, null, "555", "555"), row("c_float", null, 1.0, 0.5, null, "666.34", "666.34"), row("c_double", null, 1.0, 0.5, null, "777.56", "777.56"), - row("c_decimal", null, 1.0, 0.5, null, "888", "888"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67000", "999.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:45:30.000", "2015-05-10 12:45:30.000"), + row("c_decimal", null, 1.0, 0.5, null, "888.0", "888.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67", "999.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-09", "2015-05-09"), row("c_string", 10.0, 1.0, 0.5, null, null, null), row("c_varchar", 10.0, 1.0, 0.5, null, null, null), @@ -843,12 +839,12 @@ public void testComputePartitionStatisticsOnInsert() row("c_tinyint", null, 1.0, 0.5, null, "120", "120"), row("c_smallint", null, 1.0, 0.5, null, "32760", "32760"), row("c_int", null, 1.0, 0.5, null, "2147483640", "2147483640"), - row("c_bigint", null, 1.0, 0.5, null, "9223372036854775800", "9223372036854775800"), + row("c_bigint", null, 1.0, 0.5, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 1.0, 0.5, null, "123.34", "123.34"), row("c_double", null, 1.0, 0.5, null, "234.56", "234.56"), - row("c_decimal", null, 1.0, 0.5, null, "343", "343"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "345.67000", "345.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:15:30.000", "2015-05-10 12:15:30.000"), + row("c_decimal", null, 1.0, 0.5, null, "343.0", "343.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "345.67", "345.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-08", "2015-05-08"), row("c_string", 10.0, 1.0, 0.5, null, null, null), row("c_varchar", 10.0, 1.0, 0.5, null, null, null), @@ -866,9 +862,9 @@ public void testComputePartitionStatisticsOnInsert() row("c_bigint", null, 1.0, 0.5, null, "555", "555"), row("c_float", null, 1.0, 0.5, null, "666.34", "666.34"), row("c_double", null, 1.0, 0.5, null, "777.56", "777.56"), - row("c_decimal", null, 1.0, 0.5, null, "888", "888"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67000", "999.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:45:30.000", "2015-05-10 12:45:30.000"), + row("c_decimal", null, 1.0, 0.5, null, "888.0", "888.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67", "999.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-09", "2015-05-09"), row("c_string", 10.0, 1.0, 0.5, null, null, null), row("c_varchar", 10.0, 1.0, 0.5, null, null, null), @@ -886,12 +882,12 @@ public void testComputePartitionStatisticsOnInsert() row("c_tinyint", null, 1.0, 0.5, null, "119", "120"), row("c_smallint", null, 1.0, 0.5, null, "32759", "32760"), row("c_int", null, 1.0, 0.5, null, "2147483639", "2147483640"), - row("c_bigint", null, 1.0, 0.5, null, "9223372036854775799", "9223372036854775800"), + row("c_bigint", null, 1.0, 0.5, null, "9223372036854775807", "9223372036854775807"), row("c_float", null, 1.0, 0.5, null, "122.34", "123.34"), row("c_double", null, 1.0, 0.5, null, "233.56", "234.56"), - row("c_decimal", null, 1.0, 0.5, null, "342", "343"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "344.67000", "345.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:15:29.000", "2015-05-10 12:15:30.000"), + row("c_decimal", null, 1.0, 0.5, null, "342.0", "343.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "344.67", "345.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-07", "2015-05-08"), row("c_string", 20.0, 1.0, 0.5, null, null, null), row("c_varchar", 20.0, 1.0, 0.5, null, null, null), @@ -912,9 +908,9 @@ public void testComputePartitionStatisticsOnInsert() row("c_bigint", null, 1.0, 0.5, null, "555", "556"), row("c_float", null, 1.0, 0.5, null, "666.34", "667.34"), row("c_double", null, 1.0, 0.5, null, "777.56", "778.56"), - row("c_decimal", null, 1.0, 0.5, null, "888", "889"), - row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67000", "1000.67000"), - row("c_timestamp", null, 1.0, 0.5, null, "2015-05-10 12:45:30.000", "2015-05-10 12:45:31.000"), + row("c_decimal", null, 1.0, 0.5, null, "888.0", "889.0"), + row("c_decimal_w_params", null, 1.0, 0.5, null, "999.67", "1000.67"), + row("c_timestamp", null, 1.0, 0.5, null, null, null), row("c_date", null, 1.0, 0.5, null, "2015-05-09", "2015-05-10"), row("c_string", 20.0, 1.0, 0.5, null, null, null), row("c_varchar", 20.0, 1.0, 0.5, null, null, null), @@ -957,9 +953,4 @@ private static void assertComputeTableStatisticsOnInsert(String sourceTableName, query(format("DROP TABLE IF EXISTS %s", copiedTableName)); } } - - private static QueryExecutor onHive() - { - return testContext().getDependency(QueryExecutor.class, "hive"); - } } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestInsertIntoHiveTable.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestInsertIntoHiveTable.java index cb77b77e89618..d16548de436bd 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestInsertIntoHiveTable.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestInsertIntoHiveTable.java @@ -26,7 +26,6 @@ import java.sql.Timestamp; import java.time.LocalDateTime; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.POST_HIVE_1_0_1; import static com.facebook.presto.tests.hive.AllSimpleTypesTableDefinitions.ALL_HIVE_SIMPLE_TYPES_TEXTFILE; import static io.prestodb.tempto.Requirements.compose; @@ -74,7 +73,7 @@ private static TableDefinition partitionedTableDefinition() .build(); } - @Test(groups = {HIVE_CONNECTOR, POST_HIVE_1_0_1}) + @Test(groups = {POST_HIVE_1_0_1}) public void testInsertIntoValuesToHiveTableAllHiveSimpleTypes() { String tableNameInDatabase = mutableTablesState().get(TABLE_NAME).getNameInDatabase(); @@ -116,7 +115,7 @@ public void testInsertIntoValuesToHiveTableAllHiveSimpleTypes() "kot binarny".getBytes())); } - @Test(groups = {HIVE_CONNECTOR, POST_HIVE_1_0_1}) + @Test(groups = {POST_HIVE_1_0_1}) public void testInsertIntoSelectToHiveTableAllHiveSimpleTypes() { String tableNameInDatabase = mutableTablesState().get(TABLE_NAME).getNameInDatabase(); @@ -141,7 +140,7 @@ public void testInsertIntoSelectToHiveTableAllHiveSimpleTypes() "kot binarny".getBytes())); } - @Test(groups = {HIVE_CONNECTOR, POST_HIVE_1_0_1}) + @Test(groups = {POST_HIVE_1_0_1}) public void testInsertIntoPartitionedWithSerdePropety() { String tableNameInDatabase = mutableTablesState().get(PARTITIONED_TABLE_WITH_SERDE).getNameInDatabase(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestSqlStandardAccessControlChecks.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestSqlStandardAccessControlChecks.java index 206a6232a81e3..4a84c899c4e56 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestSqlStandardAccessControlChecks.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestSqlStandardAccessControlChecks.java @@ -19,7 +19,6 @@ import org.testng.annotations.Test; import static com.facebook.presto.tests.TestGroups.AUTHORIZATION; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.PROFILE_SPECIFIC_TESTS; import static com.facebook.presto.tests.utils.QueryExecutors.connectToPresto; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; @@ -48,7 +47,7 @@ public void setup() aliceExecutor.executeQuery(format("CREATE VIEW %s AS SELECT month, day FROM %s", viewName, tableName)); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlSelect() { assertThat(() -> bobExecutor.executeQuery(format("SELECT * FROM %s", tableName))) @@ -63,7 +62,7 @@ public void testAccessControlSelect() assertThat(bobExecutor.executeQuery(format("SELECT * FROM %s", viewName))).hasNoRows(); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlSelectFromPartitions() { assertThat(() -> bobExecutor.executeQuery(format("SELECT * FROM \"%s$partitions\"", tableName))) @@ -73,7 +72,7 @@ public void testAccessControlSelectFromPartitions() assertThat(bobExecutor.executeQuery(format("SELECT * FROM \"%s$partitions\"", tableName))).hasNoRows(); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlInsert() { assertThat(() -> bobExecutor.executeQuery(format("INSERT INTO %s VALUES (3, 22)", tableName))) @@ -84,7 +83,7 @@ public void testAccessControlInsert() assertThat(aliceExecutor.executeQuery(format("SELECT * FROM %s", tableName))).hasRowsCount(1); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlDelete() { aliceExecutor.executeQuery(format("INSERT INTO %s VALUES (4, 13)", tableName)); @@ -97,7 +96,7 @@ public void testAccessControlDelete() assertThat(aliceExecutor.executeQuery(format("SELECT * FROM %s", tableName))).hasNoRows(); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlCreateTableAsSelect() { String createTableAsSelect = "bob_create_table_as_select"; @@ -112,7 +111,7 @@ public void testAccessControlCreateTableAsSelect() bobExecutor.executeQuery("DROP TABLE " + createTableAsSelect); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlDropTable() { assertThat(() -> bobExecutor.executeQuery(format("DROP TABLE %s", tableName))) @@ -123,7 +122,7 @@ public void testAccessControlDropTable() .failsWithMessage("does not exist"); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlAlterTable() { assertThat(aliceExecutor.executeQuery(format("SHOW COLUMNS FROM %s", tableName))).hasRowsCount(2); @@ -134,7 +133,7 @@ public void testAccessControlAlterTable() assertThat(aliceExecutor.executeQuery(format("SHOW COLUMNS FROM %s", tableName))).hasRowsCount(3); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlCreateView() { String viewName = "bob_view"; @@ -173,7 +172,7 @@ public void testAccessControlCreateView() bobExecutor.executeQuery("DROP VIEW " + viewName); } - @Test(groups = {AUTHORIZATION, HIVE_CONNECTOR, PROFILE_SPECIFIC_TESTS}) + @Test(groups = {AUTHORIZATION, PROFILE_SPECIFIC_TESTS}) public void testAccessControlDropView() { String viewName = "alice_view_for_drop"; diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningInsertInto.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningInsertInto.java index 2c4513d8b1d3b..c324f0d9e4865 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningInsertInto.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningInsertInto.java @@ -19,7 +19,6 @@ import io.prestodb.tempto.query.QueryResult; import org.testng.annotations.Test; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static com.facebook.presto.tests.TestGroups.SMOKE; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_BIGINT_REGIONKEY; import static com.facebook.presto.tests.hive.HiveTableDefinitions.NATION_PARTITIONED_BY_REGIONKEY_NUMBER_OF_LINES_PER_SPLIT; @@ -45,7 +44,7 @@ public Requirement getRequirements(Configuration configuration) mutableTable(NATION, TARGET_NATION_NAME, CREATED)); } - @Test(groups = {HIVE_CONNECTOR, SMOKE}) + @Test(groups = {SMOKE}) public void selectFromPartitionedNation() throws Exception { diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningSelect.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningSelect.java index 8ee6058f03265..0d21979cf880e 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningSelect.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/hive/TestTablePartitioningSelect.java @@ -27,9 +27,7 @@ import java.util.Locale; import java.util.Optional; -import java.util.concurrent.ThreadLocalRandom; -import static com.facebook.presto.tests.TestGroups.HIVE_CONNECTOR; import static io.prestodb.tempto.Requirements.allOf; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; @@ -56,8 +54,8 @@ public class TestTablePartitioningSelect private static HiveTableDefinition singleIntColumnPartitionedTableDefinition(String fileFormat, Optional serde) { String tableName = fileFormat.toLowerCase(Locale.ENGLISH) + "_single_int_column_partitioned"; - HiveDataSource dataSource = createResourceDataSource(tableName, String.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE)), "com/facebook/presto/tests/hive/data/single_int_column/data." + fileFormat.toLowerCase(Locale.ENGLISH)); - HiveDataSource invalidData = createStringDataSource(tableName, String.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE)), "INVALID DATA"); + HiveDataSource dataSource = createResourceDataSource(tableName, "com/facebook/presto/tests/hive/data/single_int_column/data." + fileFormat.toLowerCase(Locale.ENGLISH)); + HiveDataSource invalidData = createStringDataSource(tableName, "INVALID DATA"); return HiveTableDefinition.builder(tableName) .setCreateTableDDLTemplate(buildSingleIntColumnPartitionedTableDDL(fileFormat, serde)) .addPartition("part_col = 1", invalidData) @@ -90,7 +88,7 @@ public Requirement getRequirements(Configuration configuration) mutableTable(SINGLE_INT_COLUMN_PARTITIONED_AVRO, TABLE_NAME, LOADED)); } - @Test(groups = {HIVE_CONNECTOR}) + @Test public void testSelectPartitionedHiveTableDifferentFormats() { String tableNameInDatabase = tablesState.get(TABLE_NAME).getNameInDatabase(); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/JdbcTests.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/JdbcTests.java index 756b31abd9042..6e2286bbc86cb 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/JdbcTests.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/JdbcTests.java @@ -33,6 +33,7 @@ import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; @@ -45,6 +46,7 @@ import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingPrestoJdbcDriver; import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingTeradataJdbc4Driver; import static com.facebook.presto.tests.utils.JdbcDriverUtils.usingTeradataJdbcDriver; +import static com.google.common.base.Strings.repeat; import static io.prestodb.tempto.Requirements.compose; import static io.prestodb.tempto.assertions.QueryAssert.Row.row; import static io.prestodb.tempto.assertions.QueryAssert.assertThat; @@ -140,7 +142,7 @@ public void shouldSetTimezone() ((PrestoConnection) connection()).setTimeZoneId(timeZoneId); assertConnectionTimezone(connection(), timeZoneId); } - else if (usingTeradataJdbcDriver(connection())) { + else { String prestoJdbcURLTestTimeZone; String testTimeZone = "TimeZoneID=" + timeZoneId + ";"; if (prestoJdbcURL.contains("TimeZoneID=")) { @@ -152,9 +154,6 @@ else if (usingTeradataJdbcDriver(connection())) { Connection testConnection = DriverManager.getConnection(prestoJdbcURLTestTimeZone, prestoJdbcUser, prestoJdbcPassword); assertConnectionTimezone(testConnection, timeZoneId); } - else { - LOGGER.warn("shouldSetTimezone() only applies to PrestoJdbcDriver"); - } } private void assertConnectionTimezone(Connection connection, String timeZoneId) @@ -284,6 +283,27 @@ public void testSessionProperties() assertThat(getSessionProperty(connection(), joinDistributionType)).isEqualTo(defaultValue); } + /** + * Same as {@code com.facebook.presto.jdbc.TestJdbcPreparedStatement#testDeallocate()}. This one is run for TeradataJdbcDriver as well. + */ + @Test(groups = JDBC) + public void testDeallocate() + throws Exception + { + try (Connection connection = connection()) { + for (int i = 0; i < 200; i++) { + try { + try (PreparedStatement preparedStatement = connection.prepareStatement("SELECT '" + repeat("a", 300) + "'")) { + preparedStatement.executeQuery().close(); // Let's not assume when PREPARE actually happens + } + } + catch (Exception e) { + throw new RuntimeException("Failed at " + i, e); + } + } + } + } + private QueryResult queryResult(Statement statement, String query) throws SQLException { diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/PreparedStatements.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/PreparedStatements.java index fd45d1e7d8066..22e656b2a35c6 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/PreparedStatements.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/jdbc/PreparedStatements.java @@ -156,13 +156,13 @@ public void preparedInsertVarbinaryApi() param(VARCHAR, null), param(CHAR, null), param(BOOLEAN, null), - param(VARBINARY, "a290IGJpbmFybnk=".getBytes())); + param(VARBINARY, new byte[] {0, 1, 2, 3, 0, 42, -7 })); QueryResult result = defaultQueryExecutor().executeQuery(selectSqlWithTable); assertColumnTypes(result); assertThat(result).containsOnly( row(null, null, null, null, null, null, null, null, null, null, null, null, null, null, - "a290IGJpbmFybnk=".getBytes())); + new byte[] {0, 1, 2, 3, 0, 42, -7 })); } else { LOGGER.warn("preparedInsertVarbinaryApi() only applies to TeradataJdbcDriver"); @@ -194,7 +194,7 @@ public void preparedInsertApi() param(VARCHAR, "ala ma kot"), param(CHAR, " ala ma"), param(BOOLEAN, Boolean.TRUE), - param(VARBINARY, null)); + param(VARBINARY, new byte[] {0, 1, 2, 3, 0, 42, -7 })); query( insertSqlWithTable, @@ -212,7 +212,7 @@ public void preparedInsertApi() param(VARCHAR, "def"), param(CHAR, " ghi"), param(BOOLEAN, Boolean.FALSE), - param(VARBINARY, null)); + param(VARBINARY, new byte[] {0, 1, 2, 3, 0, 42, -7 })); query( insertSqlWithTable, @@ -250,7 +250,7 @@ public void preparedInsertApi() "ala ma kot", " ala ma", Boolean.TRUE, - null), + new byte[] {0, 1, 2, 3, 0, 42, -7 }), row( 1, 2, @@ -266,7 +266,7 @@ public void preparedInsertApi() "def", " ghi", Boolean.FALSE, - null), + new byte[] {0, 1, 2, 3, 0, 42, -7 }), row(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)); } else { @@ -302,7 +302,7 @@ public void preparedInsertSql() "'ala ma kot', " + "cast('ala ma' as char(10)), " + "true, " + - "varbinary 'a290IGJpbmFybnk='"); + "X'00010203002AF9'"); statement.execute(executeSql + "cast(1 as tinyint), " + @@ -356,7 +356,7 @@ public void preparedInsertSql() "ala ma kot", "ala ma ", Boolean.TRUE, - "a290IGJpbmFybnk=".getBytes()), + new byte[] {0, 1, 2, 3, 0, 42, -7 }), row( 1, 2, @@ -408,13 +408,13 @@ public void preparedInsertVarbinarySql() "null, " + "null, " + "null, " + - "varbinary 'a290IGJpbmFybnk='"); + "X'00010203002AF9'"); QueryResult result = query(selectSqlWithTable); assertColumnTypes(result); assertThat(result).containsOnly( row(null, null, null, null, null, null, null, null, null, null, null, null, null, null, - "a290IGJpbmFybnk=".getBytes())); + new byte[] {0, 1, 2, 3, 0, 42, -7 })); } else { LOGGER.warn("preparedInsertVarbinarySql() only applies to TeradataJdbcDriver"); diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaAvroSmokeTest.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaAvroSmokeTest.java index 953638ba3a4bd..e877e517b716a 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaAvroSmokeTest.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaAvroSmokeTest.java @@ -113,7 +113,7 @@ private static byte[] convertRecordToAvro(Schema schema, Map val dataFileWriter.close(); } catch (IOException e) { - throw new UncheckedIOException("Failed to convert to AVRO.", e); + throw new UncheckedIOException("Failed to convert to Avro.", e); } return outputStream.toByteArray(); } diff --git a/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaSmokeTest.java b/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaSmokeTest.java index 04e56e79f1bc3..a0f2da11262ec 100644 --- a/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaSmokeTest.java +++ b/presto-product-tests/src/main/java/com/facebook/presto/tests/kafka/KafkaSmokeTest.java @@ -23,12 +23,13 @@ import io.prestodb.tempto.fulfillment.table.kafka.KafkaTableDefinition; import io.prestodb.tempto.fulfillment.table.kafka.ListKafkaDataSource; import io.prestodb.tempto.query.QueryResult; -import org.joda.time.DateTimeZone; import org.testng.annotations.Test; import java.sql.Date; import java.sql.Time; +import java.sql.Timestamp; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; import static com.facebook.presto.tests.TestGroups.KAFKA; @@ -37,7 +38,6 @@ import static io.prestodb.tempto.fulfillment.table.TableRequirements.immutableTable; import static io.prestodb.tempto.fulfillment.table.kafka.KafkaMessageContentsBuilder.contentsBuilder; import static io.prestodb.tempto.query.QueryExecutor.query; -import static io.prestodb.tempto.util.DateTimeUtils.parseTimestampInLocalTime; import static java.lang.Double.doubleToRawLongBits; import static java.lang.Float.floatToIntBits; import static java.lang.String.format; @@ -323,11 +323,11 @@ public void testSelectAllJsonTable() 127, 1234567890.123456789, true, - parseTimestampInLocalTime("2018-02-09 13:15:16.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:17.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:18.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:19.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:20.000", DateTimeZone.UTC), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 16)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 17)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 18)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 19)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 20)), Date.valueOf(LocalDate.of(2018, 2, 11)), Date.valueOf(LocalDate.of(2018, 2, 12)), Date.valueOf(LocalDate.of(2018, 2, 13)), @@ -336,11 +336,11 @@ public void testSelectAllJsonTable() Time.valueOf(LocalTime.of(18, 45, 18)), Time.valueOf(LocalTime.of(18, 45, 19)), Time.valueOf(LocalTime.of(18, 45, 20)), - parseTimestampInLocalTime("2018-02-09 13:15:16.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:17.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:18.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:19.000", DateTimeZone.UTC), - parseTimestampInLocalTime("2018-02-09 13:15:20.000", DateTimeZone.UTC), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 16)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 17)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 18)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 19)), + Timestamp.valueOf(LocalDateTime.of(2018, 2, 9, 19, 0, 20)), Time.valueOf(LocalTime.of(18, 45, 16)), // different due to broken TIME datatype semantics Time.valueOf(LocalTime.of(18, 45, 17)), Time.valueOf(LocalTime.of(18, 45, 18)), diff --git a/presto-product-tests/src/main/resources/avro/add_column_schema.avsc b/presto-product-tests/src/main/resources/avro/add_column_schema.avsc new file mode 100644 index 0000000000000..a16a583ed9a10 --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/add_column_schema.avsc @@ -0,0 +1,9 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"string_col", "type":"string"}, + { "name":"int_col", "type":"int" }, + { "name":"int_col_added", "type":"int", "default": 100 } +]} diff --git a/presto-product-tests/src/main/resources/avro/change_column_type_schema.avsc b/presto-product-tests/src/main/resources/avro/change_column_type_schema.avsc new file mode 100644 index 0000000000000..bb97d9b205a9e --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/change_column_type_schema.avsc @@ -0,0 +1,8 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"string_col", "type":"string"}, + { "name":"int_col", "type":"long"} +]} diff --git a/presto-product-tests/src/main/resources/avro/incompatible_type_schema.avsc b/presto-product-tests/src/main/resources/avro/incompatible_type_schema.avsc new file mode 100644 index 0000000000000..d083384e9796a --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/incompatible_type_schema.avsc @@ -0,0 +1,8 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"string_col", "type":"string"}, + { "name":"int_col", "type":"string"} +]} diff --git a/presto-product-tests/src/main/resources/avro/original_schema.avsc b/presto-product-tests/src/main/resources/avro/original_schema.avsc new file mode 100644 index 0000000000000..a7135c18816a0 --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/original_schema.avsc @@ -0,0 +1,8 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"string_col", "type":"string"}, + { "name":"int_col", "type":"int" } +]} diff --git a/presto-product-tests/src/main/resources/avro/remove_column_schema.avsc b/presto-product-tests/src/main/resources/avro/remove_column_schema.avsc new file mode 100644 index 0000000000000..123070fef59d4 --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/remove_column_schema.avsc @@ -0,0 +1,7 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"int_col", "type":"int" } +]} diff --git a/presto-product-tests/src/main/resources/avro/rename_column_schema.avsc b/presto-product-tests/src/main/resources/avro/rename_column_schema.avsc new file mode 100644 index 0000000000000..3c1df11ed397a --- /dev/null +++ b/presto-product-tests/src/main/resources/avro/rename_column_schema.avsc @@ -0,0 +1,8 @@ +{ + "namespace": "com.facebook.test", + "name": "product_tests_avro_table", + "type": "record", + "fields": [ + { "name":"string_col", "type":"string"}, + { "name":"int_col_renamed", "type":["null", "int"], "default": null } +]} diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/datatype.data-revision deleted file mode 100644 index 5d1740ed38427..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.7 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_mysql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_mysql.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_mysql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_psql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_psql.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/datatype_psql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/empty.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/empty.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/empty.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/orc_varchar_dictionary.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/orc_varchar_dictionary.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/orc_varchar_dictionary.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_mysql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_mysql.data-revision deleted file mode 100644 index 4bb16ad829351..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_mysql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.1 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_psql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_psql.data-revision deleted file mode 100644 index 4bb16ad829351..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/real_table_psql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.1 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_footer.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_footer.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_footer.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header_and_footer.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header_and_footer.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/table_with_header_and_footer.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/workers.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/workers.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/workers.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_mysql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/workers_mysql.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_mysql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_psql.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/workers_psql.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_psql.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_sqlserver.data-revision b/presto-product-tests/src/main/resources/sql-tests/datasets/workers_sqlserver.data-revision deleted file mode 100644 index 5806b0c43eee2..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/datasets/workers_sqlserver.data-revision +++ /dev/null @@ -1 +0,0 @@ -v.1.0 diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/connectors/postgresql/show_schemas.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/connectors/postgresql/show_schemas.sql index efb2fa8880f69..ff637f1225234 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/connectors/postgresql/show_schemas.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/connectors/postgresql/show_schemas.sql @@ -2,7 +2,7 @@ --! show schemas from postgresql --! --- delimeter: |; trimValues: true; ignoreOrder: true; +-- delimiter: |; trimValues: true; ignoreOrder: true; information_schema| pg_catalog| public| diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q05.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q05.sql index 58ec7efaccdb9..28ee21d325c5e 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q05.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q05.sql @@ -1,4 +1,4 @@ --- database: presto; groups: big_query, tpch; tables: customer,orders,lineitem,supplier,nation,region +-- database: presto; groups: tpch; tables: customer,orders,lineitem,supplier,nation,region SELECT n_name, sum(l_extendedprice * (1 - l_discount)) AS revenue diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q08.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q08.sql index a43b1cd3a3df1..db73155b09d62 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q08.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q08.sql @@ -1,4 +1,4 @@ --- database: presto; groups: tpch, big_query; tables: part,supplier,lineitem,orders,customer,nation +-- database: presto; groups: tpch; tables: part,supplier,lineitem,orders,customer,nation SELECT o_year, sum(CASE diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q17.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q17.sql index 091f1cd500173..8ed8cd84ac32a 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q17.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q17.sql @@ -1,4 +1,4 @@ --- database: presto; groups: tpch, big_query; tables: lineitem,part +-- database: presto; groups: tpch; tables: lineitem,part SELECT sum(l_extendedprice) / 7.0 AS avg_yearly FROM lineitem, diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q18.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q18.sql index ab1d73a3619a3..bd084cafb755a 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q18.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/hive_tpch/q18.sql @@ -1,4 +1,4 @@ --- database: presto; groups: tpch, big_query; tables: customer,orders,lineitem +-- database: presto; groups: tpch; tables: customer,orders,lineitem SELECT c_name, c_custkey, diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/insert/insert_const_special_char.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/insert/insert_const_special_char.sql deleted file mode 100644 index cb1c553db7f90..0000000000000 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/insert/insert_const_special_char.sql +++ /dev/null @@ -1,22 +0,0 @@ --- database: presto; groups: insert; mutable_tables: datatype|created; tables: datatype --- delimiter: |; ignoreOrder: true; ---! -insert into ${mutableTables.hive.datatype} select 1, cast(2.1 as double), 'abc \n def', cast(null as date), cast(null as timestamp), cast(null as boolean), cast(null as decimal(5,2)), cast(null as decimal(30,10)) from datatype; -select * from ${mutableTables.hive.datatype} ---! -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| -1|2.1|abc \n def|null|null|null|null|null| diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/system/selectInformationSchemaColumns.result b/presto-product-tests/src/main/resources/sql-tests/testcases/system/selectInformationSchemaColumns.result index c7b60415c72ec..e504ea5816ba8 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/system/selectInformationSchemaColumns.result +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/system/selectInformationSchemaColumns.result @@ -49,7 +49,6 @@ system| runtime| nodes| http_uri| varchar| YES| null| null| system| runtime| nodes| node_version| varchar| YES| null| null| system| runtime| nodes| coordinator| boolean| YES| null| null| system| runtime| nodes| state| varchar| YES| null| null| -system| runtime| queries| node_id| varchar| YES| null| null| system| runtime| queries| query_id| varchar| YES| null| null| system| runtime| queries| state| varchar| YES| null| null| system| runtime| queries| user| varchar| YES| null| null| @@ -74,7 +73,6 @@ system| runtime| tasks| running_splits| bigint| YES| null| null| system| runtime| tasks| completed_splits| bigint| YES| null| null| system| runtime| tasks| split_scheduled_time_ms| bigint| YES| null| null| system| runtime| tasks| split_cpu_time_ms| bigint| YES| null| null| -system| runtime| tasks| split_user_time_ms| bigint| YES| null| null| system| runtime| tasks| split_blocked_time_ms| bigint| YES| null| null| system| runtime| tasks| raw_input_bytes| bigint| YES| null| null| system| runtime| tasks| raw_input_rows| bigint| YES| null| null| diff --git a/presto-product-tests/src/main/resources/sql-tests/testcases/tpcds/q06.sql b/presto-product-tests/src/main/resources/sql-tests/testcases/tpcds/q06.sql index af4c100e563db..8c38c2ad45ba6 100644 --- a/presto-product-tests/src/main/resources/sql-tests/testcases/tpcds/q06.sql +++ b/presto-product-tests/src/main/resources/sql-tests/testcases/tpcds/q06.sql @@ -1,4 +1,4 @@ --- database: presto_tpcds; groups: tpcds, big_query; requires: io.prestodb.tempto.fulfillment.table.hive.tpcds.ImmutableTpcdsTablesRequirements +-- database: presto_tpcds; groups: tpcds; requires: io.prestodb.tempto.fulfillment.table.hive.tpcds.ImmutableTpcdsTablesRequirements --- takes over 30 minutes on travis to complete SELECT "a"."ca_state" "STATE" diff --git a/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json b/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json index 7aadeaef9bfc6..4034be7437284 100644 --- a/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json +++ b/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json @@ -27,10 +27,13 @@ "lastHeartbeat": "2015-05-06T09:42:00.010Z", "endTime": "2015-05-05T16:01:17.125Z", "elapsedTime": "134.00ms", + "resourceWaitingTime": "11.00ms", "queuedTime": "2.11ms", + "executionTime": "13.00ms", "analysisTime": "7.47ms", "distributedPlanningTime": "311.77us", "totalPlanningTime": "9.99ms", + "finishingTime": "17.00ms", "totalTasks": 1, "runningTasks": 0, "completedTasks": 1, @@ -46,7 +49,6 @@ "peakTaskTotalMemory": "0B", "totalScheduledTime": "1.22ms", "totalCpuTime": "1.19ms", - "totalUserTime": "0.00ns", "totalBlockedTime": "14.50ms", "rawInputDataSize": "0B", "rawInputPositions": 0, @@ -167,7 +169,6 @@ "totalMemoryReservation": "0B", "totalScheduledTime": "1.22ms", "totalCpuTime": "1.19ms", - "totalUserTime": "0.00ns", "totalBlockedTime": "14.50ms", "rawInputDataSize": "0B", "rawInputPositions": 0, @@ -217,7 +218,6 @@ "memoryReservation": "0B", "totalScheduledTime": "1.22ms", "totalCpuTime": "1.19ms", - "totalUserTime": "0.00ns", "totalBlockedTime": "14.50ms", "rawInputDataSize": "0B", "rawInputPositions": 0, @@ -375,7 +375,6 @@ "totalMemoryReservation": "0B", "totalScheduledTime": "0.00ns", "totalCpuTime": "0.00ns", - "totalUserTime": "0.00ns", "totalBlockedTime": "0.00ns", "rawInputDataSize": "0B", "rawInputPositions": 0, diff --git a/presto-proxy/pom.xml b/presto-proxy/pom.xml index 73777737e11d1..6fc4231583386 100644 --- a/presto-proxy/pom.xml +++ b/presto-proxy/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-proxy diff --git a/presto-proxy/src/main/java/com/facebook/presto/proxy/JsonWebTokenHandler.java b/presto-proxy/src/main/java/com/facebook/presto/proxy/JsonWebTokenHandler.java index 73e5ee2178ea0..d78058ddff046 100644 --- a/presto-proxy/src/main/java/com/facebook/presto/proxy/JsonWebTokenHandler.java +++ b/presto-proxy/src/main/java/com/facebook/presto/proxy/JsonWebTokenHandler.java @@ -43,7 +43,7 @@ public class JsonWebTokenHandler private final Optional jwtAudience; @Inject - public JsonWebTokenHandler(ProxyConfig config) + public JsonWebTokenHandler(JwtHandlerConfig config) { this.jwtSigner = setupJwtSigner(config.getJwtKeyFile(), config.getJwtKeyFilePassword()); this.jwtKeyId = Optional.ofNullable(config.getJwtKeyId()); diff --git a/presto-proxy/src/main/java/com/facebook/presto/proxy/JwtHandlerConfig.java b/presto-proxy/src/main/java/com/facebook/presto/proxy/JwtHandlerConfig.java new file mode 100644 index 0000000000000..af1f1cef3b9a4 --- /dev/null +++ b/presto-proxy/src/main/java/com/facebook/presto/proxy/JwtHandlerConfig.java @@ -0,0 +1,95 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.proxy; + +import io.airlift.configuration.Config; +import io.airlift.configuration.ConfigDescription; +import io.airlift.configuration.ConfigSecuritySensitive; + +import java.io.File; + +public class JwtHandlerConfig +{ + private File jwtKeyFile; + private String jwtKeyFilePassword; + private String jwtKeyId; + private String jwtIssuer; + private String jwtAudience; + + public File getJwtKeyFile() + { + return jwtKeyFile; + } + + @Config("jwt.key-file") + @ConfigDescription("Key file used for generating JWT signatures") + public JwtHandlerConfig setJwtKeyFile(File jwtKeyFile) + { + this.jwtKeyFile = jwtKeyFile; + return this; + } + + public String getJwtKeyFilePassword() + { + return jwtKeyFilePassword; + } + + @Config("jwt.key-file-password") + @ConfigDescription("Password for encrypted key file") + @ConfigSecuritySensitive + public JwtHandlerConfig setJwtKeyFilePassword(String jwtKeyFilePassword) + { + this.jwtKeyFilePassword = jwtKeyFilePassword; + return this; + } + + public String getJwtKeyId() + { + return jwtKeyId; + } + + @Config("jwt.key-id") + @ConfigDescription("Key ID for JWT") + public JwtHandlerConfig setJwtKeyId(String jwtKeyId) + { + this.jwtKeyId = jwtKeyId; + return this; + } + + public String getJwtIssuer() + { + return jwtIssuer; + } + + @Config("jwt.issuer") + @ConfigDescription("Issuer for JWT") + public JwtHandlerConfig setJwtIssuer(String jwtIssuer) + { + this.jwtIssuer = jwtIssuer; + return this; + } + + public String getJwtAudience() + { + return jwtAudience; + } + + @Config("jwt.audience") + @ConfigDescription("Audience for JWT") + public JwtHandlerConfig setJwtAudience(String jwtAudience) + { + this.jwtAudience = jwtAudience; + return this; + } +} diff --git a/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyConfig.java b/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyConfig.java index a8538d358bbe1..2cc67c8b9158f 100644 --- a/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyConfig.java +++ b/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyConfig.java @@ -15,7 +15,6 @@ import io.airlift.configuration.Config; import io.airlift.configuration.ConfigDescription; -import io.airlift.configuration.ConfigSecuritySensitive; import javax.validation.constraints.NotNull; @@ -26,11 +25,6 @@ public class ProxyConfig { private URI uri; private File sharedSecretFile; - private File jwtKeyFile; - private String jwtKeyFilePassword; - private String jwtKeyId; - private String jwtIssuer; - private String jwtAudience; @NotNull public URI getUri() @@ -59,70 +53,4 @@ public ProxyConfig setSharedSecretFile(File sharedSecretFile) this.sharedSecretFile = sharedSecretFile; return this; } - - public File getJwtKeyFile() - { - return jwtKeyFile; - } - - @Config("proxy.jwt.key-file") - @ConfigDescription("Key file used for generating JWT signatures") - public ProxyConfig setJwtKeyFile(File jwtKeyFile) - { - this.jwtKeyFile = jwtKeyFile; - return this; - } - - public String getJwtKeyFilePassword() - { - return jwtKeyFilePassword; - } - - @Config("proxy.jwt.key-file-password") - @ConfigDescription("Password for encrypted key file") - @ConfigSecuritySensitive - public ProxyConfig setJwtKeyFilePassword(String jwtKeyFilePassword) - { - this.jwtKeyFilePassword = jwtKeyFilePassword; - return this; - } - - public String getJwtKeyId() - { - return jwtKeyId; - } - - @Config("proxy.jwt.key-id") - @ConfigDescription("Key ID for JWT") - public ProxyConfig setJwtKeyId(String jwtKeyId) - { - this.jwtKeyId = jwtKeyId; - return this; - } - - public String getJwtIssuer() - { - return jwtIssuer; - } - - @Config("proxy.jwt.issuer") - @ConfigDescription("Issuer for JWT") - public ProxyConfig setJwtIssuer(String jwtIssuer) - { - this.jwtIssuer = jwtIssuer; - return this; - } - - public String getJwtAudience() - { - return jwtAudience; - } - - @Config("proxy.jwt.audience") - @ConfigDescription("Audience for JWT") - public ProxyConfig setJwtAudience(String jwtAudience) - { - this.jwtAudience = jwtAudience; - return this; - } } diff --git a/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyModule.java b/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyModule.java index ac346b76077eb..2b983c0278b2c 100644 --- a/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyModule.java +++ b/presto-proxy/src/main/java/com/facebook/presto/proxy/ProxyModule.java @@ -30,6 +30,7 @@ public void configure(Binder binder) httpClientBinder(binder).bindHttpClient("proxy", ForProxy.class); configBinder(binder).bindConfig(ProxyConfig.class); + configBinder(binder).bindConfig(JwtHandlerConfig.class, "proxy"); jaxrsBinder(binder).bind(ProxyResource.class); diff --git a/presto-proxy/src/test/java/com/facebook/presto/proxy/TestJwtHandlerConfig.java b/presto-proxy/src/test/java/com/facebook/presto/proxy/TestJwtHandlerConfig.java new file mode 100644 index 0000000000000..f3c950fa6ef11 --- /dev/null +++ b/presto-proxy/src/test/java/com/facebook/presto/proxy/TestJwtHandlerConfig.java @@ -0,0 +1,59 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.proxy; + +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.Map; + +import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; +import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; +import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; + +public class TestJwtHandlerConfig +{ + @Test + public void testDefaults() + { + assertRecordedDefaults(recordDefaults(JwtHandlerConfig.class) + .setJwtKeyFile(null) + .setJwtKeyFilePassword(null) + .setJwtKeyId(null) + .setJwtIssuer(null) + .setJwtAudience(null)); + } + + @Test + public void testExplicitPropertyMappings() + { + Map properties = new ImmutableMap.Builder() + .put("jwt.key-file", "test.key") + .put("jwt.key-file-password", "password") + .put("jwt.key-id", "testkeyid") + .put("jwt.issuer", "testissuer") + .put("jwt.audience", "testaudience") + .build(); + + JwtHandlerConfig expected = new JwtHandlerConfig() + .setJwtKeyFile(new File("test.key")) + .setJwtKeyFilePassword("password") + .setJwtKeyId("testkeyid") + .setJwtIssuer("testissuer") + .setJwtAudience("testaudience"); + + assertFullMapping(properties, expected); + } +} diff --git a/presto-proxy/src/test/java/com/facebook/presto/proxy/TestProxyConfig.java b/presto-proxy/src/test/java/com/facebook/presto/proxy/TestProxyConfig.java index dd9067ecc6fa2..f606895ad22e5 100644 --- a/presto-proxy/src/test/java/com/facebook/presto/proxy/TestProxyConfig.java +++ b/presto-proxy/src/test/java/com/facebook/presto/proxy/TestProxyConfig.java @@ -31,12 +31,7 @@ public void testDefaults() { assertRecordedDefaults(recordDefaults(ProxyConfig.class) .setUri(null) - .setSharedSecretFile(null) - .setJwtKeyFile(null) - .setJwtKeyFilePassword(null) - .setJwtKeyId(null) - .setJwtIssuer(null) - .setJwtAudience(null)); + .setSharedSecretFile(null)); } @Test @@ -45,21 +40,11 @@ public void testExplicitPropertyMappings() Map properties = new ImmutableMap.Builder() .put("proxy.uri", "http://example.net/") .put("proxy.shared-secret-file", "test.secret") - .put("proxy.jwt.key-file", "test.key") - .put("proxy.jwt.key-file-password", "password") - .put("proxy.jwt.key-id", "testkeyid") - .put("proxy.jwt.issuer", "testissuer") - .put("proxy.jwt.audience", "testaudience") .build(); ProxyConfig expected = new ProxyConfig() .setUri(URI.create("http://example.net/")) - .setSharedSecretFile(new File("test.secret")) - .setJwtKeyFile(new File("test.key")) - .setJwtKeyFilePassword("password") - .setJwtKeyId("testkeyid") - .setJwtIssuer("testissuer") - .setJwtAudience("testaudience"); + .setSharedSecretFile(new File("test.secret")); assertFullMapping(properties, expected); } diff --git a/presto-raptor/pom.xml b/presto-raptor/pom.xml index 3f4c0969d7fd5..24bbef8a19944 100644 --- a/presto-raptor/pom.xml +++ b/presto-raptor/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-raptor @@ -102,6 +102,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.validation validation-api diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorConnectorFactory.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorConnectorFactory.java index b5aa29fbd9d88..7544b20bbc248 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorConnectorFactory.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorConnectorFactory.java @@ -69,7 +69,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { NodeManager nodeManager = context.getNodeManager(); try { @@ -85,8 +85,8 @@ public Connector create(String connectorId, Map config, Connecto }, metadataModule, new BackupModule(backupProviders), - new StorageModule(connectorId), - new RaptorModule(connectorId), + new StorageModule(catalogName), + new RaptorModule(catalogName), new RaptorSecurityModule()); Injector injector = app diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorMetadata.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorMetadata.java index 2d518d05a25b8..c89ceee8c5531 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorMetadata.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorMetadata.java @@ -22,6 +22,7 @@ import com.facebook.presto.raptor.metadata.Table; import com.facebook.presto.raptor.metadata.TableColumn; import com.facebook.presto.raptor.metadata.ViewResult; +import com.facebook.presto.raptor.systemtables.ColumnRangesSystemTable; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.ConnectorInsertTableHandle; @@ -39,6 +40,7 @@ import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.SchemaTablePrefix; +import com.facebook.presto.spi.SystemTable; import com.facebook.presto.spi.TableNotFoundException; import com.facebook.presto.spi.ViewNotFoundException; import com.facebook.presto.spi.connector.ConnectorMetadata; @@ -97,6 +99,7 @@ import static com.facebook.presto.raptor.RaptorTableProperties.getSortColumns; import static com.facebook.presto.raptor.RaptorTableProperties.getTemporalColumn; import static com.facebook.presto.raptor.RaptorTableProperties.isOrganized; +import static com.facebook.presto.raptor.systemtables.ColumnRangesSystemTable.getSourceTable; import static com.facebook.presto.raptor.util.DatabaseUtil.daoTransaction; import static com.facebook.presto.raptor.util.DatabaseUtil.onDemandDao; import static com.facebook.presto.raptor.util.DatabaseUtil.runIgnoringConstraintViolation; @@ -165,7 +168,7 @@ public ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTable return getTableHandle(tableName); } - private ConnectorTableHandle getTableHandle(SchemaTableName tableName) + private RaptorTableHandle getTableHandle(SchemaTableName tableName) { requireNonNull(tableName, "tableName is null"); Table table = dao.getTableInformation(tableName.getSchemaName(), tableName.getTableName()); @@ -188,6 +191,14 @@ private ConnectorTableHandle getTableHandle(SchemaTableName tableName) false); } + @Override + public Optional getSystemTable(ConnectorSession session, SchemaTableName tableName) + { + return getSourceTable(tableName) + .map(this::getTableHandle) + .map(handle -> new ColumnRangesSystemTable(handle, dbi)); + } + @Override public ConnectorTableMetadata getTableMetadata(ConnectorSession session, ConnectorTableHandle tableHandle) { diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorNodePartitioningProvider.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorNodePartitioningProvider.java index cc82beb9fc74f..def4027538b53 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorNodePartitioningProvider.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorNodePartitioningProvider.java @@ -18,20 +18,21 @@ import com.facebook.presto.spi.ConnectorSplit; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; import javax.inject.Inject; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.function.ToIntFunction; import static com.facebook.presto.spi.StandardErrorCode.NO_NODES_AVAILABLE; +import static com.facebook.presto.spi.connector.ConnectorBucketNodeMap.createBucketNodeMap; import static com.google.common.collect.Maps.uniqueIndex; import static java.util.Objects.requireNonNull; @@ -47,21 +48,21 @@ public RaptorNodePartitioningProvider(NodeSupplier nodeSupplier) } @Override - public Map getBucketToNode(ConnectorTransactionHandle transaction, ConnectorSession session, ConnectorPartitioningHandle partitioning) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transaction, ConnectorSession session, ConnectorPartitioningHandle partitioning) { RaptorPartitioningHandle handle = (RaptorPartitioningHandle) partitioning; Map nodesById = uniqueIndex(nodeSupplier.getWorkerNodes(), Node::getNodeIdentifier); - ImmutableMap.Builder bucketToNode = ImmutableMap.builder(); - for (Entry entry : handle.getBucketToNode().entrySet()) { - Node node = nodesById.get(entry.getValue()); + ImmutableList.Builder bucketToNode = ImmutableList.builder(); + for (String nodeIdentifier : handle.getBucketToNode()) { + Node node = nodesById.get(nodeIdentifier); if (node == null) { - throw new PrestoException(NO_NODES_AVAILABLE, "Node for bucket is offline: " + entry.getValue()); + throw new PrestoException(NO_NODES_AVAILABLE, "Node for bucket is offline: " + nodeIdentifier); } - bucketToNode.put(entry.getKey(), node); + bucketToNode.add(node); } - return bucketToNode.build(); + return createBucketNodeMap(bucketToNode.build()); } @Override diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorPartitioningHandle.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorPartitioningHandle.java index bdcf52399c6d2..a0048013048e5 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorPartitioningHandle.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorPartitioningHandle.java @@ -16,9 +16,9 @@ import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; -import java.util.Map; +import java.util.List; import java.util.Objects; import static java.util.Objects.requireNonNull; @@ -27,15 +27,15 @@ public class RaptorPartitioningHandle implements ConnectorPartitioningHandle { private final long distributionId; - private final Map bucketToNode; + private final List bucketToNode; @JsonCreator public RaptorPartitioningHandle( @JsonProperty("distributionId") long distributionId, - @JsonProperty("bucketToNode") Map bucketToNode) + @JsonProperty("bucketToNode") List bucketToNode) { this.distributionId = distributionId; - this.bucketToNode = ImmutableMap.copyOf(requireNonNull(bucketToNode, "bucketToNode is null")); + this.bucketToNode = ImmutableList.copyOf(requireNonNull(bucketToNode, "bucketToNode is null")); } @JsonProperty @@ -45,7 +45,7 @@ public long getDistributionId() } @JsonProperty - public Map getBucketToNode() + public List getBucketToNode() { return bucketToNode; } diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorSplitManager.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorSplitManager.java index 8e27390796448..816c7ae156efa 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorSplitManager.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/RaptorSplitManager.java @@ -102,7 +102,7 @@ public ConnectorSplitSource getSplits(ConnectorTransactionHandle transaction, Co boolean bucketed = table.getBucketCount().isPresent(); boolean merged = bucketed && !table.isDelete() && (table.getBucketCount().getAsInt() >= getOneSplitPerBucketThreshold(session)); OptionalLong transactionId = table.getTransactionId(); - Optional> bucketToNode = handle.getPartitioning().map(RaptorPartitioningHandle::getBucketToNode); + Optional> bucketToNode = handle.getPartitioning().map(RaptorPartitioningHandle::getBucketToNode); verify(bucketed == bucketToNode.isPresent(), "mismatched bucketCount and bucketToNode presence"); return new RaptorSplitSource(tableId, merged, effectivePredicate, transactionId, bucketToNode); } @@ -138,7 +138,7 @@ private class RaptorSplitSource private final long tableId; private final TupleDomain effectivePredicate; private final OptionalLong transactionId; - private final Optional> bucketToNode; + private final Optional> bucketToNode; private final ResultIterator iterator; @GuardedBy("this") @@ -149,7 +149,7 @@ public RaptorSplitSource( boolean merged, TupleDomain effectivePredicate, OptionalLong transactionId, - Optional> bucketToNode) + Optional> bucketToNode) { this.tableId = tableId; this.effectivePredicate = requireNonNull(effectivePredicate, "effectivePredicate is null"); @@ -231,7 +231,7 @@ private ConnectorSplit createSplit(BucketShards bucketShards) throw new PrestoException(NO_NODES_AVAILABLE, "No nodes available to run query"); } Node node = selectRandom(availableNodes); - shardManager.assignShard(tableId, shardId, node.getNodeIdentifier(), true); + shardManager.replaceShardAssignment(tableId, shardId, node.getNodeIdentifier(), true); addresses = ImmutableList.of(node.getHostAndPort()); } diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/BucketReassigner.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/BucketReassigner.java new file mode 100644 index 0000000000000..85a3ee5e839eb --- /dev/null +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/BucketReassigner.java @@ -0,0 +1,78 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.raptor.metadata; + +import com.google.common.collect.ImmutableList; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class BucketReassigner +{ + private final Map nodeBucketCounts = new HashMap<>(); + private final List activeNodes; + private final List bucketNodes; + private boolean initialized; + + public BucketReassigner(Set activeNodes, List bucketNodes) + { + checkArgument(!activeNodes.isEmpty(), "activeNodes must not be empty"); + this.activeNodes = ImmutableList.copyOf(requireNonNull(activeNodes, "activeNodes is null")); + this.bucketNodes = requireNonNull(bucketNodes, "bucketNodes is null"); + } + + // NOTE: This method is not thread safe + public String getNextReassignmentDestination() + { + if (!initialized) { + for (String node : activeNodes) { + nodeBucketCounts.put(node, 0); + } + for (BucketNode bucketNode : bucketNodes) { + nodeBucketCounts.computeIfPresent(bucketNode.getNodeIdentifier(), (node, bucketCount) -> bucketCount + 1); + } + initialized = true; + } + + String assignedNode; + if (activeNodes.size() > 1) { + assignedNode = randomTwoChoices(); + } + else { + assignedNode = activeNodes.get(0); + } + + nodeBucketCounts.compute(assignedNode, (node, count) -> count + 1); + return assignedNode; + } + + private String randomTwoChoices() + { + // Purely random choices can overload unlucky node while selecting the least loaded one based on stale + // local information can overload the previous idle node. Here we randomly pick 2 nodes and select the + // less loaded one. This prevents those issues and renders good enough load balance. + int randomPosition = ThreadLocalRandom.current().nextInt(activeNodes.size()); + int randomOffset = ThreadLocalRandom.current().nextInt(1, activeNodes.size()); + String candidate1 = activeNodes.get(randomPosition); + String candidate2 = activeNodes.get((randomPosition + randomOffset) % activeNodes.size()); + + return (nodeBucketCounts.get(candidate1) <= nodeBucketCounts.get(candidate2)) ? candidate1 : candidate2; + } +} diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/DatabaseShardManager.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/DatabaseShardManager.java index adb155177d985..bfb39bd7a2a63 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/DatabaseShardManager.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/DatabaseShardManager.java @@ -27,7 +27,6 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -82,6 +81,7 @@ import static com.facebook.presto.spi.StandardErrorCode.TRANSACTION_CONFLICT; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfInstanceOf; +import static com.google.common.base.Verify.verify; import static com.google.common.collect.Iterables.partition; import static java.lang.Boolean.TRUE; import static java.lang.Math.multiplyExact; @@ -116,7 +116,7 @@ public class DatabaseShardManager .maximumSize(10_000) .build(CacheLoader.from(this::loadNodeId)); - private final LoadingCache> bucketAssignmentsCache = CacheBuilder.newBuilder() + private final LoadingCache> bucketAssignmentsCache = CacheBuilder.newBuilder() .expireAfterWrite(1, SECONDS) .build(CacheLoader.from(this::loadBucketAssignments)); @@ -543,13 +543,13 @@ public ResultIterator getShardNodes(long tableId, TupleDomain getShardNodesBucketed(long tableId, boolean merged, Map bucketToNode, TupleDomain effectivePredicate) + public ResultIterator getShardNodesBucketed(long tableId, boolean merged, List bucketToNode, TupleDomain effectivePredicate) { return new ShardIterator(tableId, merged, Optional.of(bucketToNode), effectivePredicate, dbi); } @Override - public void assignShard(long tableId, UUID shardUuid, String nodeIdentifier, boolean gracePeriod) + public void replaceShardAssignment(long tableId, UUID shardUuid, String nodeIdentifier, boolean gracePeriod) { if (gracePeriod && (nanosSince(startTime).compareTo(startupGracePeriod) < 0)) { throw new PrestoException(SERVER_STARTING_UP, "Cannot reassign shards while server is starting"); @@ -560,30 +560,11 @@ public void assignShard(long tableId, UUID shardUuid, String nodeIdentifier, boo runTransaction(dbi, (handle, status) -> { ShardDao dao = shardDaoSupplier.attach(handle); - Set nodes = new HashSet<>(fetchLockedNodeIds(handle, tableId, shardUuid)); - if (nodes.add(nodeId)) { - updateNodeIds(handle, tableId, shardUuid, nodes); - dao.insertShardNode(shardUuid, nodeId); - } - - return null; - }); - } - - @Override - public void unassignShard(long tableId, UUID shardUuid, String nodeIdentifier) - { - int nodeId = getOrCreateNodeId(nodeIdentifier); - - runTransaction(dbi, (handle, status) -> { - ShardDao dao = shardDaoSupplier.attach(handle); - - Set nodes = new HashSet<>(fetchLockedNodeIds(handle, tableId, shardUuid)); - if (nodes.remove(nodeId)) { - updateNodeIds(handle, tableId, shardUuid, nodes); - dao.deleteShardNode(shardUuid, nodeId); - } + Set oldAssignments = new HashSet<>(fetchLockedNodeIds(handle, tableId, shardUuid)); + updateNodeIds(handle, tableId, shardUuid, ImmutableSet.of(nodeId)); + dao.deleteShardNodes(shardUuid, oldAssignments); + dao.insertShardNode(shardUuid, nodeId); return null; }); } @@ -623,7 +604,7 @@ public void createBuckets(long distributionId, int bucketCount) } @Override - public Map getBucketAssignments(long distributionId) + public List getBucketAssignments(long distributionId) { try { return bucketAssignmentsCache.getUnchecked(distributionId); @@ -688,16 +669,17 @@ private List getBuckets(long distributionId) return dao.getBucketNodes(distributionId); } - private Map loadBucketAssignments(long distributionId) + private List loadBucketAssignments(long distributionId) { Set nodeIds = getNodeIdentifiers(); - Iterator nodeIterator = cyclingShuffledIterator(nodeIds); + List bucketNodes = getBuckets(distributionId); + BucketReassigner reassigner = new BucketReassigner(nodeIds, bucketNodes); - ImmutableMap.Builder assignments = ImmutableMap.builder(); + List assignments = new ArrayList<>(nCopies(bucketNodes.size(), null)); PrestoException limiterException = null; Set offlineNodes = new HashSet<>(); - for (BucketNode bucketNode : getBuckets(distributionId)) { + for (BucketNode bucketNode : bucketNodes) { int bucket = bucketNode.getBucketNumber(); String nodeId = bucketNode.getNodeIdentifier(); @@ -719,20 +701,19 @@ private Map loadBucketAssignments(long distributionId) } String oldNodeId = nodeId; - // TODO: use smarter system to choose replacement node - nodeId = nodeIterator.next(); + nodeId = reassigner.getNextReassignmentDestination(); dao.updateBucketNode(distributionId, bucket, getOrCreateNodeId(nodeId)); log.info("Reassigned bucket %s for distribution ID %s from %s to %s", bucket, distributionId, oldNodeId, nodeId); } - assignments.put(bucket, nodeId); + verify(assignments.set(bucket, nodeId) == null, "Duplicate bucket"); } if (limiterException != null) { throw limiterException; } - return assignments.build(); + return ImmutableList.copyOf(assignments); } private Set getNodeIdentifiers() diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDao.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDao.java index 22a6f43353a35..1ff4edeca4434 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDao.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDao.java @@ -46,10 +46,10 @@ public interface ShardDao "VALUES ((SELECT shard_id FROM shards WHERE shard_uuid = :shardUuid), :nodeId)") void insertShardNode(@Bind("shardUuid") UUID shardUuid, @Bind("nodeId") int nodeId); - @SqlUpdate("DELETE FROM shard_nodes\n" + + @SqlBatch("DELETE FROM shard_nodes\n" + "WHERE shard_id = (SELECT shard_id FROM shards WHERE shard_uuid = :shardUuid)\n" + " AND node_id = :nodeId") - void deleteShardNode(@Bind("shardUuid") UUID shardUuid, @Bind("nodeId") int nodeId); + void deleteShardNodes(@Bind("shardUuid") UUID shardUuid, @Bind("nodeId") Iterable nodeId); @SqlQuery("SELECT node_id FROM nodes WHERE node_identifier = :nodeIdentifier") Integer getNodeId(@Bind("nodeIdentifier") String nodeIdentifier); diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDelta.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDelta.java index c89bddd50b7dd..54f233a764ca9 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDelta.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardDelta.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.raptor.metadata; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; @@ -27,6 +28,7 @@ public class ShardDelta private final List oldShardUuids; private final List newShards; + @JsonCreator public ShardDelta( @JsonProperty("oldShardUuids") List oldShardUuids, @JsonProperty("newShards") List newShards) diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardIterator.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardIterator.java index 7b68fb1709bed..a8f6ed2a49668 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardIterator.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardIterator.java @@ -54,7 +54,7 @@ final class ShardIterator private final Map nodeMap = new HashMap<>(); private final boolean merged; - private final Map bucketToNode; + private final List bucketToNode; private final ShardDao dao; private final Connection connection; private final PreparedStatement statement; @@ -64,7 +64,7 @@ final class ShardIterator public ShardIterator( long tableId, boolean merged, - Optional> bucketToNode, + Optional> bucketToNode, TupleDomain effectivePredicate, IDBI dbi) { diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardManager.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardManager.java index fd258d4b32d96..2bdf8b779c5c4 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardManager.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/metadata/ShardManager.java @@ -75,17 +75,12 @@ public interface ShardManager /** * Return the shard nodes for a bucketed table. */ - ResultIterator getShardNodesBucketed(long tableId, boolean merged, Map bucketToNode, TupleDomain effectivePredicate); + ResultIterator getShardNodesBucketed(long tableId, boolean merged, List bucketToNode, TupleDomain effectivePredicate); /** - * Assign a shard to a node. + * Remove all old shard assignments and assign a shard to a node */ - void assignShard(long tableId, UUID shardUuid, String nodeIdentifier, boolean gracePeriod); - - /** - * Remove shard assignment from a node. - */ - void unassignShard(long tableId, UUID shardUuid, String nodeIdentifier); + void replaceShardAssignment(long tableId, UUID shardUuid, String nodeIdentifier, boolean gracePeriod); /** * Get the number of bytes used by assigned shards per node. @@ -112,7 +107,7 @@ public interface ShardManager /** * Get map of buckets to node identifiers for a distribution. */ - Map getBucketAssignments(long distributionId); + List getBucketAssignments(long distributionId); /** * Change the node a bucket is assigned to. diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/OrcFileRewriter.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/OrcFileRewriter.java index fee610ae5dfc3..7ac52c6b57863 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/OrcFileRewriter.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/OrcFileRewriter.java @@ -28,6 +28,7 @@ import org.apache.hadoop.hive.ql.io.orc.RecordReader; import org.apache.hadoop.hive.ql.io.orc.Writer; import org.apache.hadoop.hive.serde2.io.DoubleWritable; +import org.apache.hadoop.hive.serde2.io.HiveDecimalWritable; import org.apache.hadoop.io.BooleanWritable; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.LongWritable; @@ -156,6 +157,9 @@ private static int uncompressedSize(Object object) if (object instanceof DoubleWritable) { return SIZE_OF_DOUBLE; } + if (object instanceof HiveDecimalWritable) { + return SIZE_OF_LONG; + } if (object instanceof Text) { return ((Text) object).getLength(); } diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardEjector.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardEjector.java index bd3f9793ea179..104dea569cdcf 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardEjector.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardEjector.java @@ -239,8 +239,7 @@ void process() nodeSize -= shardSize; // move assignment - shardManager.assignShard(shard.getTableId(), shardUuid, target, false); - shardManager.unassignShard(shard.getTableId(), shardUuid, currentNode); + shardManager.replaceShardAssignment(shard.getTableId(), shardUuid, target, false); // delete local file File file = storageService.getStorageFile(shardUuid); diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardRecoveryManager.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardRecoveryManager.java index 30f5d4f5aed63..53684b8765d9e 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardRecoveryManager.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/ShardRecoveryManager.java @@ -215,12 +215,7 @@ void restoreFromBackup(UUID shardUuid, long shardSize, OptionalLong shardXxhash6 return; } stats.incrementCorruptLocalFile(); - File quarantine = getQuarantineFile(shardUuid); - log.error("Local file is corrupt. Quarantining local file: %s", quarantine); - if (!storageFile.renameTo(quarantine)) { - log.warn("Quarantine of corrupt local file failed: %s", shardUuid); - storageFile.delete(); - } + quarantineFile(shardUuid, storageFile, "Local file is corrupt."); } // create a temporary file in the staging directory @@ -271,22 +266,29 @@ void restoreFromBackup(UUID shardUuid, long shardSize, OptionalLong shardXxhash6 if (isFileCorrupt(storageFile, shardSize, shardXxhash64)) { stats.incrementShardRecoveryFailure(); stats.incrementCorruptRecoveredFile(); - File quarantine = getQuarantineFile(shardUuid); - log.error("Local file is corrupt after recovery. Quarantining local file: %s", quarantine); - if (!storageFile.renameTo(quarantine)) { - log.warn("Quarantine of corrupt recovered file failed: %s", shardUuid); - storageFile.delete(); - } + quarantineFile(shardUuid, storageFile, "Local file is corrupt after recovery."); throw new PrestoException(RAPTOR_BACKUP_CORRUPTION, "Backup is corrupt after read: " + shardUuid); } stats.incrementShardRecoverySuccess(); } - private File getQuarantineFile(UUID shardUuid) + private void quarantineFile(UUID shardUuid, File file, String message) { - File file = storageService.getQuarantineFile(shardUuid); - return new File(file.getPath() + ".corrupt." + System.currentTimeMillis()); + File quarantine = new File(storageService.getQuarantineFile(shardUuid).getPath() + ".corrupt"); + if (quarantine.exists()) { + log.warn("%s Quarantine already exists: %s", message, quarantine); + return; + } + + log.error("%s Quarantining corrupt file: %s", message, quarantine); + try { + Files.move(file.toPath(), quarantine.toPath(), ATOMIC_MOVE); + } + catch (IOException e) { + log.warn(e, "Quarantine of corrupt file failed: " + quarantine); + file.delete(); + } } private static boolean isFileCorrupt(File file, long size, OptionalLong xxhash64) diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/StorageManagerConfig.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/StorageManagerConfig.java index 56f4e2e1b1ca6..70d930c811496 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/StorageManagerConfig.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/StorageManagerConfig.java @@ -57,6 +57,7 @@ public class StorageManagerConfig private int recoveryThreads = 10; private int organizationThreads = 5; private boolean organizationEnabled = true; + private Duration organizationDiscoveryInterval = new Duration(6, TimeUnit.HOURS); private Duration organizationInterval = new Duration(7, TimeUnit.DAYS); private long maxShardRows = 1_000_000; @@ -231,6 +232,21 @@ public StorageManagerConfig setOrganizationInterval(Duration organizationInterva return this; } + @NotNull + @MinDuration("1s") + public Duration getOrganizationDiscoveryInterval() + { + return organizationDiscoveryInterval; + } + + @Config("storage.organization-discovery-interval") + @ConfigDescription("How long to wait between discovering tables that need to be organized") + public StorageManagerConfig setOrganizationDiscoveryInterval(Duration organizationDiscoveryInterval) + { + this.organizationDiscoveryInterval = organizationDiscoveryInterval; + return this; + } + @MinDuration("5m") public Duration getShardEjectorInterval() { diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/organization/ShardOrganizationManager.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/organization/ShardOrganizationManager.java index 2030ef0f40e26..ab327e94720d0 100644 --- a/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/organization/ShardOrganizationManager.java +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/storage/organization/ShardOrganizationManager.java @@ -41,6 +41,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static com.facebook.presto.raptor.storage.organization.ShardOrganizerUtil.createOrganizationSet; @@ -55,7 +56,6 @@ import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newScheduledThreadPool; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; @@ -75,6 +75,7 @@ public class ShardOrganizationManager private final boolean enabled; private final long organizationIntervalMillis; + private final long organizationDiscoveryIntervalMillis; private final String currentNodeIdentifier; private final ShardOrganizer organizer; @@ -96,7 +97,8 @@ public ShardOrganizationManager( organizer, temporalFunction, config.isOrganizationEnabled(), - config.getOrganizationInterval()); + config.getOrganizationInterval(), + config.getOrganizationDiscoveryInterval()); } public ShardOrganizationManager( @@ -106,7 +108,8 @@ public ShardOrganizationManager( ShardOrganizer organizer, TemporalFunction temporalFunction, boolean enabled, - Duration organizationInterval) + Duration organizationInterval, + Duration organizationDiscoveryInterval) { this.dbi = requireNonNull(dbi, "dbi is null"); this.metadataDao = onDemandDao(dbi, MetadataDao.class); @@ -121,6 +124,7 @@ public ShardOrganizationManager( requireNonNull(organizationInterval, "organizationInterval is null"); this.organizationIntervalMillis = max(1, organizationInterval.roundTo(MILLISECONDS)); + this.organizationDiscoveryIntervalMillis = max(1, organizationDiscoveryInterval.roundTo(MILLISECONDS)); } @PostConstruct @@ -143,8 +147,8 @@ private void startDiscovery() { discoveryService.scheduleWithFixedDelay(() -> { try { - // jitter to avoid overloading database - SECONDS.sleep(ThreadLocalRandom.current().nextLong(1, 5 * 60)); + // jitter to avoid overloading database and overloading the backup store + SECONDS.sleep(ThreadLocalRandom.current().nextLong(1, organizationDiscoveryIntervalMillis)); log.info("Running shard organizer..."); submitJobs(discoverAndInitializeTablesToOrganize()); @@ -155,7 +159,7 @@ private void startDiscovery() catch (Throwable t) { log.error(t, "Error running shard organizer"); } - }, 0, 5, MINUTES); + }, 0, organizationDiscoveryIntervalMillis, TimeUnit.MILLISECONDS); } @VisibleForTesting diff --git a/presto-raptor/src/main/java/com/facebook/presto/raptor/systemtables/ColumnRangesSystemTable.java b/presto-raptor/src/main/java/com/facebook/presto/raptor/systemtables/ColumnRangesSystemTable.java new file mode 100644 index 0000000000000..9144a1b049da3 --- /dev/null +++ b/presto-raptor/src/main/java/com/facebook/presto/raptor/systemtables/ColumnRangesSystemTable.java @@ -0,0 +1,177 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.raptor.systemtables; + +import com.facebook.presto.raptor.RaptorTableHandle; +import com.facebook.presto.raptor.metadata.MetadataDao; +import com.facebook.presto.raptor.metadata.TableColumn; +import com.facebook.presto.spi.ColumnMetadata; +import com.facebook.presto.spi.ConnectorPageSource; +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.ConnectorTableMetadata; +import com.facebook.presto.spi.FixedPageSource; +import com.facebook.presto.spi.SchemaTableName; +import com.facebook.presto.spi.SystemTable; +import com.facebook.presto.spi.block.BlockBuilder; +import com.facebook.presto.spi.connector.ConnectorTransactionHandle; +import com.facebook.presto.spi.predicate.TupleDomain; +import com.facebook.presto.spi.type.Type; +import com.google.common.base.VerifyException; +import org.skife.jdbi.v2.IDBI; +import org.skife.jdbi.v2.exceptions.DBIException; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static com.facebook.presto.raptor.metadata.DatabaseShardManager.maxColumn; +import static com.facebook.presto.raptor.metadata.DatabaseShardManager.minColumn; +import static com.facebook.presto.raptor.metadata.DatabaseShardManager.shardIndexTable; +import static com.facebook.presto.raptor.util.DatabaseUtil.metadataError; +import static com.facebook.presto.spi.SystemTable.Distribution.SINGLE_COORDINATOR; +import static com.facebook.presto.spi.type.BigintType.BIGINT; +import static com.facebook.presto.spi.type.BooleanType.BOOLEAN; +import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; + +public class ColumnRangesSystemTable + implements SystemTable +{ + private static final String MIN_COLUMN_SUFFIX = "_min"; + private static final String MAX_COLUMN_SUFFIX = "_max"; + private static final String COLUMN_RANGES_TABLE_SUFFIX = "$column_ranges"; + + private final IDBI dbi; + private final RaptorTableHandle sourceTable; + private final List indexedRaptorColumns; + private final ConnectorTableMetadata tableMetadata; + + public ColumnRangesSystemTable(RaptorTableHandle sourceTable, IDBI dbi) + { + this.sourceTable = requireNonNull(sourceTable, "sourceTable is null"); + this.dbi = requireNonNull(dbi, "dbi is null"); + + this.indexedRaptorColumns = dbi.onDemand(MetadataDao.class) + .listTableColumns(sourceTable.getTableId()).stream() + .filter(column -> isIndexedType(column.getDataType())) + .collect(toImmutableList()); + List systemTableColumns = indexedRaptorColumns.stream() + .flatMap(column -> Stream.of( + new ColumnMetadata(column.getColumnName() + MIN_COLUMN_SUFFIX, column.getDataType(), null, false), + new ColumnMetadata(column.getColumnName() + MAX_COLUMN_SUFFIX, column.getDataType(), null, false))) + .collect(toImmutableList()); + SchemaTableName tableName = new SchemaTableName(sourceTable.getSchemaName(), sourceTable.getTableName() + COLUMN_RANGES_TABLE_SUFFIX); + this.tableMetadata = new ConnectorTableMetadata(tableName, systemTableColumns); + } + + public static Optional getSourceTable(SchemaTableName tableName) + { + if (tableName.getTableName().endsWith(COLUMN_RANGES_TABLE_SUFFIX) && + !tableName.getTableName().equals(COLUMN_RANGES_TABLE_SUFFIX)) { + int tableNameLength = tableName.getTableName().length() - COLUMN_RANGES_TABLE_SUFFIX.length(); + return Optional.of(new SchemaTableName( + tableName.getSchemaName(), + tableName.getTableName().substring(0, tableNameLength))); + } + return Optional.empty(); + } + + @Override + public Distribution getDistribution() + { + return SINGLE_COORDINATOR; + } + + @Override + public ConnectorTableMetadata getTableMetadata() + { + return tableMetadata; + } + + @Override + public ConnectorPageSource pageSource(ConnectorTransactionHandle transactionHandle, ConnectorSession session, TupleDomain constraint) + { + String metadataSqlQuery = getColumnRangesMetadataSqlQuery(sourceTable, indexedRaptorColumns); + List columnTypes = tableMetadata.getColumns().stream() + .map(ColumnMetadata::getType) + .collect(toImmutableList()); + + PageListBuilder pageListBuilder = new PageListBuilder(columnTypes); + + try (Connection connection = dbi.open().getConnection(); + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery(metadataSqlQuery)) { + if (resultSet.next()) { + pageListBuilder.beginRow(); + for (int i = 0; i < columnTypes.size(); ++i) { + BlockBuilder blockBuilder = pageListBuilder.nextBlockBuilder(); + Type columnType = columnTypes.get(i); + if (columnType.equals(BIGINT) || columnType.equals(DATE) || columnType.equals(TIMESTAMP)) { + long value = resultSet.getLong(i + 1); + if (!resultSet.wasNull()) { + columnType.writeLong(blockBuilder, value); + } + else { + blockBuilder.appendNull(); + } + } + else if (columnType.equals(BOOLEAN)) { + boolean value = resultSet.getBoolean(i + 1); + if (!resultSet.wasNull()) { + BOOLEAN.writeBoolean(blockBuilder, value); + } + else { + blockBuilder.appendNull(); + } + } + else { + throw new VerifyException("Unknown or unsupported column type: " + columnType); + } + } + } + } + catch (SQLException | DBIException e) { + throw metadataError(e); + } + + return new FixedPageSource(pageListBuilder.build()); + } + + private static boolean isIndexedType(Type type) + { + // We only consider the following types in the column_ranges system table + // Exclude INTEGER because we don't collect column stats for INTEGER type. + // Exclude DOUBLE because Java double is not completely compatible with MySQL double + // Exclude VARCHAR because they can be truncated + return type.equals(BOOLEAN) || type.equals(BIGINT) || type.equals(DATE) || type.equals(TIMESTAMP); + } + + private static String getColumnRangesMetadataSqlQuery(RaptorTableHandle raptorTableHandle, List raptorColumns) + { + String columns = raptorColumns.stream() + .flatMap(column -> Stream.of( + format("min(%s)", minColumn(column.getColumnId())), + format("max(%s)", maxColumn(column.getColumnId())))) + .collect(joining(", ")); + return format("SELECT %s FROM %s", columns, shardIndexTable(raptorTableHandle.getTableId())); + } +} diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorConnector.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorConnector.java index 7e2efd5676268..bd8426fffdc55 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorConnector.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorConnector.java @@ -216,7 +216,6 @@ private void assertSplitShard(Type temporalType, String min, String max, String { ConnectorSession session = new TestingConnectorSession( "user", - "path", Optional.of("test"), Optional.empty(), getTimeZoneKey(userTimeZone), diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorIntegrationSmokeTest.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorIntegrationSmokeTest.java index ed4acd8f202ce..9378f647ff9d0 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorIntegrationSmokeTest.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/TestRaptorIntegrationSmokeTest.java @@ -239,7 +239,7 @@ public void testShardingByTemporalTimestampColumn() assertUpdate(joiner.toString(), format("VALUES(%s)", rows)); - MaterializedResult results = computeActual("SELECT format_datetime(col2, 'yyyyMMdd'), \"$shard_uuid\" FROM test_shard_temporal_timestamp"); + MaterializedResult results = computeActual("SELECT format_datetime(col2 AT TIME ZONE 'UTC', 'yyyyMMdd'), \"$shard_uuid\" FROM test_shard_temporal_timestamp"); assertEquals(results.getRowCount(), rows); // Each shard will only contain data of one date. @@ -272,7 +272,7 @@ public void testShardingByTemporalTimestampColumnBucketed() assertUpdate(joiner.toString(), format("VALUES(%s)", rows)); MaterializedResult results = computeActual("" + - "SELECT format_datetime(col2, 'yyyyMMdd'), \"$shard_uuid\" " + + "SELECT format_datetime(col2 AT TIME ZONE 'UTC', 'yyyyMMdd'), \"$shard_uuid\" " + "FROM test_shard_temporal_timestamp_bucketed"); assertEquals(results.getRowCount(), rows); @@ -385,6 +385,44 @@ public void testShardsSystemTableWithTemporalColumn() "(SELECT count(*) FROM orders WHERE orderdate BETWEEN date '1992-01-01' AND date '1992-02-08')"); } + @Test + public void testColumnRangesSystemTable() + { + assertQuery("SELECT orderkey_min, orderkey_max, custkey_min, custkey_max, orderdate_min, orderdate_max FROM \"orders$column_ranges\"", + "SELECT min(orderkey), max(orderkey), min(custkey), max(custkey), min(orderdate), max(orderdate) FROM orders"); + + assertQuery("SELECT orderkey_min, orderkey_max FROM \"orders$column_ranges\"", + "SELECT min(orderkey), max(orderkey) FROM orders"); + + // No such table test + assertQueryFails("SELECT * FROM \"no_table$column_ranges\"", ".*raptor\\.tpch\\.no_table\\$column_ranges does not exist.*"); + + // No range column for DOUBLE, INTEGER or VARCHAR + assertQueryFails("SELECT totalprice_min FROM \"orders$column_ranges\"", ".*Column 'totalprice_min' cannot be resolved.*"); + assertQueryFails("SELECT shippriority_min FROM \"orders$column_ranges\"", ".*Column 'shippriority_min' cannot be resolved.*"); + assertQueryFails("SELECT orderstatus_min FROM \"orders$column_ranges\"", ".*Column 'orderstatus_min' cannot be resolved.*"); + assertQueryFails("SELECT orderpriority_min FROM \"orders$column_ranges\"", ".*Column 'orderpriority_min' cannot be resolved.*"); + assertQueryFails("SELECT clerk_min FROM \"orders$column_ranges\"", ".*Column 'clerk_min' cannot be resolved.*"); + assertQueryFails("SELECT comment_min FROM \"orders$column_ranges\"", ".*Column 'comment_min' cannot be resolved.*"); + + // Empty table + assertUpdate("CREATE TABLE column_ranges_test (a BIGINT, b BIGINT)"); + assertQuery("SELECT a_min, a_max, b_min, b_max FROM \"column_ranges_test$column_ranges\"", "SELECT NULL, NULL, NULL, NULL"); + + // Table with NULL values + assertUpdate("INSERT INTO column_ranges_test VALUES (1, NULL)", 1); + assertQuery("SELECT a_min, a_max, b_min, b_max FROM \"column_ranges_test$column_ranges\"", "SELECT 1, 1, NULL, NULL"); + assertUpdate("INSERT INTO column_ranges_test VALUES (NULL, 99)", 1); + assertQuery("SELECT a_min, a_max, b_min, b_max FROM \"column_ranges_test$column_ranges\"", "SELECT 1, 1, 99, 99"); + assertUpdate("INSERT INTO column_ranges_test VALUES (50, 50)", 1); + assertQuery("SELECT a_min, a_max, b_min, b_max FROM \"column_ranges_test$column_ranges\"", "SELECT 1, 50, 50, 99"); + + // Drop table + assertUpdate("DROP TABLE column_ranges_test"); + assertQueryFails("SELECT a_min, a_max, b_min, b_max FROM \"column_ranges_test$column_ranges\"", + ".*raptor\\.tpch\\.column_ranges_test\\$column_ranges does not exist.*"); + } + @Test public void testCreateBucketedTable() { diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestDatabaseShardManager.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestDatabaseShardManager.java index 5b120573f57e2..d67fa5e43cfed 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestDatabaseShardManager.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestDatabaseShardManager.java @@ -181,30 +181,24 @@ public void testAssignShard() assertEquals(actual, new ShardNodes(shard, ImmutableSet.of("node1"))); try { - shardManager.assignShard(tableId, shard, "node2", true); + shardManager.replaceShardAssignment(tableId, shard, "node2", true); fail("expected exception"); } catch (PrestoException e) { assertEquals(e.getErrorCode(), SERVER_STARTING_UP.toErrorCode()); } - shardManager.assignShard(tableId, shard, "node2", false); + // replace shard assignment to another node + shardManager.replaceShardAssignment(tableId, shard, "node2", false); - // assign shard to another node actual = getOnlyElement(getShardNodes(tableId, TupleDomain.all())); - assertEquals(actual, new ShardNodes(shard, ImmutableSet.of("node1", "node2"))); - - // assigning a shard should be idempotent - shardManager.assignShard(tableId, shard, "node2", false); + assertEquals(actual, new ShardNodes(shard, ImmutableSet.of("node2"))); - // remove assignment from first node - shardManager.unassignShard(tableId, shard, "node1"); + // replacing shard assignment should be idempotent + shardManager.replaceShardAssignment(tableId, shard, "node2", false); actual = getOnlyElement(getShardNodes(tableId, TupleDomain.all())); assertEquals(actual, new ShardNodes(shard, ImmutableSet.of("node2"))); - - // removing an assignment should be idempotent - shardManager.unassignShard(tableId, shard, "node1"); } @Test @@ -231,13 +225,13 @@ public void testGetNodeBytes() assertEquals(shardManager.getNodeBytes(), ImmutableMap.of("node1", 88L)); - shardManager.assignShard(tableId, shard1, "node2", false); + shardManager.replaceShardAssignment(tableId, shard1, "node2", false); assertEquals(getShardNodes(tableId, TupleDomain.all()), ImmutableSet.of( - new ShardNodes(shard1, ImmutableSet.of("node1", "node2")), + new ShardNodes(shard1, ImmutableSet.of("node2")), new ShardNodes(shard2, ImmutableSet.of("node1")))); - assertEquals(shardManager.getNodeBytes(), ImmutableMap.of("node1", 88L, "node2", 33L)); + assertEquals(shardManager.getNodeBytes(), ImmutableMap.of("node1", 55L, "node2", 33L)); } @Test @@ -390,9 +384,9 @@ public void testBucketAssignments() shardManager.createBuckets(distributionId, bucketCount); - Map assignments = shardManager.getBucketAssignments(distributionId); + List assignments = shardManager.getBucketAssignments(distributionId); assertEquals(assignments.size(), bucketCount); - assertEquals(ImmutableSet.copyOf(assignments.values()), nodeIds(originalNodes)); + assertEquals(ImmutableSet.copyOf(assignments), nodeIds(originalNodes)); Set newNodes = ImmutableSet.of(node1, node3); shardManager = createShardManager(dbi, () -> newNodes, ticker); @@ -408,7 +402,14 @@ public void testBucketAssignments() ticker.increment(2, DAYS); assignments = shardManager.getBucketAssignments(distributionId); assertEquals(assignments.size(), bucketCount); - assertEquals(ImmutableSet.copyOf(assignments.values()), nodeIds(newNodes)); + assertEquals(ImmutableSet.copyOf(assignments), nodeIds(newNodes)); + + Set singleNode = ImmutableSet.of(node1); + shardManager = createShardManager(dbi, () -> singleNode, ticker); + ticker.increment(2, DAYS); + assignments = shardManager.getBucketAssignments(distributionId); + assertEquals(assignments.size(), bucketCount); + assertEquals(ImmutableSet.copyOf(assignments), nodeIds(singleNode)); } @Test @@ -430,7 +431,7 @@ public void testEmptyTableBucketed() List columns = ImmutableList.of(new ColumnInfo(1, BIGINT)); shardManager.createTable(tableId, columns, true, OptionalLong.empty()); - try (ResultIterator iterator = shardManager.getShardNodesBucketed(tableId, true, ImmutableMap.of(), TupleDomain.all())) { + try (ResultIterator iterator = shardManager.getShardNodesBucketed(tableId, true, ImmutableList.of(), TupleDomain.all())) { assertFalse(iterator.hasNext()); } } diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestRaptorMetadata.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestRaptorMetadata.java index dc08f64456c82..f344bf3c1235a 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestRaptorMetadata.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestRaptorMetadata.java @@ -155,8 +155,8 @@ public void testDropColumn() { assertNull(metadata.getTableHandle(SESSION, DEFAULT_TEST_ORDERS)); metadata.createTable(SESSION, buildTable(ImmutableMap.of(), tableMetadataBuilder(DEFAULT_TEST_ORDERS) - .column("orderkey", BIGINT) - .column("price", BIGINT)), + .column("orderkey", BIGINT) + .column("price", BIGINT)), false); ConnectorTableHandle tableHandle = metadata.getTableHandle(SESSION, DEFAULT_TEST_ORDERS); assertInstanceOf(tableHandle, RaptorTableHandle.class); diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestShardDao.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestShardDao.java index c15eece410d34..b40179e01519d 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestShardDao.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestShardDao.java @@ -24,9 +24,9 @@ import org.testng.annotations.Test; import java.sql.SQLException; -import java.util.List; import java.util.OptionalInt; import java.util.OptionalLong; +import java.util.Set; import java.util.UUID; import static com.facebook.presto.raptor.metadata.SchemaDaoUtil.createTablesWithRetry; @@ -184,8 +184,8 @@ public void testNodeShards() ShardMetadata shard4 = new ShardMetadata(bucketedTableId, shardId4, shardUuid4, OptionalInt.of(9), 4, 44, 444, OptionalLong.of(888_444), noRange, noRange); ShardMetadata shard5 = new ShardMetadata(bucketedTableId, shardId5, shardUuid5, OptionalInt.of(7), 5, 55, 555, OptionalLong.of(888_555), noRange, noRange); - assertEquals(dao.getShards(plainTableId), ImmutableList.of(shardUuid1, shardUuid2)); - assertEquals(dao.getShards(bucketedTableId), ImmutableList.of(shardUuid3, shardUuid4, shardUuid5)); + assertEquals(dao.getShards(plainTableId), ImmutableSet.of(shardUuid1, shardUuid2)); + assertEquals(dao.getShards(bucketedTableId), ImmutableSet.of(shardUuid3, shardUuid4, shardUuid5)); assertEquals(dao.getNodeShards(nodeName1, null), ImmutableSet.of(shard3)); assertEquals(dao.getNodeShards(nodeName2, null), ImmutableSet.of(shard4, shard5)); @@ -214,8 +214,8 @@ public void testNodeShards() dao.dropShards(plainTableId); dao.dropShards(bucketedTableId); - assertEquals(dao.getShards(plainTableId), ImmutableList.of()); - assertEquals(dao.getShards(bucketedTableId), ImmutableList.of()); + assertEquals(dao.getShards(plainTableId), ImmutableSet.of()); + assertEquals(dao.getShards(bucketedTableId), ImmutableSet.of()); assertEquals(dao.getNodeSizes(), ImmutableSet.of()); } @@ -246,7 +246,7 @@ public void testShardSelection() long shardId3 = dao.insertShard(shardUuid3, tableId, null, 0, 0, 0, 0); long shardId4 = dao.insertShard(shardUuid4, tableId, null, 0, 0, 0, 0); - List shards = dao.getShards(tableId); + Set shards = dao.getShards(tableId); assertEquals(shards.size(), 4); assertTrue(shards.contains(shardUuid1)); @@ -265,7 +265,7 @@ public void testShardSelection() assertEquals(dao.getShards(tableId), shards); - List shardNodes = dao.getShardNodes(tableId); + Set shardNodes = dao.getShardNodes(tableId); assertEquals(shardNodes.size(), 6); assertContainsShardNode(shardNodes, nodeName1, shardUuid1); @@ -281,7 +281,7 @@ private long createTable(String name) return dbi.onDemand(MetadataDao.class).insertTable("test", name, false, false, null, 0); } - private static void assertContainsShardNode(List nodes, String nodeName, UUID shardUuid) + private static void assertContainsShardNode(Set nodes, String nodeName, UUID shardUuid) { assertTrue(nodes.contains(new ShardNode(shardUuid, nodeName))); } diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestingShardDao.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestingShardDao.java index b94b2c7782bc4..b90bb5249377d 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestingShardDao.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/metadata/TestingShardDao.java @@ -23,7 +23,6 @@ import org.skife.jdbi.v2.sqlobject.customizers.RegisterArgumentFactory; import org.skife.jdbi.v2.sqlobject.customizers.RegisterMapperFactory; -import java.util.List; import java.util.Set; import java.util.UUID; @@ -33,7 +32,7 @@ interface TestingShardDao extends H2ShardDao { @SqlQuery("SELECT shard_uuid FROM shards WHERE table_id = :tableId") - List getShards(@Bind("tableId") long tableId); + Set getShards(@Bind("tableId") long tableId); @SqlQuery("SELECT s.shard_uuid, n.node_identifier\n" + "FROM shards s\n" + @@ -41,7 +40,7 @@ interface TestingShardDao "JOIN nodes n ON (sn.node_id = n.node_id)\n" + "WHERE s.table_id = :tableId") @Mapper(ShardNode.Mapper.class) - List getShardNodes(@Bind("tableId") long tableId); + Set getShardNodes(@Bind("tableId") long tableId); @SqlQuery("SELECT node_identifier FROM nodes") Set getAllNodesInUse(); diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestOrcFileRewriter.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestOrcFileRewriter.java index 2c080d44f6713..d86c8dee5ad78 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestOrcFileRewriter.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestOrcFileRewriter.java @@ -21,6 +21,7 @@ import com.facebook.presto.spi.Page; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.type.ArrayType; +import com.facebook.presto.spi.type.DecimalType; import com.facebook.presto.spi.type.StandardTypes; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.TypeManager; @@ -36,6 +37,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.math.BigDecimal; import java.util.BitSet; import java.util.List; @@ -95,17 +97,19 @@ public void testRewrite() Type mapType = typeManager.getParameterizedType(StandardTypes.MAP, ImmutableList.of( TypeSignatureParameter.of(createVarcharType(5).getTypeSignature()), TypeSignatureParameter.of(BOOLEAN.getTypeSignature()))); - List columnIds = ImmutableList.of(3L, 7L, 9L, 10L, 11L); - List columnTypes = ImmutableList.of(BIGINT, createVarcharType(20), arrayType, mapType, arrayOfArrayType); + List columnIds = ImmutableList.of(3L, 7L, 9L, 10L, 11L, 12L); + DecimalType decimalType = DecimalType.createDecimalType(4, 4); + + List columnTypes = ImmutableList.of(BIGINT, createVarcharType(20), arrayType, mapType, arrayOfArrayType, decimalType); File file = new File(temporary, randomUUID().toString()); try (OrcFileWriter writer = new OrcFileWriter(columnIds, columnTypes, file)) { List pages = rowPagesBuilder(columnTypes) - .row(123L, "hello", arrayBlockOf(BIGINT, 1, 2), mapBlockOf(createVarcharType(5), BOOLEAN, "k1", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 5))) - .row(777L, "sky", arrayBlockOf(BIGINT, 3, 4), mapBlockOf(createVarcharType(5), BOOLEAN, "k2", false), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 6))) - .row(456L, "bye", arrayBlockOf(BIGINT, 5, 6), mapBlockOf(createVarcharType(5), BOOLEAN, "k3", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 7))) - .row(888L, "world", arrayBlockOf(BIGINT, 7, 8), mapBlockOf(createVarcharType(5), BOOLEAN, "k4", true), arrayBlockOf(arrayType, null, arrayBlockOf(BIGINT, 8), null)) - .row(999L, "done", arrayBlockOf(BIGINT, 9, 10), mapBlockOf(createVarcharType(5), BOOLEAN, "k5", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 9, 10))) + .row(123L, "hello", arrayBlockOf(BIGINT, 1, 2), mapBlockOf(createVarcharType(5), BOOLEAN, "k1", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 5)), new BigDecimal("2.3")) + .row(777L, "sky", arrayBlockOf(BIGINT, 3, 4), mapBlockOf(createVarcharType(5), BOOLEAN, "k2", false), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 6)), new BigDecimal("2.3")) + .row(456L, "bye", arrayBlockOf(BIGINT, 5, 6), mapBlockOf(createVarcharType(5), BOOLEAN, "k3", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 7)), new BigDecimal("2.3")) + .row(888L, "world", arrayBlockOf(BIGINT, 7, 8), mapBlockOf(createVarcharType(5), BOOLEAN, "k4", true), arrayBlockOf(arrayType, null, arrayBlockOf(BIGINT, 8), null), new BigDecimal("2.3")) + .row(999L, "done", arrayBlockOf(BIGINT, 9, 10), mapBlockOf(createVarcharType(5), BOOLEAN, "k5", true), arrayBlockOf(arrayType, arrayBlockOf(BIGINT, 9, 10)), new BigDecimal("2.3")) .build(); writer.appendPages(pages); } @@ -183,6 +187,7 @@ public void testRewrite() .put(9L, arrayType.getTypeSignature()) .put(10L, mapType.getTypeSignature()) .put(11L, arrayOfArrayType.getTypeSignature()) + .put(12L, decimalType.getTypeSignature()) .build())); } @@ -194,7 +199,7 @@ public void testRewrite() File newFile = new File(temporary, randomUUID().toString()); OrcFileInfo info = OrcFileRewriter.rewrite(file, newFile, rowsToDelete); assertEquals(info.getRowCount(), 2); - assertEquals(info.getUncompressedSize(), 78); + assertEquals(info.getUncompressedSize(), 94); try (OrcDataSource dataSource = fileOrcDataSource(newFile)) { OrcRecordReader reader = createReader(dataSource, columnIds, columnTypes); @@ -254,6 +259,7 @@ public void testRewrite() .put(9L, arrayType.getTypeSignature()) .put(10L, mapType.getTypeSignature()) .put(11L, arrayOfArrayType.getTypeSignature()) + .put(12L, decimalType.getTypeSignature()) .build())); } } diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestStorageManagerConfig.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestStorageManagerConfig.java index 3f840524e385c..6ee890e6fade6 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestStorageManagerConfig.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/TestStorageManagerConfig.java @@ -62,6 +62,7 @@ public void testDefaults() .setCompactionEnabled(true) .setOrganizationEnabled(true) .setOrganizationInterval(new Duration(7, DAYS)) + .setOrganizationDiscoveryInterval(new Duration(6, HOURS)) .setMaxShardRows(1_000_000) .setMaxShardSize(new DataSize(256, MEGABYTE)) .setMaxBufferSize(new DataSize(256, MEGABYTE)) @@ -87,6 +88,7 @@ public void testExplicitPropertyMappings() .put("storage.compaction-interval", "4h") .put("storage.organization-enabled", "false") .put("storage.organization-interval", "4h") + .put("storage.organization-discovery-interval", "2h") .put("storage.ejector-interval", "9h") .put("storage.max-recovery-threads", "12") .put("storage.max-organization-threads", "12") @@ -112,6 +114,7 @@ public void testExplicitPropertyMappings() .setCompactionInterval(new Duration(4, HOURS)) .setOrganizationEnabled(false) .setOrganizationInterval(new Duration(4, HOURS)) + .setOrganizationDiscoveryInterval(new Duration(2, HOURS)) .setShardEjectorInterval(new Duration(9, HOURS)) .setRecoveryThreads(12) .setOrganizationThreads(12) diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizationManager.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizationManager.java index 3ea60f13133f2..e65d4483e2752 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizationManager.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizationManager.java @@ -45,6 +45,7 @@ import static com.google.common.collect.Iterables.getOnlyElement; import static io.airlift.units.Duration.nanosSince; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.stream.Collectors.toSet; import static org.joda.time.DateTimeZone.UTC; import static org.testng.Assert.assertEquals; @@ -201,6 +202,7 @@ private ShardOrganizationManager createShardOrganizationManager(long intervalMil createShardOrganizer(), TEMPORAL_FUNCTION, true, - new Duration(intervalMillis, MILLISECONDS)); + new Duration(intervalMillis, MILLISECONDS), + new Duration(5, MINUTES)); } } diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizerUtil.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizerUtil.java index cb3f262a394ad..df61e4f0a9070 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizerUtil.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/storage/organization/TestShardOrganizerUtil.java @@ -105,12 +105,12 @@ public void testGetOrganizationEligibleShards() SchemaTableName tableName = new SchemaTableName("default", "test"); metadata.createTable(SESSION, tableMetadataBuilder(tableName) - .column("orderkey", BIGINT) - .column("orderdate", DATE) - .column("orderstatus", createVarcharType(3)) - .property("ordering", ImmutableList.of("orderstatus", "orderkey")) - .property("temporal_column", "orderdate") - .build(), + .column("orderkey", BIGINT) + .column("orderdate", DATE) + .column("orderstatus", createVarcharType(3)) + .property("ordering", ImmutableList.of("orderstatus", "orderkey")) + .property("temporal_column", "orderdate") + .build(), false); Table tableInfo = metadataDao.getTableInformation(tableName.getSchemaName(), tableName.getTableName()); List tableColumns = metadataDao.listTableColumns(tableInfo.getTableId()); diff --git a/presto-raptor/src/test/java/com/facebook/presto/raptor/systemtables/TestShardMetadataRecordCursor.java b/presto-raptor/src/test/java/com/facebook/presto/raptor/systemtables/TestShardMetadataRecordCursor.java index 704cfbf0c731b..2df831840b39e 100644 --- a/presto-raptor/src/test/java/com/facebook/presto/raptor/systemtables/TestShardMetadataRecordCursor.java +++ b/presto-raptor/src/test/java/com/facebook/presto/raptor/systemtables/TestShardMetadataRecordCursor.java @@ -20,6 +20,7 @@ import com.facebook.presto.raptor.metadata.ShardManager; import com.facebook.presto.raptor.metadata.TableColumn; import com.facebook.presto.spi.ColumnMetadata; +import com.facebook.presto.spi.ConnectorTableMetadata; import com.facebook.presto.spi.RecordCursor; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.connector.ConnectorMetadata; @@ -81,12 +82,12 @@ public void setup() this.metadata = new RaptorMetadata("raptor", dbi, createShardManager(dbi)); // Create table - metadata.createTable(SESSION, tableMetadataBuilder(DEFAULT_TEST_ORDERS) + ConnectorTableMetadata table = tableMetadataBuilder(DEFAULT_TEST_ORDERS) .column("orderkey", BIGINT) .column("orderdate", DATE) .property("temporal_column", "orderdate") - .build(), - false); + .build(); + createTable(table); } @AfterMethod(alwaysRun = true) @@ -154,16 +155,14 @@ public void testSimple() public void testNoSchemaFilter() { // Create "orders" table in a different schema - metadata.createTable(SESSION, tableMetadataBuilder(new SchemaTableName("other", "orders")) + createTable(tableMetadataBuilder(new SchemaTableName("other", "orders")) .column("orderkey", BIGINT) - .build(), - false); + .build()); // Create another table that should not be selected - metadata.createTable(SESSION, tableMetadataBuilder(new SchemaTableName("schema1", "foo")) + createTable(tableMetadataBuilder(new SchemaTableName("schema1", "foo")) .column("orderkey", BIGINT) - .build(), - false); + .build()); TupleDomain tupleDomain = TupleDomain.withColumnDomains( ImmutableMap.builder() @@ -182,16 +181,14 @@ public void testNoSchemaFilter() public void testNoTableFilter() { // Create "orders" table in a different schema - metadata.createTable(SESSION, tableMetadataBuilder(new SchemaTableName("test", "orders2")) + createTable(tableMetadataBuilder(new SchemaTableName("test", "orders2")) .column("orderkey", BIGINT) - .build(), - false); + .build()); // Create another table that should not be selected - metadata.createTable(SESSION, tableMetadataBuilder(new SchemaTableName("schema1", "foo")) + createTable(tableMetadataBuilder(new SchemaTableName("schema1", "foo")) .column("orderkey", BIGINT) - .build(), - false); + .build()); TupleDomain tupleDomain = TupleDomain.withColumnDomains( ImmutableMap.builder() @@ -206,6 +203,11 @@ public void testNoTableFilter() assertEquals(actual, expected); } + private void createTable(ConnectorTableMetadata table) + { + metadata.createTable(SESSION, table, false); + } + private static List getMaterializedResults(RecordCursor cursor, List columns) { List types = columns.stream().map(ColumnMetadata::getType).collect(toList()); diff --git a/presto-rcfile/pom.xml b/presto-rcfile/pom.xml index cd91fce6c3b3c..2f5f081b7af9b 100644 --- a/presto-rcfile/pom.xml +++ b/presto-rcfile/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-rcfile @@ -32,6 +32,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + com.google.guava guava diff --git a/presto-rcfile/src/test/java/com/facebook/presto/rcfile/RcFileTester.java b/presto-rcfile/src/test/java/com/facebook/presto/rcfile/RcFileTester.java index ee7771b7fbcbe..bf4bc506b180e 100644 --- a/presto-rcfile/src/test/java/com/facebook/presto/rcfile/RcFileTester.java +++ b/presto-rcfile/src/test/java/com/facebook/presto/rcfile/RcFileTester.java @@ -197,7 +197,7 @@ public class RcFileTester HadoopNative.requireHadoopNative(); } - public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("Asia/Katmandu"); + public static final DateTimeZone HIVE_STORAGE_TIME_ZONE = DateTimeZone.forID("America/Bahia_Banderas"); public enum Format { diff --git a/presto-record-decoder/pom.xml b/presto-record-decoder/pom.xml index 905b2c612b3bb..9e827191ca9f2 100644 --- a/presto-record-decoder/pom.xml +++ b/presto-record-decoder/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-record-decoder @@ -51,6 +51,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + org.apache.avro avro diff --git a/presto-record-decoder/src/main/java/com/facebook/presto/decoder/RowDecoder.java b/presto-record-decoder/src/main/java/com/facebook/presto/decoder/RowDecoder.java index ec7911d60b97c..8c22308e59298 100644 --- a/presto-record-decoder/src/main/java/com/facebook/presto/decoder/RowDecoder.java +++ b/presto-record-decoder/src/main/java/com/facebook/presto/decoder/RowDecoder.java @@ -26,7 +26,6 @@ public interface RowDecoder * * @param data The row data to decode. * @param dataMap The row data as fields map - * * @return Returns mapping from column handle to decoded value. Unmapped columns will be reported as null. Optional.empty() signals decoding error. */ Optional> decodeRow( diff --git a/presto-record-decoder/src/main/java/com/facebook/presto/decoder/avro/AvroRowDecoder.java b/presto-record-decoder/src/main/java/com/facebook/presto/decoder/avro/AvroRowDecoder.java index 51c5eaee63962..8fb5ae4c58497 100644 --- a/presto-record-decoder/src/main/java/com/facebook/presto/decoder/avro/AvroRowDecoder.java +++ b/presto-record-decoder/src/main/java/com/facebook/presto/decoder/avro/AvroRowDecoder.java @@ -70,7 +70,7 @@ public Optional> decodeRow(byte[] d } } catch (Exception e) { - throw new PrestoException(GENERIC_INTERNAL_ERROR, "Decoding AVRO record failed.", e); + throw new PrestoException(GENERIC_INTERNAL_ERROR, "Decoding Avro record failed.", e); } finally { closeQuietly(dataFileReader); diff --git a/presto-record-decoder/src/test/java/com/facebook/presto/decoder/avro/TestAvroDecoder.java b/presto-record-decoder/src/test/java/com/facebook/presto/decoder/avro/TestAvroDecoder.java index 6fe8bb8b3c2f0..c08064ba50a88 100644 --- a/presto-record-decoder/src/test/java/com/facebook/presto/decoder/avro/TestAvroDecoder.java +++ b/presto-record-decoder/src/test/java/com/facebook/presto/decoder/avro/TestAvroDecoder.java @@ -152,7 +152,7 @@ private static GenericData.Record buildAvroRecord(Schema schema, ByteArrayOutput dataFileWriter.close(); } catch (IOException e) { - throw new RuntimeException("Failed to convert to AVRO.", e); + throw new RuntimeException("Failed to convert to Avro.", e); } return record; } @@ -286,7 +286,7 @@ public void testSchemaEvolutionToIncompatibleType() .isInstanceOf(PrestoException.class) .hasCauseExactlyInstanceOf(AvroTypeException.class) .hasStackTraceContaining("Found int, expecting string") - .hasMessageMatching("Decoding AVRO record failed."); + .hasMessageMatching("Decoding Avro record failed."); } @Test diff --git a/presto-redis/pom.xml b/presto-redis/pom.xml index 0ec0b4e3ed7d6..524389f05fa2f 100644 --- a/presto-redis/pom.xml +++ b/presto-redis/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-redis @@ -67,6 +67,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + javax.annotation javax.annotation-api diff --git a/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorConfig.java b/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorConfig.java index 44cb45625c902..4e72af58a5c95 100644 --- a/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorConfig.java +++ b/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorConfig.java @@ -17,6 +17,7 @@ import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import io.airlift.configuration.Config; +import io.airlift.configuration.ConfigSecuritySensitive; import io.airlift.units.Duration; import io.airlift.units.MinDuration; @@ -194,6 +195,7 @@ public String getRedisPassword() } @Config("redis.password") + @ConfigSecuritySensitive public RedisConnectorConfig setRedisPassword(String redisPassword) { this.redisPassword = redisPassword; diff --git a/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorFactory.java b/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorFactory.java index 3ab8e8a4d6a20..adfa182f3f232 100644 --- a/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorFactory.java +++ b/presto-redis/src/main/java/com/facebook/presto/redis/RedisConnectorFactory.java @@ -59,9 +59,9 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { - requireNonNull(connectorId, "connectorId is null"); + requireNonNull(catalogName, "catalogName is null"); requireNonNull(config, "config is null"); try { @@ -69,7 +69,7 @@ public Connector create(String connectorId, Map config, Connecto new JsonModule(), new RedisConnectorModule(), binder -> { - binder.bind(RedisConnectorId.class).toInstance(new RedisConnectorId(connectorId)); + binder.bind(RedisConnectorId.class).toInstance(new RedisConnectorId(catalogName)); binder.bind(TypeManager.class).toInstance(context.getTypeManager()); binder.bind(NodeManager.class).toInstance(context.getNodeManager()); diff --git a/presto-redshift/pom.xml b/presto-redshift/pom.xml index 107f2c0f4b852..70ff67192ffed 100644 --- a/presto-redshift/pom.xml +++ b/presto-redshift/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-redshift diff --git a/presto-resource-group-managers/pom.xml b/presto-resource-group-managers/pom.xml index 4e59130c0807c..96ca37a108db5 100644 --- a/presto-resource-group-managers/pom.xml +++ b/presto-resource-group-managers/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-resource-group-managers diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/AbstractResourceConfigurationManager.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/AbstractResourceConfigurationManager.java index 0d3a4e535e8de..71e10c8785177 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/AbstractResourceConfigurationManager.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/AbstractResourceConfigurationManager.java @@ -205,8 +205,6 @@ protected void configureGroup(ResourceGroup group, ResourceGroupSpec match) group.setMaxQueuedQueries(match.getMaxQueued()); group.setSoftConcurrencyLimit(match.getSoftConcurrencyLimit().orElse(match.getHardConcurrencyLimit())); group.setHardConcurrencyLimit(match.getHardConcurrencyLimit()); - match.getQueuedTimeLimit().ifPresent(group::setQueuedTimeLimit); - match.getRunningTimeLimit().ifPresent(group::setRunningTimeLimit); match.getSchedulingPolicy().ifPresent(group::setSchedulingPolicy); match.getSchedulingWeight().ifPresent(group::setSchedulingWeight); match.getJmxExport().filter(isEqual(group.getJmxExport()).negate()).ifPresent(group::setJmxExport); diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/FileResourceGroupConfigurationManagerFactory.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/FileResourceGroupConfigurationManagerFactory.java index 42d5edc655736..60ee551d7f3cf 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/FileResourceGroupConfigurationManagerFactory.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/FileResourceGroupConfigurationManagerFactory.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.resourceGroups; -import com.facebook.presto.spi.classloader.ThreadContextClassLoader; import com.facebook.presto.spi.memory.ClusterMemoryPoolManager; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManager; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManagerContext; @@ -25,18 +24,10 @@ import java.util.Map; import static com.google.common.base.Throwables.throwIfUnchecked; -import static java.util.Objects.requireNonNull; public class FileResourceGroupConfigurationManagerFactory implements ResourceGroupConfigurationManagerFactory { - private final ClassLoader classLoader; - - public FileResourceGroupConfigurationManagerFactory(ClassLoader classLoader) - { - this.classLoader = requireNonNull(classLoader, "classLoader is null"); - } - @Override public String getName() { @@ -46,7 +37,7 @@ public String getName() @Override public ResourceGroupConfigurationManager create(Map config, ResourceGroupConfigurationManagerContext context) { - try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + try { Bootstrap app = new Bootstrap( new JsonModule(), new FileResourceGroupsModule(), diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupManagerPlugin.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupManagerPlugin.java index b7e91592d5441..58c04e2093063 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupManagerPlugin.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupManagerPlugin.java @@ -18,8 +18,6 @@ import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManagerFactory; import com.google.common.collect.ImmutableList; -import static com.google.common.base.MoreObjects.firstNonNull; - public class ResourceGroupManagerPlugin implements Plugin { @@ -27,12 +25,7 @@ public class ResourceGroupManagerPlugin public Iterable getResourceGroupConfigurationManagerFactories() { return ImmutableList.of( - new FileResourceGroupConfigurationManagerFactory(getClassLoader()), - new DbResourceGroupConfigurationManagerFactory(getClassLoader())); - } - - private static ClassLoader getClassLoader() - { - return firstNonNull(Thread.currentThread().getContextClassLoader(), ResourceGroupManagerPlugin.class.getClassLoader()); + new FileResourceGroupConfigurationManagerFactory(), + new DbResourceGroupConfigurationManagerFactory()); } } diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupSpec.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupSpec.java index 63bed091e5e53..aae74717206bd 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupSpec.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/ResourceGroupSpec.java @@ -48,8 +48,6 @@ public class ResourceGroupSpec private final Optional jmxExport; private final Optional softCpuLimit; private final Optional hardCpuLimit; - private final Optional queuedTimeLimit; - private final Optional runningTimeLimit; @JsonCreator public ResourceGroupSpec( @@ -64,15 +62,11 @@ public ResourceGroupSpec( @JsonProperty("subGroups") Optional> subGroups, @JsonProperty("jmxExport") Optional jmxExport, @JsonProperty("softCpuLimit") Optional softCpuLimit, - @JsonProperty("hardCpuLimit") Optional hardCpuLimit, - @JsonProperty("queuedTimeLimit") Optional queuedTimeLimit, - @JsonProperty("runningTimeLimit") Optional runningTimeLimit) + @JsonProperty("hardCpuLimit") Optional hardCpuLimit) { this.softCpuLimit = requireNonNull(softCpuLimit, "softCpuLimit is null"); this.hardCpuLimit = requireNonNull(hardCpuLimit, "hardCpuLimit is null"); this.jmxExport = requireNonNull(jmxExport, "jmxExport is null"); - this.queuedTimeLimit = requireNonNull(queuedTimeLimit, "queuedTimeLimit is null"); - this.runningTimeLimit = requireNonNull(runningTimeLimit, "runningTimeLimit is null"); this.name = requireNonNull(name, "name is null"); checkArgument(maxQueued >= 0, "maxQueued is negative"); this.maxQueued = maxQueued; @@ -170,16 +164,6 @@ public Optional getHardCpuLimit() return hardCpuLimit; } - public Optional getQueuedTimeLimit() - { - return queuedTimeLimit; - } - - public Optional getRunningTimeLimit() - { - return runningTimeLimit; - } - @Override public boolean equals(Object other) { @@ -200,9 +184,7 @@ public boolean equals(Object other) subGroups.equals(that.subGroups) && jmxExport.equals(that.jmxExport) && softCpuLimit.equals(that.softCpuLimit) && - hardCpuLimit.equals(that.hardCpuLimit) && - queuedTimeLimit.equals(that.queuedTimeLimit) && - runningTimeLimit.equals(that.runningTimeLimit)); + hardCpuLimit.equals(that.hardCpuLimit)); } // Subgroups not included, used to determine whether a group needs to be reconfigured @@ -220,9 +202,7 @@ public boolean sameConfig(ResourceGroupSpec other) schedulingWeight.equals(other.schedulingWeight) && jmxExport.equals(other.jmxExport) && softCpuLimit.equals(other.softCpuLimit) && - hardCpuLimit.equals(other.hardCpuLimit) && - queuedTimeLimit.equals(other.queuedTimeLimit) && - runningTimeLimit.equals(other.runningTimeLimit)); + hardCpuLimit.equals(other.hardCpuLimit)); } @Override @@ -239,9 +219,7 @@ public int hashCode() subGroups, jmxExport, softCpuLimit, - hardCpuLimit, - queuedTimeLimit, - runningTimeLimit); + hardCpuLimit); } @Override @@ -258,8 +236,6 @@ public String toString() .add("jmxExport", jmxExport) .add("softCpuLimit", softCpuLimit) .add("hardCpuLimit", hardCpuLimit) - .add("queuedTimeLimit", queuedTimeLimit) - .add("runningTimeLimit", runningTimeLimit) .toString(); } } diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/DbResourceGroupConfigurationManagerFactory.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/DbResourceGroupConfigurationManagerFactory.java index 3729b72a880f4..412d1bc78e244 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/DbResourceGroupConfigurationManagerFactory.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/DbResourceGroupConfigurationManagerFactory.java @@ -14,7 +14,6 @@ package com.facebook.presto.resourceGroups.db; import com.facebook.presto.resourceGroups.VariableMap; -import com.facebook.presto.spi.classloader.ThreadContextClassLoader; import com.facebook.presto.spi.memory.ClusterMemoryPoolManager; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManager; import com.facebook.presto.spi.resourceGroups.ResourceGroupConfigurationManagerContext; @@ -26,18 +25,10 @@ import java.util.Map; import static com.google.common.base.Throwables.throwIfUnchecked; -import static java.util.Objects.requireNonNull; public class DbResourceGroupConfigurationManagerFactory implements ResourceGroupConfigurationManagerFactory { - private final ClassLoader classLoader; - - public DbResourceGroupConfigurationManagerFactory(ClassLoader classLoader) - { - this.classLoader = requireNonNull(classLoader, "classLoader is null"); - } - @Override public String getName() { @@ -47,7 +38,7 @@ public String getName() @Override public ResourceGroupConfigurationManager create(Map config, ResourceGroupConfigurationManagerContext context) { - try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + try { Bootstrap app = new Bootstrap( new JsonModule(), new DbResourceGroupsModule(), diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupSpecBuilder.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupSpecBuilder.java index 905643a22d5e9..dce91f5aa2cec 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupSpecBuilder.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupSpecBuilder.java @@ -39,8 +39,6 @@ public class ResourceGroupSpecBuilder private final Optional jmxExport; private final Optional softCpuLimit; private final Optional hardCpuLimit; - private final Optional queuedTimeLimit; - private final Optional runningTimeLimit; private final Optional parentId; private final ImmutableList.Builder subGroups = ImmutableList.builder(); @@ -56,8 +54,6 @@ public class ResourceGroupSpecBuilder Optional jmxExport, Optional softCpuLimit, Optional hardCpuLimit, - Optional queuedTimeLimit, - Optional runningTimeLimit, Optional parentId) { this.id = id; @@ -71,8 +67,6 @@ public class ResourceGroupSpecBuilder this.jmxExport = requireNonNull(jmxExport, "jmxExport is null"); this.softCpuLimit = requireNonNull(softCpuLimit, "softCpuLimit is null").map(Duration::valueOf); this.hardCpuLimit = requireNonNull(hardCpuLimit, "hardCpuLimit is null").map(Duration::valueOf); - this.queuedTimeLimit = requireNonNull(queuedTimeLimit, "queuedTimeLimit is null").map(Duration::valueOf); - this.runningTimeLimit = requireNonNull(runningTimeLimit, "runningTimeLimit is null").map(Duration::valueOf); this.parentId = parentId; } @@ -120,9 +114,7 @@ public ResourceGroupSpec build() Optional.of(subGroups.build()), jmxExport, softCpuLimit, - hardCpuLimit, - queuedTimeLimit, - runningTimeLimit); + hardCpuLimit); } public static class Mapper @@ -156,8 +148,6 @@ public ResourceGroupSpecBuilder map(ResultSet resultSet, StatementContext contex if (resultSet.wasNull()) { parentId = Optional.empty(); } - Optional queuedTimeLimit = Optional.ofNullable(resultSet.getString("queued_time_limit")); - Optional runningTimeLimit = Optional.ofNullable(resultSet.getString("running_time_limit")); return new ResourceGroupSpecBuilder( id, nameTemplate, @@ -170,8 +160,6 @@ public ResourceGroupSpecBuilder map(ResultSet resultSet, StatementContext contex jmxExport, softCpuLimit, hardCpuLimit, - queuedTimeLimit, - runningTimeLimit, parentId); } } diff --git a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupsDao.java b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupsDao.java index d08cf5d6a4917..836ef0d200902 100644 --- a/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupsDao.java +++ b/presto-resource-group-managers/src/main/java/com/facebook/presto/resourceGroups/db/ResourceGroupsDao.java @@ -45,8 +45,6 @@ public interface ResourceGroupsDao " jmx_export BOOLEAN NULL,\n" + " soft_cpu_limit VARCHAR(128) NULL,\n" + " hard_cpu_limit VARCHAR(128) NULL,\n" + - " queued_time_limit VARCHAR(128) NULL,\n" + - " running_time_limit VARCHAR(128) NULL,\n" + " parent BIGINT NULL,\n" + " environment VARCHAR(128) NULL,\n" + " PRIMARY KEY (resource_group_id),\n" + @@ -56,7 +54,7 @@ public interface ResourceGroupsDao @SqlQuery("SELECT resource_group_id, name, soft_memory_limit, max_queued, soft_concurrency_limit, " + " hard_concurrency_limit, scheduling_policy, scheduling_weight, jmx_export, soft_cpu_limit, " + - " hard_cpu_limit, queued_time_limit, running_time_limit, parent\n" + + " hard_cpu_limit, parent\n" + "FROM resource_groups\n" + "WHERE environment = :environment\n") @UseRowMapper(ResourceGroupSpecBuilder.Mapper.class) diff --git a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestFileResourceGroupConfigurationManager.java b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestFileResourceGroupConfigurationManager.java index 44d06e5d7b7b1..27219fe8a95d3 100644 --- a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestFileResourceGroupConfigurationManager.java +++ b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestFileResourceGroupConfigurationManager.java @@ -29,14 +29,14 @@ import java.util.Optional; import static com.facebook.presto.spi.resourceGroups.SchedulingPolicy.WEIGHTED; +import static com.google.common.io.Resources.getResource; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.lang.String.format; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.HOURS; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; -import static org.testng.Assert.fail; public class TestFileResourceGroupConfigurationManager { @@ -75,14 +75,14 @@ public void testInvalidQueryTypeConfiguration() parse("resource_groups_config_bad_query_type.json"); } - private void assertMatch(List selectors, SelectionCriteria context, String expectedResourceGroup) + private static void assertMatch(List selectors, SelectionCriteria context, String expectedResourceGroup) { Optional group = tryMatch(selectors, context); assertTrue(group.isPresent(), "match expected"); assertEquals(group.get().toString(), expectedResourceGroup, format("Expected: '%s' resource group, found: %s", expectedResourceGroup, group.get())); } - private Optional tryMatch(List selectors, SelectionCriteria context) + private static Optional tryMatch(List selectors, SelectionCriteria context) { for (ResourceGroupSelector selector : selectors) { Optional> group = selector.match(context); @@ -93,6 +93,7 @@ private Optional tryMatch(List selectors return Optional.empty(); } + @SuppressWarnings("SimplifiedTestNGAssertion") @Test public void testConfiguration() { @@ -109,8 +110,6 @@ public void testConfiguration() assertEquals(global.getSchedulingPolicy(), WEIGHTED); assertEquals(global.getSchedulingWeight(), 0); assertEquals(global.getJmxExport(), true); - assertEquals(global.getQueuedTimeLimit(), new Duration(1, HOURS)); - assertEquals(global.getRunningTimeLimit(), new Duration(1, HOURS)); ResourceGroupId subId = new ResourceGroupId(globalId, "sub"); ResourceGroup sub = new TestingResourceGroup(subId); @@ -121,8 +120,6 @@ public void testConfiguration() assertEquals(sub.getSchedulingPolicy(), null); assertEquals(sub.getSchedulingWeight(), 5); assertEquals(sub.getJmxExport(), false); - assertEquals(global.getQueuedTimeLimit(), new Duration(1, HOURS)); - assertEquals(global.getRunningTimeLimit(), new Duration(1, HOURS)); } @Test @@ -160,26 +157,15 @@ public void testNonExistentGroup() parse("resource_groups_config_bad_selector.json"); } - private FileResourceGroupConfigurationManager parse(String fileName) + private static FileResourceGroupConfigurationManager parse(String fileName) { FileResourceGroupConfig config = new FileResourceGroupConfig(); - config.setConfigFile(getResourceFilePath(fileName)); + config.setConfigFile(getResource(fileName).getPath()); return new FileResourceGroupConfigurationManager((poolId, listener) -> {}, config); } - private String getResourceFilePath(String fileName) + private static void assertFails(String fileName, String expectedPattern) { - return this.getClass().getClassLoader().getResource(fileName).getPath(); - } - - private void assertFails(String fileName, String expectedPattern) - { - try { - parse(fileName); - fail("Expected parsing to fail"); - } - catch (RuntimeException e) { - assertThat(e.getMessage()).matches(expectedPattern); - } + assertThatThrownBy(() -> parse(fileName)).hasMessageMatching(expectedPattern); } } diff --git a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestingResourceGroup.java b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestingResourceGroup.java index 0fe2e3d312b1e..11bab68b5d646 100644 --- a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestingResourceGroup.java +++ b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/TestingResourceGroup.java @@ -35,8 +35,6 @@ public class TestingResourceGroup private int schedulingWeight; private SchedulingPolicy policy; private boolean jmxExport; - private Duration queuedTimeLimit; - private Duration runningTimeLimit; public TestingResourceGroup(ResourceGroupId id) { @@ -168,28 +166,4 @@ public void setJmxExport(boolean export) { jmxExport = export; } - - @Override - public Duration getQueuedTimeLimit() - { - return queuedTimeLimit; - } - - @Override - public void setQueuedTimeLimit(Duration queuedTimeLimit) - { - this.queuedTimeLimit = queuedTimeLimit; - } - - @Override - public Duration getRunningTimeLimit() - { - return runningTimeLimit; - } - - @Override - public void setRunningTimeLimit(Duration runningTimeLimit) - { - this.runningTimeLimit = runningTimeLimit; - } } diff --git a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/H2ResourceGroupsDao.java b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/H2ResourceGroupsDao.java index cf968b8599fd7..d383f103ffd1d 100644 --- a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/H2ResourceGroupsDao.java +++ b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/H2ResourceGroupsDao.java @@ -29,8 +29,8 @@ void insertResourceGroupsGlobalProperties( void updateResourceGroupsGlobalProperties(@Bind("name") String name); @SqlUpdate("INSERT INTO resource_groups\n" + - "(resource_group_id, name, soft_memory_limit, max_queued, soft_concurrency_limit, hard_concurrency_limit, scheduling_policy, scheduling_weight, jmx_export, soft_cpu_limit, hard_cpu_limit, queued_time_limit, running_time_limit, parent, environment)\n" + - "VALUES (:resource_group_id, :name, :soft_memory_limit, :max_queued, :soft_concurrency_limit, :hard_concurrency_limit, :scheduling_policy, :scheduling_weight, :jmx_export, :soft_cpu_limit, :hard_cpu_limit, :queued_time_limit, :running_time_limit, :parent, :environment)") + "(resource_group_id, name, soft_memory_limit, max_queued, soft_concurrency_limit, hard_concurrency_limit, scheduling_policy, scheduling_weight, jmx_export, soft_cpu_limit, hard_cpu_limit, parent, environment)\n" + + "VALUES (:resource_group_id, :name, :soft_memory_limit, :max_queued, :soft_concurrency_limit, :hard_concurrency_limit, :scheduling_policy, :scheduling_weight, :jmx_export, :soft_cpu_limit, :hard_cpu_limit, :parent, :environment)") void insertResourceGroup( @Bind("resource_group_id") long resourceGroupId, @Bind("name") String name, @@ -43,8 +43,6 @@ void insertResourceGroup( @Bind("jmx_export") Boolean jmxExport, @Bind("soft_cpu_limit") String softCpuLimit, @Bind("hard_cpu_limit") String hardCpuLimit, - @Bind("queued_time_limit") String queuedTimeLimit, - @Bind("running_time_limit") String runningTimeLimit, @Bind("parent") Long parent, @Bind("environment") String environment); @@ -60,8 +58,6 @@ void insertResourceGroup( ", jmx_export = :jmx_export\n" + ", soft_cpu_limit = :soft_cpu_limit\n" + ", hard_cpu_limit = :hard_cpu_limit\n" + - ", queued_time_limit = :queued_time_limit\n" + - ", running_time_limit = :running_time_limit\n" + ", parent = :parent\n" + ", environment = :environment\n" + "WHERE resource_group_id = :resource_group_id") @@ -77,8 +73,6 @@ void updateResourceGroup( @Bind("jmx_export") Boolean jmxExport, @Bind("soft_cpu_limit") String softCpuLimit, @Bind("hard_cpu_limit") String hardCpuLimit, - @Bind("queued_time_limit") String queuedTimeLimit, - @Bind("running_time_limit") String runningTimeLimit, @Bind("parent") Long parent, @Bind("environment") String environment); diff --git a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestDbResourceGroupConfigurationManager.java b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestDbResourceGroupConfigurationManager.java index 0b944bc9a6861..22f6024b79a77 100644 --- a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestDbResourceGroupConfigurationManager.java +++ b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestDbResourceGroupConfigurationManager.java @@ -74,8 +74,8 @@ public void testEnvironments() String devEnvironment = "dev"; dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); // two resource groups are the same except the group for the prod environment has a larger softMemoryLimit - dao.insertResourceGroup(1, "prod_global", "10MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", "1h", "1h", null, prodEnvironment); - dao.insertResourceGroup(2, "dev_global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", "1h", "1h", null, devEnvironment); + dao.insertResourceGroup(1, "prod_global", "10MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, prodEnvironment); + dao.insertResourceGroup(2, "dev_global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, devEnvironment); dao.insertSelector(1, 1, ".*prod_user.*", null, null, null, null); dao.insertSelector(2, 2, ".*dev_user.*", null, null, null, null); @@ -85,7 +85,7 @@ public void testEnvironments() assertEquals(groups.size(), 1); InternalResourceGroup prodGlobal = new InternalResourceGroup.RootInternalResourceGroup("prod_global", (group, export) -> {}, directExecutor()); manager.configure(prodGlobal, new SelectionContext<>(prodGlobal.getId(), new VariableMap(ImmutableMap.of("USER", "user")))); - assertEqualsResourceGroup(prodGlobal, "10MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS), new Duration(1, HOURS), new Duration(1, HOURS)); + assertEqualsResourceGroup(prodGlobal, "10MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS)); assertEquals(manager.getSelectors().size(), 1); ResourceGroupSelector prodSelector = manager.getSelectors().get(0); ResourceGroupId prodResourceGroupId = prodSelector.match(new SelectionCriteria(true, "prod_user", Optional.empty(), ImmutableSet.of(), EMPTY_RESOURCE_ESTIMATES, Optional.empty())).get().getResourceGroupId(); @@ -96,7 +96,7 @@ public void testEnvironments() assertEquals(groups.size(), 1); InternalResourceGroup devGlobal = new InternalResourceGroup.RootInternalResourceGroup("dev_global", (group, export) -> {}, directExecutor()); manager.configure(devGlobal, new SelectionContext<>(prodGlobal.getId(), new VariableMap(ImmutableMap.of("USER", "user")))); - assertEqualsResourceGroup(devGlobal, "1MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS), new Duration(1, HOURS), new Duration(1, HOURS)); + assertEqualsResourceGroup(devGlobal, "1MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS)); assertEquals(manager.getSelectors().size(), 1); ResourceGroupSelector devSelector = manager.getSelectors().get(0); ResourceGroupId devResourceGroupId = devSelector.match(new SelectionCriteria(true, "dev_user", Optional.empty(), ImmutableSet.of(), EMPTY_RESOURCE_ESTIMATES, Optional.empty())).get().getResourceGroupId(); @@ -112,18 +112,18 @@ public void testConfiguration() dao.createResourceGroupsTable(); dao.createSelectorsTable(); dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", "1h", "1h", null, ENVIRONMENT); - dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, "1h", "1h", 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, 1L, ENVIRONMENT); dao.insertSelector(2, 1, null, null, null, null, null); DbResourceGroupConfigurationManager manager = new DbResourceGroupConfigurationManager((poolId, listener) -> {}, new DbResourceGroupConfig(), daoProvider.get(), ENVIRONMENT); AtomicBoolean exported = new AtomicBoolean(); InternalResourceGroup global = new InternalResourceGroup.RootInternalResourceGroup("global", (group, export) -> exported.set(export), directExecutor()); manager.configure(global, new SelectionContext<>(global.getId(), new VariableMap(ImmutableMap.of("USER", "user")))); - assertEqualsResourceGroup(global, "1MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS), new Duration(1, HOURS), new Duration(1, HOURS)); + assertEqualsResourceGroup(global, "1MB", 1000, 100, 100, WEIGHTED, DEFAULT_WEIGHT, true, new Duration(1, HOURS), new Duration(1, DAYS)); exported.set(false); InternalResourceGroup sub = global.getOrCreateSubGroup("sub"); manager.configure(sub, new SelectionContext<>(sub.getId(), new VariableMap(ImmutableMap.of("USER", "user")))); - assertEqualsResourceGroup(sub, "2MB", 4, 3, 3, FAIR, 5, false, new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(1, HOURS), new Duration(1, HOURS)); + assertEqualsResourceGroup(sub, "2MB", 4, 3, 3, FAIR, 5, false, new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS)); } @Test @@ -134,9 +134,9 @@ public void testDuplicates() dao.createResourceGroupsGlobalPropertiesTable(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, ENVIRONMENT); try { - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, ENVIRONMENT); fail("Expected to fail"); } catch (RuntimeException ex) { @@ -150,10 +150,10 @@ public void testDuplicates() dao.createResourceGroupsGlobalPropertiesTable(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); - dao.insertResourceGroup(2, "sub", "1MB", 1000, 100, 100, null, null, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "1MB", 1000, 100, 100, null, null, null, null, null, 1L, ENVIRONMENT); try { - dao.insertResourceGroup(2, "sub", "1MB", 1000, 100, 100, null, null, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "1MB", 1000, 100, 100, null, null, null, null, null, 1L, ENVIRONMENT); } catch (RuntimeException ex) { assertTrue(ex instanceof UnableToExecuteStatementException); @@ -172,8 +172,8 @@ public void testMissing() dao.createResourceGroupsGlobalPropertiesTable(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, null, null, ENVIRONMENT); - dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, 1L, ENVIRONMENT); dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); dao.insertSelector(2, 1, null, null, null, null, null); DbResourceGroupConfigurationManager manager = new DbResourceGroupConfigurationManager((poolId, listener) -> {}, new DbResourceGroupConfig(), daoProvider.get(), ENVIRONMENT); @@ -190,8 +190,8 @@ public void testReconfig() dao.createResourceGroupsGlobalPropertiesTable(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, null, null, ENVIRONMENT); - dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, 1L, ENVIRONMENT); dao.insertSelector(2, 1, null, null, null, null, null); dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); DbResourceGroupConfigurationManager manager = new DbResourceGroupConfigurationManager((poolId, listener) -> {}, new DbResourceGroupConfig(), daoProvider.get(), ENVIRONMENT); @@ -202,14 +202,14 @@ public void testReconfig() InternalResourceGroup globalSub = global.getOrCreateSubGroup("sub"); manager.configure(globalSub, new SelectionContext<>(globalSub.getId(), new VariableMap(ImmutableMap.of("USER", "user")))); // Verify record exists - assertEqualsResourceGroup(globalSub, "2MB", 4, 3, 3, FAIR, 5, false, new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS)); - dao.updateResourceGroup(2, "sub", "3MB", 2, 1, 1, "weighted", 6, true, "1h", "1d", null, null, 1L, ENVIRONMENT); + assertEqualsResourceGroup(globalSub, "2MB", 4, 3, 3, FAIR, 5, false, new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS)); + dao.updateResourceGroup(2, "sub", "3MB", 2, 1, 1, "weighted", 6, true, "1h", "1d", 1L, ENVIRONMENT); do { MILLISECONDS.sleep(500); } while (globalSub.getJmxExport() == false); // Verify update - assertEqualsResourceGroup(globalSub, "3MB", 2, 1, 1, WEIGHTED, 6, true, new Duration(1, HOURS), new Duration(1, DAYS), new Duration(Long.MAX_VALUE, MILLISECONDS), new Duration(Long.MAX_VALUE, MILLISECONDS)); + assertEqualsResourceGroup(globalSub, "3MB", 2, 1, 1, WEIGHTED, 6, true, new Duration(1, HOURS), new Duration(1, DAYS)); // Verify delete dao.deleteSelectors(2); dao.deleteResourceGroup(2); @@ -228,8 +228,8 @@ public void testExactMatchSelector() dao.createResourceGroupsTable(); dao.createSelectorsTable(); dao.createExactMatchSelectorsTable(); - dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, null, null, ENVIRONMENT); - dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 1000, 100, 100, "weighted", null, true, "1h", "1d", null, ENVIRONMENT); + dao.insertResourceGroup(2, "sub", "2MB", 4, 3, 3, null, 5, null, null, null, 1L, ENVIRONMENT); dao.insertSelector(2, 1, null, null, null, null, null); dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); DbResourceGroupConfig config = new DbResourceGroupConfig(); @@ -253,7 +253,7 @@ public void testSelectorPriority() H2ResourceGroupsDao dao = daoProvider.get(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, ENVIRONMENT); final int numberOfUsers = 100; List expectedUsers = new ArrayList<>(); @@ -295,7 +295,7 @@ public void testInvalidConfiguration() H2ResourceGroupsDao dao = daoProvider.get(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, ENVIRONMENT); DbResourceGroupConfigurationManager manager = new DbResourceGroupConfigurationManager( (poolId, listener) -> {}, @@ -313,7 +313,7 @@ public void testRefreshInterval() H2ResourceGroupsDao dao = daoProvider.get(); dao.createResourceGroupsTable(); dao.createSelectorsTable(); - dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, ENVIRONMENT); DbResourceGroupConfigurationManager manager = new DbResourceGroupConfigurationManager( (poolId, listener) -> {}, @@ -353,9 +353,7 @@ private static void assertEqualsResourceGroup( int schedulingWeight, boolean jmxExport, Duration softCpuLimit, - Duration hardCpuLimit, - Duration queuedTimeLimit, - Duration runningTimeLimit) + Duration hardCpuLimit) { assertEquals(group.getSoftMemoryLimit(), DataSize.valueOf(softMemoryLimit)); assertEquals(group.getMaxQueuedQueries(), maxQueued); @@ -366,7 +364,5 @@ private static void assertEqualsResourceGroup( assertEquals(group.getJmxExport(), jmxExport); assertEquals(group.getSoftCpuLimit(), softCpuLimit); assertEquals(group.getHardCpuLimit(), hardCpuLimit); - assertEquals(group.getQueuedTimeLimit(), queuedTimeLimit); - assertEquals(group.getRunningTimeLimit(), runningTimeLimit); } } diff --git a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestResourceGroupsDao.java b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestResourceGroupsDao.java index 1b5054e81f4c4..c268f8b4cada8 100644 --- a/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestResourceGroupsDao.java +++ b/presto-resource-group-managers/src/test/java/com/facebook/presto/resourceGroups/db/TestResourceGroupsDao.java @@ -77,19 +77,19 @@ public void testResourceGroups() private static void testResourceGroupInsert(H2ResourceGroupsDao dao, Map map) { - dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, null, null, ENVIRONMENT); - dao.insertResourceGroup(2, "bi", "50%", 50, 50, 50, null, null, null, null, null, null, null, 1L, ENVIRONMENT); + dao.insertResourceGroup(1, "global", "100%", 100, 100, 100, null, null, null, null, null, null, ENVIRONMENT); + dao.insertResourceGroup(2, "bi", "50%", 50, 50, 50, null, null, null, null, null, 1L, ENVIRONMENT); List records = dao.getResourceGroups(ENVIRONMENT); assertEquals(records.size(), 2); - map.put(1L, new ResourceGroupSpecBuilder(1, new ResourceGroupNameTemplate("global"), "100%", 100, Optional.of(100), 100, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), null)); - map.put(2L, new ResourceGroupSpecBuilder(2, new ResourceGroupNameTemplate("bi"), "50%", 50, Optional.of(50), 50, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(1L))); + map.put(1L, new ResourceGroupSpecBuilder(1, new ResourceGroupNameTemplate("global"), "100%", 100, Optional.of(100), 100, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), null)); + map.put(2L, new ResourceGroupSpecBuilder(2, new ResourceGroupNameTemplate("bi"), "50%", 50, Optional.of(50), 50, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(1L))); compareResourceGroups(map, records); } private static void testResourceGroupUpdate(H2ResourceGroupsDao dao, Map map) { - dao.updateResourceGroup(2, "bi", "40%", 40, 30, 30, null, null, true, null, null, null, null, 1L, ENVIRONMENT); - ResourceGroupSpecBuilder updated = new ResourceGroupSpecBuilder(2, new ResourceGroupNameTemplate("bi"), "40%", 40, Optional.of(30), 30, Optional.empty(), Optional.empty(), Optional.of(true), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(1L)); + dao.updateResourceGroup(2, "bi", "40%", 40, 30, 30, null, null, true, null, null, 1L, ENVIRONMENT); + ResourceGroupSpecBuilder updated = new ResourceGroupSpecBuilder(2, new ResourceGroupNameTemplate("bi"), "40%", 40, Optional.of(30), 30, Optional.empty(), Optional.empty(), Optional.of(true), Optional.empty(), Optional.empty(), Optional.of(1L)); map.put(2L, updated); compareResourceGroups(map, dao.getResourceGroups(ENVIRONMENT)); } @@ -146,10 +146,10 @@ private static void testSelectorInsert(H2ResourceGroupsDao dao, Map com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-server-rpm @@ -15,9 +15,9 @@ ${project.parent.basedir} + true true true - true true presto-server-${project.version} @@ -98,8 +98,13 @@ /usr/sbin/groupadd + /usr/bin/uuidgen + + + /usr/bin/sudo + diff --git a/presto-server-rpm/src/main/resources/dist/config/log.properties b/presto-server-rpm/src/main/resources/dist/config/log.properties new file mode 100644 index 0000000000000..41e1172211891 --- /dev/null +++ b/presto-server-rpm/src/main/resources/dist/config/log.properties @@ -0,0 +1,2 @@ +# Enable verbose logging from Presto +#com.facebook.presto=DEBUG diff --git a/presto-server-rpm/src/main/resources/dist/etc/init.d/presto b/presto-server-rpm/src/main/resources/dist/etc/init.d/presto index 4dcd311f54ca4..4575a5b5daed1 100644 --- a/presto-server-rpm/src/main/resources/dist/etc/init.d/presto +++ b/presto-server-rpm/src/main/resources/dist/etc/init.d/presto @@ -24,9 +24,9 @@ SERVICE_USER='presto' # Launcher Config. # Use data-dir from node.properties file (assumes it to be at /etc/presto). For other args use defaults from rpm install NODE_PROPERTIES=/etc/presto/node.properties -DATA_DIR=$(grep -Po "(?<=^node.data-dir=).*" $NODE_PROPERTIES) -SERVER_LOG_FILE=$(grep -Po "(?<=^node.server-log-file=).*" $NODE_PROPERTIES) -LAUNCHER_LOG_FILE=$(grep -Po "(?<=^node.launcher-log-file=).*" $NODE_PROPERTIES) +DATA_DIR=$(grep -Po "(?<=^node.data-dir=).*" $NODE_PROPERTIES | tail -n 1) +SERVER_LOG_FILE=$(grep -Po "(?<=^node.server-log-file=).*" $NODE_PROPERTIES | tail -n 1) +LAUNCHER_LOG_FILE=$(grep -Po "(?<=^node.launcher-log-file=).*" $NODE_PROPERTIES | tail -n 1) CONFIGURATION=(--launcher-config /usr/lib/presto/bin/launcher.properties --data-dir "$DATA_DIR" --node-config "$NODE_PROPERTIES" --jvm-config /etc/presto/jvm.config --config /etc/presto/config.properties --launcher-log-file "${LAUNCHER_LOG_FILE:-/var/log/presto/launcher.log}" --server-log-file "${SERVER_LOG_FILE:-/var/log/presto/server.log}") source /etc/presto/env.sh diff --git a/presto-server-rpm/src/main/rpm/preinstall b/presto-server-rpm/src/main/rpm/preinstall index 814e8daef5813..98584416b537d 100644 --- a/presto-server-rpm/src/main/rpm/preinstall +++ b/presto-server-rpm/src/main/rpm/preinstall @@ -9,14 +9,6 @@ java_version() { "$JAVA" -version 2>&1 | grep "\(java\|openjdk\) version" | awk '{ print substr($3, 2, length($3)-2); }' } -java_vendor() { -# The one argument is the location of java (either $JAVA_HOME or a potential -# candidate for JAVA_HOME). -# Returns the java vendor name. eg: Oracle Corporation - JAVA="$1"/bin/java - "$JAVA" -XshowSettings:properties -version 2>&1 | grep "java.vendor =" | awk '{ print $3 " " $4; }' -} - check_if_correct_java_version() { # If the string is empty return non-zero code. We don't want false positives if /bin/java is @@ -29,9 +21,8 @@ check_if_correct_java_version() { # The one argument is the location of java (either $JAVA_HOME or a potential # candidate for JAVA_HOME). JAVA_VERSION=$(java_version "$1") - JAVA_VENDOR=$(java_vendor "$1") JAVA_UPDATE=$(echo $JAVA_VERSION | cut -d'_' -f2) - if [[ ("$JAVA_VERSION" > "1.8") && ($JAVA_UPDATE -ge 92) && ("$JAVA_VENDOR" = "Oracle Corporation") ]]; then + if [[ ("$JAVA_VERSION" > "1.8") && ($JAVA_UPDATE -ge 151) ]]; then echo "JAVA8_HOME=$1" > /tmp/presto_env.sh return 0 else @@ -39,7 +30,7 @@ check_if_correct_java_version() { fi } -# if Java version of $JAVA_HOME is not 1.8 update 92 (8u92) and is not Oracle Java, then try to find it again below +# if Java version of $JAVA_HOME is not 1.8 update 151 (8u151) and is not Oracle Java, then try to find it again below if ! check_if_correct_java_version "$JAVA8_HOME" && ! check_if_correct_java_version "$JAVA_HOME"; then java_found=false for candidate in \ @@ -71,7 +62,7 @@ if [ "$java_found" = false ]; then | Please download the latest Oracle JDK/JRE from the Java web site | | > http://www.oracle.com/technetwork/java/javase/downloads < | | | -| Presto requires Java 1.8 update 92 (8u92) | +| Presto requires Java 1.8 update 151 (8u151) | | NOTE: This script will attempt to find Java whether you install | | using the binary or the RPM based installer. | +======================================================================+ diff --git a/presto-server/README.txt b/presto-server/README.txt index 46017e937c7bb..94715bb0c2f8b 100644 --- a/presto-server/README.txt +++ b/presto-server/README.txt @@ -2,4 +2,4 @@ Presto is a distributed SQL query engine. Please see the website for installation instructions: -https://prestodb.io/ +https://prestodb.github.io/ diff --git a/presto-server/pom.xml b/presto-server/pom.xml index 31b36eeac5381..31e6b98688c22 100644 --- a/presto-server/pom.xml +++ b/presto-server/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-server @@ -14,10 +14,11 @@ ${project.parent.basedir} + + true true true true - true com.facebook.presto.server.PrestoServer diff --git a/presto-server/src/main/provisio/presto.xml b/presto-server/src/main/provisio/presto.xml index 24f732fdf08e5..e10e0cdde25a8 100644 --- a/presto-server/src/main/provisio/presto.xml +++ b/presto-server/src/main/provisio/presto.xml @@ -38,6 +38,12 @@ + + + + + + diff --git a/presto-session-property-managers/pom.xml b/presto-session-property-managers/pom.xml new file mode 100644 index 0000000000000..469aa5559b908 --- /dev/null +++ b/presto-session-property-managers/pom.xml @@ -0,0 +1,97 @@ + + + 4.0.0 + + + com.facebook.presto + presto-root + 0.216-SNAPSHOT + + + presto-session-property-managers + Presto - Session Property Managers + presto-plugin + + + ${project.parent.basedir} + + + + + io.airlift + bootstrap + + + + io.airlift + json + + + + io.airlift + configuration + + + + com.google.guava + guava + + + + com.google.inject + guice + + + + javax.inject + javax.inject + + + + javax.validation + validation-api + + + + com.fasterxml.jackson.core + jackson-databind + + + + com.fasterxml.jackson.core + jackson-core + + + + + com.facebook.presto + presto-spi + provided + + + + io.airlift + units + provided + + + + com.fasterxml.jackson.core + jackson-annotations + provided + + + + + org.testng + testng + test + + + + io.airlift + testing + test + + + diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManager.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManager.java new file mode 100644 index 0000000000000..65ef81a51a5e6 --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManager.java @@ -0,0 +1,98 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.facebook.presto.spi.session.SessionConfigurationContext; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManager; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; +import com.google.common.collect.ImmutableMap; +import io.airlift.json.JsonCodec; +import io.airlift.json.JsonCodecFactory; +import io.airlift.json.ObjectMapperProvider; + +import javax.inject.Inject; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class FileSessionPropertyManager + implements SessionPropertyConfigurationManager +{ + public static final JsonCodec> CODEC = new JsonCodecFactory( + () -> new ObjectMapperProvider().get().enable(FAIL_ON_UNKNOWN_PROPERTIES)) + .listJsonCodec(SessionMatchSpec.class); + + private final List sessionMatchSpecs; + + @Inject + public FileSessionPropertyManager(FileSessionPropertyManagerConfig config) + { + requireNonNull(config, "config is null"); + + Path configurationFile = config.getConfigFile().toPath(); + try { + sessionMatchSpecs = CODEC.fromJson(Files.readAllBytes(configurationFile)); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + catch (IllegalArgumentException e) { + Throwable cause = e.getCause(); + if (cause instanceof UnrecognizedPropertyException) { + UnrecognizedPropertyException ex = (UnrecognizedPropertyException) cause; + String message = format("Unknown property at line %s:%s: %s", + ex.getLocation().getLineNr(), + ex.getLocation().getColumnNr(), + ex.getPropertyName()); + throw new IllegalArgumentException(message, e); + } + if (cause instanceof JsonMappingException) { + // remove the extra "through reference chain" message + if (cause.getCause() != null) { + cause = cause.getCause(); + } + throw new IllegalArgumentException(cause.getMessage(), e); + } + throw e; + } + } + + @Override + public Map getSystemSessionProperties(SessionConfigurationContext context) + { + // later properties override earlier properties + Map combinedProperties = new HashMap<>(); + for (SessionMatchSpec sessionMatchSpec : sessionMatchSpecs) { + combinedProperties.putAll(sessionMatchSpec.match(context)); + } + + return ImmutableMap.copyOf(combinedProperties); + } + + @Override + public Map> getCatalogSessionProperties(SessionConfigurationContext context) + { + return ImmutableMap.of(); + } +} diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerConfig.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerConfig.java new file mode 100644 index 0000000000000..abd169f27eaf1 --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerConfig.java @@ -0,0 +1,38 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import io.airlift.configuration.Config; + +import javax.validation.constraints.NotNull; + +import java.io.File; + +public class FileSessionPropertyManagerConfig +{ + private File configFile; + + @NotNull + public File getConfigFile() + { + return configFile; + } + + @Config("session-property-manager.config-file") + public FileSessionPropertyManagerConfig setConfigFile(File configFile) + { + this.configFile = configFile; + return this; + } +} diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerFactory.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerFactory.java new file mode 100644 index 0000000000000..80d46e03d1fe9 --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerFactory.java @@ -0,0 +1,56 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.facebook.presto.spi.resourceGroups.SessionPropertyConfigurationManagerContext; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManager; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; +import com.google.inject.Injector; +import io.airlift.bootstrap.Bootstrap; +import io.airlift.json.JsonModule; + +import java.util.Map; + +import static com.google.common.base.Throwables.throwIfUnchecked; + +public class FileSessionPropertyManagerFactory + implements SessionPropertyConfigurationManagerFactory +{ + @Override + public String getName() + { + return "file"; + } + + @Override + public SessionPropertyConfigurationManager create(Map config, SessionPropertyConfigurationManagerContext context) + { + try { + Bootstrap app = new Bootstrap( + new JsonModule(), + new FileSessionPropertyManagerModule()); + + Injector injector = app + .strictConfig() + .doNotInitializeLogging() + .setRequiredConfigurationProperties(config) + .initialize(); + return injector.getInstance(FileSessionPropertyManager.class); + } + catch (Exception e) { + throwIfUnchecked(e); + throw new RuntimeException(e); + } + } +} diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerModule.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerModule.java new file mode 100644 index 0000000000000..205be8f8c18e1 --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerModule.java @@ -0,0 +1,31 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Scopes; + +import static io.airlift.configuration.ConfigBinder.configBinder; + +public class FileSessionPropertyManagerModule + implements Module +{ + @Override + public void configure(Binder binder) + { + configBinder(binder).bindConfig(FileSessionPropertyManagerConfig.class); + binder.bind(FileSessionPropertyManager.class).in(Scopes.SINGLETON); + } +} diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerPlugin.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerPlugin.java new file mode 100644 index 0000000000000..bde86e9400f31 --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/FileSessionPropertyManagerPlugin.java @@ -0,0 +1,28 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.facebook.presto.spi.Plugin; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManagerFactory; +import com.google.common.collect.ImmutableList; + +public class FileSessionPropertyManagerPlugin + implements Plugin +{ + @Override + public Iterable getSessionPropertyConfigurationManagerFactories() + { + return ImmutableList.of(new FileSessionPropertyManagerFactory()); + } +} diff --git a/presto-session-property-managers/src/main/java/com/facebook/presto/session/SessionMatchSpec.java b/presto-session-property-managers/src/main/java/com/facebook/presto/session/SessionMatchSpec.java new file mode 100644 index 0000000000000..4c6882793d74d --- /dev/null +++ b/presto-session-property-managers/src/main/java/com/facebook/presto/session/SessionMatchSpec.java @@ -0,0 +1,123 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.facebook.presto.spi.session.SessionConfigurationContext; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; + +import static java.util.Objects.requireNonNull; + +public class SessionMatchSpec +{ + private final Optional userRegex; + private final Optional sourceRegex; + private final Set clientTags; + private final Optional queryType; + private final Optional resourceGroupRegex; + private final Map sessionProperties; + + @JsonCreator + public SessionMatchSpec( + @JsonProperty("user") Optional userRegex, + @JsonProperty("source") Optional sourceRegex, + @JsonProperty("clientTags") Optional> clientTags, + @JsonProperty("queryType") Optional queryType, + @JsonProperty("group") Optional resourceGroupRegex, + @JsonProperty("sessionProperties") Map sessionProperties) + { + this.userRegex = requireNonNull(userRegex, "userRegex is null"); + this.sourceRegex = requireNonNull(sourceRegex, "sourceRegex is null"); + requireNonNull(clientTags, "clientTags is null"); + this.clientTags = ImmutableSet.copyOf(clientTags.orElse(ImmutableList.of())); + this.queryType = requireNonNull(queryType, "queryType is null"); + this.resourceGroupRegex = requireNonNull(resourceGroupRegex, "resourceGroupRegex is null"); + requireNonNull(sessionProperties, "sessionProperties is null"); + this.sessionProperties = ImmutableMap.copyOf(sessionProperties); + } + + public Map match(SessionConfigurationContext context) + { + if (userRegex.isPresent() && !userRegex.get().matcher(context.getUser()).matches()) { + return ImmutableMap.of(); + } + if (sourceRegex.isPresent()) { + String source = context.getSource().orElse(""); + if (!sourceRegex.get().matcher(source).matches()) { + return ImmutableMap.of(); + } + } + if (!clientTags.isEmpty() && !context.getClientTags().containsAll(clientTags)) { + return ImmutableMap.of(); + } + + if (queryType.isPresent()) { + String contextQueryType = context.getQueryType().orElse(""); + if (!queryType.get().equalsIgnoreCase(contextQueryType)) { + return ImmutableMap.of(); + } + } + + if (resourceGroupRegex.isPresent() && !resourceGroupRegex.get().matcher(context.getResourceGroupId().toString()).matches()) { + return ImmutableMap.of(); + } + + return sessionProperties; + } + + @JsonProperty("user") + public Optional getUserRegex() + { + return userRegex; + } + + @JsonProperty("source") + public Optional getSourceRegex() + { + return sourceRegex; + } + + @JsonProperty + public Set getClientTags() + { + return clientTags; + } + + @JsonProperty + public Optional getQueryType() + { + return queryType; + } + + @JsonProperty("group") + public Optional getResourceGroupRegex() + { + return resourceGroupRegex; + } + + @JsonProperty + public Map getSessionProperties() + { + return sessionProperties; + } +} diff --git a/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManager.java b/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManager.java new file mode 100644 index 0000000000000..4ab299e6f8a10 --- /dev/null +++ b/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManager.java @@ -0,0 +1,126 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.facebook.presto.session; + +import com.facebook.presto.spi.resourceGroups.QueryType; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.spi.session.SessionConfigurationContext; +import com.facebook.presto.spi.session.SessionPropertyConfigurationManager; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import io.airlift.testing.TempFile; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; + +import static com.facebook.presto.session.FileSessionPropertyManager.CODEC; +import static org.testng.Assert.assertEquals; + +public class TestFileSessionPropertyManager +{ + private static final SessionConfigurationContext CONTEXT = new SessionConfigurationContext( + "user", + Optional.of("source"), + ImmutableSet.of("tag1", "tag2"), + Optional.of(QueryType.DATA_DEFINITION.toString()), + new ResourceGroupId(ImmutableList.of("global", "pipeline", "user_foo", "bar"))); + + @Test + public void testResourceGroupMatch() + throws IOException + { + Map properties = ImmutableMap.of("PROPERTY1", "VALUE1", "PROPERTY2", "VALUE2"); + SessionMatchSpec spec = new SessionMatchSpec( + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(Pattern.compile("global.pipeline.user_.*")), + properties); + + assertProperties(properties, spec); + } + + @Test + public void testClientTagMatch() + throws IOException + { + ImmutableMap properties = ImmutableMap.of("PROPERTY", "VALUE"); + SessionMatchSpec spec = new SessionMatchSpec( + Optional.empty(), + Optional.empty(), + Optional.of(ImmutableList.of("tag2")), + Optional.empty(), + Optional.empty(), + properties); + + assertProperties(properties, spec); + } + + @Test + public void testMultipleMatch() + throws IOException + { + SessionMatchSpec spec1 = new SessionMatchSpec( + Optional.empty(), + Optional.empty(), + Optional.of(ImmutableList.of("tag2")), + Optional.empty(), + Optional.empty(), + ImmutableMap.of("PROPERTY1", "VALUE1")); + SessionMatchSpec spec2 = new SessionMatchSpec( + Optional.empty(), + Optional.empty(), + Optional.of(ImmutableList.of("tag1", "tag2")), + Optional.empty(), + Optional.empty(), + ImmutableMap.of("PROPERTY1", "VALUE1", "PROPERTY2", "VALUE2")); + + assertProperties(ImmutableMap.of("PROPERTY1", "VALUE1", "PROPERTY2", "VALUE2"), spec1, spec2); + } + + @Test + public void testNoMatch() + throws IOException + { + SessionMatchSpec spec = new SessionMatchSpec( + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.empty(), + Optional.of(Pattern.compile("global.interactive.user_.*")), + ImmutableMap.of("PROPERTY", "VALUE")); + + assertProperties(ImmutableMap.of(), spec); + } + + private static void assertProperties(Map properties, SessionMatchSpec... spec) + throws IOException + { + try (TempFile tempFile = new TempFile()) { + Path configurationFile = tempFile.path(); + Files.write(configurationFile, CODEC.toJsonBytes(Arrays.asList(spec))); + SessionPropertyConfigurationManager manager = new FileSessionPropertyManager(new FileSessionPropertyManagerConfig().setConfigFile(configurationFile.toFile())); + assertEquals(manager.getSystemSessionProperties(CONTEXT), properties); + } + } +} diff --git a/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManagerConfig.java b/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManagerConfig.java new file mode 100644 index 0000000000000..9d5f13dae78ab --- /dev/null +++ b/presto-session-property-managers/src/test/java/com/facebook/presto/session/TestFileSessionPropertyManagerConfig.java @@ -0,0 +1,47 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.session; + +import com.google.common.collect.ImmutableMap; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.Map; + +import static io.airlift.configuration.testing.ConfigAssertions.assertFullMapping; +import static io.airlift.configuration.testing.ConfigAssertions.assertRecordedDefaults; +import static io.airlift.configuration.testing.ConfigAssertions.recordDefaults; + +public class TestFileSessionPropertyManagerConfig +{ + @Test + public void testDefaults() + { + assertRecordedDefaults(recordDefaults(FileSessionPropertyManagerConfig.class) + .setConfigFile(null)); + } + + @Test + public void testExplicitPropertyMappings() + { + Map properties = new ImmutableMap.Builder() + .put("session-property-manager.config-file", "/test.json") + .build(); + + FileSessionPropertyManagerConfig expected = new FileSessionPropertyManagerConfig() + .setConfigFile(new File("/test.json")); + + assertFullMapping(properties, expected); + } +} diff --git a/presto-spi/pom.xml b/presto-spi/pom.xml index 268f0ade4dbf6..702b7a32acaa9 100644 --- a/presto-spi/pom.xml +++ b/presto-spi/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-spi @@ -97,5 +97,11 @@ json test + + + org.assertj + assertj-core + test + diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ColumnMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/ColumnMetadata.java index 5fac585cd8794..080565e2bdc8d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ColumnMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ColumnMetadata.java @@ -19,9 +19,11 @@ import java.util.Map; import java.util.Objects; +import static com.facebook.presto.spi.SchemaUtil.checkNotEmpty; import static java.util.Collections.emptyMap; import static java.util.Collections.unmodifiableMap; import static java.util.Locale.ENGLISH; +import static java.util.Objects.requireNonNull; public class ColumnMetadata { @@ -49,15 +51,9 @@ public ColumnMetadata(String name, Type type, String comment, String extraInfo, public ColumnMetadata(String name, Type type, String comment, String extraInfo, boolean hidden, Map properties) { - if (name == null || name.isEmpty()) { - throw new NullPointerException("name is null or empty"); - } - if (type == null) { - throw new NullPointerException("type is null"); - } - if (properties == null) { - throw new NullPointerException("properties is null"); - } + checkNotEmpty(name, "name"); + requireNonNull(type, "type is null"); + requireNonNull(properties, "properties is null"); this.name = name.toLowerCase(ENGLISH); this.type = type; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ColumnNotFoundException.java b/presto-spi/src/main/java/com/facebook/presto/spi/ColumnNotFoundException.java index 26b09fbcd5046..33af4f93758b1 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ColumnNotFoundException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ColumnNotFoundException.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.spi; +import static java.util.Objects.requireNonNull; + public class ColumnNotFoundException extends NotFoundException { @@ -27,14 +29,8 @@ public ColumnNotFoundException(SchemaTableName tableName, String columnName) public ColumnNotFoundException(SchemaTableName tableName, String columnName, String message) { super(message); - if (tableName == null) { - throw new NullPointerException("tableName is null"); - } - if (columnName == null) { - throw new NullPointerException("columnName is null"); - } - this.tableName = tableName; - this.columnName = columnName; + this.tableName = requireNonNull(tableName, "tableName is null"); + this.columnName = requireNonNull(columnName, "columnName is null"); } public ColumnNotFoundException(SchemaTableName tableName, String columnName, Throwable cause) @@ -45,14 +41,8 @@ public ColumnNotFoundException(SchemaTableName tableName, String columnName, Thr public ColumnNotFoundException(SchemaTableName tableName, String columnName, String message, Throwable cause) { super(message, cause); - if (tableName == null) { - throw new NullPointerException("tableName is null"); - } - if (columnName == null) { - throw new NullPointerException("columnName is null"); - } - this.tableName = tableName; - this.columnName = columnName; + this.tableName = requireNonNull(tableName, "tableName is null"); + this.columnName = requireNonNull(columnName, "columnName is null"); } public SchemaTableName getTableName() diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorPageSink.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorPageSink.java index 0ffc6022815d9..9113e0287e597 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorPageSink.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorPageSink.java @@ -42,6 +42,17 @@ default long getSystemMemoryUsage() return 0; } + /** + * ConnectorPageSink can provide optional validation to check + * the data is correctly consumed by connector (e.g. output table has the correct data). + *

    + * This method returns the CPU spent on validation, if any. + */ + default long getValidationCpuNanos() + { + return 0; + } + /** * Returns a future that will be completed when the page sink can accept * more pages. If the page sink can accept more pages immediately, diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSession.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSession.java index bfeebff97e539..7f430e40815a4 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSession.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSession.java @@ -30,8 +30,6 @@ default String getUser() return getIdentity().getUser(); } - String getPath(); - Identity getIdentity(); TimeZoneKey getTimeZoneKey(); @@ -45,8 +43,5 @@ default String getUser() @Deprecated boolean isLegacyTimestamp(); - @Deprecated - boolean isLegacyRoundNBigint(); - T getProperty(String name, Class type); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSplitSource.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSplitSource.java index 0510177b5fe92..dfdf9a43109c7 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSplitSource.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorSplitSource.java @@ -31,7 +31,7 @@ public interface ConnectorSplitSource /** * Returns whether any more {@link ConnectorSplit} may be produced. - * + *

    * This method should only be called when there has been no invocation of getNextBatch, * or result Future of previous getNextBatch is done. * Calling this method at other time is not useful because the contract of such an invocation diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableLayout.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableLayout.java index d99780eafeda1..4d22497d38c70 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableLayout.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableLayout.java @@ -84,8 +84,11 @@ public Optional> getColumns() } /** - * A predicate that describes the universe of data in this layout. It may be used by the query engine to - * infer additional properties and perform further optimizations + * A TupleDomain that represents a predicate that every row this TableScan node + * produces is guaranteed to satisfy. + *

    + * This guarantee can have different origins. + * For example, it may be successful predicate push down, or inherent guarantee provided by the underlying data. */ public TupleDomain getPredicate() { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableMetadata.java index babd27efa73f0..bce4941af0c47 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConnectorTableMetadata.java @@ -21,6 +21,7 @@ import java.util.Optional; import static java.util.Collections.emptyMap; +import static java.util.Objects.requireNonNull; public class ConnectorTableMetadata { @@ -41,15 +42,9 @@ public ConnectorTableMetadata(SchemaTableName table, List column public ConnectorTableMetadata(SchemaTableName table, List columns, Map properties, Optional comment) { - if (table == null) { - throw new NullPointerException("table is null or empty"); - } - if (columns == null) { - throw new NullPointerException("columns is null"); - } - if (comment == null) { - throw new NullPointerException("comment is null"); - } + requireNonNull(table, "table is null"); + requireNonNull(columns, "columns is null"); + requireNonNull(comment, "comment is null"); this.table = table; this.columns = Collections.unmodifiableList(new ArrayList<>(columns)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/ConstantProperty.java b/presto-spi/src/main/java/com/facebook/presto/spi/ConstantProperty.java index 5c507ed2ca223..a090e927e338f 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/ConstantProperty.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/ConstantProperty.java @@ -55,13 +55,8 @@ public Set getColumns() @Override public Optional> translate(Function> translator) { - Optional translated = translator.apply(column); - - if (translated.isPresent()) { - return Optional.of(new ConstantProperty<>(translated.get())); - } - - return Optional.empty(); + return translator.apply(column) + .map(ConstantProperty::new); } @Override diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/FixedSplitSource.java b/presto-spi/src/main/java/com/facebook/presto/spi/FixedSplitSource.java index 73b4d1954ba2a..848416b408bab 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/FixedSplitSource.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/FixedSplitSource.java @@ -21,6 +21,7 @@ import java.util.concurrent.CompletableFuture; import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.CompletableFuture.completedFuture; public class FixedSplitSource @@ -31,9 +32,7 @@ public class FixedSplitSource public FixedSplitSource(Iterable splits) { - if (splits == null) { - throw new NullPointerException("splits is null"); - } + requireNonNull(splits, "splits is null"); List splitsList = new ArrayList<>(); for (ConnectorSplit split : splits) { splitsList.add(split); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/HostAddress.java b/presto-spi/src/main/java/com/facebook/presto/spi/HostAddress.java index 377352ab76cfd..05cc24815e983 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/HostAddress.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/HostAddress.java @@ -23,6 +23,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static java.util.Objects.requireNonNull; + /** * An immutable representation of a host and port. *

    @@ -158,9 +160,7 @@ public static HostAddress fromParts(String host, int port) @JsonCreator public static HostAddress fromString(String hostPortString) { - if (hostPortString == null) { - throw new NullPointerException("hostPortString is null"); - } + requireNonNull(hostPortString, "hostPortString is null"); String host; String portString = null; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/InMemoryRecordSet.java b/presto-spi/src/main/java/com/facebook/presto/spi/InMemoryRecordSet.java index 6ae43f7b4beb7..e2dd8bd74166f 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/InMemoryRecordSet.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/InMemoryRecordSet.java @@ -34,6 +34,7 @@ import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE; import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; +import static java.util.Objects.requireNonNull; public class InMemoryRecordSet implements RecordSet @@ -108,7 +109,7 @@ record = records.next(); public boolean getBoolean(int field) { checkState(record != null, "no current record"); - checkNotNull(record.get(field), "value is null"); + requireNonNull(record.get(field), "value is null"); return (Boolean) record.get(field); } @@ -116,7 +117,7 @@ public boolean getBoolean(int field) public long getLong(int field) { checkState(record != null, "no current record"); - checkNotNull(record.get(field), "value is null"); + requireNonNull(record.get(field), "value is null"); return ((Number) record.get(field)).longValue(); } @@ -124,7 +125,7 @@ public long getLong(int field) public double getDouble(int field) { checkState(record != null, "no current record"); - checkNotNull(record.get(field), "value is null"); + requireNonNull(record.get(field), "value is null"); return (Double) record.get(field); } @@ -133,7 +134,7 @@ public Slice getSlice(int field) { checkState(record != null, "no current record"); Object value = record.get(field); - checkNotNull(value, "value is null"); + requireNonNull(value, "value is null"); if (value instanceof byte[]) { return Slices.wrappedBuffer((byte[]) value); } @@ -151,7 +152,7 @@ public Object getObject(int field) { checkState(record != null, "no current record"); Object value = record.get(field); - checkNotNull(value, "value is null"); + requireNonNull(value, "value is null"); return value; } @@ -194,14 +195,14 @@ public static class Builder private Builder(Collection types) { - checkNotNull(types, "types is null"); + requireNonNull(types, "types is null"); this.types = Collections.unmodifiableList(new ArrayList<>(types)); checkArgument(!this.types.isEmpty(), "types is empty"); } public Builder addRow(Object... values) { - checkNotNull(values, "values is null"); + requireNonNull(values, "values is null"); checkArgument(values.length == types.size(), "Expected %s values in row, but got %s values", types.size(), values.length); for (int i = 0; i < values.length; i++) { Object value = values[i]; @@ -257,13 +258,6 @@ private static void checkArgument(boolean test, String message, Object... args) } } - private static void checkNotNull(Object value, String message) - { - if (value == null) { - throw new NullPointerException(message); - } - } - private static void checkState(boolean test, String message) { if (!test) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/NotFoundException.java b/presto-spi/src/main/java/com/facebook/presto/spi/NotFoundException.java index 38a598c91d8ad..b1767d600a648 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/NotFoundException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/NotFoundException.java @@ -37,9 +37,4 @@ protected NotFoundException(String message, Throwable cause) { super(NOT_FOUND, message, cause); } - - protected NotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) - { - super(NOT_FOUND, message, cause, enableSuppression, writableStackTrace); - } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/Page.java b/presto-spi/src/main/java/com/facebook/presto/spi/Page.java index 8f9a590ca11ae..dab1380e16988 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/Page.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/Page.java @@ -40,6 +40,7 @@ public class Page private final int positionCount; private final AtomicLong sizeInBytes = new AtomicLong(-1); private final AtomicLong retainedSizeInBytes = new AtomicLong(-1); + private final AtomicLong logicalSizeInBytes = new AtomicLong(-1); public Page(Block... blocks) { @@ -76,6 +77,19 @@ public long getSizeInBytes() return sizeInBytes; } + public long getLogicalSizeInBytes() + { + long size = logicalSizeInBytes.get(); + if (size < 0) { + size = 0; + for (Block block : blocks) { + size += block.getLogicalSizeInBytes(); + } + logicalSizeInBytes.set(size); + } + return size; + } + public long getRetainedSizeInBytes() { if (retainedSizeInBytes.get() < 0) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/PrestoException.java b/presto-spi/src/main/java/com/facebook/presto/spi/PrestoException.java index a3e7cacfebabd..70ac66c217206 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/PrestoException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/PrestoException.java @@ -34,12 +34,6 @@ public PrestoException(ErrorCodeSupplier errorCodeSupplier, String message, Thro this.errorCode = errorCodeSupplier.toErrorCode(); } - public PrestoException(ErrorCodeSupplier errorCodeSupplier, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) - { - super(message, cause, enableSuppression, writableStackTrace); - this.errorCode = errorCodeSupplier.toErrorCode(); - } - public ErrorCode getErrorCode() { return errorCode; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/PrestoWarning.java b/presto-spi/src/main/java/com/facebook/presto/spi/PrestoWarning.java new file mode 100644 index 0000000000000..d5d7ddabf894d --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/PrestoWarning.java @@ -0,0 +1,79 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public final class PrestoWarning +{ + private final WarningCode warningCode; + private final String message; + + @JsonCreator + public PrestoWarning( + @JsonProperty("warningCode") WarningCode warningCode, + @JsonProperty("message") String message) + { + this.warningCode = requireNonNull(warningCode, "warningCode is null"); + this.message = requireNonNull(message, "message is null"); + } + + public PrestoWarning(WarningCodeSupplier warningCodeSupplier, String message) + { + this(requireNonNull(warningCodeSupplier, "warningCodeSupplier is null").toWarningCode(), message); + } + + @JsonProperty + public WarningCode getWarningCode() + { + return warningCode; + } + + @JsonProperty + public String getMessage() + { + return message; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PrestoWarning that = (PrestoWarning) o; + return Objects.equals(warningCode, that.warningCode) && Objects.equals(message, that.message); + } + + @Override + public int hashCode() + { + return Objects.hash(warningCode, message); + } + + @Override + public String toString() + { + return format("%s, %s", warningCode, message); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/SchemaNotFoundException.java b/presto-spi/src/main/java/com/facebook/presto/spi/SchemaNotFoundException.java index ff1ad8493bbbf..2efb49218cb8a 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/SchemaNotFoundException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/SchemaNotFoundException.java @@ -13,6 +13,8 @@ */ package com.facebook.presto.spi; +import static java.util.Objects.requireNonNull; + public class SchemaNotFoundException extends NotFoundException { @@ -26,10 +28,7 @@ public SchemaNotFoundException(String schemaName) public SchemaNotFoundException(String schemaName, String message) { super(message); - if (schemaName == null) { - throw new NullPointerException("schemaName is null"); - } - this.schemaName = schemaName; + this.schemaName = requireNonNull(schemaName, "schemaName is null"); } public SchemaNotFoundException(String schemaName, Throwable cause) @@ -40,10 +39,7 @@ public SchemaNotFoundException(String schemaName, Throwable cause) public SchemaNotFoundException(String schemaName, String message, Throwable cause) { super(message, cause); - if (schemaName == null) { - throw new NullPointerException("schemaName is null"); - } - this.schemaName = schemaName; + this.schemaName = requireNonNull(schemaName, "schemaName is null"); } public String getSchemaName() diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/SortingProperty.java b/presto-spi/src/main/java/com/facebook/presto/spi/SortingProperty.java index c29189a12c61d..b46a4a48678c9 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/SortingProperty.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/SortingProperty.java @@ -72,13 +72,8 @@ public SortOrder getOrder() @Override public Optional> translate(Function> translator) { - Optional translated = translator.apply(column); - - if (translated.isPresent()) { - return Optional.of(new SortingProperty<>(translated.get(), order)); - } - - return Optional.empty(); + return translator.apply(column) + .map(translated -> new SortingProperty<>(translated, order)); } @Override diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java b/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java index b5b55e83bc6f4..dad56cb675b61 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/StandardErrorCode.java @@ -61,6 +61,7 @@ public enum StandardErrorCode ADMINISTRATIVELY_KILLED(0x0000_0026, USER_ERROR), INVALID_COLUMN_PROPERTY(0x0000_0027, USER_ERROR), QUERY_HAS_TOO_MANY_STAGES(0x0000_0028, USER_ERROR), + INVALID_SPATIAL_PARTITIONING(0x0000_0029, USER_ERROR), GENERIC_INTERNAL_ERROR(0x0001_0000, INTERNAL_ERROR), TOO_MANY_REQUESTS_FAILED(0x0001_0001, INTERNAL_ERROR), @@ -87,14 +88,17 @@ public enum StandardErrorCode REMOTE_HOST_GONE(0x0001_0016, INTERNAL_ERROR), CONFIGURATION_INVALID(0x0001_0017, INTERNAL_ERROR), CONFIGURATION_UNAVAILABLE(0x0001_0018, INTERNAL_ERROR), + INVALID_RESOURCE_GROUP(0x0001_0019, INTERNAL_ERROR), GENERIC_INSUFFICIENT_RESOURCES(0x0002_0000, INSUFFICIENT_RESOURCES), - EXCEEDED_MEMORY_LIMIT(0x0002_0001, INSUFFICIENT_RESOURCES), + EXCEEDED_GLOBAL_MEMORY_LIMIT(0x0002_0001, INSUFFICIENT_RESOURCES), QUERY_QUEUE_FULL(0x0002_0002, INSUFFICIENT_RESOURCES), EXCEEDED_TIME_LIMIT(0x0002_0003, INSUFFICIENT_RESOURCES), CLUSTER_OUT_OF_MEMORY(0x0002_0004, INSUFFICIENT_RESOURCES), EXCEEDED_CPU_LIMIT(0x0002_0005, INSUFFICIENT_RESOURCES), EXCEEDED_SPILL_LIMIT(0x0002_0006, INSUFFICIENT_RESOURCES), + EXCEEDED_LOCAL_MEMORY_LIMIT(0x0002_0007, INSUFFICIENT_RESOURCES), + ADMINISTRATIVELY_PREEMPTED(0x0002_0008, INSUFFICIENT_RESOURCES), /**/; // Connectors can use error codes starting at the range 0x0100_0000 diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/TableNotFoundException.java b/presto-spi/src/main/java/com/facebook/presto/spi/TableNotFoundException.java index 4142d7a97b2a9..aada58f369634 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/TableNotFoundException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/TableNotFoundException.java @@ -14,6 +14,7 @@ package com.facebook.presto.spi; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; public class TableNotFoundException extends NotFoundException @@ -28,10 +29,7 @@ public TableNotFoundException(SchemaTableName tableName) public TableNotFoundException(SchemaTableName tableName, String message) { super(message); - if (tableName == null) { - throw new NullPointerException("tableName is null"); - } - this.tableName = tableName; + this.tableName = requireNonNull(tableName, "tableName is null"); } public TableNotFoundException(SchemaTableName tableName, Throwable cause) @@ -42,10 +40,7 @@ public TableNotFoundException(SchemaTableName tableName, Throwable cause) public TableNotFoundException(SchemaTableName tableName, String message, Throwable cause) { super(message, cause); - if (tableName == null) { - throw new NullPointerException("tableName is null"); - } - this.tableName = tableName; + this.tableName = requireNonNull(tableName, "tableName is null"); } public SchemaTableName getTableName() diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/WarningCode.java b/presto-spi/src/main/java/com/facebook/presto/spi/WarningCode.java new file mode 100644 index 0000000000000..9f866ac893d4d --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/WarningCode.java @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class WarningCode +{ + private final int code; + private final String name; + + @JsonCreator + public WarningCode( + @JsonProperty("code") int code, + @JsonProperty("name") String name) + { + if (code < 0) { + throw new IllegalArgumentException("code is negative"); + } + this.code = code; + this.name = requireNonNull(name, "name is null"); + } + + @JsonProperty + public int getCode() + { + return code; + } + + @JsonProperty + public String getName() + { + return name; + } + + @Override + public String toString() + { + return name + ":" + code; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + WarningCode that = (WarningCode) obj; + return this.code == that.code && Objects.equals(this.name, that.name); + } + + @Override + public int hashCode() + { + return Objects.hash(code, name); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/WarningCodeSupplier.java b/presto-spi/src/main/java/com/facebook/presto/spi/WarningCodeSupplier.java new file mode 100644 index 0000000000000..e04f473730c82 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/WarningCodeSupplier.java @@ -0,0 +1,19 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi; + +public interface WarningCodeSupplier +{ + WarningCode toWarningCode(); +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractArrayBlock.java index 647a4464eb955..665772583ee06 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractArrayBlock.java @@ -13,8 +13,11 @@ */ package com.facebook.presto.spi.block; +import javax.annotation.Nullable; + import static com.facebook.presto.spi.block.ArrayBlock.createArrayBlockInternal; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; +import static com.facebook.presto.spi.block.BlockUtil.checkValidPositions; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; import static com.facebook.presto.spi.block.BlockUtil.compactOffsets; @@ -28,6 +31,7 @@ public abstract class AbstractArrayBlock protected abstract int getOffsetBase(); + @Nullable protected abstract boolean[] getValueIsNull(); int getOffset(int position) @@ -100,6 +104,25 @@ public long getRegionSizeInBytes(int position, int length) return getRawElementBlock().getRegionSizeInBytes(valueStart, valueEnd - valueStart) + ((Integer.BYTES + Byte.BYTES) * (long) length); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + checkValidPositions(positions, getPositionCount()); + boolean[] used = new boolean[getRawElementBlock().getPositionCount()]; + int usedPositionCount = 0; + for (int i = 0; i < positions.length; ++i) { + if (positions[i]) { + usedPositionCount++; + int valueStart = getOffsets()[getOffsetBase() + i]; + int valueEnd = getOffsets()[getOffsetBase() + i + 1]; + for (int j = valueStart; j < valueEnd; ++j) { + used[j] = true; + } + } + } + return getRawElementBlock().getPositionsSizeInBytes(used) + ((Integer.BYTES + Byte.BYTES) * (long) usedPositionCount); + } + @Override public Block copyRegion(int position, int length) { @@ -111,9 +134,10 @@ public Block copyRegion(int position, int length) Block newValues = getRawElementBlock().copyRegion(startValueOffset, endValueOffset - startValueOffset); int[] newOffsets = compactOffsets(getOffsets(), position + getOffsetBase(), length); - boolean[] newValueIsNull = compactArray(getValueIsNull(), position + getOffsetBase(), length); + boolean[] valueIsNull = getValueIsNull(); + boolean[] newValueIsNull = valueIsNull == null ? null : compactArray(valueIsNull, position + getOffsetBase(), length); - if (newValues == getRawElementBlock() && newOffsets == getOffsets() && newValueIsNull == getValueIsNull()) { + if (newValues == getRawElementBlock() && newOffsets == getOffsets() && newValueIsNull == valueIsNull) { return this; } return createArrayBlockInternal(0, length, newValueIsNull, newOffsets, newValues); @@ -180,7 +204,8 @@ public long getEstimatedDataSizeForStats(int position) public boolean isNull(int position) { checkReadablePosition(position); - return getValueIsNull()[position + getOffsetBase()]; + boolean[] valueIsNull = getValueIsNull(); + return valueIsNull == null ? false : valueIsNull[position + getOffsetBase()]; } public T apply(ArrayBlockFunction function, int position) diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractFixedWidthBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractFixedWidthBlock.java index 6cce636d3ad91..99d305faa07f1 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractFixedWidthBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractFixedWidthBlock.java @@ -18,6 +18,7 @@ import io.airlift.slice.XxHash64; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; public abstract class AbstractFixedWidthBlock implements Block @@ -186,6 +187,12 @@ public long getRegionSizeInBytes(int positionOffset, int length) return (fixedSize + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (fixedSize + Byte.BYTES) * (long) countUsedPositions(positions); + } + private int valueOffset(int position) { return position * fixedSize; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractMapBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractMapBlock.java index c280df09422df..8b86570047f0b 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractMapBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractMapBlock.java @@ -16,10 +16,14 @@ import com.facebook.presto.spi.type.Type; +import javax.annotation.Nullable; + import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.Optional; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; +import static com.facebook.presto.spi.block.BlockUtil.checkValidPositions; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; import static com.facebook.presto.spi.block.BlockUtil.compactOffsets; @@ -63,6 +67,7 @@ public AbstractMapBlock(Type keyType, MethodHandle keyNativeHashCode, MethodHand */ protected abstract int getOffsetBase(); + @Nullable protected abstract boolean[] getMapIsNull(); int getOffset(int position) @@ -121,7 +126,7 @@ public Block copyPositions(int[] positions, int offset, int length) Block newKeys = getRawKeyBlock().copyPositions(entriesPositions.elements(), 0, entriesPositions.size()); Block newValues = getRawValueBlock().copyPositions(entriesPositions.elements(), 0, entriesPositions.size()); - return createMapBlockInternal(0, length, newMapIsNull, newOffsets, newKeys, newValues, newHashTable, keyType, keyBlockNativeEquals, keyNativeHashCode); + return createMapBlockInternal(0, length, Optional.of(newMapIsNull), newOffsets, newKeys, newValues, newHashTable, keyType, keyBlockNativeEquals, keyNativeHashCode); } @Override @@ -133,7 +138,7 @@ public Block getRegion(int position, int length) return createMapBlockInternal( position + getOffsetBase(), length, - getMapIsNull(), + Optional.ofNullable(getMapIsNull()), getOffsets(), getRawKeyBlock(), getRawValueBlock(), @@ -159,6 +164,35 @@ public long getRegionSizeInBytes(int position, int length) Integer.BYTES * HASH_MULTIPLIER * (long) entryCount; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + // We can use either the getRegionSizeInBytes or getPositionsSizeInBytes + // from the underlying raw blocks to implement this function. We chose + // getPositionsSizeInBytes with the assumption that constructing a + // positions array is cheaper than calling getRegionSizeInBytes for each + // used position. + int positionCount = getPositionCount(); + checkValidPositions(positions, positionCount); + boolean[] entryPositions = new boolean[getRawKeyBlock().getPositionCount()]; + int usedEntryCount = 0; + int usedPositionCount = 0; + for (int i = 0; i < positions.length; ++i) { + if (positions[i]) { + usedPositionCount++; + int entriesStart = getOffsets()[getOffsetBase() + i]; + int entriesEnd = getOffsets()[getOffsetBase() + i + 1]; + for (int j = entriesStart; j < entriesEnd; j++) { + entryPositions[j] = true; + } + usedEntryCount += (entriesEnd - entriesStart); + } + } + return getRawKeyBlock().getPositionsSizeInBytes(entryPositions) + + getRawValueBlock().getPositionsSizeInBytes(entryPositions) + + (Integer.BYTES + Byte.BYTES) * (long) usedPositionCount + + Integer.BYTES * HASH_MULTIPLIER * (long) usedEntryCount; + } @Override public Block copyRegion(int position, int length) { @@ -171,16 +205,17 @@ public Block copyRegion(int position, int length) Block newValues = getRawValueBlock().copyRegion(startValueOffset, endValueOffset - startValueOffset); int[] newOffsets = compactOffsets(getOffsets(), position + getOffsetBase(), length); - boolean[] newMapIsNull = compactArray(getMapIsNull(), position + getOffsetBase(), length); + boolean[] mapIsNull = getMapIsNull(); + boolean[] newMapIsNull = mapIsNull == null ? null : compactArray(mapIsNull, position + getOffsetBase(), length); int[] newHashTable = compactArray(getHashTables(), startValueOffset * HASH_MULTIPLIER, (endValueOffset - startValueOffset) * HASH_MULTIPLIER); - if (newKeys == getRawKeyBlock() && newValues == getRawValueBlock() && newOffsets == getOffsets() && newMapIsNull == getMapIsNull() && newHashTable == getHashTables()) { + if (newKeys == getRawKeyBlock() && newValues == getRawValueBlock() && newOffsets == getOffsets() && newMapIsNull == mapIsNull && newHashTable == getHashTables()) { return this; } return createMapBlockInternal( 0, length, - newMapIsNull, + Optional.ofNullable(newMapIsNull), newOffsets, newKeys, newValues, @@ -233,7 +268,7 @@ public Block getSingleValueBlock(int position) return createMapBlockInternal( 0, 1, - new boolean[] {isNull(position)}, + Optional.of(new boolean[] {isNull(position)}), new int[] {0, valueLength}, newKeys, newValues, @@ -269,7 +304,8 @@ public long getEstimatedDataSizeForStats(int position) public boolean isNull(int position) { checkReadablePosition(position); - return getMapIsNull()[position + getOffsetBase()]; + boolean[] mapIsNull = getMapIsNull(); + return mapIsNull != null && mapIsNull[position + getOffsetBase()]; } private void checkReadablePosition(int position) diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractRowBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractRowBlock.java index c856d5321fb7f..cbec4518fd0bd 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractRowBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractRowBlock.java @@ -15,6 +15,7 @@ import static com.facebook.presto.spi.block.BlockUtil.arraySame; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; +import static com.facebook.presto.spi.block.BlockUtil.checkValidPositions; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; import static com.facebook.presto.spi.block.BlockUtil.compactOffsets; @@ -107,6 +108,30 @@ public long getRegionSizeInBytes(int position, int length) return regionSizeInBytes; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + checkValidPositions(positions, getPositionCount()); + + int usedPositionCount = 0; + boolean[] fieldPositions = new boolean[getRawFieldBlocks()[0].getPositionCount()]; + for (int i = 0; i < positions.length; i++) { + if (positions[i]) { + usedPositionCount++; + int startFieldBlockOffset = getFieldBlockOffset(i); + int endFieldBlockOffset = getFieldBlockOffset(i + 1); + for (int j = startFieldBlockOffset; j < endFieldBlockOffset; j++) { + fieldPositions[j] = true; + } + } + } + long sizeInBytes = 0; + for (int j = 0; j < numFields; j++) { + sizeInBytes += getRawFieldBlocks()[j].getPositionsSizeInBytes(fieldPositions); + } + return sizeInBytes + (Integer.BYTES + Byte.BYTES) * (long) usedPositionCount; + } + @Override public Block copyRegion(int position, int length) { @@ -122,9 +147,10 @@ public Block copyRegion(int position, int length) } int[] newOffsets = compactOffsets(getFieldBlockOffsets(), position + getOffsetBase(), length); - boolean[] newRowIsNull = compactArray(getRowIsNull(), position + getOffsetBase(), length); + boolean[] rowIsNull = getRowIsNull(); + boolean[] newRowIsNull = rowIsNull == null ? null : compactArray(rowIsNull, position + getOffsetBase(), length); - if (arraySame(newBlocks, getRawFieldBlocks()) && newOffsets == getFieldBlockOffsets() && newRowIsNull == getRowIsNull()) { + if (arraySame(newBlocks, getRawFieldBlocks()) && newOffsets == getFieldBlockOffsets() && newRowIsNull == rowIsNull) { return this; } return createRowBlockInternal(0, length, newRowIsNull, newOffsets, newBlocks); @@ -187,7 +213,8 @@ public long getEstimatedDataSizeForStats(int position) public boolean isNull(int position) { checkReadablePosition(position); - return getRowIsNull()[position + getOffsetBase()]; + boolean[] rowIsNull = getRowIsNull(); + return rowIsNull != null && rowIsNull[position + getOffsetBase()]; } private void checkReadablePosition(int position) diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleArrayBlock.java index e4df37238bd19..46025a316fcca 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleArrayBlock.java @@ -178,6 +178,12 @@ public long getRegionSizeInBytes(int position, int length) throw new UnsupportedOperationException(); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + throw new UnsupportedOperationException(); + } + @Override public Block copyRegion(int position, int length) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleMapBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleMapBlock.java index 26b9075b381db..63fe0d67f6ae4 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleMapBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleMapBlock.java @@ -246,6 +246,12 @@ public long getRegionSizeInBytes(int position, int length) throw new UnsupportedOperationException(); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + throw new UnsupportedOperationException(); + } + @Override public Block copyPositions(int[] positions, int offset, int length) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleRowBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleRowBlock.java index 95f2200507e41..37562272185a0 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleRowBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/AbstractSingleRowBlock.java @@ -160,6 +160,12 @@ public long getRegionSizeInBytes(int position, int length) throw new UnsupportedOperationException(); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + throw new UnsupportedOperationException(); + } + @Override public Block copyPositions(int[] positions, int offset, int length) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlock.java index fcd7a33c3ab61..79803e84dad45 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlock.java @@ -15,6 +15,9 @@ import org.openjdk.jol.info.ClassLayout; +import javax.annotation.Nullable; + +import java.util.Optional; import java.util.function.BiConsumer; import static io.airlift.slice.SizeOf.sizeOf; @@ -39,9 +42,9 @@ public class ArrayBlock * Create an array block directly from columnar nulls, values, and offsets into the values. * A null array must have no entries. */ - public static Block fromElementBlock(int positionCount, boolean[] valueIsNull, int[] arrayOffset, Block values) + public static Block fromElementBlock(int positionCount, Optional valueIsNull, int[] arrayOffset, Block values) { - validateConstructorArguments(0, positionCount, valueIsNull, arrayOffset, values); + validateConstructorArguments(0, positionCount, valueIsNull.orElse(null), arrayOffset, values); // for performance reasons per element checks are only performed on the public construction for (int i = 0; i < positionCount; i++) { int offset = arrayOffset[i]; @@ -49,23 +52,23 @@ public static Block fromElementBlock(int positionCount, boolean[] valueIsNull, i if (length < 0) { throw new IllegalArgumentException(format("Offset is not monotonically ascending. offsets[%s]=%s, offsets[%s]=%s", i, arrayOffset[i], i + 1, arrayOffset[i + 1])); } - if (valueIsNull[i] && length != 0) { + if (valueIsNull.isPresent() && valueIsNull.get()[i] && length != 0) { throw new IllegalArgumentException("A null array must have zero entries"); } } - return new ArrayBlock(0, positionCount, valueIsNull, arrayOffset, values); + return new ArrayBlock(0, positionCount, valueIsNull.orElse(null), arrayOffset, values); } /** * Create an array block directly without per element validations. */ - static ArrayBlock createArrayBlockInternal(int arrayOffset, int positionCount, boolean[] valueIsNull, int[] offsets, Block values) + static ArrayBlock createArrayBlockInternal(int arrayOffset, int positionCount, @Nullable boolean[] valueIsNull, int[] offsets, Block values) { validateConstructorArguments(arrayOffset, positionCount, valueIsNull, offsets, values); return new ArrayBlock(arrayOffset, positionCount, valueIsNull, offsets, values); } - private static void validateConstructorArguments(int arrayOffset, int positionCount, boolean[] valueIsNull, int[] offsets, Block values) + private static void validateConstructorArguments(int arrayOffset, int positionCount, @Nullable boolean[] valueIsNull, int[] offsets, Block values) { if (arrayOffset < 0) { throw new IllegalArgumentException("arrayOffset is negative"); @@ -75,8 +78,7 @@ private static void validateConstructorArguments(int arrayOffset, int positionCo throw new IllegalArgumentException("positionCount is negative"); } - requireNonNull(valueIsNull, "valueIsNull is null"); - if (valueIsNull.length - arrayOffset < positionCount) { + if (valueIsNull != null && valueIsNull.length - arrayOffset < positionCount) { throw new IllegalArgumentException("isNull length is less than positionCount"); } @@ -92,7 +94,7 @@ private static void validateConstructorArguments(int arrayOffset, int positionCo * Use createArrayBlockInternal or fromElementBlock instead of this method. The caller of this method is assumed to have * validated the arguments with validateConstructorArguments. */ - private ArrayBlock(int arrayOffset, int positionCount, boolean[] valueIsNull, int[] offsets, Block values) + private ArrayBlock(int arrayOffset, int positionCount, @Nullable boolean[] valueIsNull, int[] offsets, Block values) { // caller must check arguments with validateConstructorArguments this.arrayOffset = arrayOffset; @@ -161,6 +163,7 @@ protected int getOffsetBase() } @Override + @Nullable protected boolean[] getValueIsNull() { return valueIsNull; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlockBuilder.java index 53f6678d76387..cd88dd7fcf2fe 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ArrayBlockBuilder.java @@ -19,7 +19,7 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.ArrayBlock.createArrayBlockInternal; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; @@ -42,6 +42,7 @@ public class ArrayBlockBuilder private int[] offsets = new int[1]; private boolean[] valueIsNull = new boolean[0]; + private boolean hasNullValue; private final BlockBuilder values; private boolean currentEntryOpened; @@ -106,7 +107,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, values.getRetainedSizeInBytes()); consumer.accept(offsets, sizeOf(offsets)); @@ -224,6 +225,7 @@ private void entryAdded(boolean isNull) } offsets[positionCount + 1] = values.getPositionCount(); valueIsNull[positionCount] = isNull; + hasNullValue |= isNull; positionCount++; if (blockBuilderStatus != null) { @@ -261,7 +263,7 @@ public ArrayBlock build() if (currentEntryOpened) { throw new IllegalStateException("Current entry must be closed before the block can be built"); } - return createArrayBlockInternal(0, positionCount, valueIsNull, offsets, values.build()); + return createArrayBlockInternal(0, positionCount, hasNullValue ? valueIsNull : null, offsets, values.build()); } @Override diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/Block.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/Block.java index 775e9ad503487..272dec5af2806 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/Block.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/Block.java @@ -15,7 +15,7 @@ import io.airlift.slice.Slice; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.DictionaryId.randomDictionaryId; @@ -163,18 +163,41 @@ default int compareTo(int leftPosition, int leftOffset, int leftLength, Block ri int getPositionCount(); /** - * Returns the logical size of this block in memory. + * Returns the size of this block as if it was compacted, ignoring any over-allocations. + * For example, in dictionary blocks, this only counts each dictionary entry once, + * rather than each time a value is referenced. */ long getSizeInBytes(); /** - * Returns the logical size of {@code block.getRegion(position, length)} in memory. + * Returns the size of the block contents, regardless of internal representation. + * The same logical data values should always have the same size, no matter + * what block type is used or how they are represented within a specific block. + * + * This can differ substantially from {@link #getSizeInBytes} for certain block + * types. For RLE, it will be {@code N} times larger. For dictionary, it will be + * larger based on how many times dictionary entries are reused. + */ + default long getLogicalSizeInBytes() + { + return getSizeInBytes(); + } + + /** + * Returns the size of {@code block.getRegion(position, length)}. * The method can be expensive. Do not use it outside an implementation of Block. */ long getRegionSizeInBytes(int position, int length); /** - * Returns the retained size of this block in memory. + * Returns the size of of all positions marked true in the positions array. + * This is equivalent to multiple calls of {@code block.getRegionSizeInBytes(position, length)} + * where you mark all positions for the regions first. + */ + long getPositionsSizeInBytes(boolean[] positions); + + /** + * Returns the retained size of this block in memory, including over-allocations. * This method is called from the inner most execution loop and must be fast. */ long getRetainedSizeInBytes(); @@ -193,7 +216,7 @@ default int compareTo(int leftPosition, int leftOffset, int leftLength, Block ri * {@code consumer} should be called at least once with the current block and * must include the instance size of the current block */ - void retainedBytesForEachPart(BiConsumer consumer); + void retainedBytesForEachPart(ObjLongConsumer consumer); /** * Get the encoding for this block. diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/BlockUtil.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/BlockUtil.java index e4b11b11d2151..4a760aec33116 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/BlockUtil.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/BlockUtil.java @@ -49,6 +49,13 @@ static void checkValidRegion(int positionCount, int positionOffset, int length) } } + static void checkValidPositions(boolean[] positions, int positionCount) + { + if (positions.length != positionCount) { + throw new IllegalArgumentException(format("Invalid positions array size %d, actual position count is %d", positions.length, positionCount)); + } + } + static void checkValidPosition(int position, int positionCount) { if (position < 0 || position >= positionCount) { @@ -173,6 +180,16 @@ static long[] compactArray(long[] array, int index, int length) } return Arrays.copyOfRange(array, index, index + length); } + static int countUsedPositions(boolean[] positions) + { + int used = 0; + for (boolean position : positions) { + if (position) { + used++; + } + } + return used; + } /** * Returns true if the two specified arrays contain the same object in every position. diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlock.java index f13a4bc7e7981..39357919e4b64 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlock.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; public class ByteArrayBlock @@ -81,6 +82,12 @@ public long getRegionSizeInBytes(int position, int length) return (Byte.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Byte.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -94,7 +101,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); if (valueIsNull != null) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlockBuilder.java index 682f3f0c2c9f6..97d8aa9062ab8 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ByteArrayBlockBuilder.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.max; @@ -146,6 +147,12 @@ public long getRegionSizeInBytes(int position, int length) return (Byte.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Byte.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -159,7 +166,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); consumer.accept(valueIsNull, sizeOf(valueIsNull)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ColumnarRow.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ColumnarRow.java index 488ab494ac99c..36e9fd8c1a1b4 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ColumnarRow.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ColumnarRow.java @@ -125,7 +125,7 @@ public int getFieldCount() /** * Gets the specified field for all rows as a column. - * + *

    * Note: A null row will not have an entry in the block, so the block * will be the size of the non-null rows. This block may still contain * null values when the row is non-null but the field value is null. diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/DictionaryBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/DictionaryBlock.java index c183d0719744e..9b8a5d5cdabf6 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/DictionaryBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/DictionaryBlock.java @@ -20,11 +20,13 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidPosition; +import static com.facebook.presto.spi.block.BlockUtil.checkValidPositions; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static com.facebook.presto.spi.block.DictionaryId.randomDictionaryId; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.min; @@ -41,6 +43,7 @@ public class DictionaryBlock private final int[] ids; private final long retainedSizeInBytes; private volatile long sizeInBytes = -1; + private volatile long logicalSizeInBytes = -1; private volatile int uniqueIds = -1; private final DictionaryId dictionarySourceId; @@ -202,23 +205,43 @@ public long getSizeInBytes() private void calculateCompactSize() { - long sizeInBytes = 0; int uniqueIds = 0; - boolean[] seen = new boolean[dictionary.getPositionCount()]; + boolean[] used = new boolean[dictionary.getPositionCount()]; for (int i = 0; i < positionCount; i++) { int position = getId(i); - if (!seen[position]) { - if (!dictionary.isNull(position)) { - sizeInBytes += dictionary.getRegionSizeInBytes(position, 1); - } + if (!used[position]) { uniqueIds++; - seen[position] = true; + used[position] = true; } } - this.sizeInBytes = sizeInBytes + (Integer.BYTES * (long) positionCount); + this.sizeInBytes = dictionary.getPositionsSizeInBytes(used) + (Integer.BYTES * (long) positionCount); this.uniqueIds = uniqueIds; } + @Override + public long getLogicalSizeInBytes() + { + if (logicalSizeInBytes >= 0) { + return logicalSizeInBytes; + } + + // Calculation of logical size can be performed as part of calculateCompactSize() with minor modifications. + // Keeping this calculation separate as this is a little more expensive and may not be called as often. + long sizeInBytes = 0; + long[] seenSizes = new long[dictionary.getPositionCount()]; + Arrays.fill(seenSizes, -1L); + for (int i = 0; i < getPositionCount(); i++) { + int position = getId(i); + if (seenSizes[position] < 0) { + seenSizes[position] = dictionary.getRegionSizeInBytes(position, 1); + } + sizeInBytes += seenSizes[position]; + } + + logicalSizeInBytes = sizeInBytes; + return sizeInBytes; + } + @Override public long getRegionSizeInBytes(int positionOffset, int length) { @@ -228,19 +251,25 @@ public long getRegionSizeInBytes(int positionOffset, int length) return getSizeInBytes(); } - long sizeInBytes = 0; - boolean[] seen = new boolean[dictionary.getPositionCount()]; + boolean[] used = new boolean[dictionary.getPositionCount()]; for (int i = positionOffset; i < positionOffset + length; i++) { - int position = getId(i); - if (!seen[position]) { - if (!dictionary.isNull(position)) { - sizeInBytes += dictionary.getRegionSizeInBytes(position, 1); - } - seen[position] = true; + used[getId(i)] = true; + } + return dictionary.getPositionsSizeInBytes(used) + Integer.BYTES * (long) length; + } + + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + checkValidPositions(positions, positionCount); + + boolean[] used = new boolean[dictionary.getPositionCount()]; + for (int i = 0; i < positions.length; i++) { + if (positions[i]) { + used[getId(i)] = true; } } - sizeInBytes += Integer.BYTES * (long) length; - return sizeInBytes; + return dictionary.getPositionsSizeInBytes(used) + (Integer.BYTES * (long) countUsedPositions(positions)); } @Override @@ -256,7 +285,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(dictionary, dictionary.getRetainedSizeInBytes()); consumer.accept(ids, sizeOf(ids)); @@ -294,7 +323,7 @@ public Block copyPositions(int[] positions, int offset, int length) public Block getRegion(int positionOffset, int length) { checkValidRegion(positionCount, positionOffset, length); - return new DictionaryBlock(idsOffset + positionOffset, length, dictionary, ids, false, randomDictionaryId()); + return new DictionaryBlock(idsOffset + positionOffset, length, dictionary, ids, false, dictionarySourceId); } @Override diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlock.java index 307abe4a91913..ce49ca431dd6e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlock.java @@ -21,7 +21,7 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidPosition; @@ -101,7 +101,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(slice, slice.getRetainedSize()); if (valueIsNull != null) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlockBuilder.java index cbd856b6adc91..79d05d248ea6d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/FixedWidthBlockBuilder.java @@ -21,7 +21,7 @@ import javax.annotation.Nullable; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.MAX_ARRAY_SIZE; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; @@ -103,7 +103,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(sliceOutput, sliceOutput.getRetainedSize()); consumer.accept(valueIsNull, valueIsNull.getRetainedSize()); @@ -127,7 +127,7 @@ public Block copyPositions(int[] positions, int offset, int length) if (hasNullValue) { newValueIsNull.appendByte(valueIsNull.getUnderlyingSlice().getByte(position)); } - newSlice.appendBytes(getRawSlice().getBytes(position * fixedSize, fixedSize)); + newSlice.writeBytes(getRawSlice(), position * fixedSize, fixedSize); } return new FixedWidthBlock(fixedSize, length, newSlice.slice(), newValueIsNull == null ? null : newValueIsNull.slice()); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlock.java index 7228f50e43139..bb414d427330b 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlock.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; public class IntArrayBlock @@ -81,6 +82,12 @@ public long getRegionSizeInBytes(int position, int length) return (Integer.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Integer.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -94,7 +101,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); if (valueIsNull != null) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlockBuilder.java index 81f9196ba7cdf..d1c39a2f86945 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/IntArrayBlockBuilder.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.max; @@ -146,6 +147,12 @@ public long getRegionSizeInBytes(int position, int length) return (Integer.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Integer.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -159,7 +166,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); consumer.accept(valueIsNull, sizeOf(valueIsNull)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/LazyBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/LazyBlock.java index c1a4951c2bd05..214f261bc555f 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/LazyBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/LazyBlock.java @@ -16,7 +16,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static java.util.Objects.requireNonNull; @@ -177,6 +177,13 @@ public long getRegionSizeInBytes(int position, int length) return block.getRegionSizeInBytes(position, length); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + assureLoaded(); + return block.getPositionsSizeInBytes(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -192,7 +199,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { assureLoaded(); block.retainedBytesForEachPart(consumer); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlock.java index e50db7a46c413..5bc79fd534af9 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlock.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.toIntExact; @@ -82,6 +83,12 @@ public long getRegionSizeInBytes(int position, int length) return (Long.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Long.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -95,7 +102,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); if (valueIsNull != null) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlockBuilder.java index 377d57cfa45a7..51fb4c3ba517e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/LongArrayBlockBuilder.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.max; import static java.lang.Math.toIntExact; @@ -147,6 +148,12 @@ public long getRegionSizeInBytes(int position, int length) return (Long.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Long.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -160,7 +167,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); consumer.accept(valueIsNull, sizeOf(valueIsNull)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlock.java index 17a03277cd1b8..0a51354f1b46c 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlock.java @@ -18,8 +18,11 @@ import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; +import javax.annotation.Nullable; + import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.Optional; import java.util.function.BiConsumer; import static com.facebook.presto.spi.block.MapBlockBuilder.buildHashTable; @@ -53,7 +56,7 @@ public class MapBlock * @param keyNativeHashCode hash of a key stack type; signature is (K)long */ public static MapBlock fromKeyValueBlock( - boolean[] mapIsNull, + Optional mapIsNull, int[] offsets, Block keyBlock, Block valueBlock, @@ -62,9 +65,9 @@ public static MapBlock fromKeyValueBlock( MethodHandle keyNativeHashCode, MethodHandle keyBlockHashCode) { - validateConstructorArguments(0, mapIsNull.length, mapIsNull, offsets, keyBlock, valueBlock, mapType.getKeyType(), keyBlockNativeEquals, keyNativeHashCode); + validateConstructorArguments(0, offsets.length - 1, mapIsNull.orElse(null), offsets, keyBlock, valueBlock, mapType.getKeyType(), keyBlockNativeEquals, keyNativeHashCode); - int mapCount = mapIsNull.length; + int mapCount = offsets.length - 1; int elementCount = keyBlock.getPositionCount(); int[] hashTables = new int[elementCount * HASH_MULTIPLIER]; Arrays.fill(hashTables, -1); @@ -74,7 +77,7 @@ public static MapBlock fromKeyValueBlock( if (keyCount < 0) { throw new IllegalArgumentException(format("Offset is not monotonically ascending. offsets[%s]=%s, offsets[%s]=%s", i, offsets[i], i + 1, offsets[i + 1])); } - if (mapIsNull[i] && keyCount != 0) { + if (mapIsNull.isPresent() && mapIsNull.get()[i] && keyCount != 0) { throw new IllegalArgumentException("A null map must have zero entries"); } buildHashTable(keyBlock, keyOffset, keyCount, keyBlockHashCode, hashTables, keyOffset * HASH_MULTIPLIER, keyCount * HASH_MULTIPLIER); @@ -95,7 +98,7 @@ public static MapBlock fromKeyValueBlock( /** * Create a map block directly without per element validations. - * + *

    * Internal use by this package and com.facebook.presto.spi.Type only. * * @param keyType key type K @@ -105,7 +108,7 @@ public static MapBlock fromKeyValueBlock( public static MapBlock createMapBlockInternal( int startOffset, int positionCount, - boolean[] mapIsNull, + Optional mapIsNull, int[] offsets, Block keyBlock, Block valueBlock, @@ -114,14 +117,14 @@ public static MapBlock createMapBlockInternal( MethodHandle keyBlockNativeEquals, MethodHandle keyNativeHashCode) { - validateConstructorArguments(startOffset, positionCount, mapIsNull, offsets, keyBlock, valueBlock, keyType, keyBlockNativeEquals, keyNativeHashCode); - return new MapBlock(startOffset, positionCount, mapIsNull, offsets, keyBlock, valueBlock, hashTables, keyType, keyBlockNativeEquals, keyNativeHashCode); + validateConstructorArguments(startOffset, positionCount, mapIsNull.orElse(null), offsets, keyBlock, valueBlock, keyType, keyBlockNativeEquals, keyNativeHashCode); + return new MapBlock(startOffset, positionCount, mapIsNull.orElse(null), offsets, keyBlock, valueBlock, hashTables, keyType, keyBlockNativeEquals, keyNativeHashCode); } private static void validateConstructorArguments( int startOffset, int positionCount, - boolean[] mapIsNull, + @Nullable boolean[] mapIsNull, int[] offsets, Block keyBlock, Block valueBlock, @@ -137,8 +140,7 @@ private static void validateConstructorArguments( throw new IllegalArgumentException("positionCount is negative"); } - requireNonNull(mapIsNull, "mapIsNull is null"); - if (mapIsNull.length - startOffset < positionCount) { + if (mapIsNull != null && mapIsNull.length - startOffset < positionCount) { throw new IllegalArgumentException("isNull length is less than positionCount"); } @@ -165,7 +167,7 @@ private static void validateConstructorArguments( private MapBlock( int startOffset, int positionCount, - boolean[] mapIsNull, + @Nullable boolean[] mapIsNull, int[] offsets, Block keyBlock, Block valueBlock, @@ -224,6 +226,7 @@ protected int getOffsetBase() } @Override + @Nullable protected boolean[] getMapIsNull() { return mapIsNull; @@ -296,7 +299,7 @@ public Block getLoadedBlock() return createMapBlockInternal( startOffset, positionCount, - mapIsNull, + Optional.ofNullable(mapIsNull), offsets, keyBlock, loadedValueBlock, diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockBuilder.java index 31a110b8478a4..d4b41cea4d6e9 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockBuilder.java @@ -14,6 +14,7 @@ package com.facebook.presto.spi.block; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.type.Type; import org.openjdk.jol.info.ClassLayout; @@ -21,8 +22,10 @@ import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.Optional; import java.util.function.BiConsumer; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.MapBlock.createMapBlockInternal; import static io.airlift.slice.SizeOf.sizeOf; @@ -304,7 +307,7 @@ public Block build() return createMapBlockInternal( 0, positionCount, - mapIsNull, + Optional.of(mapIsNull), offsets, keyBlockBuilder.build(), valueBlockBuilder.build(), @@ -456,9 +459,10 @@ private static void buildHashTableStrict( break; } - boolean isDuplicateKey; + Boolean isDuplicateKey; try { - isDuplicateKey = (boolean) keyBlockEquals.invokeExact(keyBlock, keyOffset + i, keyBlock, keyOffset + outputHashTable[hashTableOffset + hash]); + // assuming maps with indeterminate keys are not supported + isDuplicateKey = (Boolean) keyBlockEquals.invokeExact(keyBlock, keyOffset + i, keyBlock, keyOffset + outputHashTable[hashTableOffset + hash]); } catch (RuntimeException e) { throw e; @@ -467,6 +471,10 @@ private static void buildHashTableStrict( throw new RuntimeException(throwable); } + if (isDuplicateKey == null) { + throw new PrestoException(NOT_SUPPORTED, "map key cannot be null or contain nulls"); + } + if (isDuplicateKey) { throw new DuplicateMapKeyException(keyBlock, keyOffset + i); } @@ -496,6 +504,13 @@ private static int getHashPosition(Block keyBlock, int position, MethodHandle ke throw new RuntimeException(throwable); } - return (int) Math.floorMod(hashCode, hashTableSize); + return computePosition(hashCode, hashTableSize); + } + + // This function reduces the 64 bit hashcode to [0, hashTableSize) uniformly. It first reduces the hashcode to 32 bit + // integer x then normalize it to x / 2^32 * hashSize to reduce the range of x from [0, 2^32) to [0, hashTableSize) + static int computePosition(long hashcode, int hashTableSize) + { + return (int) ((Integer.toUnsignedLong(Long.hashCode(hashcode)) * hashTableSize) >> 32); } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockEncoding.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockEncoding.java index f876b70e42715..cc0b43537b653 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockEncoding.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/MapBlockEncoding.java @@ -21,6 +21,8 @@ import io.airlift.slice.SliceInput; import io.airlift.slice.SliceOutput; +import java.util.Optional; + import static com.facebook.presto.spi.block.AbstractMapBlock.HASH_MULTIPLIER; import static io.airlift.slice.Slices.wrappedIntArray; import static java.lang.String.format; @@ -91,7 +93,7 @@ public Block readBlock(BlockEncodingSerde blockEncodingSerde, SliceInput sliceIn int positionCount = sliceInput.readInt(); int[] offsets = new int[positionCount + 1]; sliceInput.readBytes(wrappedIntArray(offsets)); - boolean[] mapIsNull = EncoderUtil.decodeNullBits(sliceInput, positionCount).orElseGet(() -> new boolean[positionCount]); + Optional mapIsNull = EncoderUtil.decodeNullBits(sliceInput, positionCount); return MapType.createMapBlockInternal(typeManager, keyType, 0, positionCount, mapIsNull, offsets, keyBlock, valueBlock, hashTable); } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlock.java index a86e67aabea62..52a7ceeaf622a 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlock.java @@ -15,6 +15,9 @@ import org.openjdk.jol.info.ClassLayout; +import javax.annotation.Nullable; + +import java.util.Optional; import java.util.function.BiConsumer; import static io.airlift.slice.SizeOf.sizeOf; @@ -39,27 +42,26 @@ public class RowBlock /** * Create a row block directly from columnar nulls and field blocks. */ - public static Block fromFieldBlocks(boolean[] rowIsNull, Block[] fieldBlocks) + public static Block fromFieldBlocks(int positionCount, Optional rowIsNull, Block[] fieldBlocks) { - requireNonNull(rowIsNull, "rowIsNull is null"); - int[] fieldBlockOffsets = new int[rowIsNull.length + 1]; - for (int position = 0; position < rowIsNull.length; position++) { - fieldBlockOffsets[position + 1] = fieldBlockOffsets[position] + (rowIsNull[position] ? 0 : 1); + int[] fieldBlockOffsets = new int[positionCount + 1]; + for (int position = 0; position < positionCount; position++) { + fieldBlockOffsets[position + 1] = fieldBlockOffsets[position] + (rowIsNull.isPresent() && rowIsNull.get()[position] ? 0 : 1); } - validateConstructorArguments(0, rowIsNull.length, rowIsNull, fieldBlockOffsets, fieldBlocks); - return new RowBlock(0, rowIsNull.length, rowIsNull, fieldBlockOffsets, fieldBlocks); + validateConstructorArguments(0, positionCount, rowIsNull.orElse(null), fieldBlockOffsets, fieldBlocks); + return new RowBlock(0, positionCount, rowIsNull.orElse(null), fieldBlockOffsets, fieldBlocks); } /** * Create a row block directly without per element validations. */ - static RowBlock createRowBlockInternal(int startOffset, int positionCount, boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) + static RowBlock createRowBlockInternal(int startOffset, int positionCount, @Nullable boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) { validateConstructorArguments(startOffset, positionCount, rowIsNull, fieldBlockOffsets, fieldBlocks); return new RowBlock(startOffset, positionCount, rowIsNull, fieldBlockOffsets, fieldBlocks); } - private static void validateConstructorArguments(int startOffset, int positionCount, boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) + private static void validateConstructorArguments(int startOffset, int positionCount, @Nullable boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) { if (startOffset < 0) { throw new IllegalArgumentException("arrayOffset is negative"); @@ -69,8 +71,7 @@ private static void validateConstructorArguments(int startOffset, int positionCo throw new IllegalArgumentException("positionCount is negative"); } - requireNonNull(rowIsNull, "rowIsNull is null"); - if (rowIsNull.length - startOffset < positionCount) { + if (rowIsNull != null && rowIsNull.length - startOffset < positionCount) { throw new IllegalArgumentException("rowIsNull length is less than positionCount"); } @@ -97,7 +98,7 @@ private static void validateConstructorArguments(int startOffset, int positionCo * Use createRowBlockInternal or fromFieldBlocks instead of this method. The caller of this method is assumed to have * validated the arguments with validateConstructorArguments. */ - private RowBlock(int startOffset, int positionCount, boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) + private RowBlock(int startOffset, int positionCount, @Nullable boolean[] rowIsNull, int[] fieldBlockOffsets, Block[] fieldBlocks) { super(fieldBlocks.length); @@ -134,6 +135,7 @@ protected int getOffsetBase() } @Override + @Nullable protected boolean[] getRowIsNull() { return rowIsNull; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlockBuilder.java index 20e9a173fc8cc..b32f6e4b45385 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/RowBlockBuilder.java @@ -21,7 +21,7 @@ import java.util.Arrays; import java.util.List; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.RowBlock.createRowBlockInternal; @@ -49,9 +49,7 @@ public RowBlockBuilder(List fieldTypes, BlockBuilderStatus blockBuilderSta { this( blockBuilderStatus, - fieldTypes.stream() - .map(type -> type.createBlockBuilder(blockBuilderStatus, expectedEntries)) - .toArray(BlockBuilder[]::new), + createFieldBlockBuilders(fieldTypes, blockBuilderStatus, expectedEntries), new int[expectedEntries + 1], new boolean[expectedEntries]); } @@ -67,6 +65,16 @@ private RowBlockBuilder(@Nullable BlockBuilderStatus blockBuilderStatus, BlockBu this.fieldBlockBuilders = requireNonNull(fieldBlockBuilders, "fieldBlockBuilders is null"); } + private static BlockBuilder[] createFieldBlockBuilders(List fieldTypes, BlockBuilderStatus blockBuilderStatus, int expectedEntries) + { + // Stream API should not be used since constructor can be called in performance sensitive sections + BlockBuilder[] fieldBlockBuilders = new BlockBuilder[fieldTypes.size()]; + for (int i = 0; i < fieldTypes.size(); i++) { + fieldBlockBuilders[i] = fieldTypes.get(i).createBlockBuilder(blockBuilderStatus, expectedEntries); + } + return fieldBlockBuilders; + } + @Override protected Block[] getRawFieldBlocks() { @@ -121,7 +129,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { for (int i = 0; i < numFields; i++) { consumer.accept(fieldBlockBuilders[i], fieldBlockBuilders[i].getRetainedSizeInBytes()); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/RunLengthEncodedBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/RunLengthEncodedBlock.java index 843ba72d72852..1e772973d2bdb 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/RunLengthEncodedBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/RunLengthEncodedBlock.java @@ -18,7 +18,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidPosition; @@ -81,6 +81,12 @@ public long getSizeInBytes() return value.getSizeInBytes(); } + @Override + public long getLogicalSizeInBytes() + { + return positionCount * value.getLogicalSizeInBytes(); + } + @Override public long getRetainedSizeInBytes() { @@ -94,7 +100,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(value, value.getRetainedSizeInBytes()); consumer.accept(this, (long) INSTANCE_SIZE); @@ -139,6 +145,12 @@ public long getRegionSizeInBytes(int position, int length) return value.getSizeInBytes(); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return value.getSizeInBytes(); + } + @Override public Block copyRegion(int positionOffset, int length) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlock.java index fe6c162173a9a..e0b90eb4c634d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlock.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; public class ShortArrayBlock @@ -81,6 +82,12 @@ public long getRegionSizeInBytes(int position, int length) return (Short.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Short.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -94,7 +101,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); if (valueIsNull != null) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlockBuilder.java index 8228d99b6c831..b43937a43943d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/ShortArrayBlockBuilder.java @@ -18,11 +18,12 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; +import static com.facebook.presto.spi.block.BlockUtil.countUsedPositions; import static io.airlift.slice.SizeOf.sizeOf; import static java.lang.Math.max; @@ -146,6 +147,12 @@ public long getRegionSizeInBytes(int position, int length) return (Short.BYTES + Byte.BYTES) * (long) length; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + return (Short.BYTES + Byte.BYTES) * (long) countUsedPositions(positions); + } + @Override public long getRetainedSizeInBytes() { @@ -159,7 +166,7 @@ public long getEstimatedDataSizeForStats(int position) } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(values, sizeOf(values)); consumer.accept(valueIsNull, sizeOf(valueIsNull)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleArrayBlockWriter.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleArrayBlockWriter.java index f80bf9c1b1672..8393edbded2b7 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleArrayBlockWriter.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleArrayBlockWriter.java @@ -16,7 +16,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static java.lang.String.format; @@ -56,7 +56,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(blockBuilder, blockBuilder.getRetainedSizeInBytes()); consumer.accept(this, (long) INSTANCE_SIZE); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlock.java index b8357032d4f91..9af4d97c5dcd8 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlock.java @@ -20,10 +20,12 @@ import org.openjdk.jol.info.ClassLayout; import java.lang.invoke.MethodHandle; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.spi.block.AbstractMapBlock.HASH_MULTIPLIER; +import static com.facebook.presto.spi.block.MapBlockBuilder.computePosition; import static io.airlift.slice.SizeOf.sizeOf; import static io.airlift.slice.SizeOf.sizeOfIntArray; import static java.lang.String.format; @@ -75,7 +77,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(keyBlock, keyBlock.getRetainedSizeInBytes()); consumer.accept(valueBlock, valueBlock.getRetainedSizeInBytes()); @@ -160,25 +162,27 @@ public int seekKey(Object nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invoke(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invoke(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -202,25 +206,27 @@ public int seekKeyExact(long nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -241,25 +247,27 @@ public int seekKeyExact(boolean nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -280,25 +288,27 @@ public int seekKeyExact(double nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -319,25 +329,27 @@ public int seekKeyExact(Slice nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -358,25 +370,27 @@ public int seekKeyExact(Block nativeValue) int hashTableOffset = offset / 2 * HASH_MULTIPLIER; int hashTableSize = positionCount / 2 * HASH_MULTIPLIER; - int hash = (int) Math.floorMod(hashCode, hashTableSize); + int position = computePosition(hashCode, hashTableSize); while (true) { - int keyPosition = hashTable[hashTableOffset + hash]; + int keyPosition = hashTable[hashTableOffset + position]; if (keyPosition == -1) { return -1; } - boolean match; + Boolean match; try { - match = (boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); + // assuming maps with indeterminate keys are not supported + match = (Boolean) keyBlockNativeEquals.invokeExact(keyBlock, offset / 2 + keyPosition, nativeValue); } catch (Throwable throwable) { throw handleThrowable(throwable); } + checkNotIndeterminate(match); if (match) { return keyPosition * 2 + 1; } - hash++; - if (hash == hashTableSize) { - hash = 0; + position++; + if (position == hashTableSize) { + position = 0; } } } @@ -391,4 +405,11 @@ private static RuntimeException handleThrowable(Throwable throwable) } throw new PrestoException(GENERIC_INTERNAL_ERROR, throwable); } + + private static void checkNotIndeterminate(Boolean equalsResult) + { + if (equalsResult == null) { + throw new PrestoException(NOT_SUPPORTED, "map key cannot be null or contain nulls"); + } + } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlockWriter.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlockWriter.java index 2192e0c4854cf..3a061d7dc1bfb 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlockWriter.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleMapBlockWriter.java @@ -16,7 +16,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static java.lang.String.format; @@ -73,7 +73,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(keyBlockBuilder, keyBlockBuilder.getRetainedSizeInBytes()); consumer.accept(valueBlockBuilder, valueBlockBuilder.getRetainedSizeInBytes()); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlock.java index 33ee8fac562b8..13265a1666166 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlock.java @@ -16,7 +16,7 @@ import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static java.lang.String.format; @@ -71,10 +71,10 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { - for (int i = 0; i < fieldBlocks.length; i++) { - consumer.accept(fieldBlocks[i], fieldBlocks[i].getRetainedSizeInBytes()); + for (Block fieldBlock : fieldBlocks) { + consumer.accept(fieldBlock, fieldBlock.getRetainedSizeInBytes()); } consumer.accept(this, (long) INSTANCE_SIZE); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlockWriter.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlockWriter.java index c65500e584edb..bbbb7b08fe21d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlockWriter.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/SingleRowBlockWriter.java @@ -16,7 +16,7 @@ import io.airlift.slice.Slice; import org.openjdk.jol.info.ClassLayout; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static java.lang.String.format; @@ -38,8 +38,8 @@ public class SingleRowBlockWriter super(rowIndex); this.fieldBlockBuilders = fieldBlockBuilders; long initialBlockBuilderSize = 0; - for (int i = 0; i < fieldBlockBuilders.length; i++) { - initialBlockBuilderSize += fieldBlockBuilders[i].getSizeInBytes(); + for (BlockBuilder fieldBlockBuilder : fieldBlockBuilders) { + initialBlockBuilderSize += fieldBlockBuilder.getSizeInBytes(); } this.initialBlockBuilderSize = initialBlockBuilderSize; } @@ -72,8 +72,8 @@ protected Block getRawFieldBlock(int fieldIndex) public long getSizeInBytes() { long currentBlockBuilderSize = 0; - for (int i = 0; i < fieldBlockBuilders.length; i++) { - currentBlockBuilderSize += fieldBlockBuilders[i].getSizeInBytes(); + for (BlockBuilder fieldBlockBuilder : fieldBlockBuilders) { + currentBlockBuilderSize += fieldBlockBuilder.getSizeInBytes(); } return currentBlockBuilderSize - initialBlockBuilderSize; } @@ -82,17 +82,17 @@ public long getSizeInBytes() public long getRetainedSizeInBytes() { long size = INSTANCE_SIZE; - for (int i = 0; i < fieldBlockBuilders.length; i++) { - size += fieldBlockBuilders[i].getRetainedSizeInBytes(); + for (BlockBuilder fieldBlockBuilder : fieldBlockBuilders) { + size += fieldBlockBuilder.getRetainedSizeInBytes(); } return size; } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { - for (int i = 0; i < fieldBlockBuilders.length; i++) { - consumer.accept(fieldBlockBuilders[i], fieldBlockBuilders[i].getRetainedSizeInBytes()); + for (BlockBuilder fieldBlockBuilder : fieldBlockBuilders) { + consumer.accept(fieldBlockBuilder, fieldBlockBuilder.getRetainedSizeInBytes()); } consumer.accept(this, (long) INSTANCE_SIZE); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlock.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlock.java index 594f6b2e23031..0b9275c9a22ba 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlock.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlock.java @@ -21,7 +21,7 @@ import javax.annotation.Nullable; import java.util.Optional; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; @@ -123,6 +123,20 @@ public long getRegionSizeInBytes(int position, int length) return offsets[arrayOffset + position + length] - offsets[arrayOffset + position] + ((Integer.BYTES + Byte.BYTES) * (long) length); } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + long sizeInBytes = 0; + int usedPositionCount = 0; + for (int i = 0; i < positions.length; ++i) { + if (positions[i]) { + usedPositionCount++; + sizeInBytes += offsets[arrayOffset + i + 1] - offsets[arrayOffset + i]; + } + } + return sizeInBytes + (Integer.BYTES + Byte.BYTES) * (long) usedPositionCount; + } + @Override public long getRetainedSizeInBytes() { @@ -130,7 +144,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(slice, slice.getRetainedSize()); consumer.accept(offsets, sizeOf(offsets)); @@ -159,7 +173,7 @@ public Block copyPositions(int[] positions, int offset, int length) for (int i = 0; i < length; i++) { int position = positions[offset + i]; if (!isEntryNull(position)) { - newSlice.appendBytes(slice.getBytes(getPositionOffset(position), getSliceLength(position))); + newSlice.writeBytes(slice, getPositionOffset(position), getSliceLength(position)); } else if (newValueIsNull != null) { newValueIsNull[i] = true; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlockBuilder.java b/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlockBuilder.java index ef50043bbaad4..a0370878b0c0f 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlockBuilder.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/block/VariableWidthBlockBuilder.java @@ -22,13 +22,14 @@ import javax.annotation.Nullable; import java.util.Arrays; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.block.BlockUtil.MAX_ARRAY_SIZE; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetBytes; import static com.facebook.presto.spi.block.BlockUtil.calculateBlockResetSize; import static com.facebook.presto.spi.block.BlockUtil.checkArrayRange; import static com.facebook.presto.spi.block.BlockUtil.checkValidPosition; +import static com.facebook.presto.spi.block.BlockUtil.checkValidPositions; import static com.facebook.presto.spi.block.BlockUtil.checkValidRegion; import static com.facebook.presto.spi.block.BlockUtil.compactArray; import static com.facebook.presto.spi.block.BlockUtil.compactOffsets; @@ -116,6 +117,21 @@ public long getRegionSizeInBytes(int positionOffset, int length) return getOffset(positionOffset + length) - getOffset(positionOffset) + arraysSizeInBytes; } + @Override + public long getPositionsSizeInBytes(boolean[] positions) + { + checkValidPositions(positions, getPositionCount()); + long sizeInBytes = 0; + int usedPositionCount = 0; + for (int i = 0; i < positions.length; ++i) { + if (positions[i]) { + usedPositionCount++; + sizeInBytes += getOffset(i + 1) - getOffset(i); + } + } + return sizeInBytes + (Integer.BYTES + Byte.BYTES) * (long) usedPositionCount; + } + @Override public long getRetainedSizeInBytes() { @@ -127,7 +143,7 @@ public long getRetainedSizeInBytes() } @Override - public void retainedBytesForEachPart(BiConsumer consumer) + public void retainedBytesForEachPart(ObjLongConsumer consumer) { consumer.accept(sliceOutput, sliceOutput.getRetainedSize()); consumer.accept(offsets, sizeOf(offsets)); @@ -157,7 +173,7 @@ public Block copyPositions(int[] positions, int offset, int length) newValueIsNull[i] = true; } else { - newSlice.appendBytes(sliceOutput.getUnderlyingSlice().getBytes(getPositionOffset(position), getSliceLength(position))); + newSlice.writeBytes(sliceOutput.getUnderlyingSlice(), getPositionOffset(position), getSliceLength(position)); } newOffsets[i + 1] = newSlice.size(); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/classloader/ClassLoaderSafeConnectorPageSink.java b/presto-spi/src/main/java/com/facebook/presto/spi/classloader/ClassLoaderSafeConnectorPageSink.java index cecbfb3fea352..cf53bd8ac0e89 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/classloader/ClassLoaderSafeConnectorPageSink.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/classloader/ClassLoaderSafeConnectorPageSink.java @@ -50,6 +50,14 @@ public long getSystemMemoryUsage() } } + @Override + public long getValidationCpuNanos() + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getValidationCpuNanos(); + } + } + @Override public CompletableFuture appendPage(Page page) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorAccessControl.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorAccessControl.java index 4058cdf15b3fc..3d7e2cc77af6e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorAccessControl.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorAccessControl.java @@ -242,7 +242,7 @@ default void checkCanCreateViewWithSelectFromColumns(ConnectorTransactionHandle * * @throws com.facebook.presto.spi.security.AccessDeniedException if not allowed */ - default void checkCanSetCatalogSessionProperty(Identity identity, String propertyName) + default void checkCanSetCatalogSessionProperty(ConnectorTransactionHandle transactionHandle, Identity identity, String propertyName) { denySetCatalogSessionProperty(propertyName); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorBucketNodeMap.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorBucketNodeMap.java new file mode 100644 index 0000000000000..38c86d4add204 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorBucketNodeMap.java @@ -0,0 +1,66 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.connector; + +import com.facebook.presto.spi.Node; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.lang.String.format; + +public final class ConnectorBucketNodeMap +{ + private final int bucketCount; + private final Optional> bucketToNode; + + public static ConnectorBucketNodeMap createBucketNodeMap(int bucketCount) + { + return new ConnectorBucketNodeMap(bucketCount, Optional.empty()); + } + + public static ConnectorBucketNodeMap createBucketNodeMap(List bucketToNode) + { + return new ConnectorBucketNodeMap(bucketToNode.size(), Optional.of(bucketToNode)); + } + + private ConnectorBucketNodeMap(int bucketCount, Optional> bucketToNode) + { + if (bucketCount <= 0) { + throw new IllegalArgumentException("bucketCount must be positive"); + } + if (bucketToNode.isPresent() && bucketToNode.get().size() != bucketCount) { + throw new IllegalArgumentException(format("Mismatched bucket count in bucketToNode (%s) and bucketCount (%s)", bucketToNode.get().size(), bucketCount)); + } + this.bucketCount = bucketCount; + this.bucketToNode = bucketToNode.map(ArrayList::new).map(Collections::unmodifiableList); + } + + public int getBucketCount() + { + return bucketCount; + } + + public boolean hasFixedMapping() + { + return bucketToNode.isPresent(); + } + + public List getFixedMapping() + { + return bucketToNode.orElseThrow(() -> new IllegalArgumentException("No fixed bucket to node mapping")); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorFactory.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorFactory.java index 4dc0f998029ee..07b36b4dca528 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorFactory.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorFactory.java @@ -23,5 +23,5 @@ public interface ConnectorFactory ConnectorHandleResolver getHandleResolver(); - Connector create(String connectorId, Map config, ConnectorContext context); + Connector create(String catalogName, Map config, ConnectorContext context); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorMetadata.java index 04e1079b4c43f..d8d21f68e7209 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorMetadata.java @@ -49,7 +49,6 @@ import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; -import static com.facebook.presto.spi.statistics.TableStatistics.EMPTY_STATISTICS; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.stream.Collectors.toList; @@ -99,6 +98,29 @@ List getTableLayouts( ConnectorTableLayout getTableLayout(ConnectorSession session, ConnectorTableLayoutHandle handle); + /** + * Return a table layout handle whose partitioning is converted to the provided partitioning handle, + * but otherwise identical to the provided table layout handle. + * The provided table layout handle must be one that the connector can transparently convert to from + * the original partitioning handle associated with the provided table layout handle, + * as promised by {@link #getCommonPartitioningHandle}. + */ + default ConnectorTableLayoutHandle getAlternativeLayoutHandle(ConnectorSession session, ConnectorTableLayoutHandle tableLayoutHandle, ConnectorPartitioningHandle partitioningHandle) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "ConnectorMetadata getCommonPartitioningHandle() is implemented without getAlternativeLayout()"); + } + + /** + * Return a partitioning handle which the connector can transparently convert both {@code left} and {@code right} into. + */ + default Optional getCommonPartitioningHandle(ConnectorSession session, ConnectorPartitioningHandle left, ConnectorPartitioningHandle right) + { + if (left.equals(right)) { + return Optional.of(left); + } + return Optional.empty(); + } + /** * Return the metadata for the specified table handle. * @@ -118,6 +140,7 @@ default Optional getInfo(ConnectorTableLayoutHandle layoutHandle) /** * List table names, possibly filtered by schema. An empty list is returned if none match. + * * @deprecated replaced by {@link ConnectorMetadata#listTables(ConnectorSession, Optional)} */ @Deprecated @@ -158,7 +181,7 @@ default List listTables(ConnectorSession session, Optional constraint) { - return EMPTY_STATISTICS; + return TableStatistics.empty(); } /** @@ -374,6 +397,7 @@ default void dropView(ConnectorSession session, SchemaTableName viewName) /** * List view names, possibly filtered by schema. An empty list is returned if none match. + * * @deprecated replaced by {@link ConnectorMetadata#listViews(ConnectorSession, Optional)} */ @Deprecated diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorNodePartitioningProvider.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorNodePartitioningProvider.java index 1dce4ec590d50..28f22b64f5858 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorNodePartitioningProvider.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/ConnectorNodePartitioningProvider.java @@ -16,11 +16,9 @@ import com.facebook.presto.spi.BucketFunction; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorSplit; -import com.facebook.presto.spi.Node; import com.facebook.presto.spi.type.Type; import java.util.List; -import java.util.Map; import java.util.function.ToIntFunction; import static com.facebook.presto.spi.connector.NotPartitionedPartitionHandle.NOT_PARTITIONED; @@ -42,7 +40,7 @@ default List listPartitionHandles(ConnectorTransaction return singletonList(NOT_PARTITIONED); } - Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle); + ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle); ToIntFunction getSplitBucketFunction(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java index dfa156f80c1ee..df571bed8ec88 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java @@ -33,6 +33,7 @@ import com.facebook.presto.spi.classloader.ThreadContextClassLoader; import com.facebook.presto.spi.connector.ConnectorMetadata; import com.facebook.presto.spi.connector.ConnectorOutputMetadata; +import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.security.GrantInfo; import com.facebook.presto.spi.security.Privilege; @@ -82,6 +83,22 @@ public ConnectorTableLayout getTableLayout(ConnectorSession session, ConnectorTa } } + @Override + public Optional getCommonPartitioningHandle(ConnectorSession session, ConnectorPartitioningHandle left, ConnectorPartitioningHandle right) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getCommonPartitioningHandle(session, left, right); + } + } + + @Override + public ConnectorTableLayoutHandle getAlternativeLayoutHandle(ConnectorSession session, ConnectorTableLayoutHandle tableLayoutHandle, ConnectorPartitioningHandle partitioningHandle) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getAlternativeLayoutHandle(session, tableLayoutHandle, partitioningHandle); + } + } + @Override public Optional getNewTableLayout(ConnectorSession session, ConnectorTableMetadata tableMetadata) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeNodePartitioningProvider.java b/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeNodePartitioningProvider.java index 73fe9b013b273..6e63f0288330b 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeNodePartitioningProvider.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/connector/classloader/ClassLoaderSafeNodePartitioningProvider.java @@ -16,8 +16,8 @@ import com.facebook.presto.spi.BucketFunction; import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.ConnectorSplit; -import com.facebook.presto.spi.Node; import com.facebook.presto.spi.classloader.ThreadContextClassLoader; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitionHandle; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; @@ -25,7 +25,6 @@ import com.facebook.presto.spi.type.Type; import java.util.List; -import java.util.Map; import java.util.function.ToIntFunction; import static java.util.Objects.requireNonNull; @@ -64,10 +63,10 @@ public List listPartitionHandles(ConnectorTransactionH } @Override - public Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { - return delegate.getBucketToNode(transactionHandle, session, partitioningHandle); + return delegate.getBucketNodeMap(transactionHandle, session, partitioningHandle); } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryCompletedEvent.java b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryCompletedEvent.java index de826ead69f9f..d1d64d08185ef 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryCompletedEvent.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryCompletedEvent.java @@ -13,7 +13,10 @@ */ package com.facebook.presto.spi.eventlistener; +import com.facebook.presto.spi.PrestoWarning; + import java.time.Instant; +import java.util.List; import java.util.Optional; import static java.util.Objects.requireNonNull; @@ -25,6 +28,7 @@ public class QueryCompletedEvent private final QueryContext context; private final QueryIOMetadata ioMetadata; private final Optional failureInfo; + private final List warnings; private final Instant createTime; private final Instant executionStartTime; @@ -36,6 +40,7 @@ public QueryCompletedEvent( QueryContext context, QueryIOMetadata ioMetadata, Optional failureInfo, + List warnings, Instant createTime, Instant executionStartTime, Instant endTime) @@ -45,6 +50,7 @@ public QueryCompletedEvent( this.context = requireNonNull(context, "context is null"); this.ioMetadata = requireNonNull(ioMetadata, "ioMetadata is null"); this.failureInfo = requireNonNull(failureInfo, "failureInfo is null"); + this.warnings = requireNonNull(warnings, "queryWarnings is null"); this.createTime = requireNonNull(createTime, "createTime is null"); this.executionStartTime = requireNonNull(executionStartTime, "executionStartTime is null"); this.endTime = requireNonNull(endTime, "endTime is null"); @@ -75,6 +81,11 @@ public Optional getFailureInfo() return failureInfo; } + public List getWarnings() + { + return warnings; + } + public Instant getCreateTime() { return createTime; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryInputMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryInputMetadata.java index a3d792e4fac7b..1187ff2e363a8 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryInputMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryInputMetadata.java @@ -22,15 +22,15 @@ public class QueryInputMetadata { - private final String connectorId; + private final String catalogName; private final String schema; private final String table; private final List columns; private final Optional connectorInfo; - public QueryInputMetadata(String connectorId, String schema, String table, List columns, Optional connectorInfo) + public QueryInputMetadata(String catalogName, String schema, String table, List columns, Optional connectorInfo) { - this.connectorId = requireNonNull(connectorId, "connectorId is null"); + this.catalogName = requireNonNull(catalogName, "catalogName is null"); this.schema = requireNonNull(schema, "schema is null"); this.table = requireNonNull(table, "table is null"); this.columns = requireNonNull(columns, "columns is null"); @@ -38,9 +38,9 @@ public QueryInputMetadata(String connectorId, String schema, String table, List< } @JsonProperty - public String getConnectorId() + public String getCatalogName() { - return connectorId; + return catalogName; } @JsonProperty diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryOutputMetadata.java b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryOutputMetadata.java index 7f8ac6f4daff9..45b9aceb2445c 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryOutputMetadata.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/QueryOutputMetadata.java @@ -21,16 +21,16 @@ public class QueryOutputMetadata { - private final String connectorId; + private final String catalogName; private final String schema; private final String table; private final Optional connectorOutputMetadata; private final Optional jsonLengthLimitExceeded; - public QueryOutputMetadata(String connectorId, String schema, String table, Optional connectorOutputMetadata, Optional jsonLengthLimitExceeded) + public QueryOutputMetadata(String catalogName, String schema, String table, Optional connectorOutputMetadata, Optional jsonLengthLimitExceeded) { - this.connectorId = requireNonNull(connectorId, "connectorId is null"); + this.catalogName = requireNonNull(catalogName, "catalogName is null"); this.schema = requireNonNull(schema, "schema is null"); this.table = requireNonNull(table, "table is null"); this.connectorOutputMetadata = requireNonNull(connectorOutputMetadata, "connectorOutputMetadata is null"); @@ -38,9 +38,9 @@ public QueryOutputMetadata(String connectorId, String schema, String table, Opti } @JsonProperty - public String getConnectorId() + public String getCatalogName() { - return connectorId; + return catalogName; } @JsonProperty diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/SplitStatistics.java b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/SplitStatistics.java index 711d951d6829a..886f4285cff85 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/SplitStatistics.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/eventlistener/SplitStatistics.java @@ -23,7 +23,6 @@ public class SplitStatistics private final Duration cpuTime; private final Duration wallTime; private final Duration queuedTime; - private final Duration userTime; private final Duration completedReadTime; private final long completedPositions; @@ -36,7 +35,6 @@ public SplitStatistics( Duration cpuTime, Duration wallTime, Duration queuedTime, - Duration userTime, Duration completedReadTime, long completedPositions, long completedDataSizeBytes, @@ -46,7 +44,6 @@ public SplitStatistics( this.cpuTime = requireNonNull(cpuTime, "cpuTime is null"); this.wallTime = requireNonNull(wallTime, "wallTime is null"); this.queuedTime = requireNonNull(queuedTime, "queuedTime is null"); - this.userTime = requireNonNull(userTime, "userTime is null"); this.completedReadTime = requireNonNull(completedReadTime, "completedReadTime is null"); this.completedPositions = completedPositions; this.completedDataSizeBytes = completedDataSizeBytes; @@ -69,11 +66,6 @@ public Duration getQueuedTime() return queuedTime; } - public Duration getUserTime() - { - return userTime; - } - public Duration getCompletedReadTime() { return completedReadTime; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/AggregationFunction.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/AggregationFunction.java index 73e4cb036c2cd..c11e6a0c953d6 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/AggregationFunction.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/AggregationFunction.java @@ -28,6 +28,10 @@ boolean decomposable() default true; + /** + * Indicates whether the result of the function depends on the order in which data is fed in, + * which allows the engine to optimize away ORDER BY clauses in aggregation calls + */ boolean isOrderSensitive() default false; boolean hidden() default false; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/Convention.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/Convention.java index 2668e7266abae..aac1080a8b845 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/Convention.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/Convention.java @@ -13,12 +13,15 @@ */ package com.facebook.presto.spi.function; -import com.facebook.presto.spi.InvocationConvention; +import com.facebook.presto.spi.function.InvocationConvention.InvocationArgumentConvention; +import com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention; public @interface Convention { - InvocationConvention.InvocationArgumentConvention[] arguments(); - InvocationConvention.InvocationReturnConvention result(); + InvocationArgumentConvention[] arguments(); + + InvocationReturnConvention result(); + boolean session() default false; boolean $notSpecified() default false; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionDependency.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionDependency.java index 408c1ffc20697..81c89280ec43c 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionDependency.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/FunctionDependency.java @@ -16,7 +16,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/InvocationConvention.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/InvocationConvention.java similarity index 97% rename from presto-spi/src/main/java/com/facebook/presto/spi/InvocationConvention.java rename to presto-spi/src/main/java/com/facebook/presto/spi/function/InvocationConvention.java index 7da8437dd907c..214cebb21ca75 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/InvocationConvention.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/InvocationConvention.java @@ -11,7 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.spi; +package com.facebook.presto.spi.function; import java.util.List; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/function/OperatorDependency.java b/presto-spi/src/main/java/com/facebook/presto/spi/function/OperatorDependency.java index d934a42fd142e..b4172aad61b35 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/function/OperatorDependency.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/function/OperatorDependency.java @@ -16,7 +16,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static com.facebook.presto.spi.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; +import static com.facebook.presto.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/predicate/TupleDomain.java b/presto-spi/src/main/java/com/facebook/presto/spi/predicate/TupleDomain.java index 21aba3e61d1f4..e4410522c190f 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/predicate/TupleDomain.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/predicate/TupleDomain.java @@ -328,10 +328,21 @@ public int hashCode() return Objects.hash(domains); } + @Override + public String toString() + { + if (isAll()) { + return "TupleDomain{ALL}"; + } + if (isNone()) { + return "TupleDomain{NONE}"; + } + return "TupleDomain{...}"; + } + public String toString(ConnectorSession session) { - StringBuilder buffer = new StringBuilder() - .append("TupleDomain:"); + StringBuilder buffer = new StringBuilder(); if (isAll()) { buffer.append("ALL"); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroup.java b/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroup.java index aa735de7e535c..ff53d66b7b5e0 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroup.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroup.java @@ -95,12 +95,4 @@ public interface ResourceGroup * Whether to export statistics about this group and allow configuration via JMX. */ void setJmxExport(boolean export); - - Duration getQueuedTimeLimit(); - - void setQueuedTimeLimit(Duration queuedTimeLimit); - - Duration getRunningTimeLimit(); - - void setRunningTimeLimit(Duration runningTimeLimit); } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroupConfigurationManager.java b/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroupConfigurationManager.java index 6593c58fb739a..eb9aedf6b6773 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroupConfigurationManager.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/resourceGroups/ResourceGroupConfigurationManager.java @@ -20,7 +20,7 @@ * and receives a {@link com.facebook.presto.spi.resourceGroups.SelectionContext} in return which * contains a fully-qualified {@link com.facebook.presto.spi.resourceGroups.ResourceGroupId}, * and a manager-specific data structure of type {@code C}. - * + *

    * At a later time, the engine may decide to construct a resource group with that ID. To do so, * it will walk the tree to find the right position for the group, and then create it. It also * creates any necessary parent groups. Every time the engine creates a group it will diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java b/presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java index 890d9e94c0a99..1336b3e581a94 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/security/AccessDeniedException.java @@ -17,6 +17,7 @@ import java.security.Principal; import java.util.Collection; +import java.util.Optional; import static com.facebook.presto.spi.StandardErrorCode.PERMISSION_DENIED; import static java.lang.String.format; @@ -29,14 +30,14 @@ public AccessDeniedException(String message) super(PERMISSION_DENIED, "Access Denied: " + message); } - public static void denySetUser(Principal principal, String userName) + public static void denySetUser(Optional principal, String userName) { denySetUser(principal, userName, null); } - public static void denySetUser(Principal principal, String userName, String extraInfo) + public static void denySetUser(Optional principal, String userName, String extraInfo) { - throw new AccessDeniedException(format("Principal %s cannot become user %s%s", principal, userName, formatExtraInfo(extraInfo))); + throw new AccessDeniedException(format("Principal %s cannot become user %s%s", principal.orElse(null), userName, formatExtraInfo(extraInfo))); } public static void denyCatalogAccess(String catalogName) diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/security/SystemAccessControl.java b/presto-spi/src/main/java/com/facebook/presto/spi/security/SystemAccessControl.java index 2344139047ac5..eb91d4e61e297 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/security/SystemAccessControl.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/security/SystemAccessControl.java @@ -19,6 +19,7 @@ import java.security.Principal; import java.util.Collections; +import java.util.Optional; import java.util.Set; import static com.facebook.presto.spi.security.AccessDeniedException.denyAddColumn; @@ -50,7 +51,7 @@ public interface SystemAccessControl * * @throws AccessDeniedException if not allowed */ - void checkCanSetUser(Principal principal, String userName); + void checkCanSetUser(Optional principal, String userName); /** * Check if identity is allowed to set the specified system property. diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/session/ResourceEstimates.java b/presto-spi/src/main/java/com/facebook/presto/spi/session/ResourceEstimates.java index 89d4be228b3d1..b86743f7320bc 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/session/ResourceEstimates.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/session/ResourceEstimates.java @@ -24,7 +24,7 @@ /** * Estimated resource usage for a query. - * + *

    * This class is under active development and should be considered beta. */ public final class ResourceEstimates diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ColumnStatistics.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ColumnStatistics.java index b7249a65a072b..135abdfb3fe4d 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ColumnStatistics.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ColumnStatistics.java @@ -13,42 +13,47 @@ */ package com.facebook.presto.spi.statistics; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; -import java.util.function.Consumer; +import java.util.Optional; -import static com.facebook.presto.spi.statistics.Estimate.unknownValue; -import static java.util.Collections.singletonList; -import static java.util.Collections.unmodifiableList; +import static java.lang.String.format; import static java.util.Objects.requireNonNull; public final class ColumnStatistics { - private static final List SINGLE_UNKNOWN_RANGE_STATISTICS = singletonList(RangeColumnStatistics.builder().build()); + private static final ColumnStatistics EMPTY = new ColumnStatistics(Estimate.unknown(), Estimate.unknown(), Estimate.unknown(), Optional.empty()); private final Estimate nullsFraction; - private final List rangeColumnStatistics; + private final Estimate distinctValuesCount; + private final Estimate dataSize; + private final Optional range; - private ColumnStatistics(Estimate nullsFraction, List rangeColumnStatistics) + public static ColumnStatistics empty() { - this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction can not be null"); - requireNonNull(rangeColumnStatistics, "rangeColumnStatistics can not be null"); - if (!rangeColumnStatistics.stream().allMatch(Objects::nonNull)) { - throw new NullPointerException("elements of rangeColumnStatistics can not be null"); - } - if (rangeColumnStatistics.size() > 1) { - // todo add support for multiple ranges. - throw new IllegalArgumentException("Statistics for multiple ranges are not supported"); + return EMPTY; + } + + public ColumnStatistics( + Estimate nullsFraction, + Estimate distinctValuesCount, + Estimate dataSize, + Optional range) + { + this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null"); + if (!nullsFraction.isUnknown()) { + if (nullsFraction.getValue() < 0 || nullsFraction.getValue() > 1) { + throw new IllegalArgumentException(format("nullsFraction must be between 0 and 1: %s", nullsFraction.getValue())); + } } - if (rangeColumnStatistics.isEmpty()) { - rangeColumnStatistics = SINGLE_UNKNOWN_RANGE_STATISTICS; + this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null"); + if (!distinctValuesCount.isUnknown() && distinctValuesCount.getValue() < 0) { + throw new IllegalArgumentException(format("distinctValuesCount must be greater than or equal to 0: %s", distinctValuesCount.getValue())); } - if (nullsFraction.isValueUnknown() != rangeColumnStatistics.get(0).getFraction().isValueUnknown()) { - throw new IllegalArgumentException("All or none fraction/nullsFraction must be set"); + this.dataSize = requireNonNull(dataSize, "dataSize is null"); + if (!dataSize.isUnknown() && dataSize.getValue() < 0) { + throw new IllegalArgumentException(format("dataSize must be greater than or equal to 0: %s", dataSize.getValue())); } - - this.rangeColumnStatistics = unmodifiableList(new ArrayList<>(rangeColumnStatistics)); + this.range = requireNonNull(range, "range is null"); } public Estimate getNullsFraction() @@ -56,9 +61,19 @@ public Estimate getNullsFraction() return nullsFraction; } - public RangeColumnStatistics getOnlyRangeColumnStatistics() + public Estimate getDistinctValuesCount() { - return rangeColumnStatistics.get(0); + return distinctValuesCount; + } + + public Estimate getDataSize() + { + return dataSize; + } + + public Optional getRange() + { + return range; } @Override @@ -72,13 +87,15 @@ public boolean equals(Object o) } ColumnStatistics that = (ColumnStatistics) o; return Objects.equals(nullsFraction, that.nullsFraction) && - Objects.equals(rangeColumnStatistics, that.rangeColumnStatistics); + Objects.equals(distinctValuesCount, that.distinctValuesCount) && + Objects.equals(dataSize, that.dataSize) && + Objects.equals(range, that.range); } @Override public int hashCode() { - return Objects.hash(nullsFraction, rangeColumnStatistics); + return Objects.hash(nullsFraction, distinctValuesCount, dataSize, range); } @Override @@ -86,7 +103,9 @@ public String toString() { return "ColumnStatistics{" + "nullsFraction=" + nullsFraction + - ", rangeColumnStatistics=" + rangeColumnStatistics + + ", distinctValuesCount=" + distinctValuesCount + + ", dataSize=" + dataSize + + ", range=" + range + '}'; } @@ -97,32 +116,44 @@ public static Builder builder() public static final class Builder { - private Estimate nullsFraction = unknownValue(); - private List rangeColumnStatistics = new ArrayList<>(); + private Estimate nullsFraction = Estimate.unknown(); + private Estimate distinctValuesCount = Estimate.unknown(); + private Estimate dataSize = Estimate.unknown(); + private Optional range = Optional.empty(); public Builder setNullsFraction(Estimate nullsFraction) { - this.nullsFraction = nullsFraction; + this.nullsFraction = requireNonNull(nullsFraction, "nullsFraction is null"); + return this; + } + + public Builder setDistinctValuesCount(Estimate distinctValuesCount) + { + this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount is null"); + return this; + } + + public Builder setDataSize(Estimate dataSize) + { + this.dataSize = requireNonNull(dataSize, "dataSize is null"); return this; } - public Builder addRange(Consumer rangeBuilderConsumer) + public Builder setRange(DoubleRange range) { - RangeColumnStatistics.Builder rangeBuilder = RangeColumnStatistics.builder(); - rangeBuilderConsumer.accept(rangeBuilder); - addRange(rangeBuilder.build()); + this.range = Optional.of(requireNonNull(range, "range is null")); return this; } - public Builder addRange(RangeColumnStatistics rangeColumnStatistics) + public Builder setRange(Optional range) { - this.rangeColumnStatistics.add(rangeColumnStatistics); + this.range = requireNonNull(range, "range is null"); return this; } public ColumnStatistics build() { - return new ColumnStatistics(nullsFraction, rangeColumnStatistics); + return new ColumnStatistics(nullsFraction, distinctValuesCount, dataSize, range); } } } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ComputedStatistics.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ComputedStatistics.java index aa3a5378c4494..07d97f5d359be 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ComputedStatistics.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/ComputedStatistics.java @@ -95,14 +95,16 @@ private Builder(List groupingColumns, List groupingValues) this.groupingValues = requireNonNull(groupingValues, "groupingValues is null"); } - public void addTableStatistic(TableStatisticType type, Block value) + public Builder addTableStatistic(TableStatisticType type, Block value) { tableStatistics.put(type, value); + return this; } - public void addColumnStatistic(ColumnStatisticMetadata columnStatisticMetadata, Block value) + public Builder addColumnStatistic(ColumnStatisticMetadata columnStatisticMetadata, Block value) { columnStatistics.put(columnStatisticMetadata, value); + return this; } public ComputedStatistics build() diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/DoubleRange.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/DoubleRange.java new file mode 100644 index 0000000000000..e359f44a8c774 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/DoubleRange.java @@ -0,0 +1,89 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.statistics; + +import java.util.Objects; + +import static java.lang.Double.isNaN; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; + +public class DoubleRange +{ + private final double min; + private final double max; + + public DoubleRange(double min, double max) + { + if (isNaN(min)) { + throw new IllegalArgumentException("min must not be NaN"); + } + if (isNaN(max)) { + throw new IllegalArgumentException("max must not be NaN"); + } + if (min > max) { + throw new IllegalArgumentException(format("max must be greater than or equal to min. min: %s. max: %s. ", min, max)); + } + this.min = min; + this.max = max; + } + + public double getMin() + { + return min; + } + + public double getMax() + { + return max; + } + + public static DoubleRange union(DoubleRange first, DoubleRange second) + { + requireNonNull(first, "first is null"); + requireNonNull(second, "second is null"); + return new DoubleRange(min(first.min, second.min), max(first.max, second.max)); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DoubleRange range = (DoubleRange) o; + return Double.compare(range.min, min) == 0 && + Double.compare(range.max, max) == 0; + } + + @Override + public int hashCode() + { + return Objects.hash(min, max); + } + + @Override + public String toString() + { + return "DoubleRange{" + + "min=" + min + + ", max=" + max + + '}'; + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/Estimate.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/Estimate.java index 1b04992473ed4..de101724abc2e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/Estimate.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/Estimate.java @@ -15,8 +15,9 @@ package com.facebook.presto.spi.statistics; import java.util.Objects; -import java.util.function.Function; +import static java.lang.Double.NaN; +import static java.lang.Double.isInfinite; import static java.lang.Double.isNaN; public final class Estimate @@ -25,27 +26,38 @@ public final class Estimate // Skipping for now as there hard to compute it properly and so far we do not have // usecase for that. - private static final Estimate UNKNOWN = new Estimate(Double.NaN); + private static final Estimate UNKNOWN = new Estimate(NaN); private static final Estimate ZERO = new Estimate(0); private final double value; - public static Estimate unknownValue() + public static Estimate unknown() { return UNKNOWN; } - public static Estimate zeroValue() + public static Estimate zero() { return ZERO; } - public Estimate(double value) + public static Estimate of(double value) + { + if (isNaN(value)) { + throw new IllegalArgumentException("value is NaN"); + } + if (isInfinite(value)) { + throw new IllegalArgumentException("value is infinite"); + } + return new Estimate(value); + } + + private Estimate(double value) { this.value = value; } - public boolean isValueUnknown() + public boolean isUnknown() { return isNaN(value); } @@ -55,16 +67,6 @@ public double getValue() return value; } - public Estimate map(Function mappingFunction) - { - if (isValueUnknown()) { - return this; - } - else { - return new Estimate(mappingFunction.apply(value)); - } - } - @Override public boolean equals(Object o) { diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/RangeColumnStatistics.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/RangeColumnStatistics.java deleted file mode 100644 index 91749ce0bce1e..0000000000000 --- a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/RangeColumnStatistics.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.spi.statistics; - -import java.util.Objects; -import java.util.Optional; - -import static com.facebook.presto.spi.statistics.Estimate.unknownValue; -import static java.util.Objects.requireNonNull; - -public final class RangeColumnStatistics -{ - private final Optional lowValue; - private final Optional highValue; - private final Estimate fraction; - private final Estimate dataSize; - private final Estimate distinctValuesCount; - - private RangeColumnStatistics( - Optional lowValue, - Optional highValue, - Estimate fraction, - Estimate dataSize, - Estimate distinctValuesCount) - { - this.lowValue = requireNonNull(lowValue, "lowValue can not be null"); - this.highValue = requireNonNull(highValue, "highValue can not be null"); - this.fraction = requireNonNull(fraction, "fraction can not be null"); - this.dataSize = requireNonNull(dataSize, "dataSize can not be null"); - this.distinctValuesCount = requireNonNull(distinctValuesCount, "distinctValuesCount can not be null"); - } - - public Optional getLowValue() - { - return lowValue; - } - - public Optional getHighValue() - { - return highValue; - } - - public Estimate getDataSize() - { - return dataSize; - } - - public Estimate getFraction() - { - return fraction; - } - - public Estimate getDistinctValuesCount() - { - return distinctValuesCount; - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - RangeColumnStatistics that = (RangeColumnStatistics) o; - return Objects.equals(lowValue, that.lowValue) && - Objects.equals(highValue, that.highValue) && - Objects.equals(fraction, that.fraction) && - Objects.equals(dataSize, that.dataSize) && - Objects.equals(distinctValuesCount, that.distinctValuesCount); - } - - @Override - public int hashCode() - { - return Objects.hash(lowValue, highValue, fraction, dataSize, distinctValuesCount); - } - - @Override - public String toString() - { - return "RangeColumnStatistics{" + "lowValue=" + lowValue + - ", highValue=" + highValue + - ", fraction=" + fraction + - ", dataSize=" + dataSize + - ", distinctValuesCount=" + distinctValuesCount + - '}'; - } - - public static Builder builder() - { - return new Builder(); - } - - public static final class Builder - { - private Optional lowValue = Optional.empty(); - private Optional highValue = Optional.empty(); - private Estimate dataSize = unknownValue(); - private Estimate fraction = unknownValue(); - private Estimate distinctValuesCount = unknownValue(); - - public Builder setLowValue(Optional lowValue) - { - this.lowValue = lowValue; - return this; - } - - public Builder setHighValue(Optional highValue) - { - this.highValue = highValue; - return this; - } - - public Builder setFraction(Estimate fraction) - { - this.fraction = fraction; - return this; - } - - public Builder setDataSize(Estimate dataSize) - { - this.dataSize = dataSize; - return this; - } - - public Builder setDistinctValuesCount(Estimate distinctValuesCount) - { - this.distinctValuesCount = distinctValuesCount; - return this; - } - - public RangeColumnStatistics build() - { - return new RangeColumnStatistics(lowValue, highValue, fraction, dataSize, distinctValuesCount); - } - } -} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/TableStatistics.java b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/TableStatistics.java index 9f87eb3c8c20c..af75c0fd46311 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/statistics/TableStatistics.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/statistics/TableStatistics.java @@ -20,20 +20,28 @@ import java.util.Map; import java.util.Objects; -import static com.facebook.presto.spi.statistics.Estimate.unknownValue; +import static java.lang.String.format; import static java.util.Collections.unmodifiableMap; import static java.util.Objects.requireNonNull; public final class TableStatistics { - public static final TableStatistics EMPTY_STATISTICS = TableStatistics.builder().build(); + private static final TableStatistics EMPTY = TableStatistics.builder().build(); private final Estimate rowCount; private final Map columnStatistics; + public static TableStatistics empty() + { + return EMPTY; + } + public TableStatistics(Estimate rowCount, Map columnStatistics) { this.rowCount = requireNonNull(rowCount, "rowCount can not be null"); + if (!rowCount.isUnknown() && rowCount.getValue() < 0) { + throw new IllegalArgumentException(format("rowCount must be greater than or equal to 0: %s", rowCount.getValue())); + } this.columnStatistics = unmodifiableMap(requireNonNull(columnStatistics, "columnStatistics can not be null")); } @@ -67,6 +75,15 @@ public int hashCode() return Objects.hash(rowCount, columnStatistics); } + @Override + public String toString() + { + return "TableStatistics{" + + "rowCount=" + rowCount + + ", columnStatistics=" + columnStatistics + + '}'; + } + public static Builder builder() { return new Builder(); @@ -74,7 +91,7 @@ public static Builder builder() public static final class Builder { - private Estimate rowCount = unknownValue(); + private Estimate rowCount = Estimate.unknown(); private Map columnStatisticsMap = new HashMap<>(); public Builder setRowCount(Estimate rowCount) @@ -83,11 +100,11 @@ public Builder setRowCount(Estimate rowCount) return this; } - public Builder setColumnStatistics(ColumnHandle columnName, ColumnStatistics columnStatistics) + public Builder setColumnStatistics(ColumnHandle columnHandle, ColumnStatistics columnStatistics) { - requireNonNull(columnName, "columnName can not be null"); + requireNonNull(columnHandle, "columnHandle can not be null"); requireNonNull(columnStatistics, "columnStatistics can not be null"); - this.columnStatisticsMap.put(columnName, columnStatistics); + this.columnStatisticsMap.put(columnHandle, columnStatistics); return this; } diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/MapType.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/MapType.java index cf2e06021172f..8a6e4b940f687 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/MapType.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/MapType.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.TypeUtils.checkElementNotNull; @@ -250,7 +251,7 @@ public String getDisplayName() return "map(" + keyType.getDisplayName() + ", " + valueType.getDisplayName() + ")"; } - public Block createBlockFromKeyValue(boolean[] mapIsNull, int[] offsets, Block keyBlock, Block valueBlock) + public Block createBlockFromKeyValue(Optional mapIsNull, int[] offsets, Block keyBlock, Block valueBlock) { return MapBlock.fromKeyValueBlock( mapIsNull, @@ -265,7 +266,7 @@ public Block createBlockFromKeyValue(boolean[] mapIsNull, int[] offsets, Block k /** * Create a map block directly without per element validations. - * + *

    * Internal use by com.facebook.presto.spi.Block only. */ public static Block createMapBlockInternal( @@ -273,7 +274,7 @@ public static Block createMapBlockInternal( Type keyType, int startOffset, int positionCount, - boolean[] mapIsNull, + Optional mapIsNull, int[] offsets, Block keyBlock, Block valueBlock, diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestParametricType.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestParametricType.java new file mode 100644 index 0000000000000..c6012fafab415 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestParametricType.java @@ -0,0 +1,50 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.type; + +import java.util.List; + +import static java.lang.String.format; + +public class QuantileDigestParametricType + implements ParametricType +{ + public static final QuantileDigestParametricType QDIGEST = new QuantileDigestParametricType(); + + @Override + public String getName() + { + return StandardTypes.QDIGEST; + } + + @Override + public Type createType(TypeManager typeManager, List parameters) + { + checkArgument(parameters.size() == 1, "QDIGEST type expects exactly one type as a parameter, got %s", parameters); + checkArgument( + parameters.get(0).getKind() == ParameterKind.TYPE, + "QDIGEST expects type as a parameter, got %s", + parameters); + // Validation check on the acceptable type (bigint, real, double) intentionally omitted + // because this is validated in each function and to allow for consistent error messaging + return new QuantileDigestType(parameters.get(0).getType()); + } + + private static void checkArgument(boolean argument, String format, Object... args) + { + if (!argument) { + throw new IllegalArgumentException(format(format, args)); + } + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestType.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestType.java new file mode 100644 index 0000000000000..d532a73c60928 --- /dev/null +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/QuantileDigestType.java @@ -0,0 +1,83 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.type; + +import com.facebook.presto.spi.ConnectorSession; +import com.facebook.presto.spi.block.Block; +import com.facebook.presto.spi.block.BlockBuilder; +import com.fasterxml.jackson.annotation.JsonCreator; +import io.airlift.slice.Slice; + +import java.util.List; + +import static java.util.Collections.singletonList; + +public class QuantileDigestType + extends AbstractVariableWidthType +{ + private final Type type; + + @JsonCreator + public QuantileDigestType(Type type) + { + super(new TypeSignature(StandardTypes.QDIGEST, TypeSignatureParameter.of(type.getTypeSignature())), Slice.class); + this.type = type; + } + + @Override + public void appendTo(Block block, int position, BlockBuilder blockBuilder) + { + if (block.isNull(position)) { + blockBuilder.appendNull(); + } + else { + block.writeBytesTo(position, 0, block.getSliceLength(position), blockBuilder); + blockBuilder.closeEntry(); + } + } + + @Override + public Slice getSlice(Block block, int position) + { + return block.getSlice(position, 0, block.getSliceLength(position)); + } + + @Override + public void writeSlice(BlockBuilder blockBuilder, Slice value) + { + writeSlice(blockBuilder, value, 0, value.length()); + } + + @Override + public void writeSlice(BlockBuilder blockBuilder, Slice value, int offset, int length) + { + blockBuilder.writeBytes(value, offset, length).closeEntry(); + } + + @Override + public Object getObjectValue(ConnectorSession session, Block block, int position) + { + if (block.isNull(position)) { + return null; + } + + return new SqlVarbinary(block.getSlice(position, 0, block.getSliceLength(position)).getBytes()); + } + + @Override + public List getTypeParameters() + { + return singletonList(type); + } +} diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/RowType.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/RowType.java index 79cdeff933ebf..498063f219fe4 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/RowType.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/RowType.java @@ -45,8 +45,8 @@ private RowType(TypeSignature typeSignature, List fields) this.fields = fields; this.fieldTypes = fields.stream() - .map(Field::getType) - .collect(Collectors.toList()); + .map(Field::getType) + .collect(Collectors.toList()); } public static RowType from(List fields) diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/StandardTypes.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/StandardTypes.java index 2d3a91c6e3119..bcdd37624c0b8 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/StandardTypes.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/StandardTypes.java @@ -25,6 +25,7 @@ public final class StandardTypes public static final String REAL = "real"; public static final String DOUBLE = "double"; public static final String HYPER_LOG_LOG = "HyperLogLog"; + public static final String QDIGEST = "qdigest"; public static final String P4_HYPER_LOG_LOG = "P4HyperLogLog"; public static final String INTERVAL_DAY_TO_SECOND = "interval day to second"; public static final String INTERVAL_YEAR_TO_MONTH = "interval year to month"; diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/Type.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/Type.java index e2601c6db0565..56e81be5df03e 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/Type.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/Type.java @@ -48,11 +48,9 @@ public interface Type /** * Gets the Java class type used to represent this value on the stack during - * expression execution. This value is used to determine which method should - * be called on Cursor, RecordSet or RandomAccessBlock to fetch a value of - * this type. + * expression execution. *

    - * Currently, this must be boolean, long, double, or Slice. + * Currently, this must be boolean, long, double, Slice or Block. */ Class getJavaType(); @@ -142,6 +140,8 @@ public interface Type /** * Are the values in the specified blocks at the specified positions equal? + *

    + * This method assumes input is not null. */ boolean equalTo(Block leftBlock, int leftPosition, Block rightBlock, int rightPosition); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSerde.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSerde.java index b3ddea195c9de..02f767b1c8ff3 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSerde.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSerde.java @@ -18,6 +18,7 @@ import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature; import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Objects.requireNonNull; public final class TypeSerde { @@ -27,21 +28,14 @@ private TypeSerde() public static void writeType(SliceOutput sliceOutput, Type type) { - if (sliceOutput == null) { - throw new NullPointerException("sliceOutput is null"); - } - if (type == null) { - throw new NullPointerException("type is null"); - } - + requireNonNull(sliceOutput, "sliceOutput is null"); + requireNonNull(type, "type is null"); writeLengthPrefixedString(sliceOutput, type.getTypeSignature().toString()); } public static Type readType(TypeManager typeManager, SliceInput sliceInput) { - if (sliceInput == null) { - throw new NullPointerException("sliceInput is null"); - } + requireNonNull(sliceInput, "sliceInput is null"); String name = readLengthPrefixedString(sliceInput); Type type = typeManager.getType(parseTypeSignature(name)); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSignature.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSignature.java index 72519acc07354..6b6367a97ca87 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSignature.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/TypeSignature.java @@ -41,7 +41,7 @@ public class TypeSignature private static final Pattern IDENTIFIER_PATTERN = Pattern.compile("[a-zA-Z_]([a-zA-Z0-9_:@])*"); private static final Map BASE_NAME_ALIAS_TO_CANONICAL = - new TreeMap(String.CASE_INSENSITIVE_ORDER); + new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private static final Set SIMPLE_TYPE_WITH_SPACES = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); diff --git a/presto-spi/src/main/java/com/facebook/presto/spi/type/VarbinaryType.java b/presto-spi/src/main/java/com/facebook/presto/spi/type/VarbinaryType.java index 9b08ead3621b3..d3e9f911e56b5 100644 --- a/presto-spi/src/main/java/com/facebook/presto/spi/type/VarbinaryType.java +++ b/presto-spi/src/main/java/com/facebook/presto/spi/type/VarbinaryType.java @@ -30,6 +30,11 @@ private VarbinaryType() super(parseTypeSignature(StandardTypes.VARBINARY), Slice.class); } + public static boolean isVarbinaryType(Type type) + { + return type instanceof VarbinaryType; + } + @Override public boolean isComparable() { diff --git a/presto-spi/src/main/resources/com/facebook/presto/spi/type/zone-index.properties b/presto-spi/src/main/resources/com/facebook/presto/spi/type/zone-index.properties index 2eb654f5527fe..6b3b40afbf463 100644 --- a/presto-spi/src/main/resources/com/facebook/presto/spi/type/zone-index.properties +++ b/presto-spi/src/main/resources/com/facebook/presto/spi/type/zone-index.properties @@ -2046,7 +2046,7 @@ 2037 Brazil/West 2038 Canada/Atlantic 2039 Canada/Central -2040 Canada/East-Saskatchewan +# 2040 Canada/East-Saskatchewan # removed from tzdata since 2017c 2041 Canada/Eastern 2042 Canada/Mountain 2043 Canada/Newfoundland @@ -2186,13 +2186,13 @@ 2177 CST6CDT 2178 Cuba 2179 EET -2180 EST +# 2180 EST # not in java.time 2181 EST5EDT 2182 Egypt 2183 Eire 2184 GB 2185 GB-Eire -2186 HST +# 2186 HST # not in java.time 2187 Hongkong 2188 Iceland 2189 Iran @@ -2202,7 +2202,7 @@ 2193 Kwajalein 2194 Libya 2195 MET -2196 MST +# 2196 MST # not in java.time 2197 MST7MDT 2198 NZ 2199 NZ-CHAT @@ -2235,53 +2235,3 @@ 2226 Asia/Atyrau 2227 Asia/Famagusta 2228 Europe/Saratov - -# Zones not supported in Java -# ROC - -# Zones not supported in Joda -# ACT -# AET -# AGT -# ART -# AST -# Asia/Khandyga -# Asia/Riyadh87 -# Asia/Riyadh88 -# Asia/Riyadh89 -# BET -# BST -# CAT -# CNT -# CST -# CTT -# EAT -# ECT -# IET -# IST -# JST -# MIT -# Mideast/Riyadh87 -# Mideast/Riyadh88 -# Mideast/Riyadh89 -# NET -# NST -# PLT -# PNT -# PRT -# PST -# SST -# SystemV/AST4 -# SystemV/AST4ADT -# SystemV/CST6 -# SystemV/CST6CDT -# SystemV/EST5 -# SystemV/EST5EDT -# SystemV/HST10 -# SystemV/MST7 -# SystemV/MST7MDT -# SystemV/PST8 -# SystemV/PST8PDT -# SystemV/YST9 -# SystemV/YST9YDT -# VST diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/TestPage.java b/presto-spi/src/test/java/com/facebook/presto/spi/TestPage.java index ac2d3ddbe8680..fb8efec97ec6a 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/TestPage.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/TestPage.java @@ -111,7 +111,7 @@ public void testGetPositions() } Block block = blockBuilder.build(); - Page page = new Page(block, block, block).getPositions(new int[]{0, 1, 1, 1, 2, 5, 5}, 1, 5); + Page page = new Page(block, block, block).getPositions(new int[] {0, 1, 1, 1, 2, 5, 5}, 1, 5); assertEquals(page.getPositionCount(), 5); for (int i = 0; i < 3; i++) { assertEquals(page.getBlock(i).getLong(0, 0), 1); diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/block/BenchmarkComputePosition.java b/presto-spi/src/test/java/com/facebook/presto/spi/block/BenchmarkComputePosition.java new file mode 100644 index 0000000000000..f8219879f9e87 --- /dev/null +++ b/presto-spi/src/test/java/com/facebook/presto/spi/block/BenchmarkComputePosition.java @@ -0,0 +1,94 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.block; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import org.openjdk.jmh.runner.options.VerboseMode; + +import java.util.concurrent.ThreadLocalRandom; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +@SuppressWarnings("MethodMayBeStatic") +@State(Scope.Thread) +@OutputTimeUnit(NANOSECONDS) +@Fork(3) +@Warmup(iterations = 10, time = 500, timeUnit = MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = MILLISECONDS) +@BenchmarkMode(Mode.AverageTime) +public class BenchmarkComputePosition +{ + private int hashTableSize = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE); + private long hashcode = ThreadLocalRandom.current().nextLong(); + + // This is the baseline. + @Benchmark + public long computePositionWithFloorMod() + { + return Math.floorMod(hashcode, hashTableSize); + } + + @Benchmark + public long computePositionWithMod() + { + return (int) (hashcode & 0x7fff_ffff_ffff_ffffL) % hashTableSize; + } + + // This reduction function requires the hashTableSize to be power of 2. + @Benchmark + public long computePositionWithMask() + { + return (int) hashcode & (hashTableSize - 1); + } + + // This function reduces the 64 bit hashcode to [0, hashTableSize) uniformly if the hashcode has uniform distribution. + // It first reduces the hashcode to 32 bit integer x then normalizes it to x / 2^32 * hashSize to reduce the range of x + // from [0, 2^32) to [0, hashTableSize). + @Benchmark + public long computePositionWithBitShifting() + { + return (int) ((Integer.toUnsignedLong(Long.hashCode(hashcode)) * hashTableSize) >> 32); + } + + // This function used division instead of bit shifting. JVM would compile it to bit shifting instructions thus it + // has similar performance to computePositionWithBitShifting() + @Benchmark + public long computePositionWithDivision() + { + return (int) ((Integer.toUnsignedLong(Long.hashCode(hashcode)) * hashTableSize) / (1 << 32)); + } + + public static void main(String[] args) + throws Throwable + { + Options options = new OptionsBuilder() + .verbosity(VerboseMode.NORMAL) + .include(".*" + BenchmarkComputePosition.class.getSimpleName() + ".*") + .build(); + + new Runner(options).run(); + } +} diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/block/TestBlockRetainedSizeBreakdown.java b/presto-spi/src/test/java/com/facebook/presto/spi/block/TestBlockRetainedSizeBreakdown.java index feb4a005e6e3f..4aeb0360f4fa6 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/block/TestBlockRetainedSizeBreakdown.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/block/TestBlockRetainedSizeBreakdown.java @@ -21,7 +21,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.BiConsumer; +import java.util.function.ObjLongConsumer; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; @@ -137,7 +137,7 @@ private static void checkRetainedSize(Block block, boolean getRegionCreateNewObj AtomicLong objectSize = new AtomicLong(); Object2LongOpenCustomHashMap trackedObjects = new Object2LongOpenCustomHashMap<>(new ObjectStrategy()); - BiConsumer consumer = (object, size) -> { + ObjLongConsumer consumer = (object, size) -> { objectSize.addAndGet(size); trackedObjects.addTo(object, 1); }; diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/block/TestingSession.java b/presto-spi/src/test/java/com/facebook/presto/spi/block/TestingSession.java index 68e0a794d66a0..2cdade5c7aff0 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/block/TestingSession.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/block/TestingSession.java @@ -35,12 +35,6 @@ public String getQueryId() return "test_query_id"; } - @Override - public String getPath() - { - return "path"; - } - @Override public Optional getSource() { @@ -83,12 +77,6 @@ public boolean isLegacyTimestamp() return true; } - @Override - public boolean isLegacyRoundNBigint() - { - return false; - } - @Override public T getProperty(String name, Class type) { diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/connector/classloader/TestClassLoaderSafeWrappers.java b/presto-spi/src/test/java/com/facebook/presto/spi/connector/classloader/TestClassLoaderSafeWrappers.java index cb10821e5c5ed..027382c045ca5 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/connector/classloader/TestClassLoaderSafeWrappers.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/connector/classloader/TestClassLoaderSafeWrappers.java @@ -20,14 +20,9 @@ import com.facebook.presto.spi.connector.ConnectorPageSinkProvider; import com.facebook.presto.spi.connector.ConnectorPageSourceProvider; import com.facebook.presto.spi.connector.ConnectorSplitManager; -import com.google.common.collect.ImmutableSet; import org.testng.annotations.Test; -import java.lang.reflect.Method; - -import static java.lang.String.format; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; +import static com.facebook.presto.spi.testing.InterfaceTestUtils.assertAllMethodsOverridden; public class TestClassLoaderSafeWrappers { @@ -41,18 +36,4 @@ public void testAllMethodsOverridden() assertAllMethodsOverridden(ConnectorSplitManager.class, ClassLoaderSafeConnectorSplitManager.class); assertAllMethodsOverridden(ConnectorNodePartitioningProvider.class, ClassLoaderSafeNodePartitioningProvider.class); } - - private static void assertAllMethodsOverridden(Class iface, Class clazz) - { - assertEquals(ImmutableSet.copyOf(clazz.getInterfaces()), ImmutableSet.of(iface)); - for (Method method : iface.getMethods()) { - try { - Method override = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); - assertEquals(override.getReturnType(), method.getReturnType()); - } - catch (NoSuchMethodException e) { - fail(format("%s does not override [%s]", clazz.getName(), method)); - } - } - } } diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/statistics/TestDoubleRange.java b/presto-spi/src/test/java/com/facebook/presto/spi/statistics/TestDoubleRange.java new file mode 100644 index 0000000000000..a3d481e902ef9 --- /dev/null +++ b/presto-spi/src/test/java/com/facebook/presto/spi/statistics/TestDoubleRange.java @@ -0,0 +1,77 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.statistics; + +import org.testng.annotations.Test; + +import static com.facebook.presto.spi.statistics.DoubleRange.union; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.testng.Assert.assertEquals; + +public class TestDoubleRange +{ + @Test + public void testRange() + { + assertRange(0, 0); + assertRange(0, 0.1); + assertRange(-0.1, 0.1); + assertRange(Double.NEGATIVE_INFINITY, 0); + assertRange(Float.NEGATIVE_INFINITY, 0); + assertRange(Double.NEGATIVE_INFINITY, -1.0 * Double.MAX_VALUE); + assertRange(Float.NEGATIVE_INFINITY, -1.0 * Double.MAX_VALUE); + assertRange(Float.NEGATIVE_INFINITY, -1.0 * Float.MAX_VALUE); + assertRange(Double.MAX_VALUE, Double.POSITIVE_INFINITY); + assertRange(Float.MAX_VALUE, Double.POSITIVE_INFINITY); + assertRange(Double.MAX_VALUE, Float.POSITIVE_INFINITY); + assertRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + assertRange(Double.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY); + assertRange(Float.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + assertThatThrownBy(() -> new DoubleRange(Double.NaN, 0)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(0, Double.NaN)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.NaN, Double.NaN)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Float.NaN, Float.NaN)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(1, 0)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(0, Double.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(0, Float.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(-1.0 * Double.MAX_VALUE, Double.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(-1.0 * Float.MAX_VALUE, Double.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(-1.0 * Double.MAX_VALUE, Float.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.POSITIVE_INFINITY, Double.MAX_VALUE)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Float.POSITIVE_INFINITY, Double.MAX_VALUE)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.POSITIVE_INFINITY, Float.MAX_VALUE)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Float.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Double.POSITIVE_INFINITY, 0)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> new DoubleRange(Float.POSITIVE_INFINITY, 0)).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void testUnion() + { + assertEquals(union(new DoubleRange(1, 2), new DoubleRange(4, 5)), new DoubleRange(1, 5)); + assertEquals(union(new DoubleRange(1, 2), new DoubleRange(1, 2)), new DoubleRange(1, 2)); + assertEquals(union(new DoubleRange(4, 5), new DoubleRange(1, 2)), new DoubleRange(1, 5)); + assertEquals(union(new DoubleRange(Double.NEGATIVE_INFINITY, 0), new DoubleRange(0, Double.POSITIVE_INFINITY)), new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + assertEquals(union(new DoubleRange(0, Double.POSITIVE_INFINITY), new DoubleRange(Double.NEGATIVE_INFINITY, 0)), new DoubleRange(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + } + + private static void assertRange(double min, double max) + { + DoubleRange range = new DoubleRange(min, max); + assertEquals(range.getMin(), min); + assertEquals(range.getMax(), max); + } +} diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/testing/InterfaceTestUtils.java b/presto-spi/src/test/java/com/facebook/presto/spi/testing/InterfaceTestUtils.java new file mode 100644 index 0000000000000..2e1d5844b3e16 --- /dev/null +++ b/presto-spi/src/test/java/com/facebook/presto/spi/testing/InterfaceTestUtils.java @@ -0,0 +1,41 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.spi.testing; + +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; + +import static java.lang.String.format; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +public final class InterfaceTestUtils +{ + private InterfaceTestUtils() {} + + public static void assertAllMethodsOverridden(Class iface, Class clazz) + { + assertEquals(ImmutableSet.copyOf(clazz.getInterfaces()), ImmutableSet.of(iface)); + for (Method method : iface.getMethods()) { + try { + Method override = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); + assertEquals(override.getReturnType(), method.getReturnType()); + } + catch (NoSuchMethodException e) { + fail(format("%s does not override [%s]", clazz.getName(), method)); + } + } + } +} diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/type/TestChars.java b/presto-spi/src/test/java/com/facebook/presto/spi/type/TestChars.java index 52a5698911590..935b1120df5dd 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/type/TestChars.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/type/TestChars.java @@ -66,9 +66,9 @@ public void testByteCountWithoutTrailingSpaces() assertByteCountWithoutTrailingSpace(" ", 0, 0, ""); // invalid code points - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, new byte[]{(byte) 0x81, (byte) 0x81}); - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 1, new byte[]{(byte) 0x81}); - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 0, new byte[]{}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, new byte[] {(byte) 0x81, (byte) 0x81}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 1, new byte[] {(byte) 0x81}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 0, new byte[] {}); } @Test @@ -82,9 +82,9 @@ public void testByteCountWithoutTrailingSpacesWithCodePointLimit() assertByteCountWithoutTrailingSpace("abc def ", 0, 5, 4, "abc"); // invalid code points - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 3, 3, new byte[]{(byte) 0x81, (byte) 0x81}); - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 2, 3, new byte[]{(byte) 0x81, (byte) 0x81}); - assertByteCountWithoutTrailingSpace(new byte[]{(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 0, 3, new byte[]{}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 3, 3, new byte[] {(byte) 0x81, (byte) 0x81}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 2, 3, new byte[] {(byte) 0x81, (byte) 0x81}); + assertByteCountWithoutTrailingSpace(new byte[] {(byte) 0x81, (byte) 0x81, (byte) ' ', (byte) 0x81}, 0, 0, 3, new byte[] {}); } private static void assertByteCountWithoutTrailingSpaceFailure(String string, int offset, int maxLength) diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/type/TestTimeZoneKey.java b/presto-spi/src/test/java/com/facebook/presto/spi/type/TestTimeZoneKey.java index 878eca268ca76..58242426eb613 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/type/TestTimeZoneKey.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/type/TestTimeZoneKey.java @@ -176,6 +176,19 @@ public void testZoneKeyIdRange() hasValue[key] = true; } + // previous spot for Canada/East-Saskatchewan + assertFalse(hasValue[2040]); + hasValue[2040] = true; + // previous spot for EST + assertFalse(hasValue[2180]); + hasValue[2180] = true; + // previous spot for HST + assertFalse(hasValue[2186]); + hasValue[2186] = true; + // previous spot for MST + assertFalse(hasValue[2196]); + hasValue[2196] = true; + for (int i = 0; i < hasValue.length; i++) { assertTrue(hasValue[i], "There is no time zone with key " + i); } @@ -200,7 +213,7 @@ public int compare(TimeZoneKey left, TimeZoneKey right) hasher.putString(timeZoneKey.getId(), StandardCharsets.UTF_8); } // Zone file should not (normally) be changed, so let's make this more difficult - assertEquals(hasher.hash().asLong(), -5839014144088293930L, "zone-index.properties file contents changed!"); + assertEquals(hasher.hash().asLong(), -4582158485614614451L, "zone-index.properties file contents changed!"); } public void assertTimeZoneNotSupported(String zoneId) diff --git a/presto-spi/src/test/java/com/facebook/presto/spi/type/VarcharsTest.java b/presto-spi/src/test/java/com/facebook/presto/spi/type/VarcharsTest.java index ab4c65ccc7120..8d73d6bda2bfc 100644 --- a/presto-spi/src/test/java/com/facebook/presto/spi/type/VarcharsTest.java +++ b/presto-spi/src/test/java/com/facebook/presto/spi/type/VarcharsTest.java @@ -102,9 +102,9 @@ public void testByteCount() assertByteCountFailure("\u6000\u6001\u6002\u6003", 21, 0, 1); // invalid code points; always return the original lengths unless code point count is 0 - assertByteCount(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 0, new byte[]{}); - assertByteCount(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 1, new byte[]{(byte) 0x81, (byte) 0x81}); - assertByteCount(new byte[]{(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 3, new byte[]{(byte) 0x81, (byte) 0x81}); + assertByteCount(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 0, new byte[] {}); + assertByteCount(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 1, new byte[] {(byte) 0x81, (byte) 0x81}); + assertByteCount(new byte[] {(byte) 0x81, (byte) 0x81, (byte) 0x81}, 0, 2, 3, new byte[] {(byte) 0x81, (byte) 0x81}); } private static void assertByteCountFailure(String string, int offset, int length, int codePointCount) diff --git a/presto-sqlserver/pom.xml b/presto-sqlserver/pom.xml index 0e0d315549719..fc7e0eeb680c2 100644 --- a/presto-sqlserver/pom.xml +++ b/presto-sqlserver/pom.xml @@ -3,7 +3,7 @@ presto-root com.facebook.presto - 0.209-SNAPSHOT + 0.216-SNAPSHOT 4.0.0 diff --git a/presto-teradata-functions/pom.xml b/presto-teradata-functions/pom.xml index 28c12c3529957..4e55e53b42db5 100644 --- a/presto-teradata-functions/pom.xml +++ b/presto-teradata-functions/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-teradata-functions @@ -31,6 +31,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + org.antlr antlr4-runtime diff --git a/presto-teradata-functions/src/test/java/com/facebook/presto/teradata/functions/TestTeradataDateFunctions.java b/presto-teradata-functions/src/test/java/com/facebook/presto/teradata/functions/TestTeradataDateFunctions.java index 69c8dd42b6513..1b626233e782f 100644 --- a/presto-teradata-functions/src/test/java/com/facebook/presto/teradata/functions/TestTeradataDateFunctions.java +++ b/presto-teradata-functions/src/test/java/com/facebook/presto/teradata/functions/TestTeradataDateFunctions.java @@ -17,32 +17,25 @@ import com.facebook.presto.operator.scalar.AbstractTestFunctions; import com.facebook.presto.spi.type.DateType; import com.facebook.presto.spi.type.SqlDate; -import com.facebook.presto.spi.type.TimeZoneKey; import com.facebook.presto.spi.type.TimestampType; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import java.time.LocalDate; import static com.facebook.presto.metadata.FunctionExtractor.extractFunctions; -import static com.facebook.presto.spi.type.TimeZoneKey.getTimeZoneKey; import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.testing.DateTimeTestingUtils.sqlTimestampOf; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; -import static com.facebook.presto.util.DateTimeZoneIndex.getDateTimeZone; import static java.lang.Math.toIntExact; public class TestTeradataDateFunctions extends AbstractTestFunctions { - private static final TimeZoneKey TIME_ZONE_KEY = getTimeZoneKey("Asia/Kathmandu"); - private static final DateTimeZone DATE_TIME_ZONE = getDateTimeZone(TIME_ZONE_KEY); private static final Session SESSION = testSessionBuilder() .setCatalog("catalog") .setSchema("schema") - .setTimeZoneKey(TIME_ZONE_KEY) .build(); protected TestTeradataDateFunctions() @@ -139,7 +132,7 @@ private void assertTimestamp(String projection, int year, int month, int day, in assertFunction( projection, TimestampType.TIMESTAMP, - sqlTimestampOf(year, month, day, hour, minutes, seconds, 0, DATE_TIME_ZONE, SESSION.getTimeZoneKey(), SESSION)); + sqlTimestampOf(year, month, day, hour, minutes, seconds, 0, SESSION)); } private void assertDate(String projection, int year, int month, int day) diff --git a/presto-testing-server-launcher/pom.xml b/presto-testing-server-launcher/pom.xml index 16d3b927b4be4..e7d28fc4084a7 100644 --- a/presto-testing-server-launcher/pom.xml +++ b/presto-testing-server-launcher/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-testing-server-launcher diff --git a/presto-tests/pom.xml b/presto-tests/pom.xml index 59746b5c388ab..718089a48385e 100644 --- a/presto-tests/pom.xml +++ b/presto-tests/pom.xml @@ -5,7 +5,7 @@ presto-root com.facebook.presto - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-tests diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestAggregations.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestAggregations.java index 9a15c0d979328..e55cc1df6b89e 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestAggregations.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestAggregations.java @@ -638,14 +638,14 @@ public void testGroupByNullIf() public void testGroupByExtract() { // whole expression in group by - assertQuery("SELECT EXTRACT(YEAR FROM now()), count(*) FROM orders GROUP BY EXTRACT(YEAR FROM now())"); + assertQuery("SELECT EXTRACT(YEAR FROM orderdate), count(*) FROM orders GROUP BY EXTRACT(YEAR FROM orderdate)"); assertQuery( - "SELECT EXTRACT(YEAR FROM now()), count(*) FROM orders GROUP BY 1", - "SELECT EXTRACT(YEAR FROM now()), count(*) FROM orders GROUP BY EXTRACT(YEAR FROM now())"); + "SELECT EXTRACT(YEAR FROM orderdate), count(*) FROM orders GROUP BY 1", + "SELECT EXTRACT(YEAR FROM orderdate), count(*) FROM orders GROUP BY EXTRACT(YEAR FROM orderdate)"); // argument in group by - assertQuery("SELECT EXTRACT(YEAR FROM now()), count(*) FROM orders GROUP BY now()"); + assertQuery("SELECT EXTRACT(YEAR FROM orderdate), count(*) FROM orders GROUP BY orderdate"); } @Test @@ -687,20 +687,20 @@ public void testApproximateCountDistinct() assertQuery("SELECT approx_distinct(orderdate, 0.023) FROM orders", "SELECT 2443"); // test timestamp - assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP)) FROM orders", "SELECT 2384"); - assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP), 0.023) FROM orders", "SELECT 2384"); + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP)) FROM orders", "SELECT 2347"); + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP), 0.023) FROM orders", "SELECT 2347"); // test timestamp with time zone - assertQuery("SELECT approx_distinct(CAST((orderdate) AS TIMESTAMP WITH TIME ZONE)) FROM orders", "SELECT 2384"); - assertQuery("SELECT approx_distinct(CAST((orderdate) AS TIMESTAMP WITH TIME ZONE), 0.023) FROM orders", "SELECT 2384"); + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP WITH TIME ZONE)) FROM orders", "SELECT 2347"); + assertQuery("SELECT approx_distinct(CAST(orderdate AS TIMESTAMP WITH TIME ZONE), 0.023) FROM orders", "SELECT 2347"); // test time - assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME)) FROM orders", "SELECT 993"); - assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME), 0.023) FROM orders", "SELECT 993"); + assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME)) FROM orders", "SELECT 996"); + assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME), 0.023) FROM orders", "SELECT 996"); // test time with time zone - assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE)) FROM orders", "SELECT 993"); - assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE), 0.023) FROM orders", "SELECT 993"); + assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE)) FROM orders", "SELECT 996"); + assertQuery("SELECT approx_distinct(CAST(from_unixtime(custkey) AS TIME WITH TIME ZONE), 0.023) FROM orders", "SELECT 996"); // test short decimal assertQuery("SELECT approx_distinct(CAST(custkey AS DECIMAL(18, 0))) FROM orders", "SELECT 990"); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java index 1a31a04188724..7bb7fc55bcc64 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java @@ -17,6 +17,7 @@ import com.facebook.presto.SystemSessionProperties; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.security.Identity; import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; import com.facebook.presto.testing.MaterializedResult; @@ -65,6 +66,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.Collectors.toList; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; public abstract class AbstractTestDistributedQueries @@ -394,7 +396,7 @@ public void testAddColumn() assertUpdate("INSERT INTO test_add_column SELECT * FROM test_add_column_a", 1); MaterializedResult materializedRows = computeActual("SELECT x, a FROM test_add_column ORDER BY x"); assertEquals(materializedRows.getMaterializedRows().get(0).getField(0), 123); - assertEquals(materializedRows.getMaterializedRows().get(0).getField(1), null); + assertNull(materializedRows.getMaterializedRows().get(0).getField(1)); assertEquals(materializedRows.getMaterializedRows().get(1).getField(0), 234); assertEquals(materializedRows.getMaterializedRows().get(1).getField(1), 111L); @@ -402,11 +404,11 @@ public void testAddColumn() assertUpdate("INSERT INTO test_add_column SELECT * FROM test_add_column_ab", 1); materializedRows = computeActual("SELECT x, a, b FROM test_add_column ORDER BY x"); assertEquals(materializedRows.getMaterializedRows().get(0).getField(0), 123); - assertEquals(materializedRows.getMaterializedRows().get(0).getField(1), null); - assertEquals(materializedRows.getMaterializedRows().get(0).getField(2), null); + assertNull(materializedRows.getMaterializedRows().get(0).getField(1)); + assertNull(materializedRows.getMaterializedRows().get(0).getField(2)); assertEquals(materializedRows.getMaterializedRows().get(1).getField(0), 234); assertEquals(materializedRows.getMaterializedRows().get(1).getField(1), 111L); - assertEquals(materializedRows.getMaterializedRows().get(1).getField(2), null); + assertNull(materializedRows.getMaterializedRows().get(1).getField(2)); assertEquals(materializedRows.getMaterializedRows().get(2).getField(0), 345); assertEquals(materializedRows.getMaterializedRows().get(2).getField(1), 222L); assertEquals(materializedRows.getMaterializedRows().get(2).getField(2), 33.3); @@ -758,8 +760,9 @@ public void testQueryLoggingCount() executeExclusively(() -> { assertUntilTimeout( () -> assertEquals( - queryManager.getAllQueryInfo() - .stream() + queryManager.getQueries().stream() + .map(BasicQueryInfo::getQueryId) + .map(queryManager::getFullQueryInfo) .filter(info -> !info.isFinalQueryInfo()) .collect(toList()), ImmutableList.of()), diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestIndexedQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestIndexedQueries.java index eae6725fc9eb8..5688f5c5c5f22 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestIndexedQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestIndexedQueries.java @@ -51,6 +51,19 @@ public void testExampleSystemTable() assertEquals(result.getOnlyColumnAsSet(), ImmutableSet.of("example")); } + @Test + public void testExplainAnalyzeIndexJoin() + { + assertQuerySucceeds(getSession(), "EXPLAIN ANALYZE " + + " SELECT *\n" + + "FROM (\n" + + " SELECT *\n" + + " FROM lineitem\n" + + " WHERE partkey % 8 = 0) l\n" + + "JOIN orders o\n" + + " ON l.orderkey = o.orderkey"); + } + @Test public void testBasicIndexJoin() { diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java index 9d8609e4eff4d..95ad6fb092686 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java @@ -21,6 +21,7 @@ import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; import com.facebook.presto.spi.type.VarcharType; +import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinDistributionType; import com.facebook.presto.sql.analyzer.FeaturesConfig.JoinReorderingStrategy; import com.facebook.presto.sql.analyzer.SemanticException; import com.facebook.presto.testing.MaterializedResult; @@ -52,6 +53,7 @@ import java.util.stream.IntStream; import static com.facebook.presto.SystemSessionProperties.DISTRIBUTED_SORT; +import static com.facebook.presto.SystemSessionProperties.JOIN_DISTRIBUTION_TYPE; import static com.facebook.presto.SystemSessionProperties.JOIN_REORDERING_STRATEGY; import static com.facebook.presto.connector.informationSchema.InformationSchemaMetadata.INFORMATION_SCHEMA; import static com.facebook.presto.operator.scalar.ApplyFunction.APPLY_FUNCTION; @@ -64,8 +66,6 @@ import static com.facebook.presto.spi.type.VarcharType.VARCHAR; import static com.facebook.presto.spi.type.VarcharType.createVarcharType; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_PARAMETER_USAGE; -import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_CATALOG; -import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_SCHEMA; import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MUST_BE_AGGREGATE_OR_GROUP_BY; import static com.facebook.presto.sql.tree.ExplainType.Type.DISTRIBUTED; import static com.facebook.presto.sql.tree.ExplainType.Type.IO; @@ -158,13 +158,6 @@ public void testParsingError() assertQueryFails("SELECT foo FROM", "line 1:16: mismatched input ''. Expecting: .*"); } - @Test - public void testShowPartitions() - { - assertQueryFails("SHOW PARTITIONS FROM orders", "line 1:1: SHOW PARTITIONS no longer exists. Use this instead: SELECT \\* FROM \"orders\\$partitions\""); - assertQueryFails("SHOW PARTITIONS FROM abc.orders", "line 1:1: SHOW PARTITIONS no longer exists. Use this instead: SELECT \\* FROM \"abc\".\"orders\\$partitions\""); - } - @Test public void selectLargeInterval() { @@ -650,6 +643,32 @@ public void testArrayAgg() assertQuery("SELECT clerk, cardinality(array_agg(orderkey)) FROM orders GROUP BY clerk", "SELECT clerk, count(*) FROM orders GROUP BY clerk"); } + @Test + public void testReduceAgg() + { + assertQuery( + "SELECT x, reduce_agg(y, 1, (a, b) -> a * b, (a, b) -> a * b) " + + "FROM (VALUES (1, 5), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 5 * 6 * 7), (2, 8 * 9), (3, 10)"); + assertQuery( + "SELECT x, reduce_agg(y, 0, (a, b) -> a + b, (a, b) -> a + b) " + + "FROM (VALUES (1, 5), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, 5 + 6 + 7), (2, 8 + 9), (3, 10)"); + + assertQuery( + "SELECT x, reduce_agg(y, 1, (a, b) -> a * b, (a, b) -> a * b) " + + "FROM (VALUES (1, CAST(5 AS DOUBLE)), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, CAST(5 AS DOUBLE) * 6 * 7), (2, 8 * 9), (3, 10)"); + assertQuery( + "SELECT x, reduce_agg(y, 0, (a, b) -> a + b, (a, b) -> a + b) " + + "FROM (VALUES (1, CAST(5 AS DOUBLE)), (1, 6), (1, 7), (2, 8), (2, 9), (3, 10)) AS t(x, y) " + + "GROUP BY x", + "VALUES (1, CAST(5 AS DOUBLE) + 6 + 7), (2, 8 + 9), (3, 10)"); + } + @Test public void testRows() { @@ -4650,27 +4669,8 @@ public void testShowTablesFrom() result = computeActual("SHOW TABLES FROM " + catalog + "." + schema); assertTrue(result.getOnlyColumnAsSet().containsAll(expectedTables)); - try { - computeActual("SHOW TABLES FROM UNKNOWN"); - fail("Showing tables in an unknown schema should fail"); - } - catch (SemanticException e) { - assertEquals(e.getCode(), MISSING_SCHEMA); - } - catch (RuntimeException e) { - assertEquals(e.getMessage(), "line 1:1: Schema 'unknown' does not exist"); - } - - try { - computeActual("SHOW TABLES FROM UNKNOWNCATALOG.UNKNOWNSCHEMA"); - fail("Showing tables in an unknown catalog and unknown schema should fail with unknown catalog"); - } - catch (SemanticException e) { - assertEquals(e.getCode(), MISSING_CATALOG); - } - catch (RuntimeException e) { - assertEquals(e.getMessage(), "line 1:1: Catalog 'unknowncatalog' does not exist"); - } + assertQueryFails("SHOW TABLES FROM UNKNOWN", "line 1:1: Schema 'unknown' does not exist"); + assertQueryFails("SHOW TABLES FROM UNKNOWNCATALOG.UNKNOWNSCHEMA", "line 1:1: Catalog 'unknowncatalog' does not exist"); } @Test @@ -4729,74 +4729,32 @@ public void testShowColumns() format("%s does not matche neither of %s and %s", actual, expectedParametrizedVarchar, expectedUnparametrizedVarchar)); } - @Test - public void testShowStatsWithoutFromFails() - { - assertQueryFails("SHOW STATS FOR (SELECT 1)", ".*There must be exactly one table in query passed to SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithMultipleFromFails() - { - assertQueryFails("SHOW STATS FOR (SELECT * FROM orders, lineitem)", ".*There must be exactly one table in query passed to SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithGroupByFails() - { - assertQueryFails("SHOW STATS FOR (SELECT avg(totalprice) FROM orders GROUP BY clerk)", ".*GROUP BY is not supported in SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithHavingFails() - { - assertQueryFails("SHOW STATS FOR (SELECT avg(orderkey) FROM orders HAVING avg(orderkey) < 5)", ".*HAVING is not supported in SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithSelectDistinctFails() - { - assertQueryFails("SHOW STATS FOR (SELECT DISTINCT * FROM orders)", ".*DISTINCT is not supported by SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithSelectFunctionCallFails() - { - assertQueryFails("SHOW STATS FOR (SELECT sin(orderkey) FROM orders)", ".*Only \\* and column references are supported by SHOW STATS SELECT clause"); - } - - @Test - public void testShowStatsWithWhereFunctionCallFails() - { - assertQueryFails("SHOW STATS FOR (SELECT orderkey FROM orders WHERE sin(orderkey) > 0)", ".*Only literals, column references, comparators, is \\(not\\) null and logical operators are allowed in WHERE of SHOW STATS SELECT clause"); - } - @Test public void testAtTimeZone() { // TODO the expected values here are non-sensical due to https://github.com/prestodb/presto/issues/7122 - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE INTERVAL '07:09' hour to minute"), zonedDateTime("2012-10-31 08:09:00.000 +07:09")); - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Oral'"), zonedDateTime("2012-10-31 06:00:00.000 Asia/Oral")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE INTERVAL '07:09' hour to minute"), zonedDateTime("2012-10-30 18:09:00.000 +07:09")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Oral'"), zonedDateTime("2012-10-30 16:00:00.000 Asia/Oral")); assertEquals(computeScalar("SELECT MIN(x) AT TIME ZONE 'America/Chicago' FROM (VALUES TIMESTAMP '1970-01-01 00:01:00+00:00') t(x)"), zonedDateTime("1969-12-31 18:01:00.000 America/Chicago")); - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE '+07:09'"), zonedDateTime("2012-10-31 08:09:00.000 +07:09")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE '+07:09'"), zonedDateTime("2012-10-30 18:09:00.000 +07:09")); assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00 UTC' AT TIME ZONE 'America/Los_Angeles'"), zonedDateTime("2012-10-30 18:00:00.000 America/Los_Angeles")); - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles'"), zonedDateTime("2012-10-30 18:00:00.000 America/Los_Angeles")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles'"), zonedDateTime("2012-10-30 04:00:00.000 America/Los_Angeles")); assertEquals(computeActual("SELECT x AT TIME ZONE 'America/Los_Angeles' FROM (values TIMESTAMP '1970-01-01 00:01:00+00:00', TIMESTAMP '1970-01-01 08:01:00+08:00', TIMESTAMP '1969-12-31 16:01:00-08:00') t(x)").getOnlyColumnAsSet(), ImmutableSet.of(zonedDateTime("1969-12-31 16:01:00.000 America/Los_Angeles"))); assertEquals(computeActual("SELECT x AT TIME ZONE 'America/Los_Angeles' FROM (values TIMESTAMP '1970-01-01 00:01:00', TIMESTAMP '1970-01-01 08:01:00', TIMESTAMP '1969-12-31 16:01:00') t(x)").getOnlyColumn().collect(toList()), - ImmutableList.of(zonedDateTime("1969-12-31 16:01:00.000 America/Los_Angeles"), zonedDateTime("1970-01-01 00:01:00.000 America/Los_Angeles"), zonedDateTime("1969-12-31 08:01:00.000 America/Los_Angeles"))); + ImmutableList.of(zonedDateTime("1970-01-01 03:01:00.000 America/Los_Angeles"), zonedDateTime("1970-01-01 11:01:00.000 America/Los_Angeles"), zonedDateTime("1969-12-31 19:01:00.000 America/Los_Angeles"))); assertEquals(computeScalar("SELECT min(x) AT TIME ZONE 'America/Los_Angeles' FROM (values TIMESTAMP '1970-01-01 00:01:00+00:00', TIMESTAMP '1970-01-01 08:01:00+08:00', TIMESTAMP '1969-12-31 16:01:00-08:00') t(x)"), zonedDateTime("1969-12-31 16:01:00.000 America/Los_Angeles")); // with chained AT TIME ZONE - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles' AT TIME ZONE 'UTC'"), zonedDateTime("2012-10-31 01:00:00.000 UTC")); - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Los_Angeles'"), zonedDateTime("2012-10-30 18:00:00.000 America/Los_Angeles")); - assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles' AT TIME ZONE 'Asia/Shanghai'"), zonedDateTime("2012-10-31 09:00:00.000 Asia/Shanghai")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles' AT TIME ZONE 'UTC'"), zonedDateTime("2012-10-30 11:00:00.000 UTC")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Tokyo' AT TIME ZONE 'America/Los_Angeles'"), zonedDateTime("2012-10-30 04:00:00.000 America/Los_Angeles")); + assertEquals(computeScalar("SELECT TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'America/Los_Angeles' AT TIME ZONE 'Asia/Shanghai'"), zonedDateTime("2012-10-30 19:00:00.000 Asia/Shanghai")); assertEquals(computeScalar("SELECT min(x) AT TIME ZONE 'America/Los_Angeles' AT TIME ZONE 'UTC' FROM (values TIMESTAMP '1970-01-01 00:01:00+00:00', TIMESTAMP '1970-01-01 08:01:00+08:00', TIMESTAMP '1969-12-31 16:01:00-08:00') t(x)"), zonedDateTime("1970-01-01 00:01:00.000 UTC")); // with AT TIME ZONE in VALUES - assertEquals(computeScalar("SELECT * FROM (VALUES TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Oral')"), zonedDateTime("2012-10-31 06:00:00.000 Asia/Oral")); + assertEquals(computeScalar("SELECT * FROM (VALUES TIMESTAMP '2012-10-31 01:00' AT TIME ZONE 'Asia/Oral')"), zonedDateTime("2012-10-30 16:00:00.000 Asia/Oral")); } private ZonedDateTime zonedDateTime(String value) @@ -4864,6 +4822,20 @@ public void testInformationSchemaFiltering() "SELECT 'customer' table_name"); } + @Test + public void testInformationSchemaUppercaseName() + { + assertQuery( + "SELECT table_name FROM information_schema.tables WHERE table_catalog = 'LOCAL'", + "SELECT '' WHERE false"); + assertQuery( + "SELECT table_name FROM information_schema.tables WHERE table_schema = 'TINY'", + "SELECT '' WHERE false"); + assertQuery( + "SELECT table_name FROM information_schema.tables WHERE table_name = 'ORDERS'", + "SELECT '' WHERE false"); + } + @Test public void testSelectColumnOfNulls() { @@ -6923,7 +6895,7 @@ public void testArrayShuffle() for (int i = 0; i < 3; i++) { MaterializedResult results = computeActual(format("SELECT shuffle(ARRAY %s) FROM orders LIMIT 10", expected)); List rows = results.getMaterializedRows(); - assertTrue(rows.size() == 10); + assertEquals(rows.size(), 10); for (MaterializedRow row : rows) { List actual = (List) row.getField(0); @@ -7182,7 +7154,7 @@ public void testMergeHyperLogLogGroupByWithNulls() @Test public void testMergeHyperLogLogOnlyNulls() { - MaterializedResult actual = computeActual("SELECT cardinality(merge(null)) FROM orders"); + MaterializedResult actual = computeActual("SELECT cardinality(merge(CAST (null AS HyperLogLog))) FROM orders"); MaterializedResult expected = resultBuilder(getSession(), BIGINT) .row(new Object[] {null}) @@ -7426,6 +7398,9 @@ public void testAccessControl() assertAccessDenied("SELECT 1 FROM region, nation where region.regionkey = nation.nationkey", "Cannot select from columns \\[nationkey\\] in table .*.nation.*", privilege("nationkey", SELECT_COLUMN)); assertAccessDenied("SELECT count(*) FROM nation", "Cannot select from columns \\[\\] in table .*.nation.*", privilege("nation", SELECT_COLUMN)); assertAccessDenied("WITH t1 AS (SELECT * FROM nation) SELECT * FROM t1", "Cannot select from columns \\[nationkey, regionkey, name, comment\\] in table .*.nation.*", privilege("nationkey", SELECT_COLUMN)); + assertAccessAllowed("SELECT name AS my_alias FROM nation", privilege("my_alias", SELECT_COLUMN)); + assertAccessAllowed("SELECT my_alias from (SELECT name AS my_alias FROM nation)", privilege("my_alias", SELECT_COLUMN)); + assertAccessDenied("SELECT name AS my_alias FROM nation", "Cannot select from columns \\[name\\] in table .*.nation.*", privilege("name", SELECT_COLUMN)); } @Test @@ -8098,6 +8073,7 @@ protected Session noJoinReordering() { return Session.builder(getSession()) .setSystemProperty(JOIN_REORDERING_STRATEGY, JoinReorderingStrategy.NONE.name()) + .setSystemProperty(JOIN_DISTRIBUTION_TYPE, JoinDistributionType.PARTITIONED.name()) .build(); } } diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java index 8cb8f495f58e5..e3fe104a1cce4 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueryFramework.java @@ -18,13 +18,16 @@ import com.facebook.presto.cost.CostCalculatorUsingExchanges; import com.facebook.presto.cost.CostCalculatorWithEstimatedExchanges; import com.facebook.presto.cost.CostComparator; +import com.facebook.presto.cost.TaskCountEstimator; import com.facebook.presto.execution.QueryManagerConfig; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.spi.security.AccessDeniedException; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.analyzer.FeaturesConfig; import com.facebook.presto.sql.analyzer.QueryExplainer; import com.facebook.presto.sql.parser.SqlParser; +import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.PlanFragmenter; import com.facebook.presto.sql.planner.PlanOptimizers; import com.facebook.presto.sql.planner.optimizations.PlanOptimizer; @@ -45,10 +48,12 @@ import java.util.List; import java.util.Optional; import java.util.OptionalLong; +import java.util.function.Consumer; import static com.facebook.presto.sql.ParsingUtil.createParsingOptions; import static com.facebook.presto.sql.SqlFormatter.formatSql; import static com.facebook.presto.transaction.TransactionBuilder.transaction; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.nullToEmpty; import static com.google.common.collect.ImmutableList.toImmutableList; @@ -136,6 +141,12 @@ protected void assertQuery(Session session, @Language("SQL") String actual, @Lan QueryAssertions.assertQuery(queryRunner, session, actual, h2QueryRunner, expected, false, false); } + protected void assertQuery(Session session, @Language("SQL") String actual, @Language("SQL") String expected, Consumer planAssertion) + { + checkArgument(queryRunner instanceof DistributedQueryRunner, "pattern assertion is only supported for DistributedQueryRunner"); + QueryAssertions.assertQuery(queryRunner, session, actual, h2QueryRunner, expected, false, false, planAssertion); + } + public void assertQueryOrdered(@Language("SQL") String sql) { assertQueryOrdered(getSession(), sql); @@ -173,7 +184,7 @@ protected void assertUpdate(@Language("SQL") String sql) protected void assertUpdate(Session session, @Language("SQL") String sql) { - QueryAssertions.assertUpdate(queryRunner, session, sql, OptionalLong.empty()); + QueryAssertions.assertUpdate(queryRunner, session, sql, OptionalLong.empty(), Optional.empty()); } protected void assertUpdate(@Language("SQL") String sql, long count) @@ -183,7 +194,17 @@ protected void assertUpdate(@Language("SQL") String sql, long count) protected void assertUpdate(Session session, @Language("SQL") String sql, long count) { - QueryAssertions.assertUpdate(queryRunner, session, sql, OptionalLong.of(count)); + QueryAssertions.assertUpdate(queryRunner, session, sql, OptionalLong.of(count), Optional.empty()); + } + + protected void assertUpdate(Session session, @Language("SQL") String sql, long count, Consumer planAssertion) + { + QueryAssertions.assertUpdate(queryRunner, session, sql, OptionalLong.of(count), Optional.of(planAssertion)); + } + + protected void assertQuerySucceeds(Session session, @Language("SQL") String sql) + { + QueryAssertions.assertQuerySucceeds(queryRunner, session, sql); } protected void assertQueryFailsEventually(@Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp, Duration timeout) @@ -288,13 +309,14 @@ protected String formatSqlText(String sql) return formatSql(sqlParser.createStatement(sql, createParsingOptions(queryRunner.getDefaultSession())), Optional.empty()); } + //TODO: should WarningCollector be added? public String getExplainPlan(String query, ExplainType.Type planType) { QueryExplainer explainer = getQueryExplainer(); return transaction(queryRunner.getTransactionManager(), queryRunner.getAccessControl()) .singleStatement() .execute(queryRunner.getDefaultSession(), session -> { - return explainer.getPlan(session, sqlParser.createStatement(query, createParsingOptions(session)), planType, emptyList()); + return explainer.getPlan(session, sqlParser.createStatement(query, createParsingOptions(session)), planType, emptyList(), WarningCollector.NOOP); }); } @@ -304,7 +326,7 @@ public String getGraphvizExplainPlan(String query, ExplainType.Type planType) return transaction(queryRunner.getTransactionManager(), queryRunner.getAccessControl()) .singleStatement() .execute(queryRunner.getDefaultSession(), session -> { - return explainer.getGraphvizPlan(session, sqlParser.createStatement(query, createParsingOptions(session)), planType, emptyList()); + return explainer.getGraphvizPlan(session, sqlParser.createStatement(query, createParsingOptions(session)), planType, emptyList(), WarningCollector.NOOP); }); } @@ -313,22 +335,25 @@ private QueryExplainer getQueryExplainer() Metadata metadata = queryRunner.getMetadata(); FeaturesConfig featuresConfig = new FeaturesConfig().setOptimizeHashGeneration(true); boolean forceSingleNode = queryRunner.getNodeCount() == 1; - CostCalculator costCalculator = new CostCalculatorUsingExchanges(queryRunner::getNodeCount); + TaskCountEstimator taskCountEstimator = new TaskCountEstimator(queryRunner::getNodeCount); + CostCalculator costCalculator = new CostCalculatorUsingExchanges(taskCountEstimator); List optimizers = new PlanOptimizers( metadata, sqlParser, featuresConfig, forceSingleNode, new MBeanExporter(new TestingMBeanServer()), + queryRunner.getSplitManager(), + queryRunner.getPageSourceManager(), queryRunner.getStatsCalculator(), costCalculator, - new CostCalculatorWithEstimatedExchanges(costCalculator, queryRunner::getNodeCount), - new CostComparator(featuresConfig)).get(); + new CostCalculatorWithEstimatedExchanges(costCalculator, taskCountEstimator), + new CostComparator(featuresConfig), + taskCountEstimator).get(); return new QueryExplainer( optimizers, - new PlanFragmenter(new QueryManagerConfig()), + new PlanFragmenter(metadata, queryRunner.getNodePartitioningManager(), new QueryManagerConfig()), metadata, - queryRunner.getNodePartitioningManager(), queryRunner.getAccessControl(), sqlParser, queryRunner.getStatsCalculator(), diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java b/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java index 323c8194a2d5a..b7b59b5be67f4 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/DistributedQueryRunner.java @@ -14,19 +14,24 @@ package com.facebook.presto.tests; import com.facebook.presto.Session; +import com.facebook.presto.Session.SessionBuilder; import com.facebook.presto.connector.ConnectorId; import com.facebook.presto.cost.StatsCalculator; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.AllNodes; import com.facebook.presto.metadata.Catalog; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.metadata.SessionPropertyManager; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.Plugin; import com.facebook.presto.spi.QueryId; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.planner.NodePartitioningManager; import com.facebook.presto.sql.planner.Plan; @@ -54,6 +59,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; import static com.facebook.presto.testing.TestingSession.TESTING_CATALOG; import static com.facebook.presto.testing.TestingSession.createBogusTestingCatalog; @@ -237,6 +243,18 @@ public Metadata getMetadata() return coordinator.getMetadata(); } + @Override + public SplitManager getSplitManager() + { + return coordinator.getSplitManager(); + } + + @Override + public PageSourceManager getPageSourceManager() + { + return coordinator.getPageSourceManager(); + } + @Override public NodePartitioningManager getNodePartitioningManager() { @@ -378,7 +396,14 @@ public ResultWithQueryId executeWithQueryId(Session session, } @Override - public Plan createPlan(Session session, String sql) + public MaterializedResultWithPlan executeWithPlan(Session session, String sql, WarningCollector warningCollector) + { + ResultWithQueryId resultWithQueryId = executeWithQueryId(session, sql); + return new MaterializedResultWithPlan(resultWithQueryId.getResult().toTestTypes(), getQueryPlan(resultWithQueryId.getQueryId())); + } + + @Override + public Plan createPlan(Session session, String sql, WarningCollector warningCollector) { QueryId queryId = executeWithQueryId(session, sql).getQueryId(); Plan queryPlan = getQueryPlan(queryId); @@ -388,12 +413,12 @@ public Plan createPlan(Session session, String sql) public QueryInfo getQueryInfo(QueryId queryId) { - return coordinator.getQueryManager().getQueryInfo(queryId); + return coordinator.getQueryManager().getFullQueryInfo(queryId); } public Plan getQueryPlan(QueryId queryId) { - return coordinator.getQueryManager().getQueryPlan(queryId); + return coordinator.getQueryPlan(queryId); } @Override @@ -417,7 +442,7 @@ public final void close() private void cancelAllQueries() { QueryManager queryManager = coordinator.getQueryManager(); - for (QueryInfo queryInfo : queryManager.getAllQueryInfo()) { + for (BasicQueryInfo queryInfo : queryManager.getQueries()) { if (!queryInfo.getState().isDone()) { queryManager.cancelQuery(queryInfo.getQueryId()); } @@ -437,7 +462,7 @@ private static void closeUnchecked(AutoCloseable closeable) public static class Builder { - private final Session defaultSession; + private Session defaultSession; private int nodeCount = 4; private Map extraProperties = ImmutableMap.of(); private Map coordinatorProperties = ImmutableMap.of(); @@ -446,7 +471,14 @@ public static class Builder protected Builder(Session defaultSession) { - this.defaultSession = defaultSession; + this.defaultSession = requireNonNull(defaultSession, "defaultSession is null"); + } + + public Builder amendSession(Function amendSession) + { + SessionBuilder builder = Session.builder(defaultSession); + this.defaultSession = amendSession.apply(builder).build(); + return this; } public Builder setNodeCount(int nodeCount) diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/PlanDeterminismChecker.java b/presto-tests/src/main/java/com/facebook/presto/tests/PlanDeterminismChecker.java index 31cc6787ba50f..db1d212b065d3 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/PlanDeterminismChecker.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/PlanDeterminismChecker.java @@ -14,6 +14,7 @@ package com.facebook.presto.tests; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.LogicalPlanner; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.planPrinter.PlanPrinter; @@ -61,13 +62,12 @@ public void checkPlanIsDeterministic(Session session, String sql) private String getPlanText(Session session, String sql) { return localQueryRunner.inTransaction(session, transactionSession -> { - Plan plan = localQueryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED); + Plan plan = localQueryRunner.createPlan(transactionSession, sql, LogicalPlanner.Stage.OPTIMIZED_AND_VALIDATED, WarningCollector.NOOP); return PlanPrinter.textLogicalPlan( plan.getRoot(), plan.getTypes(), localQueryRunner.getMetadata().getFunctionRegistry(), - localQueryRunner.getStatsCalculator(), - localQueryRunner.getCostCalculator(), + plan.getStatsAndCosts(), transactionSession, 0, false); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java b/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java index d7441f9deac31..5185839fd4825 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/QueryAssertions.java @@ -14,10 +14,13 @@ package com.facebook.presto.tests; import com.facebook.presto.Session; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.metadata.QualifiedObjectName; +import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.testing.MaterializedResult; import com.facebook.presto.testing.MaterializedRow; import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.testing.QueryRunner.MaterializedResultWithPlan; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.Iterables; @@ -29,7 +32,9 @@ import org.intellij.lang.annotations.Language; import java.util.List; +import java.util.Optional; import java.util.OptionalLong; +import java.util.function.Consumer; import java.util.function.Supplier; import static com.google.common.base.Strings.nullToEmpty; @@ -51,12 +56,26 @@ private QueryAssertions() { } - public static void assertUpdate(QueryRunner queryRunner, Session session, @Language("SQL") String sql, OptionalLong count) + public static void assertUpdate(QueryRunner queryRunner, Session session, @Language("SQL") String sql, OptionalLong count, Optional> planAssertion) { long start = System.nanoTime(); - MaterializedResult results = queryRunner.execute(session, sql); + MaterializedResult results; + Plan queryPlan; + if (planAssertion.isPresent()) { + MaterializedResultWithPlan resultWithPlan = queryRunner.executeWithPlan(session, sql, WarningCollector.NOOP); + queryPlan = resultWithPlan.getQueryPlan(); + results = resultWithPlan.getMaterializedResult().toTestTypes(); + } + else { + queryPlan = null; + results = queryRunner.execute(session, sql); + } log.info("FINISHED in presto: %s", nanosSince(start)); + if (planAssertion.isPresent()) { + planAssertion.get().accept(queryPlan); + } + if (!results.getUpdateType().isPresent()) { fail("update type is not set"); } @@ -72,21 +91,64 @@ else if (count.isPresent()) { } } - public static void assertQuery(QueryRunner actualQueryRunner, + public static void assertQuery( + QueryRunner actualQueryRunner, Session session, @Language("SQL") String actual, H2QueryRunner h2QueryRunner, @Language("SQL") String expected, boolean ensureOrdering, boolean compareUpdate) + { + assertQuery(actualQueryRunner, session, actual, h2QueryRunner, expected, ensureOrdering, compareUpdate, Optional.empty()); + } + + public static void assertQuery( + QueryRunner actualQueryRunner, + Session session, + @Language("SQL") String actual, + H2QueryRunner h2QueryRunner, + @Language("SQL") String expected, + boolean ensureOrdering, + boolean compareUpdate, + Consumer planAssertion) + { + assertQuery(actualQueryRunner, session, actual, h2QueryRunner, expected, ensureOrdering, compareUpdate, Optional.of(planAssertion)); + } + + private static void assertQuery( + QueryRunner actualQueryRunner, + Session session, + @Language("SQL") String actual, + H2QueryRunner h2QueryRunner, + @Language("SQL") String expected, + boolean ensureOrdering, + boolean compareUpdate, + Optional> planAssertion) { long start = System.nanoTime(); MaterializedResult actualResults = null; - try { - actualResults = actualQueryRunner.execute(session, actual).toTestTypes(); + Plan queryPlan = null; + if (planAssertion.isPresent()) { + try { + MaterializedResultWithPlan resultWithPlan = actualQueryRunner.executeWithPlan(session, actual, WarningCollector.NOOP); + queryPlan = resultWithPlan.getQueryPlan(); + actualResults = resultWithPlan.getMaterializedResult().toTestTypes(); + } + catch (RuntimeException ex) { + fail("Execution of 'actual' query failed: " + actual, ex); + } } - catch (RuntimeException ex) { - fail("Execution of 'actual' query failed: " + actual, ex); + else { + try { + actualResults = actualQueryRunner.execute(session, actual).toTestTypes(); + } + catch (RuntimeException ex) { + fail("Execution of 'actual' query failed: " + actual, ex); + } + } + if (planAssertion.isPresent()) { + planAssertion.get().accept(queryPlan); } Duration actualTime = nanosSince(start); @@ -154,8 +216,8 @@ public static void assertEqualsIgnoreOrder(Iterable actual, Iterable expec int limit = 100; fail(format( "%snot equal\n" + - "Actual rows (up to %s of %s extra rows shown, %s matching and extra rows in total):\n %s\n" + - "Expected rows (up to %s of %s missing rows shown, %s matching and missing rows in total):\n %s\n", + "Actual rows (up to %s of %s extra rows shown, %s rows in total):\n %s\n" + + "Expected rows (up to %s of %s missing rows shown, %s rows in total):\n %s\n", message == null ? "" : (message + "\n"), limit, unexpectedRows.size(), @@ -199,6 +261,16 @@ public static void assertContains(MaterializedResult all, MaterializedResult exp } } + protected static void assertQuerySucceeds(QueryRunner queryRunner, Session session, @Language("SQL") String sql) + { + try { + queryRunner.execute(session, sql); + } + catch (RuntimeException e) { + fail(format("Expected query to succeed: %s", sql), e); + } + } + protected static void assertQueryFailsEventually(QueryRunner queryRunner, Session session, @Language("SQL") String sql, @Language("RegExp") String expectedMessageRegExp, Duration timeout) { long start = System.nanoTime(); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java b/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java index ae31616ce9328..efea4c2cadb01 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/StandaloneQueryRunner.java @@ -23,6 +23,8 @@ import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.Plugin; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.parser.SqlParserOptions; import com.facebook.presto.sql.planner.NodePartitioningManager; import com.facebook.presto.testing.MaterializedResult; @@ -134,6 +136,18 @@ public Metadata getMetadata() return server.getMetadata(); } + @Override + public SplitManager getSplitManager() + { + return server.getSplitManager(); + } + + @Override + public PageSourceManager getPageSourceManager() + { + return server.getPageSourceManager(); + } + @Override public NodePartitioningManager getNodePartitioningManager() { diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java b/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java index 6a0919ec5713f..f9150f17b4838 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/TestingPrestoClient.java @@ -24,7 +24,6 @@ import com.facebook.presto.spi.type.MapType; import com.facebook.presto.spi.type.SqlTimestamp; import com.facebook.presto.spi.type.SqlTimestampWithTimeZone; -import com.facebook.presto.spi.type.TimeZoneKey; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.VarcharType; import com.facebook.presto.testing.MaterializedResult; @@ -94,7 +93,7 @@ public TestingPrestoClient(TestingPrestoServer prestoServer, Session defaultSess @Override protected ResultsSession getResultSession(Session session) { - return new MaterializedResultSession(session); + return new MaterializedResultSession(); } private class MaterializedResultSession @@ -108,13 +107,6 @@ private class MaterializedResultSession private final AtomicReference> updateType = new AtomicReference<>(Optional.empty()); private final AtomicReference updateCount = new AtomicReference<>(OptionalLong.empty()); - private final TimeZoneKey timeZoneKey; - - private MaterializedResultSession(Session session) - { - this.timeZoneKey = session.getTimeZoneKey(); - } - @Override public void setUpdateType(String type) { @@ -140,7 +132,7 @@ public void addResults(QueryStatusInfo statusInfo, QueryData data) if (data.getData() != null) { checkState(types.get() != null, "data received without types"); - rows.addAll(transform(data.getData(), dataToRow(timeZoneKey, types.get()))); + rows.addAll(transform(data.getData(), dataToRow(types.get()))); } } @@ -158,7 +150,7 @@ public MaterializedResult build(Map setSessionProperties, Set, MaterializedRow> dataToRow(final TimeZoneKey timeZoneKey, final List types) + private static Function, MaterializedRow> dataToRow(final List types) { return data -> { checkArgument(data.size() == types.size(), "columns size does not match types size"); @@ -166,13 +158,13 @@ private static Function, MaterializedRow> dataToRow(final TimeZoneK for (int i = 0; i < data.size(); i++) { Object value = data.get(i); Type type = types.get(i); - row.add(convertToRowValue(type, value, timeZoneKey)); + row.add(convertToRowValue(type, value)); } return new MaterializedRow(DEFAULT_PRECISION, row); }; } - private static Object convertToRowValue(Type type, Object value, TimeZoneKey timeZoneKey) + private static Object convertToRowValue(Type type, Object value) { if (value == null) { return null; @@ -237,14 +229,14 @@ else if (INTERVAL_YEAR_MONTH.equals(type)) { } else if (type instanceof ArrayType) { return ((List) value).stream() - .map(element -> convertToRowValue(((ArrayType) type).getElementType(), element, timeZoneKey)) + .map(element -> convertToRowValue(((ArrayType) type).getElementType(), element)) .collect(toList()); } else if (type instanceof MapType) { return ((Map) value).entrySet().stream() .collect(Collectors.toMap( - e -> convertToRowValue(((MapType) type).getKeyType(), e.getKey(), timeZoneKey), - e -> convertToRowValue(((MapType) type).getValueType(), e.getValue(), timeZoneKey))); + e -> convertToRowValue(((MapType) type).getKeyType(), e.getKey()), + e -> convertToRowValue(((MapType) type).getValueType(), e.getValue()))); } else if (type instanceof DecimalType) { return new BigDecimal((String) value); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataType.java b/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataType.java index c905b76677934..a382d8277d894 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataType.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataType.java @@ -38,15 +38,14 @@ import static com.google.common.io.BaseEncoding.base16; import static java.lang.String.format; import static java.math.RoundingMode.UNNECESSARY; -import static java.util.Optional.empty; import static java.util.function.Function.identity; public class DataType { - private String insertType; - private Type prestoResultType; - private Function toLiteral; - private Function toPrestoQueryResult; + private final String insertType; + private final Type prestoResultType; + private final Function toLiteral; + private final Function toPrestoQueryResult; public static DataType booleanDataType() { @@ -95,7 +94,7 @@ public static DataType varcharDataType(int size, String properties) public static DataType varcharDataType() { - return varcharDataType(empty(), ""); + return varcharDataType(Optional.empty(), ""); } private static DataType varcharDataType(Optional length, String properties) diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataTypeTest.java b/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataTypeTest.java index 55307eb436241..9c318075c9241 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataTypeTest.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/datatype/DataTypeTest.java @@ -29,7 +29,7 @@ public class DataTypeTest { - private List> inputs = new ArrayList<>(); + private final List> inputs = new ArrayList<>(); private DataTypeTest() {} diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/statistics/MetricComparator.java b/presto-tests/src/main/java/com/facebook/presto/tests/statistics/MetricComparator.java index ebc08314d3055..52a4f1a9a1965 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/statistics/MetricComparator.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/statistics/MetricComparator.java @@ -15,12 +15,10 @@ import com.facebook.presto.Session; import com.facebook.presto.cost.PlanNodeStatsEstimate; -import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.planner.Symbol; -import com.facebook.presto.sql.planner.TypeProvider; import com.facebook.presto.sql.planner.plan.OutputNode; -import com.facebook.presto.sql.planner.plan.PlanNode; import com.facebook.presto.testing.MaterializedRow; import com.facebook.presto.testing.QueryRunner; import com.google.common.collect.ImmutableList; @@ -29,7 +27,6 @@ import java.util.List; import java.util.OptionalDouble; -import static com.facebook.presto.sql.planner.iterative.Lookup.noLookup; import static com.facebook.presto.transaction.TransactionBuilder.transaction; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.Iterables.getOnlyElement; @@ -64,26 +61,15 @@ private static List getEstimatedValues(List metrics, Str } private static List getEstimatedValuesInternal(List metrics, String query, QueryRunner runner, Session session) - // TODO inline back this method + // TODO inline back this method { - Plan queryPlan = runner.createPlan(session, query); + Plan queryPlan = runner.createPlan(session, query, WarningCollector.NOOP); OutputNode outputNode = (OutputNode) queryPlan.getRoot(); - PlanNodeStatsEstimate outputNodeStats = calculateStats(outputNode, runner.getStatsCalculator(), session, queryPlan.getTypes()); + PlanNodeStatsEstimate outputNodeStats = queryPlan.getStatsAndCosts().getStats().getOrDefault(queryPlan.getRoot().getId(), PlanNodeStatsEstimate.unknown()); StatsContext statsContext = buildStatsContext(queryPlan, outputNode); return getEstimatedValues(metrics, outputNodeStats, statsContext); } - private static PlanNodeStatsEstimate calculateStats(PlanNode node, StatsCalculator statsCalculator, Session session, TypeProvider types) - { - // We calculate stats one-off, so caching is not necessary - return statsCalculator.calculateStats( - node, - source -> calculateStats(source, statsCalculator, session, types), - noLookup(), - session, - types); - } - private static StatsContext buildStatsContext(Plan queryPlan, OutputNode outputNode) { ImmutableMap.Builder columnSymbols = ImmutableMap.builder(); diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/statistics/StatisticsAssertion.java b/presto-tests/src/main/java/com/facebook/presto/tests/statistics/StatisticsAssertion.java index e13102c85981a..5a2fbc750429c 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/statistics/StatisticsAssertion.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/statistics/StatisticsAssertion.java @@ -33,7 +33,7 @@ import static com.google.common.base.Verify.verify; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Objects.requireNonNull; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertSame; public class StatisticsAssertion implements AutoCloseable @@ -136,7 +136,7 @@ void run(@Language("SQL") String query, QueryRunner runner) for (int i = 0; i < checks.size(); i++) { MetricsCheck check = checks.get(i); MetricComparison metricComparison = metricComparisons.get(i); - assertTrue(metricComparison.result(check.strategy) == check.expectedComparisonResult, "Metric doesn't match: " + metricComparison); + assertSame(metricComparison.result(check.strategy), check.expectedComparisonResult, "Metric doesn't match: " + metricComparison); } } } diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchConnectorFactory.java b/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchConnectorFactory.java index 4cc0b4ba700e5..9c10e74a47dfa 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchConnectorFactory.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchConnectorFactory.java @@ -63,10 +63,10 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map properties, ConnectorContext context) + public Connector create(String catalogName, Map properties, ConnectorContext context) { int splitsPerNode = getSplitsPerNode(properties); - TpchIndexedData indexedData = new TpchIndexedData(connectorId, indexSpec); + TpchIndexedData indexedData = new TpchIndexedData(catalogName, indexSpec); NodeManager nodeManager = context.getNodeManager(); return new Connector() @@ -80,7 +80,7 @@ public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel @Override public ConnectorMetadata getMetadata(ConnectorTransactionHandle transactionHandle) { - return new TpchIndexMetadata(connectorId, indexedData); + return new TpchIndexMetadata(catalogName, indexedData); } @Override diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchPlugin.java b/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchPlugin.java index 191a20f8a4a75..3d30cc29e3511 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchPlugin.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/tpch/IndexedTpchPlugin.java @@ -22,7 +22,7 @@ public class IndexedTpchPlugin implements Plugin { - private TpchIndexSpec indexSpec; + private final TpchIndexSpec indexSpec; public IndexedTpchPlugin(TpchIndexSpec indexSpec) { diff --git a/presto-tests/src/test/java/com/facebook/presto/connector/informationschema/BenchmarkInformationSchema.java b/presto-tests/src/test/java/com/facebook/presto/connector/informationschema/BenchmarkInformationSchema.java index 55e4d08ed9505..f8b9fea37fbba 100644 --- a/presto-tests/src/test/java/com/facebook/presto/connector/informationschema/BenchmarkInformationSchema.java +++ b/presto-tests/src/test/java/com/facebook/presto/connector/informationschema/BenchmarkInformationSchema.java @@ -85,9 +85,9 @@ public static class BenchmarkData private QueryRunner queryRunner; private Session session = testSessionBuilder() - .setCatalog("test_catalog") - .setSchema("test_schema") - .build(); + .setCatalog("test_catalog") + .setSchema("test_schema") + .build(); private String query; @@ -96,7 +96,8 @@ public void setup() throws Exception { queryRunner = DistributedQueryRunner.builder(session).build(); - queryRunner.installPlugin(new Plugin() { + queryRunner.installPlugin(new Plugin() + { @Override public Iterable getConnectorFactories() { diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestBeginQuery.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestBeginQuery.java index 1d580beb60418..70bb7d2a541ab 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestBeginQuery.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestBeginQuery.java @@ -175,7 +175,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { return new TestConnector(metadata); } diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestCompletedEventWarnings.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestCompletedEventWarnings.java new file mode 100644 index 0000000000000..5fa22f728b1a9 --- /dev/null +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestCompletedEventWarnings.java @@ -0,0 +1,106 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session.SessionBuilder; +import com.facebook.presto.execution.TestEventListener.EventsBuilder; +import com.facebook.presto.execution.TestEventListenerPlugin.TestingEventListenerPlugin; +import com.facebook.presto.execution.warnings.WarningCollectorConfig; +import com.facebook.presto.spi.PrestoWarning; +import com.facebook.presto.spi.WarningCode; +import com.facebook.presto.testing.QueryRunner; +import com.facebook.presto.testing.TestingWarningCollector; +import com.facebook.presto.testing.TestingWarningCollectorConfig; +import com.facebook.presto.tests.DistributedQueryRunner; +import com.google.common.collect.ImmutableMap; +import org.intellij.lang.annotations.Language; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static com.google.common.collect.Iterables.getOnlyElement; +import static org.testng.Assert.fail; + +public class TestCompletedEventWarnings +{ + private static final int EXPECTED_EVENTS = 3; + private static final int TEST_WARNINGS = 5; + private QueryRunner queryRunner; + private EventsBuilder generatedEvents; + + @BeforeMethod + public void setUp() + throws Exception + { + SessionBuilder sessionBuilder = testSessionBuilder(); + generatedEvents = new EventsBuilder(); + queryRunner = DistributedQueryRunner.builder(sessionBuilder.build()) + .setExtraProperties(ImmutableMap.of("testing-warning-collector.preloaded-warnings", String.valueOf(TEST_WARNINGS))) + .setNodeCount(1) + .build(); + queryRunner.installPlugin(new TestingEventListenerPlugin(generatedEvents)); + generatedEvents.initialize(EXPECTED_EVENTS); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() + { + queryRunner.close(); + queryRunner = null; + generatedEvents = null; + } + + @Test + public void testCompletedEventWarnings() + throws InterruptedException + { + TestingWarningCollectorConfig warningCollectorConfig = new TestingWarningCollectorConfig().setPreloadedWarnings(TEST_WARNINGS); + TestingWarningCollector testingWarningCollector = new TestingWarningCollector(new WarningCollectorConfig(), warningCollectorConfig); + assertWarnings( + "select 1", + ImmutableMap.of(), + testingWarningCollector.getWarnings().stream() + .map(PrestoWarning::getWarningCode) + .collect(toImmutableList())); + } + + private void assertWarnings(@Language("SQL") String sql, Map sessionProperties, List expectedWarnings) + throws InterruptedException + { + // Task concurrency must be 1 otherwise these tests fail due to change in the number of EXPECTED_EVENTS + SessionBuilder sessionBuilder = testSessionBuilder() + .setSystemProperty("task_concurrency", "1"); + sessionProperties.forEach(sessionBuilder::setSystemProperty); + queryRunner.execute(sessionBuilder.build(), sql); + generatedEvents.waitForEvents(10); + + Set warnings = getOnlyElement(generatedEvents.getQueryCompletedEvents()) + .getWarnings() + .stream() + .map(PrestoWarning::getWarningCode) + .collect(toImmutableSet()); + for (WarningCode warningCode : expectedWarnings) { + if (!warnings.contains(warningCode)) { + fail("Expected warning: " + warningCode); + } + } + } +} diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java index cfbf12620944c..496870e97fed0 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java @@ -142,7 +142,7 @@ public void testNormalQuery() assertEquals(queryCompletedEvent.getIoMetadata().getOutput(), Optional.empty()); assertEquals(queryCompletedEvent.getIoMetadata().getInputs().size(), 1); assertEquals(queryCompletedEvent.getContext().getClientInfo().get(), "{\"clientVersion\":\"testVersion\"}"); - assertEquals(getOnlyElement(queryCompletedEvent.getIoMetadata().getInputs()).getConnectorId(), "tpch"); + assertEquals(getOnlyElement(queryCompletedEvent.getIoMetadata().getInputs()).getCatalogName(), "tpch"); assertEquals(queryCreatedEvent.getMetadata().getQueryId(), queryCompletedEvent.getMetadata().getQueryId()); assertEquals(queryCompletedEvent.getStatistics().getCompletedSplits(), SPLITS_PER_NODE + 2); diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestFinalQueryInfo.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestFinalQueryInfo.java new file mode 100644 index 0000000000000..5c43563ff545b --- /dev/null +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestFinalQueryInfo.java @@ -0,0 +1,117 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.client.ClientSession; +import com.facebook.presto.client.StatementClient; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.tests.DistributedQueryRunner; +import com.facebook.presto.tpch.TpchPlugin; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.util.concurrent.SettableFuture; +import io.airlift.units.Duration; +import okhttp3.OkHttpClient; +import org.testng.annotations.Test; + +import java.util.Locale; +import java.util.Optional; + +import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.client.StatementClientFactory.newStatementClient; +import static io.airlift.concurrent.MoreFutures.tryGetFutureValue; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.testng.Assert.assertTrue; + +public class TestFinalQueryInfo +{ + @Test(timeOut = 240_000) + public void testFinalQueryInfoSetOnAbort() + throws Exception + { + try (DistributedQueryRunner queryRunner = createQueryRunner(TEST_SESSION)) { + QueryId queryId = startQuery("SELECT COUNT(*) FROM tpch.sf1000.lineitem", queryRunner); + SettableFuture finalQueryInfoFuture = SettableFuture.create(); + queryRunner.getCoordinator().addFinalQueryInfoListener(queryId, finalQueryInfoFuture::set); + + // wait 1s then kill query + Thread.sleep(1_000); + queryRunner.getCoordinator().getQueryManager().cancelQuery(queryId); + + // wait for final query info + QueryInfo finalQueryInfo = tryGetFutureValue(finalQueryInfoFuture, 10, SECONDS) + .orElseThrow(() -> new AssertionError("Final query info never set")); + assertTrue(finalQueryInfo.isFinalQueryInfo()); + } + } + + private static QueryId startQuery(String sql, DistributedQueryRunner queryRunner) + { + OkHttpClient httpClient = new OkHttpClient(); + try { + ClientSession clientSession = new ClientSession( + queryRunner.getCoordinator().getBaseUrl(), + "user", + "source", + Optional.empty(), + ImmutableSet.of(), + null, + null, + null, + null, + "America/Los_Angeles", + Locale.ENGLISH, + ImmutableMap.of(), + ImmutableMap.of(), + ImmutableMap.of(), + null, + new Duration(2, MINUTES)); + + // start query + StatementClient client = newStatementClient(httpClient, clientSession, sql); + + // wait for query to be fully scheduled + while (client.isRunning() && !client.currentStatusInfo().getStats().isScheduled()) { + client.advance(); + } + + return new QueryId(client.currentStatusInfo().getId()); + } + finally { + // close the client since, query is not managed by the client protocol + httpClient.dispatcher().executorService().shutdown(); + httpClient.connectionPool().evictAll(); + } + } + + public static DistributedQueryRunner createQueryRunner(Session session) + throws Exception + { + DistributedQueryRunner queryRunner = DistributedQueryRunner.builder(session) + .setNodeCount(2) + .build(); + + try { + queryRunner.installPlugin(new TpchPlugin()); + queryRunner.createCatalog("tpch", "tpch"); + return queryRunner; + } + catch (Exception e) { + queryRunner.close(); + throw e; + } + } +} diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestMemoryAwareExecution.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestMemoryAwareExecution.java deleted file mode 100644 index 3190aab4b7aa0..0000000000000 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestMemoryAwareExecution.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.execution; - -import com.facebook.presto.memory.ClusterMemoryManager; -import com.facebook.presto.memory.LocalMemoryManager; -import com.facebook.presto.memory.MemoryManagerConfig; -import com.facebook.presto.server.testing.TestingPrestoServer; -import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.session.ResourceEstimates; -import com.facebook.presto.sql.parser.SqlParserOptions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Key; -import io.airlift.units.DataSize; -import org.testng.annotations.AfterClass; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import static com.facebook.presto.execution.QueryState.FAILED; -import static com.facebook.presto.execution.QueryState.RUNNING; -import static com.facebook.presto.execution.QueryState.WAITING_FOR_RESOURCES; -import static com.facebook.presto.testing.TestingSession.testSessionBuilder; -import static com.facebook.presto.testing.assertions.Assert.assertEquals; -import static io.airlift.units.DataSize.succinctBytes; - -@Test(singleThreaded = true) -public class TestMemoryAwareExecution -{ - private TestingPrestoServer server; - private QueryManager queryManager; - private long totalAvailableMemory; - private long queryMaxMemoryBytes; - - @BeforeClass - public void setUp() - throws Exception - { - server = new TestingPrestoServer( - true, - ImmutableMap.of("experimental.preallocate-memory-threshold", "1B"), - null, - null, - new SqlParserOptions(), - ImmutableList.of()); - - LocalMemoryManager localMemoryManager = server.getInstance(Key.get(LocalMemoryManager.class)); - ClusterMemoryManager clusterMemoryManager = server.getInstance(Key.get(ClusterMemoryManager.class)); - queryManager = server.getQueryManager(); - queryMaxMemoryBytes = server.getInstance(Key.get(MemoryManagerConfig.class)).getMaxQueryMemory().toBytes(); - - // wait for cluster memory to be picked up - while (clusterMemoryManager.getClusterMemoryBytes() == 0) { - Thread.sleep(1000); - } - totalAvailableMemory = localMemoryManager.getPool(LocalMemoryManager.GENERAL_POOL).getMaxBytes(); - } - - @AfterMethod(alwaysRun = true) - private void afterMethod() - throws Exception - { - for (QueryInfo info : queryManager.getAllQueryInfo()) { - if (!info.getState().isDone()) { - queryManager.cancelQuery(info.getQueryId()); - waitForState(info.getQueryId(), FAILED); - } - } - } - - @AfterClass(alwaysRun = true) - private void tearDown() - throws Exception - { - server.close(); - server = null; - queryManager = null; - } - - @Test - public void testWaitingForResources() - throws Exception - { - QueryId normalQuery = queryWithResourceEstimate(new ResourceEstimates(Optional.empty(), Optional.empty(), Optional.empty()), queryManager); - ResourceEstimates estimate = new ResourceEstimates(Optional.empty(), Optional.empty(), Optional.of(DataSize.valueOf("20GB"))); - QueryId highMemoryQuery = queryWithResourceEstimate(estimate, queryManager); - assertState(normalQuery, RUNNING); - assertState(highMemoryQuery, WAITING_FOR_RESOURCES); - queryManager.failQuery(normalQuery, new Exception("Killed")); - waitForState(normalQuery, FAILED); - } - - @Test - public void testPreAllocateTooMuch() - throws Exception - { - DataSize tooMuch = succinctBytes(queryMaxMemoryBytes + 1); - ResourceEstimates estimate = new ResourceEstimates(Optional.empty(), Optional.empty(), Optional.of(tooMuch)); - QueryId highMemoryQuery = queryWithResourceEstimate(estimate, queryManager); - assertState(highMemoryQuery, FAILED); - } - - @Test(invocationCount = 5, invocationTimeOut = 60000) - public void testStartWhenPreAllocationClears() - throws Exception - { - // Invoke multiple times to make sure that pre-allocation state resets properly and that there aren't weird data races - ResourceEstimates estimate = new ResourceEstimates(Optional.empty(), Optional.empty(), Optional.of(succinctBytes(totalAvailableMemory))); - - QueryId highMemoryQuery1 = queryWithResourceEstimate(estimate, queryManager); - assertState(highMemoryQuery1, RUNNING); - - QueryId highMemoryQuery2 = queryWithResourceEstimate(estimate, queryManager); - assertState(highMemoryQuery2, WAITING_FOR_RESOURCES); - - queryManager.failQuery(highMemoryQuery1, new Exception("Killed")); - waitForState(highMemoryQuery1, FAILED); - assertState(highMemoryQuery2, RUNNING); - - queryManager.failQuery(highMemoryQuery2, new Exception("Killed")); - waitForState(highMemoryQuery2, FAILED); - } - - private static QueryId queryWithResourceEstimate(ResourceEstimates estimate, QueryManager queryManager) - throws ExecutionException, InterruptedException - { - QueryId queryId = queryManager.createQueryId(); - queryManager.createQuery(queryId, new TestingSessionContext(testSessionBuilder().setResourceEstimates(estimate).build()), "SELECT 1").get(); - return queryId; - } - - private void assertState(QueryId queryId, QueryState state) - throws InterruptedException - { - assertEquals(waitForState(queryId, state), state); - } - - private QueryState waitForState(QueryId queryId, QueryState state) - throws InterruptedException - { - QueryState actualState; - do { - actualState = queryManager.getQueryInfo(queryId).getState(); - if (actualState.isDone() || actualState == state) { - return actualState; - } - Thread.sleep(1000); - } - while (true); - } -} diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java index a8cfe05991944..033b772391ff1 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.QueryId; import com.facebook.presto.tests.DistributedQueryRunner; import com.facebook.presto.tpch.TpchPlugin; @@ -33,9 +34,8 @@ private TestQueryRunnerUtil() {} public static QueryId createQuery(DistributedQueryRunner queryRunner, Session session, String sql) { QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - QueryId queryId = queryManager.createQueryId(); - getFutureValue(queryManager.createQuery(queryId, new TestingSessionContext(session), sql)); - return queryId; + getFutureValue(queryManager.createQuery(session.getQueryId(), new TestingSessionContext(session), sql)); + return session.getQueryId(); } public static void cancelQuery(DistributedQueryRunner queryRunner, QueryId queryId) @@ -55,14 +55,14 @@ public static void waitForQueryState(DistributedQueryRunner queryRunner, QueryId QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); do { // Heartbeat all the running queries, so they don't die while we're waiting - for (QueryInfo queryInfo : queryManager.getAllQueryInfo()) { + for (BasicQueryInfo queryInfo : queryManager.getQueries()) { if (queryInfo.getState() == RUNNING) { queryManager.recordHeartbeat(queryInfo.getQueryId()); } } MILLISECONDS.sleep(500); } - while (!expectedQueryStates.contains(queryManager.getQueryInfo(queryId).getState())); + while (!expectedQueryStates.contains(queryManager.getQueryState(queryId))); } public static DistributedQueryRunner createQueryRunner() diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java index 069f996130a48..ffd6383cf49e0 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java @@ -290,7 +290,7 @@ private void assertResourceGroup(DistributedQueryRunner queryRunner, Session ses { QueryId queryId = createQuery(queryRunner, session, query); waitForQueryState(queryRunner, queryId, ImmutableSet.of(RUNNING, FINISHED)); - Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getQueryInfo(queryId).getResourceGroupId(); + Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getFullQueryInfo(queryId).getResourceGroupId(); assertTrue(resourceGroupId.isPresent(), "Query should have a resource group"); assertEquals(resourceGroupId.get(), expectedResourceGroup, format("Expected: '%s' resource group, found: %s", expectedResourceGroup, resourceGroupId.get())); } diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/H2TestUtil.java b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/H2TestUtil.java index 15fd534443ff0..d279fd975276d 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/H2TestUtil.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/H2TestUtil.java @@ -94,7 +94,7 @@ public static void waitForQueryCount(DistributedQueryRunner queryRunner, Set countingStates.contains(q.getState())).count() != expectedCount) { MILLISECONDS.sleep(500); } @@ -158,13 +158,13 @@ private static void setup(DistributedQueryRunner queryRunner, H2ResourceGroupsDa throws InterruptedException { dao.insertResourceGroupsGlobalProperties("cpu_quota_period", "1h"); - dao.insertResourceGroup(1, "global", "1MB", 100, 1000, 1000, null, null, null, null, null, null, null, null, TEST_ENVIRONMENT); - dao.insertResourceGroup(2, "bi-${USER}", "1MB", 3, 2, 2, null, null, null, null, null, null, null, 1L, TEST_ENVIRONMENT); - dao.insertResourceGroup(3, "user-${USER}", "1MB", 3, 3, 3, null, null, null, null, null, null, null, 1L, TEST_ENVIRONMENT); - dao.insertResourceGroup(4, "adhoc-${USER}", "1MB", 3, 3, 3, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); - dao.insertResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); - dao.insertResourceGroup(6, "no-queueing", "1MB", 0, 1, 1, null, null, null, null, null, null, null, null, TEST_ENVIRONMENT_2); - dao.insertResourceGroup(7, "explain", "1MB", 0, 1, 1, null, null, null, null, null, null, null, null, TEST_ENVIRONMENT); + dao.insertResourceGroup(1, "global", "1MB", 100, 1000, 1000, null, null, null, null, null, null, TEST_ENVIRONMENT); + dao.insertResourceGroup(2, "bi-${USER}", "1MB", 3, 2, 2, null, null, null, null, null, 1L, TEST_ENVIRONMENT); + dao.insertResourceGroup(3, "user-${USER}", "1MB", 3, 3, 3, null, null, null, null, null, 1L, TEST_ENVIRONMENT); + dao.insertResourceGroup(4, "adhoc-${USER}", "1MB", 3, 3, 3, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.insertResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.insertResourceGroup(6, "no-queueing", "1MB", 0, 1, 1, null, null, null, null, null, null, TEST_ENVIRONMENT_2); + dao.insertResourceGroup(7, "explain", "1MB", 0, 1, 1, null, null, null, null, null, null, TEST_ENVIRONMENT); dao.insertSelector(2, 10_000, "user.*", "test", null, null, null); dao.insertSelector(4, 1_000, "user.*", "(?i).*adhoc.*", null, null, null); dao.insertSelector(5, 100, "user.*", "(?i).*dashboard.*", null, null, null); diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java index bc8af71a4cb21..031b26aba2f3b 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java @@ -53,6 +53,7 @@ import static com.facebook.presto.execution.resourceGroups.db.H2TestUtil.waitForCompleteQueryCount; import static com.facebook.presto.execution.resourceGroups.db.H2TestUtil.waitForRunningQueryCount; import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_TIME_LIMIT; +import static com.facebook.presto.spi.StandardErrorCode.INVALID_RESOURCE_GROUP; import static com.facebook.presto.spi.StandardErrorCode.QUERY_REJECTED; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static io.airlift.testing.Assertions.assertContains; @@ -116,8 +117,8 @@ public void testBasic() waitForQueryState(queryRunner, secondDashboardQuery, QUEUED); waitForRunningQueryCount(queryRunner, 1); // Update db to allow for 1 more running query in dashboard resource group - dao.updateResourceGroup(3, "user-${USER}", "1MB", 3, 4, 4, null, null, null, null, null, null, null, 1L, TEST_ENVIRONMENT); - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 2, 2, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.updateResourceGroup(3, "user-${USER}", "1MB", 3, 4, 4, null, null, null, null, null, 1L, TEST_ENVIRONMENT); + dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 2, 2, null, null, null, null, null, 3L, TEST_ENVIRONMENT); waitForQueryState(queryRunner, secondDashboardQuery, RUNNING); QueryId thirdDashboardQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, thirdDashboardQuery, QUEUED); @@ -164,8 +165,8 @@ public void testTooManyQueries() waitForQueryState(queryRunner, thirdDashboardQuery, FAILED); // Allow one more query to run and resubmit third query - dao.updateResourceGroup(3, "user-${USER}", "1MB", 3, 4, 4, null, null, null, null, null, null, null, 1L, TEST_ENVIRONMENT); - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 2, 2, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.updateResourceGroup(3, "user-${USER}", "1MB", 3, 4, 4, null, null, null, null, null, 1L, TEST_ENVIRONMENT); + dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 2, 2, null, null, null, null, null, 3L, TEST_ENVIRONMENT); InternalResourceGroupManager manager = queryRunner.getCoordinator().getResourceGroupManager().get(); DbResourceGroupConfigurationManager dbConfigurationManager = (DbResourceGroupConfigurationManager) manager.getConfigurationManager(); @@ -177,7 +178,7 @@ public void testTooManyQueries() waitForQueryState(queryRunner, thirdDashboardQuery, QUEUED); // Lower running queries in dashboard resource groups and reload the config - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, 3L, TEST_ENVIRONMENT); dbConfigurationManager.load(); // Cancel query and verify that third query is still queued @@ -233,12 +234,12 @@ public void testSelectorPriority() QueryId firstQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, firstQuery, RUNNING); - Optional resourceGroup = queryManager.getQueryInfo(firstQuery).getResourceGroupId(); + Optional resourceGroup = queryManager.getFullQueryInfo(firstQuery).getResourceGroupId(); assertTrue(resourceGroup.isPresent()); assertEquals(resourceGroup.get().toString(), "global.user-user.dashboard-user"); // create a new resource group that rejects all queries submitted to it - dao.insertResourceGroup(8, "reject-all-queries", "1MB", 0, 0, 0, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.insertResourceGroup(8, "reject-all-queries", "1MB", 0, 0, 0, null, null, null, null, null, 3L, TEST_ENVIRONMENT); // add a new selector that has a higher priority than the existing dashboard selector and that routes queries to the "reject-all-queries" resource group dao.insertSelector(8, 200, "user.*", "(?i).*dashboard.*", null, null, null); @@ -249,60 +250,49 @@ public void testSelectorPriority() QueryId secondQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, secondQuery, FAILED); - resourceGroup = queryManager.getQueryInfo(secondQuery).getResourceGroupId(); + resourceGroup = queryManager.getFullQueryInfo(secondQuery).getResourceGroupId(); assertTrue(resourceGroup.isPresent()); assertEquals(resourceGroup.get(), createResourceGroupId("global", "user-user", "reject-all-queries")); } - @Test(timeOut = 60_000) - public void testRunningTimeLimit() - throws Exception - { - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, null, "3s", 3L, TEST_ENVIRONMENT); - QueryId firstDashboardQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); - waitForQueryState(queryRunner, firstDashboardQuery, FAILED); - } - - @Test(timeOut = 60_000) - public void testQueuedTimeLimit() - throws Exception - { - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, 1, 1, null, null, null, null, null, "5s", null, 3L, TEST_ENVIRONMENT); - QueryId firstDashboardQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); - waitForQueryState(queryRunner, firstDashboardQuery, RUNNING); - QueryId secondDashboardQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); - waitForQueryState(queryRunner, secondDashboardQuery, QUEUED); - waitForQueryState(queryRunner, secondDashboardQuery, FAILED); - } - @Test(timeOut = 60_000) public void testQueryExecutionTimeLimit() throws Exception { - Session session = testSessionBuilder() - .setCatalog("tpch") - .setSchema("sf100000") - .setSource("dashboard") - .setSystemProperty(QUERY_MAX_EXECUTION_TIME, "1ms") - .build(); QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); InternalResourceGroupManager manager = queryRunner.getCoordinator().getResourceGroupManager().get(); DbResourceGroupConfigurationManager dbConfigurationManager = (DbResourceGroupConfigurationManager) manager.getConfigurationManager(); - QueryId firstQuery = createQuery(queryRunner, session, LONG_LASTING_QUERY); + QueryId firstQuery = createQuery( + queryRunner, + testSessionBuilder() + .setCatalog("tpch") + .setSchema("sf100000") + .setSource("dashboard") + .setSystemProperty(QUERY_MAX_EXECUTION_TIME, "1ms") + .build(), + LONG_LASTING_QUERY); waitForQueryState(queryRunner, firstQuery, FAILED); - assertEquals(queryManager.getQueryInfo(firstQuery).getErrorCode(), EXCEEDED_TIME_LIMIT.toErrorCode()); - assertContains(queryManager.getQueryInfo(firstQuery).getFailureInfo().getMessage(), "Query exceeded the maximum execution time limit of 1.00ms"); + assertEquals(queryManager.getFullQueryInfo(firstQuery).getErrorCode(), EXCEEDED_TIME_LIMIT.toErrorCode()); + assertContains(queryManager.getFullQueryInfo(firstQuery).getFailureInfo().getMessage(), "Query exceeded the maximum execution time limit of 1.00ms"); // set max running queries to 0 for the dashboard resource group so that new queries get queued immediately - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, null, 0, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, null, 0, null, null, null, null, null, 3L, TEST_ENVIRONMENT); dbConfigurationManager.load(); - QueryId secondQuery = createQuery(queryRunner, session, LONG_LASTING_QUERY); + QueryId secondQuery = createQuery( + queryRunner, + testSessionBuilder() + .setCatalog("tpch") + .setSchema("sf100000") + .setSource("dashboard") + .setSystemProperty(QUERY_MAX_EXECUTION_TIME, "1ms") + .build(), + LONG_LASTING_QUERY); //this query should immediately get queued waitForQueryState(queryRunner, secondQuery, QUEUED); // after a 5s wait this query should still be QUEUED, not FAILED as the max execution time should be enforced after the query starts running Thread.sleep(5_000); - assertEquals(queryManager.getQueryInfo(secondQuery).getState(), QUEUED); + assertEquals(queryManager.getQueryState(secondQuery), QUEUED); // reconfigure the resource group to run the second query - dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, null, 1, null, null, null, null, null, null, null, 3L, TEST_ENVIRONMENT); + dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, null, 1, null, null, null, null, null, 3L, TEST_ENVIRONMENT); dbConfigurationManager.load(); // cancel the first one and let the second one start queryManager.cancelQuery(firstQuery); @@ -320,7 +310,7 @@ public void testQueryTypeBasedSelection() .build(); QueryId queryId = createQuery(queryRunner, session, "EXPLAIN " + LONG_LASTING_QUERY); waitForQueryState(queryRunner, queryId, ImmutableSet.of(RUNNING, FINISHED)); - Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getQueryInfo(queryId).getResourceGroupId(); + Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getFullQueryInfo(queryId).getResourceGroupId(); assertTrue(resourceGroupId.isPresent(), "Query should have a resource group"); assertEquals(resourceGroupId.get(), createResourceGroupId("explain")); } @@ -333,6 +323,36 @@ public void testClientTagsBasedSelection() assertResourceGroupWithClientTags(ImmutableSet.of("tag1", "tag2"), createResourceGroupId("global", "user-user", "adhoc-user")); } + @Test + public void testNonLeafGroup() + throws Exception + { + Session session = testSessionBuilder() + .setCatalog("tpch") + .setSchema("sf100000") + .setSource("non-leaf") + .build(); + QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); + InternalResourceGroupManager manager = queryRunner.getCoordinator().getResourceGroupManager().get(); + DbResourceGroupConfigurationManager dbConfigurationManager = (DbResourceGroupConfigurationManager) manager.getConfigurationManager(); + int originalSize = getSelectors(queryRunner).size(); + // Add a selector for a non leaf group + dao.insertSelector(3, 100, "user.*", "(?i).*non-leaf.*", null, null, null); + dbConfigurationManager.load(); + while (getSelectors(queryRunner).size() != originalSize + 1) { + MILLISECONDS.sleep(500); + } + // Submit query with side effect of creating resource groups + QueryId firstDashboardQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); + waitForQueryState(queryRunner, firstDashboardQuery, RUNNING); + cancelQuery(queryRunner, firstDashboardQuery); + waitForQueryState(queryRunner, firstDashboardQuery, FAILED); + // Submit a query to a non-leaf resource group + QueryId invalidResourceGroupQuery = createQuery(queryRunner, session, LONG_LASTING_QUERY); + waitForQueryState(queryRunner, invalidResourceGroupQuery, FAILED); + assertEquals(queryRunner.getQueryInfo(invalidResourceGroupQuery).getErrorCode(), INVALID_RESOURCE_GROUP.toErrorCode()); + } + private void assertResourceGroupWithClientTags(Set clientTags, ResourceGroupId expectedResourceGroup) throws InterruptedException { @@ -344,7 +364,7 @@ private void assertResourceGroupWithClientTags(Set clientTags, ResourceG .build(); QueryId queryId = createQuery(queryRunner, session, LONG_LASTING_QUERY); waitForQueryState(queryRunner, queryId, ImmutableSet.of(RUNNING, FINISHED)); - Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getQueryInfo(queryId).getResourceGroupId(); + Optional resourceGroupId = queryRunner.getCoordinator().getQueryManager().getFullQueryInfo(queryId).getResourceGroupId(); assertTrue(resourceGroupId.isPresent(), "Query should have a resource group"); assertEquals(resourceGroupId.get(), expectedResourceGroup, format("Expected: '%s' resource group, found: %s", expectedResourceGroup, resourceGroupId.get())); } diff --git a/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java b/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java index 845728820434c..1c9873e07d39c 100644 --- a/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java +++ b/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java @@ -13,10 +13,11 @@ */ package com.facebook.presto.memory; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryState; -import com.facebook.presto.execution.QueryStats; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.server.BasicQueryStats; import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -27,6 +28,7 @@ import java.net.URI; import java.util.Optional; +import java.util.OptionalDouble; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; import static com.facebook.presto.execution.QueryState.FINISHED; @@ -64,75 +66,39 @@ public void testLeakDetector() assertEquals(leakDetector.getNumberOfLeakedQueries(), 1); } - private QueryInfo createQueryInfo(String queryId, QueryState state) + private static BasicQueryInfo createQueryInfo(String queryId, QueryState state) { - return new QueryInfo( + return new BasicQueryInfo( new QueryId(queryId), TEST_SESSION.toSessionRepresentation(), + Optional.of(new ResourceGroupId("global")), state, GENERAL_POOL, true, URI.create("1"), - ImmutableList.of("2", "3"), "", - new QueryStats( + new BasicQueryStats( DateTime.parse("1991-09-06T05:00-05:30"), DateTime.parse("1991-09-06T05:01-05:30"), - DateTime.parse("1991-09-06T05:02-05:30"), - DateTime.parse("1991-09-06T06:00-05:30"), Duration.valueOf("8m"), Duration.valueOf("7m"), Duration.valueOf("34m"), - Duration.valueOf("9m"), - Duration.valueOf("10m"), - Duration.valueOf("11m"), - Duration.valueOf("12m"), 13, 14, 15, 100, - 17, - 18, - 34, - 19, - 20.0, DataSize.valueOf("21GB"), - DataSize.valueOf("22GB"), + 22, + 23, DataSize.valueOf("23GB"), DataSize.valueOf("24GB"), DataSize.valueOf("25GB"), - true, Duration.valueOf("23m"), Duration.valueOf("24m"), - Duration.valueOf("25m"), - Duration.valueOf("26m"), true, ImmutableSet.of(WAITING_FOR_MEMORY), - DataSize.valueOf("27GB"), - 28, - DataSize.valueOf("29GB"), - 30, - DataSize.valueOf("31GB"), - 32, - DataSize.valueOf("33GB"), - ImmutableList.of(), - ImmutableList.of()), - Optional.empty(), - Optional.empty(), - Optional.empty(), - ImmutableMap.of(), - ImmutableSet.of(), - ImmutableMap.of(), - ImmutableSet.of(), - Optional.empty(), - false, - "33", - Optional.empty(), - null, + OptionalDouble.of(20)), null, - ImmutableSet.of(), - Optional.empty(), - false, - Optional.empty()); + null); } } diff --git a/presto-tests/src/test/java/com/facebook/presto/memory/TestMemoryManager.java b/presto-tests/src/test/java/com/facebook/presto/memory/TestMemoryManager.java index fc17f7b0e14ea..8694718059328 100644 --- a/presto-tests/src/test/java/com/facebook/presto/memory/TestMemoryManager.java +++ b/presto-tests/src/test/java/com/facebook/presto/memory/TestMemoryManager.java @@ -14,9 +14,8 @@ package com.facebook.presto.memory; import com.facebook.presto.Session; -import com.facebook.presto.execution.QueryInfo; -import com.facebook.presto.execution.TaskInfo; -import com.facebook.presto.operator.DriverStats; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.server.BasicQueryStats; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.QueryId; import com.facebook.presto.testing.QueryRunner; @@ -32,13 +31,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import static com.facebook.presto.SystemSessionProperties.RESOURCE_OVERCOMMIT; import static com.facebook.presto.execution.QueryState.FINISHED; -import static com.facebook.presto.execution.StageInfo.getAllStages; import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; import static com.facebook.presto.memory.LocalMemoryManager.RESERVED_POOL; import static com.facebook.presto.operator.BlockedReason.WAITING_FOR_MEMORY; @@ -135,25 +134,74 @@ public void testOutOfMemoryKiller() } // Wait for one of the queries to die - boolean queryDone = false; - while (!queryDone) { - for (QueryInfo info : queryRunner.getCoordinator().getQueryManager().getAllQueryInfo()) { - if (info.getState().isDone()) { - assertNotNull(info.getErrorCode()); - assertEquals(info.getErrorCode().getCode(), CLUSTER_OUT_OF_MEMORY.toErrorCode().getCode()); - queryDone = true; - break; - } + waitForQueryToBeKilled(queryRunner); + + // Release the memory in the reserved pool + for (TestingPrestoServer server : queryRunner.getServers()) { + Optional reserved = server.getLocalMemoryManager().getReservedPool(); + assertTrue(reserved.isPresent()); + // Free up the entire pool + reserved.get().free(fakeQueryId, "test", reserved.get().getMaxBytes()); + assertTrue(reserved.get().getFreeBytes() > 0); + } + + for (Future query : queryFutures) { + query.get(); + } + } + } + + private void waitForQueryToBeKilled(DistributedQueryRunner queryRunner) + throws InterruptedException + { + while (true) { + for (BasicQueryInfo info : queryRunner.getCoordinator().getQueryManager().getQueries()) { + if (info.getState().isDone()) { + assertNotNull(info.getErrorCode()); + assertEquals(info.getErrorCode().getCode(), CLUSTER_OUT_OF_MEMORY.toErrorCode().getCode()); + return; } - MILLISECONDS.sleep(10); } + MILLISECONDS.sleep(10); + } + } - // Release the memory in the reserved pool + @Test(timeOut = 240_000, expectedExceptions = ExecutionException.class, expectedExceptionsMessageRegExp = ".*Query killed because the cluster is out of memory. Please try again in a few minutes.") + public void testReservedPoolDisabled() + throws Exception + { + Map properties = ImmutableMap.builder() + .put("experimental.reserved-pool-enabled", "false") + .put("query.low-memory-killer.delay", "5s") + .put("query.low-memory-killer.policy", "total-reservation") + .build(); + + try (DistributedQueryRunner queryRunner = createQueryRunner(TINY_SESSION, properties)) { + // Reserve all the memory + QueryId fakeQueryId = new QueryId("fake"); for (TestingPrestoServer server : queryRunner.getServers()) { - MemoryPool reserved = server.getLocalMemoryManager().getPool(RESERVED_POOL); + List memoryPools = server.getLocalMemoryManager().getPools(); + assertEquals(memoryPools.size(), 1, "Only general pool should exist"); + assertTrue(memoryPools.get(0).tryReserve(fakeQueryId, "test", memoryPools.get(0).getMaxBytes())); + } + + List> queryFutures = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + queryFutures.add(executor.submit(() -> queryRunner.execute("SELECT COUNT(*), clerk FROM orders GROUP BY clerk"))); + } + + // Wait for one of the queries to die + waitForQueryToBeKilled(queryRunner); + + // Reserved pool shouldn't exist on the workers and allocation should have been done in the general pool + for (TestingPrestoServer server : queryRunner.getServers()) { + Optional reserved = server.getLocalMemoryManager().getReservedPool(); + MemoryPool general = server.getLocalMemoryManager().getGeneralPool(); + assertFalse(reserved.isPresent()); + assertTrue(general.getReservedBytes() > 0); // Free up the entire pool - reserved.free(fakeQueryId, "test", reserved.getMaxBytes()); - assertTrue(reserved.getFreeBytes() > 0); + general.free(fakeQueryId, "test", general.getMaxBytes()); + assertTrue(general.getFreeBytes() > 0); } for (Future query : queryFutures) { @@ -180,16 +228,16 @@ private void testNoLeak(@Language("SQL") String query) try (DistributedQueryRunner queryRunner = createQueryRunner(TINY_SESSION, properties)) { executor.submit(() -> queryRunner.execute(query)).get(); - List queryInfos = queryRunner.getCoordinator().getQueryManager().getAllQueryInfo(); - for (QueryInfo info : queryInfos) { + for (BasicQueryInfo info : queryRunner.getCoordinator().getQueryManager().getQueries()) { assertEquals(info.getState(), FINISHED); } // Make sure we didn't leak any memory on the workers for (TestingPrestoServer worker : queryRunner.getServers()) { - MemoryPool reserved = worker.getLocalMemoryManager().getPool(RESERVED_POOL); - assertEquals(reserved.getMaxBytes(), reserved.getFreeBytes()); - MemoryPool general = worker.getLocalMemoryManager().getPool(GENERAL_POOL); + Optional reserved = worker.getLocalMemoryManager().getReservedPool(); + assertTrue(reserved.isPresent()); + assertEquals(reserved.get().getMaxBytes(), reserved.get().getFreeBytes()); + MemoryPool general = worker.getLocalMemoryManager().getGeneralPool(); assertEquals(general.getMaxBytes(), general.getFreeBytes()); } } @@ -232,28 +280,29 @@ public void testClusterPools() } // Make sure the queries are blocked - List currentQueryInfos = queryRunner.getCoordinator().getQueryManager().getAllQueryInfo(); - for (QueryInfo info : currentQueryInfos) { + List currentQueryInfos = queryRunner.getCoordinator().getQueryManager().getQueries(); + for (BasicQueryInfo info : currentQueryInfos) { assertFalse(info.getState().isDone()); } assertEquals(currentQueryInfos.size(), 2); // Check that the pool information propagated to the query objects assertNotEquals(currentQueryInfos.get(0).getMemoryPool(), currentQueryInfos.get(1).getMemoryPool()); - while (!allQueriesBlocked(currentQueryInfos)) { + while (!currentQueryInfos.stream().allMatch(TestMemoryManager::isBlockedWaitingForMemory)) { MILLISECONDS.sleep(10); - currentQueryInfos = queryRunner.getCoordinator().getQueryManager().getAllQueryInfo(); - for (QueryInfo info : currentQueryInfos) { + currentQueryInfos = queryRunner.getCoordinator().getQueryManager().getQueries(); + for (BasicQueryInfo info : currentQueryInfos) { assertFalse(info.getState().isDone()); } } // Release the memory in the reserved pool for (TestingPrestoServer server : queryRunner.getServers()) { - MemoryPool reserved = server.getLocalMemoryManager().getPool(RESERVED_POOL); + Optional reserved = server.getLocalMemoryManager().getReservedPool(); + assertTrue(reserved.isPresent()); // Free up the entire pool - reserved.free(fakeQueryId, "test", reserved.getMaxBytes()); - assertTrue(reserved.getFreeBytes() > 0); + reserved.get().free(fakeQueryId, "test", reserved.get().getMaxBytes()); + assertTrue(reserved.get().getFreeBytes() > 0); } // Make sure both queries finish now that there's memory free in the reserved pool. @@ -262,16 +311,16 @@ public void testClusterPools() query.get(); } - List queryInfos = queryRunner.getCoordinator().getQueryManager().getAllQueryInfo(); - for (QueryInfo info : queryInfos) { + for (BasicQueryInfo info : queryRunner.getCoordinator().getQueryManager().getQueries()) { assertEquals(info.getState(), FINISHED); } // Make sure we didn't leak any memory on the workers for (TestingPrestoServer worker : queryRunner.getServers()) { - MemoryPool reserved = worker.getLocalMemoryManager().getPool(RESERVED_POOL); - assertEquals(reserved.getMaxBytes(), reserved.getFreeBytes()); - MemoryPool general = worker.getLocalMemoryManager().getPool(GENERAL_POOL); + Optional reserved = worker.getLocalMemoryManager().getReservedPool(); + assertTrue(reserved.isPresent()); + assertEquals(reserved.get().getMaxBytes(), reserved.get().getFreeBytes()); + MemoryPool general = worker.getLocalMemoryManager().getGeneralPool(); // Free up the memory we reserved earlier general.free(fakeQueryId, "test", general.getMaxBytes()); assertEquals(general.getMaxBytes(), general.getFreeBytes()); @@ -279,25 +328,16 @@ public void testClusterPools() } } - private static boolean allQueriesBlocked(List current) + private static boolean isBlockedWaitingForMemory(BasicQueryInfo info) { - boolean allDriversBlocked = current.stream() - .flatMap(query -> getAllStages(query.getOutputStage()).stream()) - .flatMap(stage -> stage.getTasks().stream()) - .flatMap(task -> task.getStats().getPipelines().stream()) - .flatMap(pipeline -> pipeline.getDrivers().stream()) - .allMatch(DriverStats::isFullyBlocked); - boolean waitingForMemory = current.stream().allMatch(TestMemoryManager::atLeastOneOperatorWaitingForMemory); - - return allDriversBlocked && waitingForMemory; - } + BasicQueryStats stats = info.getQueryStats(); + boolean isWaitingForMemory = stats.getBlockedReasons().contains(WAITING_FOR_MEMORY); + if (!isWaitingForMemory) { + return false; + } - private static boolean atLeastOneOperatorWaitingForMemory(QueryInfo query) - { - return getAllStages(query.getOutputStage()).stream() - .flatMap(stage -> stage.getTasks().stream()) - .map(TaskInfo::getStats) - .anyMatch(task -> task.getBlockedReasons().contains(WAITING_FOR_MEMORY)); + // queries are not marked as fully blocked if there are no running drivers + return stats.isFullyBlocked() || stats.getRunningDrivers() == 0; } @DataProvider(name = "legacy_system_pool_enabled") @@ -306,7 +346,7 @@ public Object[][] legacySystemPoolEnabled() return new Object[][] {{true}, {false}}; } - @Test(timeOut = 60_000, dataProvider = "legacy_system_pool_enabled", expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded max user memory size of 1kB.*") + @Test(timeOut = 60_000, dataProvider = "legacy_system_pool_enabled", expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded distributed user memory limit of 1kB.*") public void testQueryUserMemoryLimit(boolean systemPoolEnabled) throws Exception { @@ -321,7 +361,7 @@ public void testQueryUserMemoryLimit(boolean systemPoolEnabled) } } - @Test(timeOut = 60_000, expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded max total memory size of 2kB.*") + @Test(timeOut = 60_000, expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded distributed total memory limit of 2kB.*") public void testQueryTotalMemoryLimit() throws Exception { @@ -334,7 +374,7 @@ public void testQueryTotalMemoryLimit() } } - @Test(timeOut = 60_000, expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded local user memory limit of 1kB.*") + @Test(timeOut = 60_000, expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = ".*Query exceeded per-node user memory limit of 1kB.*") public void testQueryMemoryPerNodeLimit() throws Exception { diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java b/presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java index 24bcc48113be2..2134c91f589dc 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/AbstractTestEngineOnlyQueries.java @@ -26,6 +26,7 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.Objects; import static com.google.common.base.Preconditions.checkState; import static org.testng.Assert.assertEquals; @@ -75,18 +76,25 @@ public void testTimeLiterals() @Test public void testLocallyUnrepresentableTimeLiterals() { - LocalDateTime localTimeThatDidNotExist = LocalDateTime.of(1986, 1, 1, 0, 10); + LocalDateTime localTimeThatDidNotExist = LocalDateTime.of(2017, 4, 2, 2, 10); checkState(ZoneId.systemDefault().getRules().getValidOffsets(localTimeThatDidNotExist).isEmpty(), "This test assumes certain JVM time zone"); // This tests that both Presto runner and H2 can return TIMESTAMP value that never happened in JVM's zone (e.g. is not representable using java.sql.Timestamp) @Language("SQL") String sql = DateTimeFormatter.ofPattern("'SELECT TIMESTAMP '''uuuu-MM-dd HH:mm:ss''").format(localTimeThatDidNotExist); assertEquals(computeScalar(sql), localTimeThatDidNotExist); // this tests Presto and the QueryRunner assertQuery(sql); // this tests H2QueryRunner - LocalDate localDateThatDidNotHaveMidnight = LocalDate.of(1986, 1, 1); + LocalDate localDateThatDidNotHaveMidnight = LocalDate.of(1970, 1, 1); checkState(ZoneId.systemDefault().getRules().getValidOffsets(localDateThatDidNotHaveMidnight.atStartOfDay()).isEmpty(), "This test assumes certain JVM time zone"); // This tests that both Presto runner and H2 can return DATE value for a day which midnight never happened in JVM's zone (e.g. is not exactly representable using java.sql.Date) sql = DateTimeFormatter.ofPattern("'SELECT DATE '''uuuu-MM-dd''").format(localDateThatDidNotHaveMidnight); assertEquals(computeScalar(sql), localDateThatDidNotHaveMidnight); // this tests Presto and the QueryRunner assertQuery(sql); // this tests H2QueryRunner + + LocalTime localTimeThatDidNotOccurOn19700101 = LocalTime.of(0, 10); + checkState(ZoneId.systemDefault().getRules().getValidOffsets(localTimeThatDidNotOccurOn19700101.atDate(LocalDate.ofEpochDay(0))).isEmpty(), "This test assumes certain JVM time zone"); + checkState(!Objects.equals(java.sql.Time.valueOf(localTimeThatDidNotOccurOn19700101).toLocalTime(), localTimeThatDidNotOccurOn19700101), "This test assumes certain JVM time zone"); + sql = DateTimeFormatter.ofPattern("'SELECT TIME '''HH:mm:ss''").format(localTimeThatDidNotOccurOn19700101); + assertEquals(computeScalar(sql), localTimeThatDidNotOccurOn19700101); // this tests Presto and the QueryRunner + assertQuery(sql); // this tests H2QueryRunner } } diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestGracefulShutdown.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestGracefulShutdown.java index 44e55de9fadc9..66b2caa33015d 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestGracefulShutdown.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestGracefulShutdown.java @@ -14,8 +14,8 @@ package com.facebook.presto.tests; import com.facebook.presto.Session; -import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.TaskManager; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.server.testing.TestingPrestoServer.TestShutdownAction; import com.google.common.collect.ImmutableMap; @@ -94,8 +94,8 @@ public void testShutdown() Futures.allAsList(queryFutures).get(); - List queryInfos = queryRunner.getCoordinator().getQueryManager().getAllQueryInfo(); - for (QueryInfo info : queryInfos) { + List queryInfos = queryRunner.getCoordinator().getQueryManager().getQueries(); + for (BasicQueryInfo info : queryInfos) { assertEquals(info.getState(), FINISHED); } diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestH2QueryRunner.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestH2QueryRunner.java index 50cdec4d09428..678450c66d621 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestH2QueryRunner.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestH2QueryRunner.java @@ -55,7 +55,7 @@ public void testDateToTimestampCoercion() assertEquals(rows.getOnlyValue(), LocalDate.of(2018, 1, 13).atStartOfDay()); // date, which midnight was skipped in JVM zone - LocalDate forwardOffsetChangeAtMidnightInJvmZone = LocalDate.of(1986, 1, 1); + LocalDate forwardOffsetChangeAtMidnightInJvmZone = LocalDate.of(1970, 1, 1); checkState(ZoneId.systemDefault().getRules().getValidOffsets(forwardOffsetChangeAtMidnightInJvmZone.atStartOfDay()).size() == 0, "This test assumes certain JVM time zone"); rows = h2QueryRunner.execute(TEST_SESSION, DateTimeFormatter.ofPattern("'SELECT DATE '''uuuu-MM-dd''").format(forwardOffsetChangeAtMidnightInJvmZone), ImmutableList.of(TIMESTAMP)); assertEquals(rows.getOnlyValue(), forwardOffsetChangeAtMidnightInJvmZone.atStartOfDay()); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java index 5189f79fd9bc1..9c09e28a4069d 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestLocalQueries.java @@ -87,10 +87,10 @@ public void testShowColumnStats() MaterializedResult expectedStatistics = resultBuilder(getSession(), VARCHAR, DOUBLE, DOUBLE, DOUBLE, DOUBLE, VARCHAR, VARCHAR) - .row("regionkey", null, 5.0, 0.0, null, "0", "4") - .row("name", 177.0, 25.0, 0.0, null, "ALGERIA", "VIETNAM") - .row("comment", 1857.0, 25.0, 0.0, null, " haggle. carefully final deposit...", "y final packages. slow foxes caj...") .row("nationkey", null, 25.0, 0.0, null, "0", "24") + .row("name", 177.0, 25.0, 0.0, null, null, null) + .row("regionkey", null, 5.0, 0.0, null, "0", "4") + .row("comment", 1857.0, 25.0, 0.0, null, null, null) .row(null, null, null, null, 25.0, null, null) .build(); @@ -116,7 +116,7 @@ public void testDecimal() } @Test - public void testExplainIO() + public void testIOExplain() { String query = "SELECT * FROM orders"; MaterializedResult result = computeActual("EXPLAIN (TYPE IO, FORMAT JSON) " + query); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java index 9235d76accc1e..e823b7e9ba1e0 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java @@ -55,7 +55,8 @@ public void setUp() throws Exception { queryRunner = TpchQueryRunnerBuilder.builder().build(); - queryRunner.installPlugin(new Plugin() { + queryRunner.installPlugin(new Plugin() + { @Override public Iterable getConnectorFactories() { @@ -122,7 +123,7 @@ public void testMetadataIsClearedAfterQueryCanceled() // wait until query starts running while (true) { - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); if (queryInfo.getState().isDone()) { assertEquals(queryInfo.getState(), FAILED); throw queryInfo.getFailureInfo().toException(); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java index 106fba7fdff95..2997f561b3b6a 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java @@ -25,7 +25,7 @@ public class TestMinWorkerRequirement { @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Cluster is still initializing, there are insufficient active worker nodes \\(4\\) to run query") - public void testInsufficientWorkerNodes() + public void testInsufficientInitialWorkerNodes() throws Exception { try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() @@ -38,7 +38,7 @@ public void testInsufficientWorkerNodes() } @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Cluster is still initializing, there are insufficient active worker nodes \\(3\\) to run query") - public void testInsufficientWorkerNodesWithCoordinatorExcluded() + public void testInsufficientInitialWorkerNodesWithCoordinatorExcluded() throws Exception { try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() @@ -52,7 +52,7 @@ public void testInsufficientWorkerNodesWithCoordinatorExcluded() } @Test - public void testSufficientWorkerNodes() + public void testSufficientInitialWorkerNodes() throws Exception { try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() @@ -84,4 +84,63 @@ public void testInitializationTimeout() assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 4); } } + + @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Insufficient active worker nodes. Waited 1.00ns for at least 5 workers, but only 4 workers are active") + public void testInsufficientWorkerNodes() + throws Exception + { + try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() + .setCoordinatorProperties(ImmutableMap.builder() + .put("query-manager.required-workers", "5") + .put("query-manager.required-workers-max-wait", "1ns") + .build()) + .setNodeCount(4) + .build()) { + queryRunner.execute("SELECT 1"); + fail("Expected exception due to insufficient active worker nodes"); + } + } + + @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Insufficient active worker nodes. Waited 1.00ns for at least 4 workers, but only 3 workers are active") + public void testInsufficientWorkerNodesWithCoordinatorExcluded() + throws Exception + { + try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() + .setCoordinatorProperties(ImmutableMap.builder() + .put("node-scheduler.include-coordinator", "false") + .put("query-manager.required-workers", "4") + .put("query-manager.required-workers-max-wait", "1ns") + .build()) + .setNodeCount(4) + .build()) { + queryRunner.execute("SELECT 1"); + fail("Expected exception due to insufficient active worker nodes"); + } + } + + @Test + public void testInsufficientWorkerNodesAfterDrop() + throws Exception + { + try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() + .setCoordinatorProperties(ImmutableMap.builder() + .put("query-manager.required-workers", "4") + .put("query-manager.required-workers-max-wait", "1ns") + .build()) + .setNodeCount(4) + .build()) { + queryRunner.execute("SELECT 1"); + assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 4); + + try { + // Query should still be allowed to run if active workers drop down below the minimum required nodes + queryRunner.getServers().get(0).close(); + assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 3); + queryRunner.execute("SELECT 1"); + } + catch (RuntimeException e) { + assertEquals(e.getMessage(), "Insufficient active worker nodes. Waited 1.00ns for at least 4 workers, but only 3 workers are active"); + } + } + } } diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestOptimizeMixedDistinctAggregations.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestOptimizeMixedDistinctAggregations.java index 5fff78c099187..c17079edc4899 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestOptimizeMixedDistinctAggregations.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestOptimizeMixedDistinctAggregations.java @@ -28,10 +28,8 @@ public TestOptimizeMixedDistinctAggregations() @Override public void testCountDistinct() { - // TODO https://github.com/prestodb/presto/issues/8894 . Once fixed, remove test override. - assertQuery("SELECT COUNT(DISTINCT custkey + 1) FROM orders", "SELECT COUNT(*) FROM (SELECT DISTINCT custkey + 1 FROM orders) t"); - // assertQuery("SELECT COUNT(DISTINCT linenumber), COUNT(*) from lineitem where linenumber < 0"); + assertQuery("SELECT COUNT(DISTINCT linenumber), COUNT(*) from lineitem where linenumber < 0"); } // TODO add dedicated test cases and remove `extends AbstractTestAggregation` diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java index ffc82564dc689..f4f6d82e1d771 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java @@ -15,7 +15,9 @@ import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.execution.QueryState; import com.facebook.presto.execution.TestingSessionContext; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.facebook.presto.tests.tpch.TpchQueryRunnerBuilder; @@ -68,11 +70,11 @@ public void testFailQuery() // wait until query starts running while (true) { - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); - if (queryInfo.getState().isDone()) { - fail("unexpected query state: " + queryInfo.getState()); + QueryState state = queryManager.getQueryState(queryId); + if (state.isDone()) { + fail("unexpected query state: " + state); } - if (queryInfo.getState() == RUNNING) { + if (state == RUNNING) { break; } Thread.sleep(100); @@ -80,7 +82,7 @@ public void testFailQuery() // cancel query queryManager.failQuery(queryId, new PrestoException(GENERIC_INTERNAL_ERROR, "mock exception")); - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); assertEquals(queryInfo.getState(), FAILED); assertEquals(queryInfo.getErrorCode(), GENERIC_INTERNAL_ERROR.toErrorCode()); assertNotNull(queryInfo.getFailureInfo()); @@ -95,7 +97,7 @@ public void testQueryCpuLimit() QueryId queryId = createQuery(queryRunner, TEST_SESSION, "SELECT COUNT(*) FROM lineitem"); waitForQueryState(queryRunner, queryId, FAILED); QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - QueryInfo queryInfo = queryManager.getQueryInfo(queryId); + BasicQueryInfo queryInfo = queryManager.getQueryInfo(queryId); assertEquals(queryInfo.getState(), FAILED); assertEquals(queryInfo.getErrorCode(), EXCEEDED_CPU_LIMIT.toErrorCode()); } diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchDistributedStats.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchDistributedStats.java index 9df1b5a19ee9e..4ffcda4863d1c 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchDistributedStats.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchDistributedStats.java @@ -22,6 +22,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import static com.facebook.presto.SystemSessionProperties.PREFER_PARTIAL_AGGREGATION; import static com.facebook.presto.tests.statistics.MetricComparisonStrategies.absoluteError; import static com.facebook.presto.tests.statistics.MetricComparisonStrategies.defaultTolerance; import static com.facebook.presto.tests.statistics.MetricComparisonStrategies.noError; @@ -38,7 +39,10 @@ public class TestTpchDistributedStats public void setup() throws Exception { - DistributedQueryRunner runner = TpchQueryRunnerBuilder.builder().buildWithoutCatalogs(); + DistributedQueryRunner runner = TpchQueryRunnerBuilder.builder() + // We are not able to calculate stats for PARTIAL aggregations + .amendSession(builder -> builder.setSystemProperty(PREFER_PARTIAL_AGGREGATION, "false")) + .buildWithoutCatalogs(); runner.createCatalog( "tpch", "tpch", @@ -102,24 +106,20 @@ public void testUnion() public void testIntersect() { statisticsAssertion.check("SELECT * FROM nation INTERSECT SELECT * FROM nation", - // real count is 25, estimation cannot know all rows are duplicate. - checks -> checks.estimate(OUTPUT_ROW_COUNT, relativeError(.7, .9))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); statisticsAssertion.check("SELECT * FROM orders WHERE o_custkey < 900 INTERSECT SELECT * FROM orders WHERE o_custkey > 600", - // TODO fix INTERSECT stats calculation as custkey values distribution is pretty linear - checks -> checks.estimate(OUTPUT_ROW_COUNT, relativeError(4, 5))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); } @Test public void testExcept() { statisticsAssertion.check("SELECT * FROM nation EXCEPT SELECT * FROM nation", - // real count is 0, estimation cannot know all rows are eliminated - checks -> checks.estimate(OUTPUT_ROW_COUNT, absoluteError(45, 45))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); statisticsAssertion.check("SELECT * FROM orders WHERE o_custkey < 900 EXCEPT SELECT * FROM orders WHERE o_custkey > 600", - // TODO fix EXCEPT stats calculation as custkey values distribution is pretty linear - checks -> checks.estimate(OUTPUT_ROW_COUNT, relativeError(1.5, 2))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); } @Test diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchLocalStats.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchLocalStats.java index d28c01acdfd08..807484fd6d113 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchLocalStats.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestTpchLocalStats.java @@ -23,6 +23,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import static com.facebook.presto.SystemSessionProperties.PREFER_PARTIAL_AGGREGATION; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static com.facebook.presto.tests.statistics.MetricComparisonStrategies.absoluteError; import static com.facebook.presto.tests.statistics.MetricComparisonStrategies.defaultTolerance; @@ -46,6 +47,8 @@ public void setUp() Session defaultSession = testSessionBuilder() .setCatalog("tpch") .setSchema(TINY_SCHEMA_NAME) + // We are not able to calculate stats for PARTIAL aggregations + .setSystemProperty(PREFER_PARTIAL_AGGREGATION, "false") .build(); LocalQueryRunner queryRunner = new LocalQueryRunner(defaultSession); @@ -157,7 +160,7 @@ public void testInnerJoinStats() statisticsAssertion.check("SELECT n1.n_nationkey FROM nation n1, nation n2 WHERE n1.n_nationkey + 1 = n2.n_nationkey - 1 AND n1.n_nationkey > 5 AND n2.n_nationkey < 20", // Join is over expressions so that predicate push down doesn't unify ranges of n_nationkey coming from n1 and n2. This, however, makes symbols // stats inaccurate (rules can't update them), so we don't verify them. - checks -> checks.estimate(OUTPUT_ROW_COUNT, absoluteError(3))); + checks -> checks.estimate(OUTPUT_ROW_COUNT, absoluteError(8))); // two joins on different keys statisticsAssertion.check("SELECT * FROM nation, supplier, partsupp WHERE n_nationkey = s_nationkey AND s_suppkey = ps_suppkey", @@ -389,11 +392,11 @@ public void testUnion() .estimate(nullsFraction("o_orderkey"), relativeError(.3, .35)) .estimate(lowValue("o_orderkey"), noError()) .estimate(highValue("o_orderkey"), noError()) - .estimate(distinctValuesCount("o_custkey"), relativeError(0.2)) + .estimate(distinctValuesCount("o_custkey"), relativeError(0.5)) .estimate(nullsFraction("o_custkey"), relativeError(.45, .55)) .estimate(lowValue("o_custkey"), noError()) .estimate(highValue("o_custkey"), noError()) - .estimate(distinctValuesCount("o_orderstatus"), relativeError(0.2)) + .estimate(distinctValuesCount("o_orderstatus"), relativeError(0.5)) .estimate(nullsFraction("o_orderstatus"), noError()) .estimate(lowValue("o_orderstatus"), noError()) .estimate(highValue("o_orderstatus"), noError())); @@ -405,11 +408,11 @@ public void testUnion() .estimate(nullsFraction("o_orderkey"), relativeError(.3, .35)) .estimate(lowValue("o_orderkey"), noError()) .estimate(highValue("o_orderkey"), noError()) - .estimate(distinctValuesCount("o_custkey"), relativeError(0.2)) + .estimate(distinctValuesCount("o_custkey"), relativeError(0.5)) .estimate(nullsFraction("o_custkey"), relativeError(.45, .55)) .estimate(lowValue("o_custkey"), noError()) .estimate(highValue("o_custkey"), noError()) - .estimate(distinctValuesCount("o_orderstatus"), relativeError(0.2)) + .estimate(distinctValuesCount("o_orderstatus"), relativeError(0.5)) .estimate(nullsFraction("o_orderstatus"), noError()) .estimate(lowValue("o_orderstatus"), noError()) .estimate(highValue("o_orderstatus"), noError())); @@ -443,45 +446,20 @@ public void testUnion() public void testIntersect() { statisticsAssertion.check("SELECT * FROM nation INTERSECT SELECT * FROM nation", - // real count is 25, estimation cannot know all rows are duplicate. - checks -> checks - .estimate(OUTPUT_ROW_COUNT, relativeError(.7, .9)) - .verifyExactColumnStatistics("n_nationkey") - .verifyExactColumnStatistics("n_regionkey")); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); statisticsAssertion.check("SELECT * FROM orders WHERE o_custkey < 900 INTERSECT SELECT * FROM orders WHERE o_custkey > 600", - // TODO fix INTERSECT stats calculation as custkey values distribution is pretty linear - checks -> checks - .estimate(OUTPUT_ROW_COUNT, relativeError(4, 5)) - .estimate(distinctValuesCount("o_orderkey"), relativeError(1.5, 2)) - .estimate(nullsFraction("o_orderkey"), relativeError(4, 5)) - .estimate(lowValue("o_orderkey"), absoluteError(-1, -1)) - .estimate(highValue("o_orderkey"), absoluteError(25, 25)) - .estimate(distinctValuesCount("o_custkey"), relativeError(1.5, 2.5)) - .estimate(nullsFraction("o_custkey"), relativeError(5, 6)) - .estimate(lowValue("o_custkey"), absoluteError(-600, -600)) - .estimate(highValue("o_custkey"), relativeError(.5, 1))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); } @Test public void testExcept() { statisticsAssertion.check("SELECT * FROM nation EXCEPT SELECT * FROM nation", - // real count is 0, estimation cannot know all rows are eliminated - checks -> checks.estimate(OUTPUT_ROW_COUNT, absoluteError(45, 45))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); statisticsAssertion.check("SELECT * FROM orders WHERE o_custkey < 900 EXCEPT SELECT * FROM orders WHERE o_custkey > 600", - // TODO fix EXCEPT stats calculation as custkey values distribution is pretty linear - checks -> checks - .estimate(OUTPUT_ROW_COUNT, relativeError(1.5, 2)) - .estimate(distinctValuesCount("o_orderkey"), relativeError(0.5, 1)) - .estimate(nullsFraction("o_orderkey"), relativeError(1.5, 2)) - .estimate(lowValue("o_orderkey"), noError()) - .estimate(highValue("o_orderkey"), absoluteError(27, 27)) - .estimate(distinctValuesCount("o_custkey"), relativeError(0.5, 0.8)) - .estimate(nullsFraction("o_custkey"), relativeError(2, 2.5)) - .estimate(lowValue("o_custkey"), noError()) - .estimate(highValue("o_custkey"), relativeError(1.5, 2))); + checks -> checks.noEstimate(OUTPUT_ROW_COUNT)); } @Test diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/tpch/TpchQueryRunnerBuilder.java b/presto-tests/src/test/java/com/facebook/presto/tests/tpch/TpchQueryRunnerBuilder.java index 72d5c0934c32b..7592c690d0f9e 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/tpch/TpchQueryRunnerBuilder.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/tpch/TpchQueryRunnerBuilder.java @@ -17,6 +17,8 @@ import com.facebook.presto.tests.DistributedQueryRunner; import com.facebook.presto.tpch.TpchPlugin; +import java.util.function.Function; + import static com.facebook.presto.testing.TestingSession.testSessionBuilder; public final class TpchQueryRunnerBuilder @@ -33,6 +35,12 @@ private TpchQueryRunnerBuilder() super(DEFAULT_SESSION); } + @Override + public TpchQueryRunnerBuilder amendSession(Function amendSession) + { + return (TpchQueryRunnerBuilder) super.amendSession(amendSession); + } + public static TpchQueryRunnerBuilder builder() { return new TpchQueryRunnerBuilder(); diff --git a/presto-thrift-connector-api/pom.xml b/presto-thrift-connector-api/pom.xml index 9c412f3495094..0d88122b91b8c 100644 --- a/presto-thrift-connector-api/pom.xml +++ b/presto-thrift-connector-api/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-thrift-connector-api diff --git a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftService.java b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftService.java index bb12c47558a3b..33131501da4a3 100644 --- a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftService.java +++ b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftService.java @@ -85,7 +85,7 @@ ListenableFuture getSplits( * @param schemaTableName schema and table name * @param indexColumnNames specifies columns and their order for keys * @param outputColumnNames a list of column names to return - * @param keys keys for which records need to be returned + * @param keys keys for which records need to be returned; includes only unique and non-null values * @param outputConstraint constraint on the returned data * @param maxSplitCount maximum number of splits to return * @param nextToken token from a previous split batch or {@literal null} if it is the first call diff --git a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftSplit.java b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftSplit.java index e5e42e2f80811..b685ba83aa2cb 100644 --- a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftSplit.java +++ b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/PrestoThriftSplit.java @@ -36,12 +36,22 @@ public PrestoThriftSplit(PrestoThriftId splitId, List h this.hosts = requireNonNull(hosts, "hosts is null"); } + /** + * Encodes all the information needed to identify a batch of rows to return to Presto. + * For a basic scan, includes schema name, table name, and output constraint. + * For an index scan, includes schema name, table name, set of keys to lookup and output constraint. + */ @ThriftField(1) public PrestoThriftId getSplitId() { return splitId; } + /** + * Identifies the set of hosts on which the rows are available. If empty, then the rows + * are expected to be available on any host. The hosts in this list may be independent + * from the hosts used to serve metadata requests. + */ @ThriftField(2) public List getHosts() { diff --git a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/datatypes/PrestoThriftBigintArray.java b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/datatypes/PrestoThriftBigintArray.java index fb0103ff56367..69ea9f55ffddb 100644 --- a/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/datatypes/PrestoThriftBigintArray.java +++ b/presto-thrift-connector-api/src/main/java/com/facebook/presto/connector/thrift/api/datatypes/PrestoThriftBigintArray.java @@ -96,7 +96,7 @@ public Block toBlock(Type desiredType) int numberOfRecords = numberOfRecords(); return ArrayBlock.fromElementBlock( numberOfRecords, - nulls == null ? new boolean[numberOfRecords] : nulls, + Optional.of(nulls == null ? new boolean[numberOfRecords] : nulls), calculateOffsets(sizes, nulls, numberOfRecords), values != null ? values.toBlock(BIGINT) : new LongArrayBlock(0, Optional.empty(), new long[] {})); } diff --git a/presto-thrift-connector/pom.xml b/presto-thrift-connector/pom.xml index cbecbc8da2cb9..b42ca26bb5806 100644 --- a/presto-thrift-connector/pom.xml +++ b/presto-thrift-connector/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-thrift-connector @@ -42,11 +42,6 @@ drift-protocol - - io.airlift.drift - drift-transport-spi - - io.airlift.drift drift-transport-netty diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftColumnHandle.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftColumnHandle.java index 21b3acc6b43d4..31db3bda43fcb 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftColumnHandle.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftColumnHandle.java @@ -23,7 +23,6 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; public final class ThriftColumnHandle @@ -107,11 +106,6 @@ public int hashCode() @Override public String toString() { - return toStringHelper(this) - .add("columnName", columnName) - .add("columnType", columnType) - .add("comment", comment) - .add("hidden", hidden) - .toString(); + return columnName + ":" + columnType; } } diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftConnectorFactory.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftConnectorFactory.java index b8059039d6cf8..4a9447c5e40e3 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftConnectorFactory.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftConnectorFactory.java @@ -58,7 +58,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { try { Bootstrap app = new Bootstrap( @@ -69,7 +69,7 @@ public Connector create(String connectorId, Map config, Connecto binder.bind(TypeManager.class).toInstance(context.getTypeManager()); }, locationModule, - new ThriftModule(connectorId)); + new ThriftModule(catalogName)); Injector injector = app .strictConfig() diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftIndexHandle.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftIndexHandle.java index 68aab66f2d19c..44813d54eccba 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftIndexHandle.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftIndexHandle.java @@ -15,14 +15,15 @@ import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ConnectorIndexHandle; +import com.facebook.presto.spi.ConnectorSession; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.predicate.TupleDomain; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Objects; +import java.util.Optional; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; public class ThriftIndexHandle @@ -30,14 +31,32 @@ public class ThriftIndexHandle { private final SchemaTableName schemaTableName; private final TupleDomain tupleDomain; + private final Optional session; + + public ThriftIndexHandle( + SchemaTableName schemaTableName, + TupleDomain tupleDomain, + ConnectorSession session) + { + this(schemaTableName, tupleDomain, Optional.of(requireNonNull(session, "session is null"))); + } @JsonCreator public ThriftIndexHandle( @JsonProperty("schemaTableName") SchemaTableName schemaTableName, @JsonProperty("tupleDomain") TupleDomain tupleDomain) + { + this(schemaTableName, tupleDomain, Optional.empty()); + } + + private ThriftIndexHandle( + SchemaTableName schemaTableName, + TupleDomain tupleDomain, + Optional session) { this.schemaTableName = requireNonNull(schemaTableName, "schemaTableName is null"); this.tupleDomain = requireNonNull(tupleDomain, "tupleDomain is null"); + this.session = requireNonNull(session, "session is null"); } @JsonProperty @@ -75,9 +94,7 @@ public int hashCode() @Override public String toString() { - return toStringHelper(this) - .add("schemaTableName", schemaTableName) - .add("tupleDomain", tupleDomain) - .toString(); + return schemaTableName.toString() + + session.map(value -> ", constraint = " + tupleDomain.toString(value)).orElse(""); } } diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftMetadata.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftMetadata.java index 6c8e66acf3aff..f1a44e0def60e 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftMetadata.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftMetadata.java @@ -176,7 +176,7 @@ public Optional resolveIndex(ConnectorSession session, C ThriftTableHandle table = (ThriftTableHandle) tableHandle; ThriftTableMetadata tableMetadata = getRequiredTableMetadata(new SchemaTableName(table.getSchemaName(), table.getTableName())); if (tableMetadata.containsIndexableColumns(indexableColumns)) { - return Optional.of(new ConnectorResolvedIndex(new ThriftIndexHandle(tableMetadata.getSchemaTableName(), tupleDomain), tupleDomain)); + return Optional.of(new ConnectorResolvedIndex(new ThriftIndexHandle(tableMetadata.getSchemaTableName(), tupleDomain, session), tupleDomain)); } else { return Optional.empty(); diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableHandle.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableHandle.java index c47db0adff710..a30ce7cccd674 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableHandle.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableHandle.java @@ -20,7 +20,6 @@ import java.util.Objects; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; public final class ThriftTableHandle @@ -78,9 +77,6 @@ public int hashCode() @Override public String toString() { - return toStringHelper(this) - .add("schemaName", getSchemaName()) - .add("tableName", getTableName()) - .toString(); + return schemaName + ":" + tableName; } } diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableLayoutHandle.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableLayoutHandle.java index a1f15667c53cb..cd17199b90f12 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableLayoutHandle.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/ThriftTableLayoutHandle.java @@ -24,7 +24,6 @@ import java.util.Optional; import java.util.Set; -import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; public class ThriftTableLayoutHandle @@ -97,11 +96,6 @@ public int hashCode() @Override public String toString() { - return toStringHelper(this) - .add("schemaName", schemaName) - .add("tableName", tableName) - .add("columns", columns) - .add("constraint", constraint) - .toString(); + return schemaName + ":" + tableName; } } diff --git a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/location/ExtendedSimpleAddressSelector.java b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/location/ExtendedSimpleAddressSelector.java index 712706dbf5e56..27cbaf5d7a291 100644 --- a/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/location/ExtendedSimpleAddressSelector.java +++ b/presto-thrift-connector/src/main/java/com/facebook/presto/connector/thrift/location/ExtendedSimpleAddressSelector.java @@ -14,36 +14,44 @@ package com.facebook.presto.connector.thrift.location; import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; import com.google.common.net.HostAndPort; import io.airlift.drift.client.address.AddressSelector; -import io.airlift.drift.transport.client.Address; +import io.airlift.drift.client.address.SimpleAddressSelector.SimpleAddress; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import static java.util.Objects.requireNonNull; public class ExtendedSimpleAddressSelector - implements AddressSelector
    + implements AddressSelector { - private final AddressSelector
    delegate; + private final AddressSelector delegate; - public ExtendedSimpleAddressSelector(AddressSelector
    delegate) + public ExtendedSimpleAddressSelector(AddressSelector delegate) { this.delegate = requireNonNull(delegate, "delegate is null"); } @Override - public Optional
    selectAddress(Optional context) + public Optional selectAddress(Optional context) + { + return selectAddress(context, ImmutableSet.of()); + } + + @Override + public Optional selectAddress(Optional context, Set attempted) { if (!context.isPresent()) { - return delegate.selectAddress(Optional.empty()); + return delegate.selectAddress(context, attempted); } List list = Splitter.on(',').splitToList(context.get()); String value = list.get(ThreadLocalRandom.current().nextInt(list.size())); HostAndPort address = HostAndPort.fromString(value); - return Optional.of(() -> address); + return Optional.of(new SimpleAddress(address)); } } diff --git a/presto-thrift-connector/src/test/java/com/facebook/presto/connector/thrift/integration/ThriftQueryRunner.java b/presto-thrift-connector/src/test/java/com/facebook/presto/connector/thrift/integration/ThriftQueryRunner.java index 9f7ed9269f257..7baefe745d679 100644 --- a/presto-thrift-connector/src/test/java/com/facebook/presto/connector/thrift/integration/ThriftQueryRunner.java +++ b/presto-thrift-connector/src/test/java/com/facebook/presto/connector/thrift/integration/ThriftQueryRunner.java @@ -22,6 +22,8 @@ import com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.server.testing.TestingPrestoServer; import com.facebook.presto.spi.Plugin; +import com.facebook.presto.split.PageSourceManager; +import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.planner.NodePartitioningManager; import com.facebook.presto.testing.MaterializedResult; import com.facebook.presto.testing.QueryRunner; @@ -200,6 +202,18 @@ public Metadata getMetadata() return source.getMetadata(); } + @Override + public SplitManager getSplitManager() + { + return source.getSplitManager(); + } + + @Override + public PageSourceManager getPageSourceManager() + { + return source.getPageSourceManager(); + } + @Override public NodePartitioningManager getNodePartitioningManager() { diff --git a/presto-thrift-testing-server/pom.xml b/presto-thrift-testing-server/pom.xml index a25c668c2d018..dc75c46c5def6 100644 --- a/presto-thrift-testing-server/pom.xml +++ b/presto-thrift-testing-server/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-thrift-testing-server diff --git a/presto-tpcds/pom.xml b/presto-tpcds/pom.xml index 649a613531400..6b7acdb0a1878 100644 --- a/presto-tpcds/pom.xml +++ b/presto-tpcds/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-tpcds @@ -31,6 +31,12 @@ joda-time + + io.airlift + joda-to-java-time-bridge + runtime + + com.fasterxml.jackson.core jackson-databind @@ -73,6 +79,13 @@ test + + com.facebook.presto + presto-main + test-jar + test + + com.facebook.presto presto-parser @@ -96,5 +109,17 @@ log-manager test + + + io.airlift + joni + test + + + + io.airlift + bytecode + test + diff --git a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsConnectorFactory.java b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsConnectorFactory.java index 591c94f17ad43..814e56e3136d9 100644 --- a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsConnectorFactory.java +++ b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsConnectorFactory.java @@ -61,7 +61,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map config, ConnectorContext context) + public Connector create(String catalogName, Map config, ConnectorContext context) { int splitsPerNode = getSplitsPerNode(config); NodeManager nodeManager = context.getNodeManager(); diff --git a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsNodePartitioningProvider.java b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsNodePartitioningProvider.java index 56b92198b759e..7a5ab1dca56bd 100644 --- a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsNodePartitioningProvider.java +++ b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/TpcdsNodePartitioningProvider.java @@ -18,19 +18,20 @@ import com.facebook.presto.spi.ConnectorSplit; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.NodeManager; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; -import com.google.common.collect.ImmutableMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.ToIntFunction; +import static com.facebook.presto.spi.connector.ConnectorBucketNodeMap.createBucketNodeMap; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import static java.lang.Math.toIntExact; import static java.util.Objects.requireNonNull; public class TpcdsNodePartitioningProvider @@ -49,21 +50,13 @@ public TpcdsNodePartitioningProvider(NodeManager nodeManager, int splitsPerNode) } @Override - public Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { Set nodes = nodeManager.getRequiredWorkerNodes(); checkState(!nodes.isEmpty(), "No TPCDS nodes available"); // Split the data using split and skew by the number of nodes available. - ImmutableMap.Builder bucketToNode = ImmutableMap.builder(); - int partNumber = 0; - for (Node node : nodes) { - for (int i = 0; i < splitsPerNode; i++) { - bucketToNode.put(partNumber, node); - partNumber++; - } - } - return bucketToNode.build(); + return createBucketNodeMap(toIntExact((long) nodes.size() * splitsPerNode)); } @Override diff --git a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/statistics/TpcdsTableStatisticsFactory.java b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/statistics/TpcdsTableStatisticsFactory.java index f22c1a9c49658..3b40f1243f970 100644 --- a/presto-tpcds/src/main/java/com/facebook/presto/tpcds/statistics/TpcdsTableStatisticsFactory.java +++ b/presto-tpcds/src/main/java/com/facebook/presto/tpcds/statistics/TpcdsTableStatisticsFactory.java @@ -16,27 +16,30 @@ import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; import com.facebook.presto.spi.statistics.TableStatistics; import com.facebook.presto.spi.type.CharType; import com.facebook.presto.spi.type.DecimalType; -import com.facebook.presto.spi.type.TimeType; +import com.facebook.presto.spi.type.Decimals; import com.facebook.presto.spi.type.Type; import com.facebook.presto.spi.type.VarcharType; import com.facebook.presto.tpcds.TpcdsColumnHandle; import com.teradata.tpcds.Table; -import io.airlift.slice.Slices; +import io.airlift.slice.Slice; import java.time.LocalDate; import java.util.Map; import java.util.Optional; import static com.facebook.presto.spi.type.BigintType.BIGINT; -import static com.facebook.presto.spi.type.Chars.truncateToLengthAndTrimSpaces; import static com.facebook.presto.spi.type.DateType.DATE; +import static com.facebook.presto.spi.type.Decimals.isLongDecimal; import static com.facebook.presto.spi.type.Decimals.isShortDecimal; import static com.facebook.presto.spi.type.DoubleType.DOUBLE; import static com.facebook.presto.spi.type.IntegerType.INTEGER; +import static com.facebook.presto.spi.type.TimeType.TIME; +import static java.lang.Double.parseDouble; public class TpcdsTableStatisticsFactory { @@ -46,14 +49,14 @@ public TableStatistics create(String schemaName, Table table, Map statisticsDataOptional = statisticsDataRepository.load(schemaName, table); return statisticsDataOptional.map(statisticsData -> toTableStatistics(columnHandles, statisticsData)) - .orElse(TableStatistics.EMPTY_STATISTICS); + .orElse(TableStatistics.empty()); } private TableStatistics toTableStatistics(Map columnHandles, TableStatisticsData statisticsData) { long rowCount = statisticsData.getRowCount(); TableStatistics.Builder tableStatistics = TableStatistics.builder() - .setRowCount(new Estimate(rowCount)); + .setRowCount(Estimate.of(rowCount)); if (rowCount > 0) { Map columnsData = statisticsData.getColumns(); @@ -70,41 +73,44 @@ private ColumnStatistics toColumnStatistics(ColumnStatisticsData columnStatistic { ColumnStatistics.Builder columnStatistics = ColumnStatistics.builder(); long nullCount = columnStatisticsData.getNullsCount(); - columnStatistics.setNullsFraction(new Estimate((double) nullCount / rowCount)); - columnStatistics.addRange(builder -> builder - .setLowValue( - columnStatisticsData.getMin() - .map(value -> toPrestoValue(value, type))) - .setHighValue( - columnStatisticsData.getMax() - .map(value -> toPrestoValue(value, type))) - .setDistinctValuesCount(new Estimate(columnStatisticsData.getDistinctValuesCount())) - .setDataSize(columnStatisticsData.getDataSize().map(Estimate::new).orElse(Estimate.unknownValue())) - .setFraction(new Estimate(((double) rowCount - nullCount) / rowCount)) - .build()); - + columnStatistics.setNullsFraction(Estimate.of((double) nullCount / rowCount)); + columnStatistics.setRange(toRange(columnStatisticsData.getMin(), columnStatisticsData.getMax(), type)); + columnStatistics.setDistinctValuesCount(Estimate.of(columnStatisticsData.getDistinctValuesCount())); + columnStatistics.setDataSize(columnStatisticsData.getDataSize().map(Estimate::of).orElse(Estimate.unknown())); return columnStatistics.build(); } - private Object toPrestoValue(Object tpcdsValue, Type type) + private static Optional toRange(Optional min, Optional max, Type columnType) { - if (type instanceof VarcharType) { - return Slices.utf8Slice((String) tpcdsValue); + if (columnType instanceof VarcharType || columnType instanceof CharType || columnType.equals(TIME)) { + return Optional.empty(); } - else if (type instanceof CharType) { - return truncateToLengthAndTrimSpaces(Slices.utf8Slice((String) tpcdsValue), type); + if (!min.isPresent() || !max.isPresent()) { + return Optional.empty(); } - else if (tpcdsValue instanceof String && type.equals(DATE)) { - return LocalDate.parse((CharSequence) tpcdsValue).toEpochDay(); + return Optional.of(new DoubleRange(toDouble(min.get(), columnType), toDouble(max.get(), columnType))); + } + + private static double toDouble(Object value, Type type) + { + if (value instanceof String && type.equals(DATE)) { + return LocalDate.parse((CharSequence) value).toEpochDay(); } - else if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(DATE) || (type instanceof DecimalType && isShortDecimal(type))) { - return ((Number) tpcdsValue).longValue(); + if (type.equals(BIGINT) || type.equals(INTEGER) || type.equals(DATE)) { + return ((Number) value).doubleValue(); } - else if (type.equals(DOUBLE)) { - return ((Number) tpcdsValue).doubleValue(); + if (type instanceof DecimalType) { + DecimalType decimalType = (DecimalType) type; + if (isShortDecimal(decimalType)) { + return parseDouble(Decimals.toString(((Number) value).longValue(), decimalType.getScale())); + } + if (isLongDecimal(decimalType)) { + return parseDouble(Decimals.toString((Slice) value, decimalType.getScale())); + } + throw new IllegalArgumentException("Unexpected decimal type: " + decimalType); } - else if (type.equals(TimeType.TIME)) { - return ((Number) tpcdsValue).longValue(); + if (type.equals(DOUBLE)) { + return ((Number) value).doubleValue(); } throw new IllegalArgumentException("unsupported column type " + type); } diff --git a/presto-tpcds/src/test/java/com/facebook/presto/tpcds/EstimateAssertion.java b/presto-tpcds/src/test/java/com/facebook/presto/tpcds/EstimateAssertion.java index 7374ad1634692..5d0b08240f7e3 100644 --- a/presto-tpcds/src/test/java/com/facebook/presto/tpcds/EstimateAssertion.java +++ b/presto-tpcds/src/test/java/com/facebook/presto/tpcds/EstimateAssertion.java @@ -20,7 +20,6 @@ import java.util.Optional; import static java.lang.String.format; -import static java.util.Optional.empty; import static org.testng.Assert.assertEquals; class EstimateAssertion @@ -39,7 +38,7 @@ public void assertClose(Estimate actual, Estimate expected, String comparedValue private Optional toOptional(Estimate estimate) { - return estimate.isValueUnknown() ? empty() : Optional.of(estimate.getValue()); + return estimate.isUnknown() ? Optional.empty() : Optional.of(estimate.getValue()); } public void assertClose(Optional actual, Optional expected, String comparedValue) diff --git a/presto-tpcds/src/test/java/com/facebook/presto/tpcds/TestTpcdsMetadataStatistics.java b/presto-tpcds/src/test/java/com/facebook/presto/tpcds/TestTpcdsMetadataStatistics.java index 38c50c7c234eb..f14da5781305b 100644 --- a/presto-tpcds/src/test/java/com/facebook/presto/tpcds/TestTpcdsMetadataStatistics.java +++ b/presto-tpcds/src/test/java/com/facebook/presto/tpcds/TestTpcdsMetadataStatistics.java @@ -19,18 +19,15 @@ import com.facebook.presto.spi.ConnectorTableHandle; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; -import com.facebook.presto.spi.statistics.RangeColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; -import com.google.common.primitives.Primitives; import com.teradata.tpcds.Table; import com.teradata.tpcds.column.CallCenterColumn; import com.teradata.tpcds.column.WebSiteColumn; -import io.airlift.slice.Slices; import org.testng.annotations.Test; import java.util.Map; -import java.util.Optional; import java.util.stream.Stream; import static com.facebook.presto.spi.Constraint.alwaysTrue; @@ -54,7 +51,7 @@ public void testNoTableStatsForNotSupportedSchema() SchemaTableName schemaTableName = new SchemaTableName(schemaName, table.getName()); ConnectorTableHandle tableHandle = metadata.getTableHandle(session, schemaTableName); TableStatistics tableStatistics = metadata.getTableStatistics(session, tableHandle, alwaysTrue()); - assertTrue(tableStatistics.getRowCount().isValueUnknown()); + assertTrue(tableStatistics.getRowCount().isUnknown()); assertTrue(tableStatistics.getColumnStatistics().isEmpty()); })); } @@ -68,20 +65,10 @@ public void testTableStatsExistenceSupportedSchema() SchemaTableName schemaTableName = new SchemaTableName(schemaName, table.getName()); ConnectorTableHandle tableHandle = metadata.getTableHandle(session, schemaTableName); TableStatistics tableStatistics = metadata.getTableStatistics(session, tableHandle, alwaysTrue()); - assertFalse(tableStatistics.getRowCount().isValueUnknown()); + assertFalse(tableStatistics.getRowCount().isUnknown()); for (ColumnHandle column : metadata.getColumnHandles(session, tableHandle).values()) { assertTrue(tableStatistics.getColumnStatistics().containsKey(column)); assertNotNull(tableStatistics.getColumnStatistics().get(column)); - - TpcdsColumnHandle tpcdsColumn = (TpcdsColumnHandle) column; - Optional low = tableStatistics.getColumnStatistics().get(column).getOnlyRangeColumnStatistics().getLowValue(); - if (low.isPresent()) { - assertEquals(low.get().getClass(), Primitives.wrap(tpcdsColumn.getType().getJavaType())); - } - Optional high = tableStatistics.getColumnStatistics().get(column).getOnlyRangeColumnStatistics().getLowValue(); - if (high.isPresent()) { - assertEquals(high.get().getClass(), Primitives.wrap(tpcdsColumn.getType().getJavaType())); - } } })); } @@ -93,7 +80,7 @@ public void testTableStatsDetails() ConnectorTableHandle tableHandle = metadata.getTableHandle(session, schemaTableName); TableStatistics tableStatistics = metadata.getTableStatistics(session, tableHandle, alwaysTrue()); - estimateAssertion.assertClose(tableStatistics.getRowCount(), new Estimate(6), "Row count does not match"); + estimateAssertion.assertClose(tableStatistics.getRowCount(), Estimate.of(6), "Row count does not match"); // all columns have stats Map columnHandles = metadata.getColumnHandles(session, tableHandle); @@ -106,80 +93,53 @@ public void testTableStatsDetails() assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_CALL_CENTER_SK.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0)) - .addRange(range -> range - .setFraction(new Estimate(1.0)) - .setDistinctValuesCount(new Estimate(6)) - .setLowValue(Optional.of(1L)) - .setHighValue(Optional.of(6L)) - .build()) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(6)) + .setRange(new DoubleRange(1, 6)) .build()); // varchar assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_CALL_CENTER_ID.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0)) - .addRange(range -> range - .setFraction(new Estimate(1.0)) - .setDistinctValuesCount(new Estimate(3)) - .setLowValue(Optional.of(Slices.utf8Slice("AAAAAAAABAAAAAAA"))) - .setHighValue(Optional.of(Slices.utf8Slice("AAAAAAAAEAAAAAAA"))) - .setDataSize(new Estimate(48.0)) - .build()) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(3)) + .setDataSize(Estimate.of(48.0)) .build()); // char assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_ZIP.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0)) - .addRange(range -> range - .setFraction(new Estimate(1.0)) - .setDistinctValuesCount(new Estimate(1)) - .setLowValue(Optional.of(Slices.utf8Slice("31904"))) - .setHighValue(Optional.of(Slices.utf8Slice("31904"))) - .setDataSize(new Estimate(5.0)) - .build()) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(1)) + .setDataSize(Estimate.of(5.0)) .build()); // decimal assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_GMT_OFFSET.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0)) - .addRange(range -> range - .setFraction(new Estimate(1.0)) - .setDistinctValuesCount(new Estimate(1)) - .setLowValue(Optional.of(-500L)) - .setHighValue(Optional.of(-500L)) - .build()) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(1)) + .setRange(new DoubleRange(-5, -5)) .build()); // date assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_REC_START_DATE.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0)) - .addRange(range -> range - .setFraction(new Estimate(1)) - .setDistinctValuesCount(new Estimate(4)) - .setLowValue(Optional.of(10227L)) - .setHighValue(Optional.of(11688L)) - .build()) + .setNullsFraction(Estimate.of(0)) + .setDistinctValuesCount(Estimate.of(4)) + .setRange(new DoubleRange(10227L, 11688L)) .build()); // only null values assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(CallCenterColumn.CC_CLOSED_DATE_SK.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(1)) - .addRange(range -> range - .setFraction(new Estimate(0)) - .setDistinctValuesCount(new Estimate(0)) - .setLowValue(Optional.empty()) - .setHighValue(Optional.empty()) - .build()) + .setNullsFraction(Estimate.of(1)) + .setDistinctValuesCount(Estimate.of(0)) .build()); } @@ -196,25 +156,17 @@ public void testNullFraction() assertColumnStatistics( tableStatistics.getColumnStatistics().get(columnHandles.get(WebSiteColumn.WEB_REC_END_DATE.getName())), ColumnStatistics.builder() - .setNullsFraction(new Estimate(0.5)) - .addRange(range -> range - .setFraction(new Estimate(0.5)) - .setDistinctValuesCount(new Estimate(3)) - .setLowValue(Optional.of(10819L)) - .setHighValue(Optional.of(11549L)) - .build()) + .setNullsFraction(Estimate.of(0.5)) + .setDistinctValuesCount(Estimate.of(3)) + .setRange(new DoubleRange(10819L, 11549L)) .build()); } private void assertColumnStatistics(ColumnStatistics actual, ColumnStatistics expected) { estimateAssertion.assertClose(actual.getNullsFraction(), expected.getNullsFraction(), "Nulls fraction"); - RangeColumnStatistics actualRange = actual.getOnlyRangeColumnStatistics(); - RangeColumnStatistics expectedRange = expected.getOnlyRangeColumnStatistics(); - estimateAssertion.assertClose(actualRange.getFraction(), expectedRange.getFraction(), "Fraction"); - estimateAssertion.assertClose(actualRange.getDataSize(), expectedRange.getDataSize(), "Data size"); - estimateAssertion.assertClose(actualRange.getDistinctValuesCount(), expectedRange.getDistinctValuesCount(), "Distinct values count"); - assertEquals(actualRange.getLowValue(), expectedRange.getLowValue()); - assertEquals(actualRange.getHighValue(), expectedRange.getHighValue()); + estimateAssertion.assertClose(actual.getDataSize(), expected.getDataSize(), "Data size"); + estimateAssertion.assertClose(actual.getDistinctValuesCount(), expected.getDistinctValuesCount(), "Distinct values count"); + assertEquals(actual.getRange(), expected.getRange()); } } diff --git a/presto-tpch/pom.xml b/presto-tpch/pom.xml index 9793f92d3b060..e3a650ba793cf 100644 --- a/presto-tpch/pom.xml +++ b/presto-tpch/pom.xml @@ -4,7 +4,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-tpch diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchConnectorFactory.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchConnectorFactory.java index 7b8f02f78fb71..89aeb63b84f3f 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchConnectorFactory.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchConnectorFactory.java @@ -36,6 +36,7 @@ public class TpchConnectorFactory private final int defaultSplitsPerNode; private final boolean predicatePushdownEnabled; + private final boolean partitioningEnabled; public TpchConnectorFactory() { @@ -44,13 +45,14 @@ public TpchConnectorFactory() public TpchConnectorFactory(int defaultSplitsPerNode) { - this(defaultSplitsPerNode, true); + this(defaultSplitsPerNode, true, true); } - public TpchConnectorFactory(int defaultSplitsPerNode, boolean predicatePushdownEnabled) + public TpchConnectorFactory(int defaultSplitsPerNode, boolean predicatePushdownEnabled, boolean partitioningEnabled) { this.defaultSplitsPerNode = defaultSplitsPerNode; this.predicatePushdownEnabled = predicatePushdownEnabled; + this.partitioningEnabled = partitioningEnabled; } @Override @@ -66,7 +68,7 @@ public ConnectorHandleResolver getHandleResolver() } @Override - public Connector create(String connectorId, Map properties, ConnectorContext context) + public Connector create(String catalogName, Map properties, ConnectorContext context) { int splitsPerNode = getSplitsPerNode(properties); ColumnNaming columnNaming = ColumnNaming.valueOf(properties.getOrDefault(TPCH_COLUMN_NAMING_PROPERTY, ColumnNaming.SIMPLIFIED.name()).toUpperCase()); @@ -83,7 +85,7 @@ public ConnectorTransactionHandle beginTransaction(IsolationLevel isolationLevel @Override public ConnectorMetadata getMetadata(ConnectorTransactionHandle transaction) { - return new TpchMetadata(connectorId, columnNaming, predicatePushdownEnabled); + return new TpchMetadata(catalogName, columnNaming, predicatePushdownEnabled, partitioningEnabled); } @Override diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchMetadata.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchMetadata.java index 4356920c38706..a4875bcac3569 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchMetadata.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchMetadata.java @@ -33,6 +33,7 @@ import com.facebook.presto.spi.predicate.NullableValue; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; import com.facebook.presto.spi.statistics.TableStatistics; import com.facebook.presto.spi.type.Type; @@ -59,6 +60,7 @@ import io.airlift.tpch.TpchEntity; import io.airlift.tpch.TpchTable; +import java.time.LocalDate; import java.util.List; import java.util.Map; import java.util.Optional; @@ -118,13 +120,14 @@ public class TpchMetadata private final ColumnNaming columnNaming; private final StatisticsEstimator statisticsEstimator; private final boolean predicatePushdownEnabled; + private final boolean partitioningEnabled; public TpchMetadata(String connectorId) { - this(connectorId, ColumnNaming.SIMPLIFIED, true); + this(connectorId, ColumnNaming.SIMPLIFIED, true, true); } - public TpchMetadata(String connectorId, ColumnNaming columnNaming, boolean predicatePushdownEnabled) + public TpchMetadata(String connectorId, ColumnNaming columnNaming, boolean predicatePushdownEnabled, boolean partitioningEnabled) { ImmutableSet.Builder tableNames = ImmutableSet.builder(); for (TpchTable tpchTable : TpchTable.getTables()) { @@ -134,6 +137,7 @@ public TpchMetadata(String connectorId, ColumnNaming columnNaming, boolean predi this.connectorId = connectorId; this.columnNaming = columnNaming; this.predicatePushdownEnabled = predicatePushdownEnabled; + this.partitioningEnabled = partitioningEnabled; this.statisticsEstimator = createStatisticsEstimator(); } @@ -165,7 +169,7 @@ public TpchTableHandle getTableHandle(ConnectorSession session, SchemaTableName return null; } - return new TpchTableHandle(connectorId, tableName.getTableName(), scaleFactor); + return new TpchTableHandle(tableName.getTableName(), scaleFactor); } @Override @@ -177,7 +181,7 @@ public List getTableLayouts( { TpchTableHandle tableHandle = (TpchTableHandle) table; - Optional nodePartition = Optional.empty(); + Optional tablePartitioning = Optional.empty(); Optional> partitioningColumns = Optional.empty(); List> localProperties = ImmutableList.of(); @@ -185,14 +189,16 @@ public List getTableLayouts( TupleDomain unenforcedConstraint = constraint.getSummary(); Map columns = getColumnHandles(session, tableHandle); if (tableHandle.getTableName().equals(TpchTable.ORDERS.getTableName())) { - ColumnHandle orderKeyColumn = columns.get(columnNaming.getName(OrderColumn.ORDER_KEY)); - nodePartition = Optional.of(new ConnectorTablePartitioning( - new TpchPartitioningHandle( - TpchTable.ORDERS.getTableName(), - calculateTotalRows(OrderGenerator.SCALE_BASE, tableHandle.getScaleFactor())), - ImmutableList.of(orderKeyColumn))); - partitioningColumns = Optional.of(ImmutableSet.of(orderKeyColumn)); - localProperties = ImmutableList.of(new SortingProperty<>(orderKeyColumn, SortOrder.ASC_NULLS_FIRST)); + if (partitioningEnabled) { + ColumnHandle orderKeyColumn = columns.get(columnNaming.getName(OrderColumn.ORDER_KEY)); + tablePartitioning = Optional.of(new ConnectorTablePartitioning( + new TpchPartitioningHandle( + TpchTable.ORDERS.getTableName(), + calculateTotalRows(OrderGenerator.SCALE_BASE, tableHandle.getScaleFactor())), + ImmutableList.of(orderKeyColumn))); + partitioningColumns = Optional.of(ImmutableSet.of(orderKeyColumn)); + localProperties = ImmutableList.of(new SortingProperty<>(orderKeyColumn, SortOrder.ASC_NULLS_FIRST)); + } if (predicatePushdownEnabled) { predicate = toTupleDomain(ImmutableMap.of( toColumnHandle(OrderColumn.ORDER_STATUS), @@ -210,23 +216,25 @@ else if (predicatePushdownEnabled && tableHandle.getTableName().equals(TpchTable unenforcedConstraint = filterOutColumnFromPredicate(unenforcedConstraint, toColumnHandle(PartColumn.TYPE)); } else if (tableHandle.getTableName().equals(TpchTable.LINE_ITEM.getTableName())) { - ColumnHandle orderKeyColumn = columns.get(columnNaming.getName(LineItemColumn.ORDER_KEY)); - nodePartition = Optional.of(new ConnectorTablePartitioning( - new TpchPartitioningHandle( - TpchTable.ORDERS.getTableName(), - calculateTotalRows(OrderGenerator.SCALE_BASE, tableHandle.getScaleFactor())), - ImmutableList.of(orderKeyColumn))); - partitioningColumns = Optional.of(ImmutableSet.of(orderKeyColumn)); - localProperties = ImmutableList.of( - new SortingProperty<>(orderKeyColumn, SortOrder.ASC_NULLS_FIRST), - new SortingProperty<>(columns.get(columnNaming.getName(LineItemColumn.LINE_NUMBER)), SortOrder.ASC_NULLS_FIRST)); + if (partitioningEnabled) { + ColumnHandle orderKeyColumn = columns.get(columnNaming.getName(LineItemColumn.ORDER_KEY)); + tablePartitioning = Optional.of(new ConnectorTablePartitioning( + new TpchPartitioningHandle( + TpchTable.ORDERS.getTableName(), + calculateTotalRows(OrderGenerator.SCALE_BASE, tableHandle.getScaleFactor())), + ImmutableList.of(orderKeyColumn))); + partitioningColumns = Optional.of(ImmutableSet.of(orderKeyColumn)); + localProperties = ImmutableList.of( + new SortingProperty<>(orderKeyColumn, SortOrder.ASC_NULLS_FIRST), + new SortingProperty<>(columns.get(columnNaming.getName(LineItemColumn.LINE_NUMBER)), SortOrder.ASC_NULLS_FIRST)); + } } ConnectorTableLayout layout = new ConnectorTableLayout( new TpchTableLayoutHandle(tableHandle, predicate), Optional.empty(), - predicate, // TODO: return well-known properties (e.g., orderkey > 0, etc) - nodePartition, + predicate, // TODO: conditionally return well-known properties (e.g., orderkey > 0, etc) + tablePartitioning, partitioningColumns, Optional.empty(), localProperties); @@ -316,7 +324,7 @@ public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTab Map columnHandles = getColumnHandles(session, tpchTableHandle); return optionalTableStatisticsData .map(tableStatisticsData -> toTableStatistics(optionalTableStatisticsData.get(), tpchTableHandle, columnHandles)) - .orElse(TableStatistics.EMPTY_STATISTICS); + .orElse(TableStatistics.empty()); } private Map, List> getColumnValuesRestrictions(TpchTable tpchTable, Constraint constraint) @@ -355,7 +363,7 @@ private Map, List> avoidTrivialOrderStatusRestriction(List private TableStatistics toTableStatistics(TableStatisticsData tableStatisticsData, TpchTableHandle tpchTableHandle, Map columnHandles) { TableStatistics.Builder builder = TableStatistics.builder() - .setRowCount(new Estimate(tableStatisticsData.getRowCount())); + .setRowCount(Estimate.of(tableStatisticsData.getRowCount())); tableStatisticsData.getColumns().forEach((columnName, stats) -> { TpchColumnHandle columnHandle = (TpchColumnHandle) getColumnHandle(tpchTableHandle, columnHandles, columnName); builder.setColumnStatistics(columnHandle, toColumnStatistics(stats, columnHandle.getType())); @@ -372,26 +380,34 @@ private ColumnHandle getColumnHandle(TpchTableHandle tpchTableHandle, Map rangeBuilder - .setDistinctValuesCount(stats.getDistinctValuesCount().map(Estimate::new).orElse(Estimate.unknownValue())) - .setDataSize(stats.getDataSize().map(Estimate::new).orElse(Estimate.unknownValue())) - .setLowValue(stats.getMin().map(value -> toPrestoValue(value, columnType))) - .setHighValue(stats.getMax().map(value -> toPrestoValue(value, columnType))) - .setFraction(new Estimate((1)))) - .setNullsFraction(Estimate.zeroValue()) + .setNullsFraction(Estimate.zero()) + .setDistinctValuesCount(stats.getDistinctValuesCount().map(Estimate::of).orElse(Estimate.unknown())) + .setDataSize(stats.getDataSize().map(Estimate::of).orElse(Estimate.unknown())) + .setRange(toRange(stats.getMin(), stats.getMax(), columnType)) .build(); } - private Object toPrestoValue(Object tpchValue, Type columnType) + private static Optional toRange(Optional min, Optional max, Type columnType) { if (columnType instanceof VarcharType) { - return Slices.utf8Slice((String) tpchValue); + return Optional.empty(); + } + if (!min.isPresent() || !max.isPresent()) { + return Optional.empty(); + } + return Optional.of(new DoubleRange(toDouble(min.get(), columnType), toDouble(max.get(), columnType))); + } + + private static double toDouble(Object value, Type columnType) + { + if (value instanceof String && columnType.equals(DATE)) { + return LocalDate.parse((CharSequence) value).toEpochDay(); } if (columnType.equals(BIGINT) || columnType.equals(INTEGER) || columnType.equals(DATE)) { - return ((Number) tpchValue).longValue(); + return ((Number) value).longValue(); } if (columnType.equals(DOUBLE)) { - return ((Number) tpchValue).doubleValue(); + return ((Number) value).doubleValue(); } throw new IllegalArgumentException("unsupported column type " + columnType); } diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchNodePartitioningProvider.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchNodePartitioningProvider.java index 172d8400135c1..d25d5545aa825 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchNodePartitioningProvider.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchNodePartitioningProvider.java @@ -18,20 +18,21 @@ import com.facebook.presto.spi.ConnectorSplit; import com.facebook.presto.spi.Node; import com.facebook.presto.spi.NodeManager; +import com.facebook.presto.spi.connector.ConnectorBucketNodeMap; import com.facebook.presto.spi.connector.ConnectorNodePartitioningProvider; import com.facebook.presto.spi.connector.ConnectorPartitioningHandle; import com.facebook.presto.spi.connector.ConnectorTransactionHandle; import com.facebook.presto.spi.type.Type; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.ToIntFunction; +import static com.facebook.presto.spi.connector.ConnectorBucketNodeMap.createBucketNodeMap; import static com.facebook.presto.spi.type.BigintType.BIGINT; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Math.toIntExact; public class TpchNodePartitioningProvider implements ConnectorNodePartitioningProvider @@ -47,20 +48,12 @@ public TpchNodePartitioningProvider(NodeManager nodeManager, int splitsPerNode) } @Override - public Map getBucketToNode(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) + public ConnectorBucketNodeMap getBucketNodeMap(ConnectorTransactionHandle transactionHandle, ConnectorSession session, ConnectorPartitioningHandle partitioningHandle) { Set nodes = nodeManager.getRequiredWorkerNodes(); // Split the data using split and skew by the number of nodes available. - ImmutableMap.Builder bucketToNode = ImmutableMap.builder(); - int partNumber = 0; - for (Node node : nodes) { - for (int i = 0; i < splitsPerNode; i++) { - bucketToNode.put(partNumber, node); - partNumber++; - } - } - return bucketToNode.build(); + return createBucketNodeMap(toIntExact((long) nodes.size() * splitsPerNode)); } @Override diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableHandle.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableHandle.java index 78e683126dcfa..c3bff26126a11 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableHandle.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableHandle.java @@ -25,25 +25,17 @@ public class TpchTableHandle implements ConnectorTableHandle { - private final String connectorId; private final String tableName; private final double scaleFactor; @JsonCreator - public TpchTableHandle(@JsonProperty("connectorId") String connectorId, @JsonProperty("tableName") String tableName, @JsonProperty("scaleFactor") double scaleFactor) + public TpchTableHandle(@JsonProperty("tableName") String tableName, @JsonProperty("scaleFactor") double scaleFactor) { - this.connectorId = requireNonNull(connectorId, "connectorId is null"); this.tableName = requireNonNull(tableName, "tableName is null"); checkArgument(scaleFactor > 0, "Scale factor must be larger than 0"); this.scaleFactor = scaleFactor; } - @JsonProperty - public String getConnectorId() - { - return connectorId; - } - @JsonProperty public String getTableName() { @@ -59,13 +51,13 @@ public double getScaleFactor() @Override public String toString() { - return "tpch:" + tableName + ":sf" + scaleFactor; + return tableName + ":sf" + scaleFactor; } @Override public int hashCode() { - return Objects.hash(connectorId, tableName, scaleFactor); + return Objects.hash(tableName, scaleFactor); } @Override @@ -79,7 +71,6 @@ public boolean equals(Object obj) } TpchTableHandle other = (TpchTableHandle) obj; return Objects.equals(this.tableName, other.tableName) && - Objects.equals(this.scaleFactor, other.scaleFactor) && - Objects.equals(this.connectorId, other.connectorId); + Objects.equals(this.scaleFactor, other.scaleFactor); } } diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableLayoutHandle.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableLayoutHandle.java index bd0e0af6c721c..422f36e49e2dc 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableLayoutHandle.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/TpchTableLayoutHandle.java @@ -44,11 +44,6 @@ public TupleDomain getPredicate() return predicate; } - public String getConnectorId() - { - return table.getConnectorId(); - } - @Override public String toString() { diff --git a/presto-tpch/src/main/java/com/facebook/presto/tpch/statistics/StatisticsEstimator.java b/presto-tpch/src/main/java/com/facebook/presto/tpch/statistics/StatisticsEstimator.java index d45a1df5d6186..1f9da7c94d8ee 100644 --- a/presto-tpch/src/main/java/com/facebook/presto/tpch/statistics/StatisticsEstimator.java +++ b/presto-tpch/src/main/java/com/facebook/presto/tpch/statistics/StatisticsEstimator.java @@ -27,7 +27,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.Iterables.getOnlyElement; -import static java.util.Optional.empty; public class StatisticsEstimator { @@ -42,7 +41,7 @@ public Optional estimateStats(TpchTable tpchTable, Map toOptional(Estimate estimate) { - return estimate.isValueUnknown() ? empty() : Optional.of(estimate.getValue()); + return estimate.isUnknown() ? Optional.empty() : Optional.of(estimate.getValue()); } public void assertClose(Optional actual, Optional expected, String comparedValue) @@ -60,6 +60,12 @@ private void assertClose(Object actual, Object expected, String comparedValue) assertEquals(actual.getClass(), expected.getClass(), comparedValue); assertEquals(((Slice) actual).toStringUtf8(), ((Slice) expected).toStringUtf8()); } + else if (actual instanceof DoubleRange) { + DoubleRange actualRange = (DoubleRange) actual; + DoubleRange expectedRange = (DoubleRange) expected; + assertClose(actualRange.getMin(), expectedRange.getMin(), comparedValue); + assertClose(actualRange.getMax(), expectedRange.getMax(), comparedValue); + } else { double actualDouble = toDouble(actual); double expectedDouble = toDouble(expected); diff --git a/presto-tpch/src/test/java/com/facebook/presto/tpch/TestTpchMetadata.java b/presto-tpch/src/test/java/com/facebook/presto/tpch/TestTpchMetadata.java index e7feaf2aa92da..fea4307cb5ea0 100644 --- a/presto-tpch/src/test/java/com/facebook/presto/tpch/TestTpchMetadata.java +++ b/presto-tpch/src/test/java/com/facebook/presto/tpch/TestTpchMetadata.java @@ -22,8 +22,8 @@ import com.facebook.presto.spi.predicate.NullableValue; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.statistics.ColumnStatistics; +import com.facebook.presto.spi.statistics.DoubleRange; import com.facebook.presto.spi.statistics.Estimate; -import com.facebook.presto.spi.statistics.RangeColumnStatistics; import com.facebook.presto.spi.statistics.TableStatistics; import com.facebook.presto.tpch.util.PredicateUtils; import com.google.common.base.Preconditions; @@ -43,7 +43,6 @@ import static com.facebook.presto.spi.Constraint.alwaysFalse; import static com.facebook.presto.spi.Constraint.alwaysTrue; -import static com.facebook.presto.spi.statistics.Estimate.zeroValue; import static com.facebook.presto.tpch.TpchMetadata.getPrestoType; import static com.facebook.presto.tpch.util.PredicateUtils.filterOutColumnFromPredicate; import static com.google.common.collect.Iterables.getOnlyElement; @@ -69,7 +68,6 @@ import static java.lang.String.format; import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; -import static java.util.Optional.empty; import static java.util.stream.Collectors.toList; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -141,7 +139,7 @@ private void testTableStats(String schema, TpchTable table, Constraint table) { TpchTableHandle tableHandle = tpchMetadata.getTableHandle(session, new SchemaTableName(schema, table.getTableName())); TableStatistics tableStatistics = tpchMetadata.getTableStatistics(session, tableHandle, alwaysTrue()); - assertTrue(tableStatistics.getRowCount().isValueUnknown()); + assertTrue(tableStatistics.getRowCount().isUnknown()); } @Test @@ -168,7 +166,7 @@ public void testColumnStats() testColumnStats(schema, PART_SUPPLIER, PART_KEY, columnStatistics(200_000 * scaleFactor, 1, 200_000 * scaleFactor)); //dictionary - testColumnStats(schema, CUSTOMER, MARKET_SEGMENT, columnStatistics(5, "AUTOMOBILE", "MACHINERY", 45)); + testColumnStats(schema, CUSTOMER, MARKET_SEGMENT, columnStatistics(5, 45)); //low-valued numeric column testColumnStats(schema, LINE_ITEM, LINE_NUMBER, columnStatistics(7, 1, 7)); @@ -178,11 +176,11 @@ public void testColumnStats() //varchar and double columns if (schema.equals("tiny")) { - testColumnStats(schema, CUSTOMER, NAME, columnStatistics(150_000 * scaleFactor, "Customer#000000001", "Customer#000001500", 27000)); + testColumnStats(schema, CUSTOMER, NAME, columnStatistics(150_000 * scaleFactor, 27000)); testColumnStats(schema, PART, RETAIL_PRICE, columnStatistics(1_099, 901, 1900.99)); } else if (schema.equals("sf1")) { - testColumnStats(schema, CUSTOMER, NAME, columnStatistics(150_000 * scaleFactor, "Customer#000000001", "Customer#000150000", 2700000)); + testColumnStats(schema, CUSTOMER, NAME, columnStatistics(150_000 * scaleFactor, 2700000)); testColumnStats(schema, PART, RETAIL_PRICE, columnStatistics(20899, 901, 2089.99)); } }); @@ -195,15 +193,15 @@ public void testColumnStatsWithConstraints() double scaleFactor = TpchMetadata.schemaNameToScaleFactor(schema); //value count, min and max are supported for the constrained column - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F"), columnStatistics(1, "F", "F", 1)); - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "O"), columnStatistics(1, "O", "O", 1)); - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "P"), columnStatistics(1, "P", "P", 1)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F"), columnStatistics(1, 1)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "O"), columnStatistics(1, 1)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "P"), columnStatistics(1, 1)); //only min and max values for non-scaling columns can be estimated for non-constrained columns testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "F"), rangeStatistics(3, 6_000_000 * scaleFactor)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "O"), rangeStatistics(1, 6_000_000 * scaleFactor)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "P"), rangeStatistics(65, 6_000_000 * scaleFactor)); - testColumnStats(schema, ORDERS, CLERK, constraint(ORDER_STATUS, "O"), rangeStatistics("Clerk#000000001", "Clerk#000001000", 15000)); + testColumnStats(schema, ORDERS, CLERK, constraint(ORDER_STATUS, "O"), createColumnStatistics(Optional.empty(), Optional.empty(), Optional.of(15000.0))); //nothing can be said for always false constraints testColumnStats(schema, ORDERS, ORDER_STATUS, alwaysFalse(), noColumnStatistics()); @@ -212,21 +210,21 @@ public void testColumnStatsWithConstraints() testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "NO SUCH STATUS"), noColumnStatistics()); //unmodified stats are returned for the always true constraint - testColumnStats(schema, ORDERS, ORDER_STATUS, alwaysTrue(), columnStatistics(3, "F", "P", 3)); + testColumnStats(schema, ORDERS, ORDER_STATUS, alwaysTrue(), columnStatistics(3, 3)); testColumnStats(schema, ORDERS, ORDER_KEY, alwaysTrue(), columnStatistics(1_500_000 * scaleFactor, 1, 6_000_000 * scaleFactor)); //constraints on columns other than ORDER_STATUS are not supported and are ignored - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(CLERK, "NO SUCH CLERK"), columnStatistics(3, "F", "P", 3)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(CLERK, "NO SUCH CLERK"), columnStatistics(3, 3)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(CLERK, "Clerk#000000001"), columnStatistics(1_500_000 * scaleFactor, 1, 6_000_000 * scaleFactor)); //compound constraints are supported - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "NO SUCH STATUS"), columnStatistics(1, "F", "F", 1)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "NO SUCH STATUS"), columnStatistics(1, 1)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "F", "NO SUCH STATUS"), rangeStatistics(3, 6_000_000 * scaleFactor)); - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "O"), columnStatistics(2, "F", "O", 2)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "O"), columnStatistics(2, 2)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "F", "O"), rangeStatistics(1, 6_000_000 * scaleFactor)); - testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "O", "P"), columnStatistics(3, "F", "P", 3)); + testColumnStats(schema, ORDERS, ORDER_STATUS, constraint(ORDER_STATUS, "F", "O", "P"), columnStatistics(3, 3)); testColumnStats(schema, ORDERS, ORDER_KEY, constraint(ORDER_STATUS, "F", "O", "P"), columnStatistics(1_500_000 * scaleFactor, 1, 6_000_000 * scaleFactor)); }); } @@ -245,14 +243,11 @@ private void testColumnStats(String schema, TpchTable table, TpchColumn co ColumnStatistics actual = tableStatistics.getColumnStatistics().get(columnHandle); EstimateAssertion estimateAssertion = new EstimateAssertion(TOLERANCE); - RangeColumnStatistics actualRange = actual.getOnlyRangeColumnStatistics(); - RangeColumnStatistics expectedRange = expected.getOnlyRangeColumnStatistics(); - estimateAssertion.assertClose(actualRange.getDistinctValuesCount(), expectedRange.getDistinctValuesCount(), "distinctValuesCount"); - estimateAssertion.assertClose(actualRange.getDataSize(), expectedRange.getDataSize(), "dataSize"); + estimateAssertion.assertClose(actual.getDistinctValuesCount(), expected.getDistinctValuesCount(), "distinctValuesCount"); + estimateAssertion.assertClose(actual.getDataSize(), expected.getDataSize(), "dataSize"); estimateAssertion.assertClose(actual.getNullsFraction(), expected.getNullsFraction(), "nullsFraction"); - estimateAssertion.assertClose(actualRange.getLowValue(), expectedRange.getLowValue(), "lowValue"); - estimateAssertion.assertClose(actualRange.getHighValue(), expectedRange.getHighValue(), "highValue"); + estimateAssertion.assertClose(actual.getRange(), expected.getRange(), "range"); } @Test @@ -384,46 +379,38 @@ private static ConnectorTableLayoutResult getTableOnlyLayout(TpchMetadata tpchMe private ColumnStatistics noColumnStatistics() { - return createColumnStatistics(Optional.of(0.0), empty(), empty(), Optional.of(0.0)); + return createColumnStatistics(Optional.of(0.0), Optional.empty(), Optional.of(0.0)); } - private ColumnStatistics columnStatistics(double distinctValuesCount, String min, String max, double dataSize) + private ColumnStatistics columnStatistics(double distinctValuesCount, double dataSize) { - return createColumnStatistics(Optional.of(distinctValuesCount), Optional.of(utf8Slice(min)), Optional.of(utf8Slice(max)), Optional.of(dataSize)); + return createColumnStatistics(Optional.of(distinctValuesCount), Optional.empty(), Optional.of(dataSize)); } private ColumnStatistics columnStatistics(double distinctValuesCount, double min, double max) { - return createColumnStatistics(Optional.of(distinctValuesCount), Optional.of(min), Optional.of(max), Optional.empty()); - } - - private ColumnStatistics rangeStatistics(String min, String max, double dataSize) - { - return createColumnStatistics(empty(), Optional.of(utf8Slice(min)), Optional.of(utf8Slice(max)), Optional.of(dataSize)); + return createColumnStatistics(Optional.of(distinctValuesCount), Optional.of(new DoubleRange(min, max)), Optional.empty()); } private ColumnStatistics rangeStatistics(double min, double max) { - return createColumnStatistics(empty(), Optional.of(min), Optional.of(max), Optional.empty()); + return createColumnStatistics(Optional.empty(), Optional.of(new DoubleRange(min, max)), Optional.empty()); } - private static ColumnStatistics createColumnStatistics(Optional distinctValuesCount, Optional min, Optional max, Optional dataSize) + private static ColumnStatistics createColumnStatistics(Optional distinctValuesCount, Optional range, Optional dataSize) { return ColumnStatistics.builder() - .addRange(rb -> rb - .setDistinctValuesCount(toEstimate(distinctValuesCount)) - .setLowValue(min) - .setHighValue(max) - .setDataSize(toEstimate(dataSize)) - .setFraction(new Estimate(1.0))) - .setNullsFraction(zeroValue()) + .setNullsFraction(Estimate.zero()) + .setDistinctValuesCount(toEstimate(distinctValuesCount)) + .setRange(range) + .setDataSize(toEstimate(dataSize)) .build(); } private static Estimate toEstimate(Optional value) { return value - .map(Estimate::new) - .orElse(Estimate.unknownValue()); + .map(Estimate::of) + .orElse(Estimate.unknown()); } } diff --git a/presto-tpch/src/test/java/com/facebook/presto/tpch/statistics/TpchStatisticsRecorder.java b/presto-tpch/src/test/java/com/facebook/presto/tpch/statistics/TpchStatisticsRecorder.java index 68a5b5c7c825f..aa63933f75273 100644 --- a/presto-tpch/src/test/java/com/facebook/presto/tpch/statistics/TpchStatisticsRecorder.java +++ b/presto-tpch/src/test/java/com/facebook/presto/tpch/statistics/TpchStatisticsRecorder.java @@ -28,7 +28,6 @@ import static io.airlift.tpch.OrderColumn.ORDER_STATUS; import static io.airlift.tpch.TpchTable.ORDERS; import static java.lang.String.format; -import static java.util.Optional.empty; /** * This is a tool used to record statistics for TPCH tables. @@ -70,7 +69,7 @@ private static ObjectMapper createObjectMapper() private void computeAndOutputStatsFor(String schemaName, TpchTable table) { - computeAndOutputStatsFor(schemaName, table, row -> true, empty(), empty()); + computeAndOutputStatsFor(schemaName, table, row -> true, Optional.empty(), Optional.empty()); } private void computeAndOutputStatsFor(String schemaName, TpchTable table, TpchColumn partitionColumn, String partitionValue) diff --git a/presto-verifier/pom.xml b/presto-verifier/pom.xml index 576ea379a0253..323fadf048e6e 100644 --- a/presto-verifier/pom.xml +++ b/presto-verifier/pom.xml @@ -5,7 +5,7 @@ com.facebook.presto presto-root - 0.209-SNAPSHOT + 0.216-SNAPSHOT presto-verifier diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryResult.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryResult.java index adebd4c4b675e..0dd7e408d0536 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryResult.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryResult.java @@ -18,6 +18,8 @@ import java.util.List; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; public class QueryResult @@ -73,4 +75,11 @@ public List> getResults() { return results; } + + public void addSuppressed(Throwable throwable) + { + checkState(exception != null, "exception is null"); + checkArgument(throwable != null, "throwable is null"); + exception.addSuppressed(throwable); + } } diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryRewriter.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryRewriter.java index 73c145ffa7931..6a650663ade45 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryRewriter.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/QueryRewriter.java @@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.SimpleTimeLimiter; import com.google.common.util.concurrent.TimeLimiter; +import com.google.common.util.concurrent.UncheckedTimeoutException; import io.airlift.units.Duration; import java.sql.Connection; @@ -223,6 +224,9 @@ private List getColumns(Connection connection, CreateTableAsSelect creat columns.add(new Column(name, APPROXIMATE_TYPES.contains(type))); } } + catch (UncheckedTimeoutException e) { + throw new SQLException("SQL statement execution timed out", e); + } finally { executor.shutdownNow(); } diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/Validator.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/Validator.java index b038edeba5cfb..83102fbb0b8f5 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/Validator.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/Validator.java @@ -32,6 +32,7 @@ import com.google.common.util.concurrent.SimpleTimeLimiter; import com.google.common.util.concurrent.TimeLimiter; import com.google.common.util.concurrent.UncheckedTimeoutException; +import io.airlift.log.Logger; import io.airlift.units.Duration; import java.math.BigDecimal; @@ -66,6 +67,8 @@ public class Validator { + private static final Logger log = Logger.get(Validator.class); + private final String testUsername; private final String controlUsername; private final String testPassword; @@ -84,6 +87,7 @@ public class Validator private final int precision; private final int controlTeardownRetries; private final int testTeardownRetries; + private final boolean runTearDownOnResultMismatch; private Boolean valid; @@ -110,6 +114,7 @@ public Validator( boolean verboseResultsComparison, int controlTeardownRetries, int testTeardownRetries, + boolean runTearDownOnResultMismatch, QueryPair queryPair) { this.testUsername = requireNonNull(queryPair.getTest().getUsername(), "test username is null"); @@ -128,6 +133,7 @@ public Validator( this.verboseResultsComparison = verboseResultsComparison; this.controlTeardownRetries = controlTeardownRetries; this.testTeardownRetries = testTeardownRetries; + this.runTearDownOnResultMismatch = runTearDownOnResultMismatch; this.queryPair = requireNonNull(queryPair, "queryPair is null"); // Test and Control always have the same session properties. @@ -203,37 +209,84 @@ public boolean isDeterministic() private boolean validate() { - controlResult = executeQueryControl(); + boolean tearDownControl = true; + boolean tearDownTest = false; + try { + controlResult = executePreAndMainForControl(); - // query has too many rows. Consider blacklisting. - if (controlResult.getState() == State.TOO_MANY_ROWS) { - testResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); - return false; - } - // query failed in the control - if (controlResult.getState() != State.SUCCESS) { - testResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); - return true; - } + // query has too many rows. Consider blacklisting. + if (controlResult.getState() == State.TOO_MANY_ROWS) { + testResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); + return false; + } + // query failed in the control + if (controlResult.getState() != State.SUCCESS) { + testResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); + return true; + } - testResult = executeQueryTest(); + testResult = executePreAndMainForTest(); + tearDownTest = true; - if (controlResult.getState() != State.SUCCESS || testResult.getState() != State.SUCCESS) { - return false; + if (controlResult.getState() != State.SUCCESS || testResult.getState() != State.SUCCESS) { + return false; + } + + if (!checkCorrectness) { + return true; + } + + boolean matches = resultsMatch(controlResult, testResult, precision); + if (!matches && checkDeterministic) { + matches = checkForDeterministicAndRerunTestQueriesIfNeeded(); + } + if (!matches && !runTearDownOnResultMismatch) { + tearDownControl = false; + tearDownTest = false; + } + return matches; } + finally { + if (tearDownControl) { + tearDownControl(); + } + if (tearDownTest) { + tearDownTest(); + } + } + } - if (!checkCorrectness) { - return true; + private void tearDownControl() + { + QueryResult controlTearDownResult = executeTearDown( + queryPair.getControl(), + controlGateway, + controlUsername, + controlPassword, + controlTimeout, + controlPostQueryResults, + controlTeardownRetries); + if (controlTearDownResult.getState() != State.SUCCESS) { + log.warn("Control table teardown failed"); } + } - boolean matches = resultsMatch(controlResult, testResult, precision); - if (!matches && checkDeterministic) { - return checkForDeterministicAndRerunTestQueriesIfNeeded(); + private void tearDownTest() + { + QueryResult testTearDownResult = executeTearDown( + queryPair.getTest(), + testGateway, + testUsername, + testPassword, + testTimeout, + testPostQueryResults, + testTeardownRetries); + if (testTearDownResult.getState() != State.SUCCESS) { + log.warn("Test table teardown failed"); } - return matches; } - private static QueryResult tearDown(Query query, List postQueryResults, Function executor) + private QueryResult tearDown(Query query, List postQueryResults, Function executor) { postQueryResults.clear(); for (String postqueryString : query.getPostQueries()) { @@ -268,7 +321,7 @@ private boolean checkForDeterministicAndRerunTestQueriesIfNeeded() { // check if the control query is deterministic for (int i = 0; i < 3; i++) { - QueryResult results = executeQueryControl(); + QueryResult results = executePreAndMainForControl(); if (results.getState() != State.SUCCESS) { return false; } @@ -282,7 +335,7 @@ private boolean checkForDeterministicAndRerunTestQueriesIfNeeded() // Re-run the test query to confirm that the results don't match, in case there was caching on the test tier, // but require that it matches 3 times in a row to rule out a non-deterministic correctness bug. for (int i = 0; i < 3; i++) { - testResult = executeQueryTest(); + testResult = executePreAndMainForTest(); if (testResult.getState() != State.SUCCESS) { return false; } @@ -295,78 +348,90 @@ private boolean checkForDeterministicAndRerunTestQueriesIfNeeded() return true; } - private QueryResult executeQueryTest() + private QueryResult executePreAndMainForTest() + { + return executePreAndMain( + queryPair.getTest(), + testPreQueryResults, + testGateway, + testUsername, + testPassword, + testTimeout, + testPostQueryResults, + testTeardownRetries); + } + + private QueryResult executePreAndMainForControl() + { + return executePreAndMain( + queryPair.getControl(), + controlPreQueryResults, + controlGateway, + controlUsername, + controlPassword, + controlTimeout, + controlPostQueryResults, + controlTeardownRetries); + } + + private QueryResult executePreAndMain( + Query query, + List preQueryResults, + String gateway, + String username, + String password, + Duration timeout, + List postQueryResults, + int teardownRetries) { - Query query = queryPair.getTest(); - QueryResult queryResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); try { // startup - queryResult = setup(query, testPreQueryResults, testPrequery -> executeQuery(testGateway, testUsername, testPassword, queryPair.getTest(), testPrequery, testTimeout, sessionProperties)); + QueryResult queryResult = setup(query, preQueryResults, preQuery -> + executeQuery(gateway, username, password, query, preQuery, timeout, sessionProperties)); // if startup is successful -> execute query if (queryResult.getState() == State.SUCCESS) { - queryResult = executeQuery(testGateway, testUsername, testPassword, queryPair.getTest(), query.getQuery(), testTimeout, sessionProperties); + queryResult = executeQuery(gateway, username, password, query, query.getQuery(), timeout, sessionProperties); } + + return queryResult; } - finally { - int retry = 0; - QueryResult tearDownResult; - do { - tearDownResult = tearDown(query, testPostQueryResults, testPostquery -> executeQuery(testGateway, testUsername, testPassword, queryPair.getTest(), testPostquery, testTimeout, sessionProperties)); - if (tearDownResult.getState() == State.SUCCESS) { - break; - } - try { - TimeUnit.MINUTES.sleep(1); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - retry++; - } - while (retry < testTeardownRetries); - // if teardown is not successful the query fails - queryResult = tearDownResult.getState() == State.SUCCESS ? queryResult : tearDownResult; + catch (Exception e) { + executeTearDown(query, gateway, username, password, timeout, postQueryResults, teardownRetries); + throw e; } - return queryResult; } - private QueryResult executeQueryControl() + private QueryResult executeTearDown( + Query query, + String gateway, + String username, + String password, + Duration timeout, + List postQueryResults, + int teardownRetries) { - Query query = queryPair.getControl(); - QueryResult queryResult = new QueryResult(State.INVALID, null, null, null, null, ImmutableList.of()); - try { - // startup - queryResult = setup(query, controlPreQueryResults, controlPrequery -> executeQuery(controlGateway, controlUsername, controlPassword, queryPair.getControl(), controlPrequery, controlTimeout, sessionProperties)); - - // if startup is successful -> execute query - if (queryResult.getState() == State.SUCCESS) { - queryResult = executeQuery(controlGateway, controlUsername, controlPassword, queryPair.getControl(), query.getQuery(), controlTimeout, sessionProperties); + int attempt = 0; + QueryResult tearDownResult; + do { + tearDownResult = tearDown(query, postQueryResults, postQuery -> + executeQuery(gateway, username, password, query, postQuery, timeout, sessionProperties)); + if (tearDownResult.getState() == State.SUCCESS) { + break; } - } - finally { - int retry = 0; - QueryResult tearDownResult; - do { - tearDownResult = tearDown(query, controlPostQueryResults, controlPostquery -> executeQuery(controlGateway, controlUsername, controlPassword, queryPair.getControl(), controlPostquery, controlTimeout, sessionProperties)); - if (tearDownResult.getState() == State.SUCCESS) { - break; - } - try { - TimeUnit.MINUTES.sleep(1); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - retry++; + try { + TimeUnit.MINUTES.sleep(1); + log.info("Query teardown failed on attempt #%s, will sleep and retry", attempt); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; } - while (retry < controlTeardownRetries); - // if teardown is not successful the query fails - queryResult = tearDownResult.getState() == State.SUCCESS ? queryResult : tearDownResult; + attempt++; } - return queryResult; + while (attempt < teardownRetries); + return tearDown(query, postQueryResults, postQuery -> + executeQuery(gateway, username, password, query, postQuery, timeout, sessionProperties)); } public QueryPair getQueryPair() @@ -459,13 +524,13 @@ private QueryResult executeQuery(String url, String username, String password, Q exception = (Exception) e.getCause(); } State state = isPrestoQueryInvalid(e) ? State.INVALID : State.FAILED; - return new QueryResult(state, exception, null, null, null, null); + return new QueryResult(state, exception, null, null, queryId, ImmutableList.of()); } catch (VerifierException e) { - return new QueryResult(State.TOO_MANY_ROWS, e, null, null, null, null); + return new QueryResult(State.TOO_MANY_ROWS, e, null, null, queryId, ImmutableList.of()); } catch (UncheckedTimeoutException e) { - return new QueryResult(State.TIMEOUT, null, null, null, queryId, ImmutableList.of()); + return new QueryResult(State.TIMEOUT, e, null, null, queryId, ImmutableList.of()); } finally { executor.shutdownNow(); diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/Verifier.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/Verifier.java index 3e70506a8601f..f6c5a1d262a3e 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/Verifier.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/Verifier.java @@ -77,7 +77,7 @@ public int run(List queries) ExecutorService executor = newFixedThreadPool(threadCount); CompletionService completionService = new ExecutorCompletionService<>(executor); - int totalQueries = queries.size() * config.getSuiteRepetitions(); + int totalQueries = queries.size() * config.getSuiteRepetitions() * config.getQueryRepetitions(); log.info("Total Queries: %d", totalQueries); log.info("Whitelisted Queries: %s", Joiner.on(',').join(whitelist)); @@ -108,6 +108,7 @@ public int run(List queries) config.isVerboseResultsComparison(), config.getControlTeardownRetries(), config.getTestTeardownRetries(), + config.getRunTearDownOnResultMismatch(), query); completionService.submit(validator::valid, validator); queriesSubmitted++; diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifierConfig.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifierConfig.java index d289b3a9954f7..17a0e8e89045c 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifierConfig.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifierConfig.java @@ -83,6 +83,7 @@ public class VerifierConfig private boolean shadowWrites = true; private String shadowTestTablePrefix = "tmp_verifier_"; private String shadowControlTablePrefix = "tmp_verifier_"; + private boolean runTearDownOnResultMismatch; private Duration regressionMinCpuTime = new Duration(5, TimeUnit.MINUTES); @@ -756,4 +757,17 @@ public VerifierConfig setShadowControlTablePrefix(String prefix) this.shadowControlTablePrefix = prefix; return this; } + + public boolean getRunTearDownOnResultMismatch() + { + return runTearDownOnResultMismatch; + } + + @ConfigDescription("If set to false, temporary tables will not be dropped after checksum failure") + @Config("run-teardown-on-result-mismatch") + public VerifierConfig setRunTearDownOnResultMismatch(boolean runTearDownOnResultMismatch) + { + this.runTearDownOnResultMismatch = runTearDownOnResultMismatch; + return this; + } } diff --git a/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifyCommand.java b/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifyCommand.java index fe54d4718de63..5a5840e7da17c 100644 --- a/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifyCommand.java +++ b/presto-verifier/src/main/java/com/facebook/presto/verifier/VerifyCommand.java @@ -31,11 +31,13 @@ import com.facebook.presto.sql.tree.ShowCatalogs; import com.facebook.presto.sql.tree.ShowColumns; import com.facebook.presto.sql.tree.ShowFunctions; -import com.facebook.presto.sql.tree.ShowPartitions; import com.facebook.presto.sql.tree.ShowSchemas; import com.facebook.presto.sql.tree.ShowSession; import com.facebook.presto.sql.tree.ShowTables; import com.facebook.presto.sql.tree.Statement; +import com.facebook.presto.verifier.QueryRewriter.QueryRewriteException; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -63,10 +65,15 @@ import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; +import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletionService; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; import static com.facebook.presto.sql.parser.ParsingOptions.DecimalLiteralTreatment.AS_DOUBLE; import static com.facebook.presto.verifier.QueryType.CREATE; @@ -75,6 +82,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.throwIfUnchecked; import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.concurrent.Executors.newFixedThreadPool; +import static java.util.concurrent.TimeUnit.MINUTES; @Command(name = "verify", description = "verify") public class VerifyCommand @@ -233,7 +242,8 @@ protected List filterQueries(List queries) return queries; } - private static List rewriteQueries(SqlParser parser, VerifierConfig config, List queries) + @VisibleForTesting + static List rewriteQueries(SqlParser parser, VerifierConfig config, List queries) { QueryRewriter testRewriter = new QueryRewriter( parser, @@ -257,18 +267,51 @@ private static List rewriteQueries(SqlParser parser, VerifierConfig c config.getDoublePrecision(), config.getControlTimeout()); - ImmutableList.Builder builder = ImmutableList.builder(); + LOG.info("Rewriting %s queries using %s threads", queries.size(), config.getThreadCount()); + ExecutorService executor = newFixedThreadPool(config.getThreadCount()); + CompletionService> completionService = new ExecutorCompletionService<>(executor); + + List rewritten = new ArrayList<>(); for (QueryPair pair : queries) { - try { - Query testQuery = testRewriter.shadowQuery(pair.getTest()); - Query controlQuery = controlRewriter.shadowQuery(pair.getControl()); - builder.add(new QueryPair(pair.getSuite(), pair.getName(), testQuery, controlQuery)); - } - catch (SQLException | QueryRewriter.QueryRewriteException e) { - LOG.warn(e, "Failed to rewrite %s for shadowing. Skipping.", pair.getName()); + completionService.submit(() -> { + try { + return Optional.of(new QueryPair( + pair.getSuite(), + pair.getName(), + testRewriter.shadowQuery(pair.getTest()), + controlRewriter.shadowQuery(pair.getControl()))); + } + catch (QueryRewriteException | SQLException e) { + if (!config.isQuiet()) { + LOG.warn(e, "Failed to rewrite %s for shadowing. Skipping.", pair.getName()); + } + return Optional.empty(); + } + }); + } + + executor.shutdown(); + + try { + Stopwatch stopwatch = Stopwatch.createStarted(); + for (int n = 1; n <= queries.size(); n++) { + completionService.take().get().ifPresent(rewritten::add); + + if (!config.isQuiet() && (stopwatch.elapsed(MINUTES) > 0)) { + stopwatch.reset().start(); + LOG.info("Rewrite progress: %s valid, %s skipped, %.2f%% done", + rewritten.size(), + n - rewritten.size(), + (((double) n) / queries.size()) * 100); + } } } - return builder.build(); + catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Query rewriting failed", e); + } + + LOG.info("Rewrote %s queries into %s queries", queries.size(), rewritten.size()); + return rewritten; } private static List filterQueryTypes(SqlParser parser, VerifierConfig config, List queries) @@ -366,9 +409,6 @@ private static QueryType statementToQueryType(Statement statement) if (statement instanceof ShowFunctions) { return READ; } - if (statement instanceof ShowPartitions) { - return READ; - } if (statement instanceof ShowSchemas) { return READ; } diff --git a/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierConfig.java b/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierConfig.java index 379f62a4e59a0..b69e64ecd88fb 100644 --- a/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierConfig.java +++ b/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierConfig.java @@ -78,7 +78,8 @@ public void testDefaults() .setShadowTestTablePrefix("tmp_verifier_") .setShadowControlTablePrefix("tmp_verifier_") .setControlTeardownRetries(1) - .setTestTeardownRetries(1)); + .setTestTeardownRetries(1) + .setRunTearDownOnResultMismatch(false)); } @Test @@ -131,6 +132,7 @@ public void testExplicitPropertyMappings() .put("shadow-writes.control-table-prefix", "schema.tmp_") .put("control.teardown-retries", "5") .put("test.teardown-retries", "7") + .put("run-teardown-on-result-mismatch", "true") .build(); VerifierConfig expected = new VerifierConfig().setTestUsernameOverride("verifier-test") @@ -179,7 +181,8 @@ public void testExplicitPropertyMappings() .setShadowTestTablePrefix("tmp_") .setShadowControlTablePrefix("schema.tmp_") .setControlTeardownRetries(5) - .setTestTeardownRetries(7); + .setTestTeardownRetries(7) + .setRunTearDownOnResultMismatch(true); ConfigAssertions.assertFullMapping(properties, expected); } diff --git a/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierRewriteQueries.java b/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierRewriteQueries.java new file mode 100644 index 0000000000000..be8b4eb0ae4fc --- /dev/null +++ b/presto-verifier/src/test/java/com/facebook/presto/verifier/TestVerifierRewriteQueries.java @@ -0,0 +1,137 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.verifier; + +import com.facebook.presto.sql.parser.SqlParser; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import io.airlift.units.Duration; +import org.jdbi.v3.core.Handle; +import org.jdbi.v3.core.Jdbi; +import org.testng.annotations.AfterClass; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static com.facebook.presto.verifier.VerifyCommand.rewriteQueries; +import static org.testng.Assert.assertEquals; + +@Test(singleThreaded = true) +public class TestVerifierRewriteQueries +{ + private static final String CATALOG = "TEST_VERIFIER_REWRITE_QUERIES"; + private static final String SCHEMA = "PUBLIC"; + private static final String URL = "jdbc:h2:mem:" + CATALOG; + private static final String QUERY_SUITE = "TEST_SUITE"; + private static final String QUERY_NAME = "TEST_QUERY"; + private static final String[] QUERY_STRINGS = { + "INSERT INTO test_table (a, b, c) values (0, 0.1, 'a')", + "INSERT INTO test_table (a, b, c) values (1, 1.1, 'b')", + "INSERT INTO test_table (a, b, c) values (2, 2.2, 'c')", + "INSERT INTO test_table (a, b, c) values (3, 3.3, 'd')", + "INSERT INTO test_table (a, b, c) values (4, 4.4, 'e')", + "INSERT INTO test_table (a, b, c) values (5, 5.5, 'f')", + "INSERT INTO test_table (a, b, c) values (6, 6.6, 'g')", + "INSERT INTO test_table (a, b, c) values (7, 7.7, 'h')", + "INSERT INTO test_table (a, b, c) values (8, 8.8, 'i')", + "INSERT INTO test_table (a, b, c) values (9, 9.9, 'j')", + }; + + private final Handle handle; + private final SqlParser parser; + private final VerifierConfig config; + private final ImmutableList queryPairs; + + public TestVerifierRewriteQueries() + { + handle = Jdbi.open(URL); + handle.execute("CREATE TABLE \"test_table\" (a BIGINT, b DOUBLE, c VARCHAR)"); + parser = new SqlParser(); + + config = new VerifierConfig(); + config.setTestGateway(URL); + config.setTestTimeout(new Duration(10, TimeUnit.SECONDS)); + config.setControlTimeout(new Duration(10, TimeUnit.SECONDS)); + config.setShadowTestTablePrefix("tmp_verifier_test_"); + config.setShadowControlTablePrefix("tmp_verifier_control_"); + + ImmutableList.Builder builder = ImmutableList.builder(); + for (String queryString : QUERY_STRINGS) { + Query query = new Query( + CATALOG, + SCHEMA, + ImmutableList.of(), + queryString, + ImmutableList.of(), + null, + null, + ImmutableMap.of()); + builder.add(new QueryPair(QUERY_SUITE, QUERY_NAME, query, query)); + } + queryPairs = builder.build(); + } + + @AfterClass(alwaysRun = true) + public void close() + { + handle.close(); + } + + @Test + public void testSingleThread() + { + config.setControlGateway(URL); + config.setThreadCount(1); + List rewrittenQueries = rewriteQueries(parser, config, queryPairs); + assertEquals(rewrittenQueries.size(), queryPairs.size()); + } + + @Test + public void testMultipleThreads() + { + config.setControlGateway(URL); + config.setThreadCount(5); + List rewrittenQueries = rewriteQueries(parser, config, queryPairs); + assertEquals(rewrittenQueries.size(), queryPairs.size()); + } + + @Test + public void testQueryRewriteException() + { + config.setControlGateway(URL); + Query invalidQuery = new Query( + CATALOG, + SCHEMA, + ImmutableList.of("INSERT INTO test_table (a, b, c) values (10, 10.11, 'k')"), + "INSERT INTO test_table (a, b, c) values (11, 11.12, 'l')", + ImmutableList.of(), + null, + null, + ImmutableMap.of()); + List rewrittenQueries = rewriteQueries(parser, config, ImmutableList.builder() + .addAll(queryPairs) + .add(new QueryPair(QUERY_SUITE, QUERY_NAME, invalidQuery, invalidQuery)) + .build()); + assertEquals(rewrittenQueries.size(), queryPairs.size()); + } + + @Test + public void testSQLException() + { + config.setControlGateway("invalid:url"); + List rewrittenQueries = rewriteQueries(parser, config, queryPairs); + assertEquals(rewrittenQueries.size(), 0); + } +}